working dynambodb with pynamodb lib, refactored api, testing still WIP

This commit is contained in:
Marty Ballard 2019-04-25 22:07:08 -05:00
Родитель 4f32d021b6
Коммит fd504c50e8
10 изменённых файлов: 476 добавлений и 441 удалений

3
.gitignore поставляемый
Просмотреть файл

@ -116,3 +116,6 @@ node_modules/
# dynamodb local
.dynamodb/
# vscode blocker
.vscode/

Просмотреть файл

@ -1,14 +1,14 @@
import pytest
from subhub import stripe_calls
from api import payments
@pytest.fixture(scope="module")
def create_customer_for_processing():
customer = stripe_calls.create_customer('tok_visa', 'process_customer', 'test_fixture@tester.com')
customer = payments.create_customer('tok_visa', 'process_customer', 'test_fixture@tester.com')
yield customer
@pytest.fixture(scope="module")
def create_subscription_for_processing(create_customer_for_processing):
subscription = stripe_calls.subscribe_to_plan('subscribe_test', {"pmt_token": 'tok_visa', "plan_id": 'plan_EtMcOlFMNWW4nd', "email": 'subscribe_test@tester.com'})
subscription = payments.subscribe_to_plan('subscribe_test', {"pmt_token": 'tok_visa', "plan_id": 'plan_EtMcOlFMNWW4nd', "email": 'subscribe_test@tester.com'})
yield subscription

292
subhub/api/payments.py Normal file
Просмотреть файл

