import stripe from flask import request from endpoints.api import (resource, nickname, ApiResource, validate_json_request, log_action, related_user_resource, internal_only, Unauthorized, NotFound) from endpoints.api.subscribe import subscribe, subscription_view from auth.permissions import AdministerOrganizationPermission from auth.auth_context import get_authenticated_user from data import model from data.plans import PLANS def carderror_response(e): return {'carderror': e.message}, 402 def get_card(user): card_info = { 'is_valid': False } if user.stripe_id: cus = stripe.Customer.retrieve(user.stripe_id) if cus and cus.default_card: # Find the default card. default_card = None for card in cus.cards.data: if card.id == cus.default_card: default_card = card break if default_card: card_info = { 'owner': default_card.name, 'type': default_card.type, 'last4': default_card.last4 } return {'card': card_info} def set_card(user, token): if user.stripe_id: cus = stripe.Customer.retrieve(user.stripe_id) if cus: try: cus.card = token cus.save() except stripe.CardError as exc: return carderror_response(exc) except stripe.InvalidRequestError as exc: return carderror_response(exc) return get_card(user) def get_invoices(customer_id): def invoice_view(i): return { 'id': i.id, 'date': i.date, 'period_start': i.period_start, 'period_end': i.period_end, 'paid': i.paid, 'amount_due': i.amount_due, 'next_payment_attempt': i.next_payment_attempt, 'attempted': i.attempted, 'closed': i.closed, 'total': i.total, 'plan': i.lines.data[0].plan.id if i.lines.data[0].plan else None } invoices = stripe.Invoice.all(customer=customer_id, count=12) return { 'invoices': [invoice_view(i) for i in invoices.data] } @resource('/v1/plans/') class ListPlans(ApiResource): """ Resource for listing the available plans. """ @nickname('listPlans') def get(self): """ List the avaialble plans. """ return { 'plans': PLANS, } @resource('/v1/user/card') @internal_only class UserCard(ApiResource): """ Resource for managing a user's credit card. """ schemas = { 'UserCard': { 'id': 'UserCard', 'type': 'object', 'description': 'Description of a user card', 'required': [ 'token', ], 'properties': { 'token': { 'type': 'string', 'description': 'Stripe token that is generated by stripe checkout.js', }, }, }, } @nickname('getUserCard') def get(self): """ Get the user's credit card. """ user = get_authenticated_user() return get_card(user) @nickname('setUserCard') @validate_json_request('UserCard') def post(self): """ Update the user's credit card. """ user = get_authenticated_user() token = request.get_json()['token'] response = set_card(user, token) log_action('account_change_cc', user.username) return response @resource('/v1/organization//card') @internal_only @related_user_resource(UserCard) class OrganizationCard(ApiResource): """ Resource for managing an organization's credit card. """ schemas = { 'OrgCard': { 'id': 'OrgCard', 'type': 'object', 'description': 'Description of a user card', 'required': [ 'token', ], 'properties': { 'token': { 'type': 'string', 'description': 'Stripe token that is generated by stripe checkout.js', }, }, }, } @nickname('getOrgCard') def get(self, orgname): """ Get the organization's credit card. """ permission = AdministerOrganizationPermission(orgname) if permission.can(): organization = model.get_organization(orgname) return get_card(organization) raise Unauthorized() @nickname('setOrgCard') @validate_json_request('OrgCard') def post(self, orgname): """ Update the orgnaization's credit card. """ permission = AdministerOrganizationPermission(orgname) if permission.can(): organization = model.get_organization(orgname) token = request.get_json()['token'] response = set_card(organization, token) log_action('account_change_cc', orgname) return response raise Unauthorized() @resource('/v1/user/plan') @internal_only class UserPlan(ApiResource): """ Resource for managing a user's subscription. """ schemas = { 'UserSubscription': { 'id': 'UserSubscription', 'type': 'object', 'description': 'Description of a user card', 'required': [ 'plan', ], 'properties': { 'token': { 'type': 'string', 'description': 'Stripe token that is generated by stripe checkout.js', }, 'plan': { 'type': 'string', 'description': 'Plan name to which the user wants to subscribe', }, }, }, } @nickname('updateUserSubscription') @validate_json_request('UserSubscription') def put(self): """ Create or update the user's subscription. """ request_data = request.get_json() plan = request_data['plan'] token = request_data['token'] if 'token' in request_data else None user = get_authenticated_user() return subscribe(user, plan, token, False) # Business features not required @nickname('getUserSubscription') def get(self): """ Fetch any existing subscription for the user. """ user = get_authenticated_user() private_repos = model.get_private_repo_count(user.username) if user.stripe_id: cus = stripe.Customer.retrieve(user.stripe_id) if cus.subscription: return subscription_view(cus.subscription, private_repos) return { 'plan': 'free', 'usedPrivateRepos': private_repos, } @resource('/v1/organization//plan') @internal_only @related_user_resource(UserPlan) class OrganizationPlan(ApiResource): """ Resource for managing a org's subscription. """ schemas = { 'OrgSubscription': { 'id': 'OrgSubscription', 'type': 'object', 'description': 'Description of a user card', 'required': [ 'plan', ], 'properties': { 'token': { 'type': 'string', 'description': 'Stripe token that is generated by stripe checkout.js', }, 'plan': { 'type': 'string', 'description': 'Plan name to which the user wants to subscribe', }, }, }, } @nickname('updateOrgSubscription') @validate_json_request('OrgSubscription') def put(self, orgname): """ Create or update the org's subscription. """ permission = AdministerOrganizationPermission(orgname) if permission.can(): request_data = request.get_json() plan = request_data['plan'] token = request_data['token'] if 'token' in request_data else None organization = model.get_organization(orgname) return subscribe(organization, plan, token, True) # Business plan required raise Unauthorized() @nickname('getOrgSubscription') def get(self, orgname): """ Fetch any existing subscription for the org. """ permission = AdministerOrganizationPermission(orgname) if permission.can(): private_repos = model.get_private_repo_count(orgname) organization = model.get_organization(orgname) if organization.stripe_id: cus = stripe.Customer.retrieve(organization.stripe_id) if cus.subscription: return subscription_view(cus.subscription, private_repos) return { 'plan': 'free', 'usedPrivateRepos': private_repos, } raise Unauthorized() @resource('/v1/user/invoices') class UserInvoiceList(ApiResource): """ Resource for listing a user's invoices. """ @nickname('listUserInvoices') def get(self): """ List the invoices for the current user. """ user = get_authenticated_user() if not user.stripe_id: raise NotFound() return get_invoices(user.stripe_id) @resource('/v1/organization//invoices') @related_user_resource(UserInvoiceList) class OrgnaizationInvoiceList(ApiResource): """ Resource for listing an orgnaization's invoices. """ @nickname('listOrgInvoices') def get(self, orgname): """ List the invoices for the specified orgnaization. """ permission = AdministerOrganizationPermission(orgname) if permission.can(): organization = model.get_organization(orgname) if not organization.stripe_id: raise NotFound() return get_invoices(organization.stripe_id) raise Unauthorized()