Merge remote-tracking branch 'remotes/origin/dev' into add-device

# Conflicts:
#	api/account/account_api/api.py
#	api/account/tests/features/steps/update_membership.py
#	shared/selene/api/endpoints/account.py
#	shared/selene/data/account/repository/sql/get_active_membership_by_account_id.sql
pull/79/head
Chris Veilleux 2019-03-18 17:05:08 -05:00
commit c851624256
9 changed files with 83 additions and 47 deletions

View File

@ -64,7 +64,8 @@ def _add_account(context, db):
type='Monthly Membership', type='Monthly Membership',
start_date=date.today(), start_date=date.today(),
payment_method='Stripe', payment_method='Stripe',
payment_account_id='foo' payment_account_id='foo',
payment_id='bar'
), ),
agreements=[ agreements=[
AccountAgreement(type=PRIVACY_POLICY, accept_date=date.today()) AccountAgreement(type=PRIVACY_POLICY, accept_date=date.today())

View File

@ -1,10 +1,11 @@
import json import json
from datetime import date
from behave import given, when, then from behave import given, when, then
from hamcrest import assert_that, equal_to, starts_with, none from hamcrest import assert_that, equal_to, starts_with, none
from selene.api.testing import generate_access_token, generate_refresh_token from selene.api.testing import generate_access_token, generate_refresh_token
from selene.data.account import AccountRepository from selene.data.account import AccountRepository, Account, AccountAgreement, PRIVACY_POLICY
from selene.util.db import get_db_connection from selene.util.db import get_db_connection
new_account_request = dict( new_account_request = dict(
@ -14,11 +15,13 @@ new_account_request = dict(
login=dict( login=dict(
federatedEmail=None, federatedEmail=None,
userEnteredEmail='test@mycroft.ai', userEnteredEmail='test@mycroft.ai',
password='test' password='12345678'
), ),
support=dict( support=dict(
openDataset=True, openDataset=True,
membership=None membership='Maybe Later',
paymentMethod=None,
paymentAccountId=None
) )
) )
@ -40,15 +43,20 @@ yearly_membership = {
@given('a user with a free account') @given('a user with a free account')
def create_account_free_account(context): def create_account(context):
context.client.post( context.account = Account(
'/api/account', email_address='test@mycroft.ai',
data=json.dumps(new_account_request), username='test',
content_type='application_json' refresh_tokens=[],
membership=None,
agreements=[
AccountAgreement(type=PRIVACY_POLICY, accept_date=date.today())
]
) )
with get_db_connection(context.client_config['DB_CONNECTION_POOL']) as db: with get_db_connection(context.client_config['DB_CONNECTION_POOL']) as db:
account = AccountRepository(db).get_account_by_email(new_account_request['login']['userEnteredEmail']) acct_repository = AccountRepository(db)
context.account = account account_id = acct_repository.add(context.account, 'foo')
context.account.id = account_id
generate_access_token(context) generate_access_token(context)
generate_refresh_token(context) generate_refresh_token(context)

View File

@ -8,6 +8,7 @@ CREATE TABLE account.account_membership (
membership_ts_range tsrange NOT NULL, membership_ts_range tsrange NOT NULL,
payment_method payment_method_enum NOT NULL, payment_method payment_method_enum NOT NULL,
payment_account_id text NOT NULL, payment_account_id text NOT NULL,
payment_id text NOT NULL,
insert_ts TIMESTAMP NOT NULL insert_ts TIMESTAMP NOT NULL
DEFAULT CURRENT_TIMESTAMP, DEFAULT CURRENT_TIMESTAMP,
EXCLUDE USING gist (account_id WITH =, membership_ts_range with &&), EXCLUDE USING gist (account_id WITH =, membership_ts_range with &&),

View File

@ -207,12 +207,19 @@ class AccountEndpoint(SeleneEndpoint):
payment_token = self.request_data['support']['paymentToken'] payment_token = self.request_data['support']['paymentToken']
email = self.request_data['login']['userEnteredEmail'] email = self.request_data['login']['userEnteredEmail']
plan = self._get_plan(membership_type).stripe_plan plan = self._get_plan(membership_type).stripe_plan
payment_account_id, start = self._create_stripe_subscription(None, payment_token, email, plan) payment_account_id, start, subscription_id = self._create_stripe_subscription(
None,
payment_token,
email,
plan
)
membership = AccountMembership( membership = AccountMembership(
type=membership_type, type=membership_type,
start_date=date.today(), start_date=date.today(),
payment_method=self.request_data['support']['paymentMethod'], payment_method=self.request_data['support']['paymentMethod'],
payment_account_id=payment_account_id payment_account_id=payment_account_id,
payment_id=subscription_id
) )
account = Account( account = Account(
email_address=email_address, email_address=email_address,
@ -236,10 +243,9 @@ class AccountEndpoint(SeleneEndpoint):
customer_id = customer.id customer_id = customer.id
subscription = stripe.Subscription.create(customer=customer_id, items=[{'plan': plan}]) subscription = stripe.Subscription.create(customer=customer_id, items=[{'plan': plan}])
# TODO: store subscription.id
start = subscription.current_period_start start = subscription.current_period_start
start = date.fromtimestamp(start) start = date.fromtimestamp(start)
return customer_id, start return customer_id, start, subscription.id
def _get_plan(self, plan): def _get_plan(self, plan):
with get_db_connection(self.config['DB_CONNECTION_POOL']) as db: with get_db_connection(self.config['DB_CONNECTION_POOL']) as db:
@ -250,38 +256,52 @@ class AccountEndpoint(SeleneEndpoint):
membership_repository = MembershipRepository(db) membership_repository = MembershipRepository(db)
active_membership = membership_repository.get_active_membership_by_account_id(self.account.id) active_membership = membership_repository.get_active_membership_by_account_id(self.account.id)
if active_membership: if active_membership:
active_membership.end_date = datetime.utcnow() self.cancel_membership(active_membership, membership_repository)
# TODO: use the subscription id to delete the membership on stripe membership_type, start_date, stripe_id, subscription_stripe_id = self.update_membership(
membership_repository.finish_membership(active_membership) active_membership)
add_membership = UpdateMembership(self.request_data.get('support'))
add_membership.validate()
support = self.request_data['support']
membership_type = support['membership']
membership = self._get_plan(membership_type)
stripe_id, start_date = self._create_stripe_subscription(
active_membership.payment_account_id,
None,
self.account.email_address,
membership.stripe_plan
)
else: else:
add_membership = AddMembership(self.request_data.get('support')) membership_type, start_date, stripe_id, subscription_stripe_id = self.create_membership()
add_membership.validate()
support = self.request_data['support']
membership_type = support['membership']
token = support['paymentToken']
membership = self._get_plan(membership_type)
stripe_id, start_date = self._create_stripe_subscription(
None,
token,
self.account.email_address,
membership.stripe_plan
)
new_membership = AccountMembership( new_membership = AccountMembership(
start_date=start_date, start_date=start_date,
payment_method=STRIPE_PAYMENT, payment_method=STRIPE_PAYMENT,
payment_account_id=stripe_id, payment_account_id=stripe_id,
payment_id=subscription_stripe_id,
type=membership_type type=membership_type
) )
AccountRepository(db).add_membership(self.account.id, new_membership) AccountRepository(db).add_membership(self.account.id, new_membership)
def create_membership(self):
add_membership = AddMembership(self.request_data.get('support'))
add_membership.validate()
support = self.request_data['support']
membership_type = support['membership']
token = support['paymentToken']
membership = self._get_plan(membership_type)
stripe_id, start_date, subscription_stripe_id = self._create_stripe_subscription(
None,
token,
self.account.email_address,
membership.stripe_plan
)
return membership_type, start_date, stripe_id, subscription_stripe_id
def update_membership(self, active_membership):
add_membership = UpdateMembership(self.request_data.get('support'))
add_membership.validate()
support = self.request_data['support']
membership_type = support['membership']
membership = self._get_plan(membership_type)
stripe_id, start_date, subscription_stripe_id = self._create_stripe_subscription(
active_membership.payment_account_id,
None,
self.account.email_address,
membership.stripe_plan
)
return membership_type, start_date, stripe_id, subscription_stripe_id
def cancel_membership(self, active_membership, membership_repository):
active_membership.end_date = datetime.utcnow()
active_stripe_subscription = stripe.Subscription.retrieve(active_membership.payment_id)
active_stripe_subscription.delete()
membership_repository.finish_membership(active_membership)

View File

@ -18,6 +18,7 @@ class AccountMembership(object):
start_date: date start_date: date
payment_method: str payment_method: str
payment_account_id: str payment_account_id: str
payment_id: str
id: str = None id: str = None
end_date: date = None end_date: date = None

View File

@ -83,7 +83,8 @@ class AccountRepository(object):
account_id=acct_id, account_id=acct_id,
membership_type=membership.type, membership_type=membership.type,
payment_method=membership.payment_method, payment_method=membership.payment_method,
payment_account_id=membership.payment_account_id payment_account_id=membership.payment_account_id,
payment_id=membership.payment_id
) )
) )
self.cursor.insert(request) self.cursor.insert(request)

View File

@ -4,7 +4,8 @@ INSERT INTO
membership_id, membership_id,
membership_ts_range, membership_ts_range,
payment_method, payment_method,
payment_account_id payment_account_id,
payment_id
) )
VALUES VALUES
( (
@ -19,5 +20,6 @@ VALUES
), ),
'[now,]', '[now,]',
%(payment_method)s, %(payment_method)s,
%(payment_account_id)s %(payment_account_id)s,
%(payment_id)s
) )

View File

@ -29,7 +29,8 @@ WITH
'type', m.type, 'type', m.type,
'start_date', lower(am.membership_ts_range)::DATE, 'start_date', lower(am.membership_ts_range)::DATE,
'payment_method', am.payment_method, 'payment_method', am.payment_method,
'payment_account_id', am.payment_account_id 'payment_account_id', am.payment_account_id,
'payment_id', am.payment_id
) )
FROM FROM
account.account_membership am account.account_membership am

View File

@ -3,10 +3,11 @@ SELECT
mem.type, mem.type,
LOWER(acc_mem.membership_ts_range) start_date, LOWER(acc_mem.membership_ts_range) start_date,
acc_mem.payment_method, acc_mem.payment_method,
payment_account_id payment_account_id,
payment_id
FROM FROM
account.account_membership acc_mem account.account_membership acc_mem
INNER JOIN INNER JOIN
account.membership mem ON acc_mem.membership_id = mem.id account.membership mem ON acc_mem.membership_id = mem.id
WHERE WHERE
account_id = %(account_id)s AND UPPER(acc_mem.membership_ts_range) IS NULL account_id = %(account_id)s AND UPPER(acc_mem.membership_ts_range) IS NULL