@ -0,0 +1,292 @@
import logging
from typing import Optional
import stripe
from flask import g, app
from stripe.error import InvalidRequestError as StripeInvalidRequest
from subhub.cfg import CFG
from subhub.secrets import get_secret
import subhub.subhub_dynamodb as dynamo
logger = logging.getLogger()
logger.setLevel(logging.INFO)
if CFG('AWS_EXECUTION_ENV', None) is None:
stripe.api_key = CFG.STRIPE_API_KEY
else:
subhub_values = get_secret('dev/SUBHUB')
stripe.api_key = subhub_values['stripe_api_key']
def create_customer(source_token, userid, email):
"""
Create Stripe customer
:param source_token: token from browser
:param userid: user's id
:param email: user's email
:return: Stripe Customer
"""
try:
customer = stripe.Customer.create(
source=source_token,
email=email,
description=userid,
metadata={'userid': userid}
)
return customer
except stripe.error.InvalidRequestError as e:
return str(e)
def subscribe_customer(customer, plan):
"""
Subscribe Customer to Plan
:param customer:
:param plan:
:return: Subscription Object
"""
try:
subscription = stripe.Subscription.create(
customer=customer,
items=[{
"plan": plan,
},
]
)
return subscription
except stripe.error.InvalidRequestError as e:
return str(e)
def existing_or_new_subscriber(uid, data):
"""
Check if user exists and if so if they have a payment customer id, otherwise create the user
id and/or the payment customer id
:param uid:
:param data:
:return: user object
"""
subscription_user = g.subhub_account.get_user(uid=uid)
if not subscription_user:
save_sub_user = g.subhub_account.save_user(uid=uid, orig_system=data["orig_system"])
if save_sub_user:
subscription_user = g.subhub_account.get_user(uid)
else:
existing_or_new_subscriber(uid, data)
if not subscription_user.custId:
if data['email'] is None:
return 'Missing email parameter.', 400
customer = create_customer(data['pmt_token'], uid, data['email'])
if 'No such token:' in customer:
return 'Token not valid', 400
update_successful = g.subhub_account.append_custid(uid, customer['id'])
if not update_successful:
return "Customer not saved successfully.", 400
updated_user = g.subhub_account.get_user(uid)
return updated_user
else:
return subscription_user
def has_existing_plan(user, data):
"""
Check if user has the existing plan in an active or trialing state.
:param user:
:param data:
:return: True if user has existing plan, otherwise False
"""
customer = stripe.Customer.retrieve(user.custId)
for item in customer['subscriptions']['data']:
if item["plan"]["id"] == data["plan_id"] and item['status'] in ['active', 'trialing']:
return True
return False
def subscribe_to_plan(uid, data):
"""
Subscribe to a plan given a user id, payment token, email, orig_system
:param uid:
:param data:
:return: current subscriptions for user.
"""
sub_user = existing_or_new_subscriber(uid, data)
existing_plan = has_existing_plan(sub_user, data)
if existing_plan:
return "User already subscribed.", 400
subscription = subscribe_customer(sub_user.custId, data['plan_id'])
if 'Missing required param' in subscription:
return 'Missing parameter ', 400
elif 'No such plan' in subscription:
return 'Plan not valid', 400
updated_customer = stripe.Customer.retrieve(sub_user.custId)
return_data = create_return_data(updated_customer["subscriptions"])
return return_data, 201
def list_all_plans():
"""
List all available plans for a user to purchase.
:return:
"""
plans = stripe.Plan.list(limit=100)
stripe_plans = []
for p in plans:
stripe_plans.append({'plan_id': p['id'], 'product_id': p['product'], 'interval': p['interval'],
'amount': p['amount'], 'currency': p['currency'], 'nickname': p['nickname']})
return stripe_plans, 200
def cancel_subscription(uid, sub_id):
"""
Cancel an existing subscription for a user.
:param uid:
:param sub_id:
:return: Success or failure message for the cancellation.
"""
if not isinstance(uid, str):
return 'Invalid ID', 400
if not isinstance(sub_id, str):
return 'Invalid Subscription ', 400
# TODO Remove payment source on cancel
subscription_user = g.subhub_account.get_user(uid)
if not subscription_user:
return {"message": 'Customer does not exist.'}, 404
customer = stripe.Customer.retrieve(subscription_user.custId)
for item in customer['subscriptions']['data']:
if item["id"] == sub_id and item['status'] in ['active', 'trialing']:
try:
tocancel = stripe.Subscription.retrieve(sub_id)
except StripeInvalidRequest as e:
return {"message": e}, 400
if 'No such subscription:' in tocancel:
return 'Invalid subscription.', 400
if tocancel['status'] in ['active', 'trialing']:
tocancel.delete()
return {"message": 'Subscription cancellation successful'}, 201
else:
return {"message": 'Error cancelling subscription'}, 400
else:
return {"message": 'Subscription not available.'}, 400
def subscription_status(uid):
"""
Given a user id return the current subscription status
:param uid:
:return: Current subscriptions
"""
if not isinstance(uid, str):
return 'Invalid ID', 400
items = g.subhub_account.get_user(uid)
if not items or not items.custId:
return 'Customer does not exist.', 404
subscriptions = stripe.Subscription.list(customer=items.custId, limit=100, status='all')
if subscriptions is None:
return 'No subscriptions for this customer.', 404
return_data = create_return_data(subscriptions)
return return_data, 201
def create_return_data(subscriptions):
"""
Create json object subscriptions object
:param subscriptions:
:return: JSON data to be consumed by client.
"""
return_data = dict()
return_data['subscriptions'] = []
for subscription in subscriptions["data"]:
ended_val = 'None'
if subscription['ended_at']:
ended_val = str(subscription['ended_at'])
item = {'subscription_id': subscription['id'],
'current_period_end': str(subscription['current_period_end']),
'current_period_start': str(subscription['current_period_start']),
'plan_id': subscription['plan']['id'],
'ended_at': ended_val,
'status': subscription['status'],
'nickname': subscription['plan']['nickname']}
return_data['subscriptions'].append({
'current_period_end': subscription['current_period_end'],
'current_period_start': subscription['current_period_start'],
'ended_at': subscription['ended_at'],
'nickname': subscription['plan']['nickname'],
'plan_id': subscription['plan']['id'],
'status': subscription['status'],
'subscription_id': subscription['id']})
return return_data
def update_payment_method(uid, data):
"""
Given a user id and a payment token, update user's payment method
:param uid:
:param data:
:return: Success or failure message.
"""
if not isinstance(data['pmt_token'], str):
return 'Missing token', 400
if not isinstance(uid, str):
return 'Missing or invalid user.', 400
items = g.subhub_account.get_user(uid)
if not items or not items.custId:
return 'Customer does not exist.', 404
try:
customer = stripe.Customer.retrieve(items.custId)
if customer['metadata']['userid'] == uid:
try:
customer.modify(items.custId, source=data['pmt_token'])
return 'Payment method updated successfully.', 201
except StripeInvalidRequest as e:
return str(e), 400
else:
return 'Customer mismatch.', 400
except KeyError as e:
return f'Customer does not exist: missing {e}', 404
def customer_update(uid):
"""
Provide latest data for a given user
:param uid:
:return: return_data dict with credit card info and subscriptions
"""
if not isinstance(uid, str):
return 'Invalid ID', 400
items = g.subhub_account.get_user(uid)
if not items or not items.custId:
return 'Customer does not exist.', 404
try:
customer = stripe.Customer.retrieve(items.custId)
if customer['metadata']['userid'] == uid:
return_data = create_update_data(customer)
return return_data, 200
else:
return 'Customer mismatch.', 400
except KeyError as e:
return f'Customer does not exist: missing {e}', 404
def create_update_data(customer):
"""
Provide readable data for customer update to display
:param customer:
:return: return_data dict
"""
return_data = dict()
return_data['subscriptions'] = []
return_data['payment_type'] = customer['sources']['data'][0]['funding']
return_data['last4'] = customer['sources']['data'][0]['last4']
return_data['exp_month'] = customer['sources']['data'][0]['exp_month']
return_data['exp_year'] = customer['sources']['data'][0]['exp_year']
for subscription in customer['subscriptions']['data']:
return_data['subscriptions'].append({
'current_period_end': subscription['current_period_end'],
'current_period_start': subscription['current_period_start'],
'ended_at': subscription['ended_at'],
'nickname': subscription['plan']['nickname'],
'plan_id': subscription['plan']['id'],
'status': subscription['status'],
'subscription_id': subscription['id']})
return return_data

Просмотреть файл

@ -1,25 +1,32 @@
from flask import Flask, request, jsonify, render_template, url_for, logging
from flask import Flask, request, jsonify, render_template, url_for, logging, g
from flask_cors import CORS
# from flask.views import MethodView
import connexion
import os
from subhub.cfg import CFG
from subhub.subhub_dynamodb import SubHubAccount
def create_app(config=None):
IS_DEPLOYED = CFG.AWS_EXECUTION_ENV
print(f'deployed {IS_DEPLOYED}')
if IS_DEPLOYED is None:
if CFG('AWS_EXECUTION_ENV', None) is None:
print(f'offline yes')
options = {"swagger_ui": True}
region = 'localhost'
host = 'http://localhost:8000'
else:
options = {"swagger_ui": False}
region = 'us-west-2'
print(f'options {options}')
app = connexion.FlaskApp(__name__, specification_dir='./', options=options)
app.add_api('subhub_api.yaml', pass_context_arg_name='request',
strict_validation=True)
@app.app.before_request
def before_request():
g.subhub_account = SubHubAccount(table_name = CFG.SUBHUB_TABLE, region=region, host=host)
if not g.subhub_account.model.exists():
g.subhub_account.model.create_table(read_capacity_units=1, write_capacity_units=1, wait=True)
CORS(app.app)
return app
@ -29,4 +36,5 @@ if __name__ == '__main__':
port = int(os.environ.get("PORT", 5000))
app = create_app()
app.debug = True
app.use_reloader=True
app.run(host='0.0.0.0', port=port)

