def cancel_subscription(uid: str, sub_id: str) -> FlaskResponse: """ Cancel an existing subscription for a user. :param uid: :param sub_id: :return: Success or failure message for the cancellation. """ customer = fetch_customer(g.subhub_account, uid) if not customer: return dict(message="Customer does not exist."), 404 for item in customer["subscriptions"]["data"]: if item["id"] == sub_id and item["status"] in [ "active", "trialing", "incomplete", ]: Subscription.modify(sub_id, cancel_at_period_end=True) updated_customer = fetch_customer(g.subhub_account, uid) logger.info("updated customer", updated_customer=updated_customer) subs = retrieve_stripe_subscriptions(updated_customer) logger.info("subs", subs=subs, type=type(subs)) for sub in subs: if sub["cancel_at_period_end"] and sub["id"] == sub_id: return {"message": "Subscription cancellation successful"}, 201 return dict(message="Subscription not available."), 400
def subscribe_to_plan(uid, data) -> FlaskResponse: """ Subscribe to a plan given a user id, payment token, email, orig_system :param uid: :param data: :return: current subscriptions for user. """ customer = existing_or_new_customer( g.subhub_account, user_id=uid, email=data["email"], source_token=data["pmt_token"], origin_system=data["orig_system"], display_name=data["display_name"], ) existing_plan = has_existing_plan(customer, plan_id=data["plan_id"]) if existing_plan: return {"message": "User already subscribed."}, 409 if not customer.get("deleted"): Subscription.create(customer=customer.id, items=[{ "plan": data["plan_id"] }]) updated_customer = fetch_customer(g.subhub_account, user_id=uid) newest_subscription = find_newest_subscription( updated_customer["subscriptions"]) return create_return_data(newest_subscription), 201 else: return dict(message=None), 400
def test_update(self): """Test update from stripe.""" stripe_subscription = StripeSubscription.construct_from( { 'id': 'sub_foo', 'status': 'active', 'current_period_start': 120778389, 'current_period_end': 123456789, 'trial_end': 1475437877, 'plan': { 'id': 'advanced', } }, None, ) subscription = fixture.get( Subscription, stripe_id='sub_foo', status='trialing', ) Subscription.objects.update_from_stripe( rtd_subscription=subscription, stripe_subscription=stripe_subscription, ) subscription.refresh_from_db() self.assertEqual(subscription.status, 'active') self.assertEqual( subscription.end_date, timezone.make_aware(datetime.fromtimestamp(123456789)), ) self.assertEqual( subscription.trial_end_date, timezone.make_aware(datetime.fromtimestamp(1475437877)), ) # Cancel event stripe_subscription = StripeSubscription.construct_from( { 'id': 'sub_foo', 'status': 'unpaid', 'plan': { 'id': 'advanced', } }, None, ) Subscription.objects.update_from_stripe( rtd_subscription=subscription, stripe_subscription=stripe_subscription, ) subscription.refresh_from_db() self.assertEqual(subscription.status, 'unpaid') self.assertEqual( subscription.trial_end_date, timezone.make_aware(datetime.fromtimestamp(1475437877)), )
def reactivate_stripe_subscription(subscription_id: str, idempotency_key: str) -> Subscription: """ Set Stripe subscription to cancel at period end :param subscription_id: :param idempotency_key: :return: Subscription """ try: sub = Subscription.modify( sid=subscription_id, cancel_at_period_end=False, idempotency_key=idempotency_key, ) return sub except ( InvalidRequestError, APIConnectionError, APIError, RateLimitError, IdempotencyError, StripeErrorWithParamCode, ) as e: logger.error("reactivate sub error", error=str(e)) raise e
def build_stripe_subscription(customer_id: str, plan_id: str, idempotency_key: str) -> Subscription: """ Create a new Stripe subscription for a given customer :param customer_id: :param plan_id: :param idempotency_key: :return: Subscription object """ try: sub = Subscription.create( customer=customer_id, items=[{ "plan": plan_id }], idempotency_key=idempotency_key, ) return sub except ( InvalidRequestError, APIConnectionError, APIError, RateLimitError, IdempotencyError, StripeErrorWithParamCode, AuthenticationError, ) as e: logger.error("sub error", error=e) raise e
def subscribe_customer(customer: Customer, plan_id: str) -> Subscription: """ Subscribe Customer to Plan :param customer: :param plan: :return: Subscription Object """ try: sub = Subscription.create(customer=customer, items=[{"plan": plan_id}]) return sub except Exception as e: logger.error("sub error", error=e) raise InvalidRequestError("Unable to create plan", param=plan_id)
def reactivate_subscription(uid: str, sub_id: str) -> FlaskResponse: """ Given a user's subscription that is flagged for cancellation, but is still active remove the cancellation flag to ensure the subscription remains active :param uid: User ID :param sub_id: Subscription ID :return: Success or failure message for the activation """ customer = fetch_customer(g.subhub_account, uid) if not customer: return dict(message="Customer does not exist."), 404 active_subscriptions = customer["subscriptions"]["data"] for subscription in active_subscriptions: if subscription["id"] == sub_id: if subscription["cancel_at_period_end"]: Subscription.modify(sub_id, cancel_at_period_end=False) return dict(message="Subscription reactivation was successful."), 200 return dict(message="Subscription is already active."), 200 return dict(message="Current subscription not found."), 404
def cancel_subscription(uid, sub_id) -> FlaskResponse: """ Cancel an existing subscription for a user. :param uid: :param sub_id: :return: Success or failure message for the cancellation. """ customer = fetch_customer(g.subhub_account, uid) if not customer: return {"message": "Customer does not exist."}, 404 for item in customer["subscriptions"]["data"]: if item["id"] == sub_id and item["status"] in [ "active", "trialing", "incomplete", ]: Subscription.modify(sub_id, cancel_at_period_end=True) updated_customer = fetch_customer(g.subhub_account, uid) check_stripe_subscriptions(updated_customer) return {"message": "Subscription cancellation successful"}, 201 return {"message": "Subscription not available."}, 400
def subscription_status(uid) -> FlaskResponse: """ Given a user id return the current subscription status :param uid: :return: Current subscriptions """ items = g.subhub_account.get_user(uid) if not items or not items.cust_id: return {"message": "Customer does not exist."}, 404 subscriptions = Subscription.list(customer=items.cust_id, limit=100, status="all") if not subscriptions: return {"message": "No subscriptions for this customer."}, 403 return_data = create_return_data(subscriptions) return return_data, 201
def list_customer_subscriptions(cust_id: str) -> List[Subscription]: """ List customer subscriptions :param cust_id: :return: List of Subscriptions """ try: subscriptions = Subscription.list(customer=cust_id, limit=100, status="all") return subscriptions except ( InvalidRequestError, APIConnectionError, APIError, RateLimitError, IdempotencyError, StripeErrorWithParamCode, ) as e: logger.error("list subscriptions error", error=e) raise e
def cancel_stripe_subscription_immediately( subscription_id: str, idempotency_key: str) -> Subscription: """ Set Stripe subscription to cancel at period end :param subscription_id: :param idempotency_key: :return: Subscription """ try: sub = Subscription.delete(sid=subscription_id, idempotency_key=idempotency_key) return sub except ( InvalidRequestError, APIConnectionError, APIError, RateLimitError, IdempotencyError, StripeErrorWithParamCode, ) as e: logger.error("cancel sub error", error=str(e)) raise e
def update_stripe_subscription(subscription: Dict[str, Any], plan_id: str, idempotency_key: str) -> Subscription: """ Update a stripe subscription to a new plan :param customer_id: :param plan_id: :param idempotency_key: :return: Subscription object """ try: subscription = Subscription.modify( subscription["id"], cancel_at_period_end=False, items=[{ "id": subscription["items"]["data"][0]["id"], "plan": plan_id }], metadata={ "previous_plan_id": subscription["plan"]["id"], "plan_change_date": int(time.time()), }, idempotency_key=idempotency_key, ) logger.debug("update stripe subscription", sub=subscription) return subscription except ( InvalidRequestError, APIConnectionError, APIError, RateLimitError, IdempotencyError, StripeErrorWithParamCode, AuthenticationError, ) as e: logger.error("sub error", error=e) raise e
def test_replace_subscription(self): """Test update from stripe.""" stripe_subscription = StripeSubscription.construct_from( { 'id': 'sub_bar', 'status': 'active', 'plan': { 'id': 'advanced', } }, None, ) subscription = fixture.get( Subscription, stripe_id='sub_foo', status='trialing', ) Subscription.objects.update_from_stripe( rtd_subscription=subscription, stripe_subscription=stripe_subscription, ) subscription.refresh_from_db() self.assertEqual(subscription.stripe_id, 'sub_bar') self.assertEqual(subscription.status, 'active')