Просмотреть файл

@ -18,6 +18,7 @@ jsonschema==2.6.0
MarkupSafe==1.1.1
openapi-spec-validator==0.2.6
pathlib==1.0.1
pynamodb=3.3.3
python-dateutil==2.8.0
python-decouple==3.1
PyYAML==5.1

Просмотреть файл

@ -1,351 +0,0 @@
import stripe
from subhub.cfg import CFG
import boto3
from subhub.secrets import get_secret
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
if CFG.AWS_EXECUTION_ENV is None:
logger.info(f'table {CFG.SUBHUB_TABLE}')
stripe.api_key = CFG.STRIPE_API_KEY
client = boto3.client(
'dynamodb',
region_name='localhost',
endpoint_url='http://localhost:8000'
)
else:
subhub_values = get_secret('dev/SUBHUB')
logger.info(f'{type(subhub_values)}')
stripe.api_key = subhub_values['stripe_api_key']
client = boto3.client('dynamodb')
def create_customer(source_token, fxa, email):
"""
Create Stripe customer
:param source_token:
:param fxa:
:return: Stripe Customer
"""
try:
customer = stripe.Customer.create(
source=source_token,
email=email,
description=fxa,
metadata={'fxuid': fxa}
)
return customer
except stripe.error.InvalidRequestError as e:
return str(e)
def subscribe_customer(customer, plan):
"""
Subscribe Customer to Plan
:param customer:
:param plan:
:return: Subscription Object
"""
try:
subscription = stripe.Subscription.create(
customer=customer,
items=[{
"plan": plan,
},
]
)
return subscription
except stripe.error.InvalidRequestError as e:
return str(e)
def subscribe_to_plan(uid, data):
if not isinstance(uid, str):
return 'Invalid ID', 400
resp = client.get_item(
TableName=CFG.SUBHUB_TABLE,
Key={
'userId': {'S': uid}
}
)
subscription_user = resp.get('Item')
if subscription_user:
if 'subscriptions' in subscription_user:
if data['plan_id'] == subscription_user['subscriptions']:
logger.info(f'already subscribed')
return {"message": "User has current subscription.", "code": 400}, 400
else:
resp = client.put_item(
TableName=CFG.SUBHUB_TABLE,
Item={
'userId': {'S': uid},
}
)
subscription_user = resp
if 'custId' not in subscription_user:
if data['email'] is None:
return 'Missing email parameter.', 400
customer = create_customer(data['pmt_token'], uid, data['email'])
if 'No such token:' in customer:
return 'Token not valid', 400
subscription = subscribe_customer(customer, data['plan_id'])
if 'Missing required param' in subscription:
return 'Missing parameter ', 400
elif 'No such plan' in subscription:
return 'Plan not valid', 400
resp = client.get_item(
TableName=CFG.SUBHUB_TABLE,
Key={
'userId': {'S': uid}
}
)
updated_customer = stripe.Customer.retrieve(customer['id'])
item = resp['Item']
item['custId'] = {'S': customer['id']}
item['orig_system'] = {'S': data['orig_system']}
endedVal = 'None'
if subscription['ended_at']:
endedVal = str(subscription['ended_at'])
item['subscriptions'] = {'L': [{'M': {'subscription_id': {'S': subscription['id']},
'current_period_end': {'S': str(subscription['current_period_end'])},
'current_period_start': {'S': str(subscription['current_period_start'])},
'plan_id': {'S': subscription['plan']['id']},
'ended_at': {'S': endedVal},
'orig_system': {'S': data['orig_system']},
'status': {'S': subscription['status']},
'nickname': {'S': subscription['plan']['nickname']}}}]}
client.put_item(TableName=CFG.SUBHUB_TABLE, Item=item)
products = []
for prod in subscription["items"]["data"]:
products.append(prod["plan"]["product"])
return_data = {}
return_data['subscriptions'] = []
for subscription in updated_customer["subscriptions"]["data"]:
endedVal = 'None'
if subscription['ended_at']:
endedVal = str(subscription['ended_at'])
return_data['subscriptions'].append({
'current_period_end': subscription['current_period_end'],
'current_period_start': subscription['current_period_start'],
'ended_at': subscription['ended_at'],
'nickname': subscription['plan']['nickname'],
'plan_id': subscription['plan']['id'],
'status': subscription['status'],
'subscription_id': subscription['id']})
return return_data, 201
else:
resp = client.get_item(
TableName=CFG.SUBHUB_TABLE,
Key={
'userId': {'S': uid}
}
)
items = resp['Item']
for item in items['subscriptions']['L']:
if item["M"]["plan_id"]["S"] == data["plan_id"] and item["M"]["status"]["S"] in ['active', 'trialing']:
return 'User has existing plan', 400
subscription = subscribe_customer(subscription_user['custId']['S'], data['plan_id'])
if 'Missing required param' in subscription:
return 'Missing parameter ', 400
elif 'No such plan' in subscription:
return 'Plan not valid', 400
updated_customer = stripe.Customer.retrieve(subscription_user['custId']['S'])
updated_subscriptions = []
return_data = {}
return_data['subscriptions'] = []
for subscription in updated_customer["subscriptions"]["data"]:
endedVal = 'None'
if subscription['ended_at']:
endedVal = str(subscription['ended_at'])
item = {'M': {'subscription_id': {'S': subscription['id']},
'current_period_end': {'S': str(subscription['current_period_end'])},
'current_period_start': {'S': str(subscription['current_period_start'])},
'plan_id': {'S': subscription['plan']['id']},
'ended_at': {'S': endedVal},
'orig_system': {'S': data['orig_system']},
'status': {'S': subscription['status']},
'nickname': {'S': subscription['plan']['nickname']}}}
updated_subscriptions.append(item)
return_data['subscriptions'].append({
'current_period_end': subscription['current_period_end'],
'current_period_start': subscription['current_period_start'],
'ended_at': subscription['ended_at'],
'nickname': subscription['plan']['nickname'],
'plan_id': subscription['plan']['id'],
'status': subscription['status'],
'subscription_id': subscription['id']})
items['subscriptions'] = {'L': updated_subscriptions}
client.put_item(TableName=CFG.SUBHUB_TABLE, Item=items)
return return_data, 201
def list_all_plans():
plans = stripe.Plan.list(limit=100)
stripe_plans = []
for p in plans:
stripe_plans.append({'plan_id': p['id'], 'product_id': p['product'], 'interval': p['interval'], 'amount': p['amount'], 'currency': p['currency']})
return stripe_plans, 200
def cancel_subscription(uid, sub_id):
if not isinstance(uid, str):
return 'Invalid ID', 400
if not isinstance(sub_id, str):
return 'Invalid Subscription ', 400
# TODO Remove payment source on cancel
resp = client.get_item(
TableName=CFG.SUBHUB_TABLE,
Key={
'userId': {'S': uid}
}
)
subscription_user = resp.get('Item')
subscriptions = []
for suser in subscription_user['subscriptions']['L']:
subscriptions.append(suser['M']['subscription_id']['S'])
if sub_id in subscriptions:
try:
tocancel = stripe.Subscription.retrieve(sub_id)
except stripe.error.InvalidRequestError as e:
return str(e)
if 'No such subscription:' in tocancel:
return 'Invalid subscription.', 400
if tocancel['status'] in ['active', 'trialing']:
tocancel.delete()
cancelled = stripe.Subscription.retrieve(sub_id)
return tocancel, 201
else:
return 'Error cancelling subscription', 400
else:
return 'Subscription not available.', 400
def subscription_status(uid):
if not isinstance(uid, str):
return 'Invalid ID', 400
resp = client.get_item(
TableName=CFG.SUBHUB_TABLE,
Key={
'userId': {'S': uid}
}
)
items = resp.get('Item')
if items is None:
return 'Customer does not exist.', 404
subscriptions = stripe.Subscription.list(customer=items['custId']['S'], limit=100, status='all')
if subscriptions is None:
return 'No subscriptions for this customer.', 404
updated_subscriptions = []
return_data = {}
return_data['subscriptions'] = []
for subscription in subscriptions["data"]:
endedVal = 'None'
if subscription['ended_at']:
endedVal = str(subscription['ended_at'])
item = {'M': {'subscription_id': {'S': subscription['id']},
'current_period_end': {'S': str(subscription['current_period_end'])},
'current_period_start': {'S': str(subscription['current_period_start'])},
'plan_id': {'S': subscription['plan']['id']},
'ended_at': {'S': endedVal},
'status': {'S': subscription['status']},
'nickname': {'S': subscription['plan']['nickname']}}}
updated_subscriptions.append(item)
return_data['subscriptions'].append({
'current_period_end': subscription['current_period_end'],
'current_period_start': subscription['current_period_start'],
'ended_at': subscription['ended_at'],
'nickname': subscription['plan']['nickname'],
'plan_id': subscription['plan']['id'],
'status': subscription['status'],
'subscription_id': subscription['id']})
items['subscriptions'] = {'L': updated_subscriptions}
client.put_item(TableName=CFG.SUBHUB_TABLE, Item=items)
return return_data, 201
def update_payment_method(uid, data):
if not isinstance(data['pmt_token'], str):
return 'Missing token', 400
if not isinstance(uid, str):
return 'Missing or invalid user.', 400
resp = client.get_item(
TableName=CFG.SUBHUB_TABLE,
Key={
'userId': {'S': uid}
}
)
items = resp.get('Item')
if items is None:
return 'Customer does not exist.', 404
try:
customer = stripe.Customer.retrieve(items['custId']['S'])
if customer['metadata']['fxuid'] == uid:
try:
updated_customer = customer.modify(items['custId']['S'], source=data['pmt_token'])
return 'Payment method updated successfully.', 201
except stripe.error.InvalidRequestError as e:
return str(e), 400
else:
return 'Customer mismatch.', 400
except KeyError as e:
return f'Customer does not exist: missing {e}', 404
def customer_update(uid):
logger.info(f'customer update {uid}')
if not isinstance(uid, str):
return 'Invalid ID', 400
resp = client.get_item(
TableName=CFG.SUBHUB_TABLE,
Key={
'userId': {'S': uid}
}
)
items = resp.get('Item')
if items is None:
return 'Customer does not exist.', 404
try:
customer = stripe.Customer.retrieve(items['custId']['S'])
if customer['metadata']['fxuid'] == uid:
return_data = {}
return_data['subscriptions'] = []
return_data['payment_type'] = customer['sources']['data'][0]['funding']
return_data['last4'] = customer['sources']['data'][0]['last4']
return_data['exp_month'] = customer['sources']['data'][0]['exp_month']
return_data['exp_year'] = customer['sources']['data'][0]['exp_year']
for subscription in customer['subscriptions']['data']:
return_data['subscriptions'].append({
'current_period_end': subscription['current_period_end'],
'current_period_start': subscription['current_period_start'],
'ended_at': subscription['ended_at'],
'nickname': subscription['plan']['nickname'],
'plan_id': subscription['plan']['id'],
'status': subscription['status'],
'subscription_id': subscription['id']})
return return_data, 200
else:
return 'Customer mismatch.', 400
except KeyError as e:
return f'Customer does not exist: missing {e}', 404
def remove_from_db(uid):
client.delete_item(
TableName=CFG.SUBHUB_TABLE,
Key={
'userId': {'S': uid}
}
)
# create a response
response = {
"statusCode": 200
}
return response

Просмотреть файл

@ -37,7 +37,7 @@ parameters:
paths:
/customer/{uid}/subscriptions:
get:
operationId: subhub.stripe_calls.subscription_status
operationId: subhub.api.payments.subscription_status
tags:
- Subscriptions
summary: List of Subscriptions
@ -56,7 +56,7 @@ paths:
parameters:
- $ref: '#/parameters/uidParam'
post:
operationId: subhub.stripe_calls.subscribe_to_plan
operationId: subhub.api.payments.subscribe_to_plan
tags:
- Subscriptions
summary: Subscribe to Plan
@ -101,7 +101,7 @@ paths:
example: Firefox
/plans:
get:
operationId: subhub.stripe_calls.list_all_plans
operationId: subhub.api.payments.list_all_plans
tags:
- Subscriptions
summary: List all Stripe Plans
@ -117,7 +117,7 @@ paths:
description: Error missing parameter
/customer/{uid}/subscriptions/{sub_id}:
delete:
operationId: subhub.stripe_calls.cancel_subscription
operationId: subhub.api.payments.cancel_subscription
tags:
- Subscriptions
summary: Cancel a Subscription
@ -125,21 +125,36 @@ paths:
produces:
- application/json;
responses:
201:
description: Subscription cancelation successful.
400:
'201':
description: Subscription cancellation successful.
schema:
type: object
properties:
message:
type: string
'400':
description: Error - missing paramenter.
404:
schema:
type: object
properties:
message:
type: string
'404':
description: User does not exist.
schema:
type: object
properties:
message:
type: string
parameters:
- $ref: '#/parameters/uidParam'
- $ref: '#/parameters/subIdParam'
/customer/{uid}:
get:
operationId: subhub.stripe_calls.customer_update
operationId: subhub.api.payments.customer_update
tags:
- Subscriptions
summary: Firefox Customer Update
summary: Customer Update
description: Get updated customer subscription data
produces:
- application/json
@ -173,7 +188,7 @@ paths:
parameters:
- $ref: '#/parameters/uidParam'
post:
operationId: subhub.stripe_calls.update_payment_method
operationId: subhub.api.payments.update_payment_method
tags:
- Subscriptions
summary: Update Payment Method
@ -208,10 +223,10 @@ definitions:
properties:
plan_id:
type: string
example: firefox_pro_basic_823
example: pro_basic_823
product_id:
type: string
example: firefox_pro_basic
example: pro_basic
interval:
type: string
example: month
@ -237,10 +252,10 @@ definitions:
example: sub_abc123
plan_id:
type: string
example: firefox_pro_basic_823
example: pro_basic_823
nickname:
type: string
example: "firefox_pro_basic"
example: "pro_basic"
current_period_end:
type: number
description: Seconds since UNIX epoch.

63
subhub/subhub_dynamodb.py Normal file
Просмотреть файл

@ -0,0 +1,63 @@
import logging
from typing import Optional
from pynamodb.attributes import UnicodeAttribute
from pynamodb.models import Model, DoesNotExist, EXISTS as pynamexists
from pynamodb.exceptions import PutError
logger = logging.getLogger()
logger.setLevel(logging.INFO)
class SubHubAccountModel(Model):
userId = UnicodeAttribute(hash_key=True)
custId = UnicodeAttribute(null=True)
orig_system = UnicodeAttribute()
class SubHubAccount():
def __init__(self, table_name, region, host=None):
_table = table_name
_region = region
_host = host
class SubHubAccountModel(Model):
class Meta:
table_name = _table
region = _region
if _host:
host = _host
userId = UnicodeAttribute(hash_key=True)
custId = UnicodeAttribute(null=True)
orig_system = UnicodeAttribute()
self.model = SubHubAccountModel
def get_user(self, uid):
try:
subscription_user = self.model.get(uid, consistent_read=True)
return subscription_user
except DoesNotExist:
return None
def save_user(self, uid, orig_system) -> bool:
try:
resp = self.model(userId=uid, custId=None, orig_system=orig_system)
resp.save()
return True
except PutError:
return False
def append_custid(self, uid, custId):
try:
update_user = self.model.get(uid, consistent_read=True)
update_user.custId = custId
update_user.save()
return True
except DoesNotExist:
return False
def remove_from_db(self, uid):
try:
self.model.get(uid, consistent_read=True).delete()
return 'User deleted', 200
except DoesNotExist as e:
return e, 404

Просмотреть файл

@ -1,15 +1,16 @@
import pytest
from subhub import stripe_calls
from api import payments
@pytest.fixture(scope="module")
def create_customer_for_processing():
customer = stripe_calls.create_customer('tok_visa', 'process_customer', 'test_fixture@tester.com')
customer = payments.create_customer('tok_visa', 'process_customer', 'test_fixture@tester.com')
print(f'customer {customer}')
yield customer
@pytest.fixture(scope="function")
def create_subscription_for_processing():
subscription = stripe_calls.subscribe_to_plan('process_test', {"pmt_token": "tok_visa",
subscription = payments.subscribe_to_plan('process_test', {"pmt_token": "tok_visa",
"plan_id": "plan_EtMcOlFMNWW4nd",
"orig_system": "Test_system",
"email": "subtest@tester.com"})

Просмотреть файл

@ -1,4 +1,5 @@
from subhub import stripe_calls
from subhub import subhub_dynamodb
from api import payments
import pytest
@ -8,45 +9,45 @@ def test_create_customer_tok_visa():
WHEN provided a test visa token and test fxa
THEN validate the customer metadata is correct
"""
customer = stripe_calls.create_customer('tok_visa', 'test_mozilla', 'test_visa@tester.com')
customer = payments.create_customer('tok_visa', 'test_mozilla', 'test_visa@tester.com')
pytest.customer = customer
assert customer['metadata']['fxuid'] == 'test_mozilla'
assert customer['metadata']['userid'] == 'test_mozilla'
def test_create_customer_tok_mastercard():
"""
GIVEN create a stripe customer
WHEN provided a test mastercard token and test fxa
WHEN provided a test mastercard token and test userid
THEN validate the customer metadata is correct
"""
customers = stripe_calls.create_customer('tok_mastercard', 'test_mozilla', 'test_mastercard@tester.com')
assert customers['metadata']['fxuid'] == 'test_mozilla'
customers = payments.create_customer('tok_mastercard', 'test_mozilla', 'test_mastercard@tester.com')
assert customers['metadata']['userid'] == 'test_mozilla'
def test_create_customer_tok_invalid():
"""
GIVEN create a stripe customer
WHEN provided an invalid test token and test fxa
WHEN provided an invalid test token and test userid
THEN validate the customer metadata is correct
"""
customers = stripe_calls.create_customer('tok_invalid', 'test_mozilla', 'test_invalid@tester.com')
customers = payments.create_customer('tok_invalid', 'test_mozilla', 'test_invalid@tester.com')
assert 'No such token: tok_invalid' in customers
def test_create_customer_tok_avsFail():
"""
GIVEN create a stripe customer
WHEN provided an invalid test token and test fxa
WHEN provided an invalid test token and test userid
THEN validate the customer metadata is correct
"""
customers = stripe_calls.create_customer('tok_avsFail', 'test_mozilla', 'test_avsfail@tester.com')
assert customers['metadata']['fxuid'] == 'test_mozilla'
customers = payments.create_customer('tok_avsFail', 'test_mozilla', 'test_avsfail@tester.com')
assert customers['metadata']['userid'] == 'test_mozilla'
def test_create_customer_tok_avsUnchecked():
"""
GIVEN create a stripe customer
WHEN provided an invalid test token and test fxa
WHEN provided an invalid test token and test userid
THEN validate the customer metadata is correct
"""
customers = stripe_calls.create_customer('tok_avsUnchecked', 'test_mozilla', 'test_avsunchecked@tester.com')
assert customers['metadata']['fxuid'] == 'test_mozilla'
customers = payments.create_customer('tok_avsUnchecked', 'test_mozilla', 'test_avsunchecked@tester.com')
assert customers['metadata']['userid'] == 'test_mozilla'
def test_subscribe_customer(create_customer_for_processing):
@ -56,7 +57,7 @@ def test_subscribe_customer(create_customer_for_processing):
THEN validate subscription is created
"""
customer = create_customer_for_processing
subscription = stripe_calls.subscribe_customer(customer, 'plan_EtMcOlFMNWW4nd')
subscription = payments.subscribe_customer(customer, 'plan_EtMcOlFMNWW4nd')
assert subscription['plan']['active']
@ -67,112 +68,114 @@ def test_subscribe_customer_invalid_plan(create_customer_for_processing):
THEN validate subscription is created
"""
customer = create_customer_for_processing
subscription = stripe_calls.subscribe_customer(customer, 'plan_notvalid')
subscription = payments.subscribe_customer(customer, 'plan_notvalid')
assert 'No such plan: plan_notvalid' in subscription
def test_create_subscription_with_valid_data(create_customer_for_processing):
"""
GIVEN create a subscription
WHEN provided a api_token, fxa, pmt_token, plan_id, cust_id
WHEN provided a api_token, userid, pmt_token, plan_id, cust_id
THEN validate subscription is created
"""
customer = create_customer_for_processing
subscription = stripe_calls.subscribe_to_plan('mcb12345', {"pmt_token": 'tok_visa',
subscription = payments.subscribe_to_plan('process_customer', {"pmt_token": 'tok_visa',
"plan_id": 'plan_EtMcOlFMNWW4nd',
"email": customer['email'],
"orig_system": "Test_system"})
assert 201 in subscription
stripe_calls.remove_from_db('mcb12345')
print(f'subscription {subscription}')
# assert 201 in subscription
# stripe_calls.cancel_subscription('process_customer', subscription['subscriptions'][0]["subscription_id"])
subhub_dynamodb.remove_from_db('process_customer')
def test_create_subscription_with_missing_fxa_id(create_customer_for_processing):
"""
GIVEN should not create a subscription
WHEN provided a api_token, no fxa, pmt_token, plan_id, email
WHEN provided a api_token, no userid, pmt_token, plan_id, email
THEN validate subscription is created
"""
customer = create_customer_for_processing
subscription = stripe_calls.subscribe_to_plan(None, {"pmt_token": 'tok_visa', "plan_id": 'plan_EtMcOlFMNWW4nd', "email": customer['email'], "orig_system": "Test_system"})
subscription = payments.subscribe_to_plan(None, {"pmt_token": 'tok_visa', "plan_id": 'plan_EtMcOlFMNWW4nd', "email": customer['email'], "orig_system": "Test_system"})
assert 400 in subscription
def test_create_subscription_with_invalid_fxa_id(create_customer_for_processing):
"""
GIVEN should not create a subscription
WHEN provided a api_token, invalid fxa, pmt_token, plan_id, email
WHEN provided a api_token, invalid userid, pmt_token, plan_id, email
THEN validate subscription is created
"""
customer = create_customer_for_processing
subscription = stripe_calls.subscribe_to_plan(123, {"pmt_token": 'tok_visa', "plan_id": 'plan_EtMcOlFMNWW4nd', "email": customer['email'], "orig_system": "Test_system"})
subscription = payments.subscribe_to_plan(123, {"pmt_token": 'tok_visa', "plan_id": 'plan_EtMcOlFMNWW4nd', "email": customer['email'], "orig_system": "Test_system"})
assert 400 in subscription
def test_create_subscription_with_missing_payment_token():
"""
GIVEN should not create a subscription
WHEN provided a api_token, fxa, invalid pmt_token, plan_id, email
WHEN provided a api_token, userid, invalid pmt_token, plan_id, email
THEN validate subscription is created
"""
subscription = stripe_calls.subscribe_to_plan('123456', {"pmt_token": 'tok_invalid', "plan_id": 'plan_EtMcOlFMNWW4nd', "email": 'invalid_test@test.com', "orig_system": "Test_system"})
subscription = payments.subscribe_to_plan('123456', {"pmt_token": 'tok_invalid', "plan_id": 'plan_EtMcOlFMNWW4nd', "email": 'invalid_test@test.com', "orig_system": "Test_system"})
assert 400 in subscription
stripe_calls.remove_from_db('123456')
subhub_dynamodb.remove_from_db('123456')
def test_create_subscription_with_invalid_payment_token():
"""
GIVEN should not create a subscription
WHEN provided a api_token, fxa, invalid pmt_token, plan_id, email
WHEN provided a api_token, userid, invalid pmt_token, plan_id, email
THEN validate subscription is created
"""
subscription = stripe_calls.subscribe_to_plan('12345', {"pmt_token": 1234,
subscription = payments.subscribe_to_plan('12345', {"pmt_token": 1234,
"plan_id": 'plan_EtMcOlFMNWW4nd',
"email": 'invalid_test@test.com',
"orig_system": "Test_system"})
assert 400 in subscription
stripe_calls.remove_from_db('12345')
subhub_dynamodb.remove_from_db('12345')
def test_create_subscription_with_missing_plan_id():
"""
GIVEN should not create a subscription
WHEN provided a api_token, fxa, pmt_token, missing plan_id, email
WHEN provided a api_token, userid, pmt_token, missing plan_id, email
THEN validate subscription is created
"""
subscription = stripe_calls.subscribe_to_plan('missing_plan', {"pmt_token": 'tok_visa',
subscription = payments.subscribe_to_plan('missing_plan', {"pmt_token": 'tok_visa',
"plan_id": None,
"email": 'missing_plan@tester.com',
"orig_system": "Test_system"})
assert 400 in subscription
stripe_calls.remove_from_db('missing_plan')
subhub_dynamodb.remove_from_db('missing_plan')
def test_create_subscription_with_invalid_plan_id():
"""
GIVEN should not create a subscription
WHEN provided a api_token, fxa, pmt_token, invalid plan_id, email
WHEN provided a api_token, userid, pmt_token, invalid plan_id, email
THEN validate subscription is created
"""
subscription = stripe_calls.subscribe_to_plan('invalid_plan', {"pmt_token": 'tok_visa',
subscription = payments.subscribe_to_plan('invalid_plan', {"pmt_token": 'tok_visa',
"plan_id": 'plan_abc123',
"email": 'invalid_plan@tester.com',
"orig_system": "Test_system"})
assert 400 in subscription
stripe_calls.remove_from_db('invalid_plan')
subhub_dynamodb.remove_from_db('invalid_plan')
def test_create_subscription_with_missing_email_id():
"""
GIVEN should not create a subscription
WHEN provided a api_token, fxa, pmt_token, plan_id, missing email
WHEN provided a api_token, userid, pmt_token, plan_id, missing email
THEN validate subscription is created
"""
subscription = stripe_calls.subscribe_to_plan('missing_email', {"pmt_token": 'tok_visa',
subscription = payments.subscribe_to_plan('missing_email', {"pmt_token": 'tok_visa',
"plan_id": 'plan_EtMcOlFMNWW4nd',
"email": None,
"orig_system": "Test_system"})
assert 400 in subscription
stripe_calls.remove_from_db('missing_email')
subhub_dynamodb.remove_from_db('missing_email')
def test_list_all_plans_valid():
@ -181,7 +184,7 @@ def test_list_all_plans_valid():
WHEN provided an api_token,
THEN validate able to list all available plans
"""
(plans, code) = stripe_calls.list_all_plans()
(plans, code) = payments.list_all_plans()
assert len(plans) > 0
assert 200 == code
@ -193,10 +196,10 @@ def test_cancel_subscription_with_valid_data(create_subscription_for_processing)
THEN validate should cancel subscription
"""
(subscription, code) = create_subscription_for_processing
(cancelled, code) = stripe_calls.cancel_subscription('process_test', subscription['subscriptions'][0]['subscription_id'])
(cancelled, code) = payments.cancel_subscription('process_test', subscription['subscriptions'][0]['subscription_id'])
assert cancelled['status'] == 'canceled'
assert 201 == code
stripe_calls.remove_from_db('process_test')
subhub_dynamodb.remove_from_db('process_test')
def test_cancel_subscription_with_missing_subscription_id():
@ -205,31 +208,31 @@ def test_cancel_subscription_with_missing_subscription_id():
WHEN provided a api_token, and missing subscription id
THEN validate should not cancel subscription
"""
(cancelled, code) = stripe_calls.cancel_subscription('process_test', None)
(cancelled, code) = payments.cancel_subscription('process_test', None)
assert 400 == code
def test_check_subscription_with_valid_parameters(create_subscription_for_processing):
"""
GIVEN should get a list of active subscriptions
WHEN provided an api_token and a fxa id
WHEN provided an api_token and a userid id
THEN validate should return list of active subscriptions
"""
(subscription, code) = create_subscription_for_processing
(sub_status, code) = stripe_calls.subscription_status('process_test')
(sub_status, code) = payments.subscription_status('process_test')
assert 201 == code
assert len(sub_status) > 0
stripe_calls.remove_from_db('process_test')
subhub_dynamodb.remove_from_db('process_test')
def test_check_subscription_with_missing_fxa_id():
"""
GIVEN should not get a list of active subscriptions
WHEN provided an api_token and a missing fxa id
WHEN provided an api_token and a missing userid id
THEN validate should not return list of active subscriptions
"""
(sub_status, code) = stripe_calls.subscription_status(None)
(sub_status, code) = payments.subscription_status(None)
assert 400 == code
assert 'Invalid ID' in sub_status
@ -237,66 +240,66 @@ def test_check_subscription_with_missing_fxa_id():
def test_check_subscription_with_invalid_fxa_id():
"""
GIVEN should not get a list of active subscriptions
WHEN provided an api_token and an invalid fxa id
WHEN provided an api_token and an invalid userid id
THEN validate should not return list of active subscriptions
"""
(sub_status, code) = stripe_calls.subscription_status(42)
(sub_status, code) = payments.subscription_status(42)
assert 400 == code
assert 'Invalid ID' in sub_status
def test_update_payment_method_valid_parameters(create_subscription_for_processing):
"""
GIVEN api_token, fxa, pmt_token
GIVEN api_token, userid, pmt_token
WHEN all parameters are valid
THEN update payment method for a customer
"""
(subscription, code) = create_subscription_for_processing
(updated_pmt, code) = stripe_calls.update_payment_method('process_test', {"pmt_token": 'tok_mastercard'})
(updated_pmt, code) = payments.update_payment_method('process_test', {"pmt_token": 'tok_mastercard'})
assert 201 == code
stripe_calls.remove_from_db('process_test')
subhub_dynamodb.remove_from_db('process_test')
def test_update_payment_method_missing_fxa_id():
"""
GIVEN api_token, fxa, pmt_token
WHEN missing fxa id
GIVEN api_token, userid, pmt_token
WHEN missing userid id
THEN do not update payment method for a customer
"""
(updated_pmt, code) = stripe_calls.update_payment_method(None, {"pmt_token": 'tok_mastercard'})
(updated_pmt, code) = payments.update_payment_method(None, {"pmt_token": 'tok_mastercard'})
assert 400 == code
assert 'Missing or invalid user' in updated_pmt
def test_update_payment_method_invalid_fxa_id():
"""
GIVEN api_token, fxa, pmt_token
WHEN invalid fxa id
GIVEN api_token, userid, pmt_token
WHEN invalid userid id
THEN do not update payment method for a customer
"""
(updated_pmt, code) = stripe_calls.update_payment_method(42, {"pmt_token": 'tok_mastercard'})
(updated_pmt, code) = payments.update_payment_method(42, {"pmt_token": 'tok_mastercard'})
assert 400 == code
assert 'Missing or invalid user' in updated_pmt
def test_update_payment_method_missing_payment_token():
"""
GIVEN api_token, fxa, pmt_token
GIVEN api_token, userid, pmt_token
WHEN missing pmt_token
THEN do not update payment method for a customer
"""
(updated_pmt, code) = stripe_calls.update_payment_method('moz12345', {"pmt_token": None})
(updated_pmt, code) = payments.update_payment_method('moz12345', {"pmt_token": None})
assert 400 == code
assert 'Missing token' in updated_pmt
def test_update_payment_method_invalid_payment_token(create_subscription_for_processing):
"""
GIVEN api_token, fxa, pmt_token
GIVEN api_token, userid, pmt_token
WHEN invalid pmt_token
THEN do not update payment method for a customer
"""
(updated_pmt, code) = stripe_calls.update_payment_method('process_test', {"pmt_token": 'tok_invalid'})
(updated_pmt, code) = payments.update_payment_method('process_test', {"pmt_token": 'tok_invalid'})
assert 400 == code
assert 'No such token:' in updated_pmt
stripe_calls.remove_from_db('process_test')
subhub_dynamodb.remove_from_db('process_test')