def upcoming_invoices(): get_stripe_secret_key() all_subscriptions = Subscription.query.all() subscriptions = [] for subscription in all_subscriptions: if subscription.upcoming_invoice() is not None: subscriptions.append(subscription) return render_template( "admin/upcoming_invoices.html", subscriptions=subscriptions, datetime=datetime, )
def stripe_onboarding(): # Determine if in live or test mode payment_provider = PaymentProvider.query.first() stripe.api_key = get_stripe_secret_key() company = Company.query.first() # Use existing stripe_connect_account_id, otherwise create stripe connect account try: print("Trying if there's an existing stripe account") account = get_stripe_connect_account() print(f"Yes, stripe account found: {account.id}") except ( stripe.error.PermissionError, stripe.error.InvalidRequestError, AttributeError, ): print("Could not find a stripe account, Creating stripe account") account = create_stripe_connect_account(company) if payment_provider.stripe_livemode: payment_provider.stripe_live_connect_account_id = account.id else: payment_provider.stripe_test_connect_account_id = account.id database.session.commit() session["account_id"] = account.id account_link_url = _generate_account_link(account.id) try: return jsonify({"url": account_link_url}) except Exception as e: return jsonify(error=str(e)), 403
def update_stripe_subscription_statuses(): """Update Stripe subscriptions with their current status by querying Stripe api""" stripe.api_key = get_stripe_secret_key() connect_account = get_stripe_connect_account() if stripe.api_key is None: logging.error( "Stripe api key not set refusing to update subscription statuses") if connect_account is None: logging.error( "Stripe connect account not set. Refusing to update subscription statuses" ) if stripe_connect_active(): for subscription in Subscription.query.all(): try: stripeSubscription = stripe.Subscription.retrieve( stripe_account=connect_account.id, id=subscription.stripe_subscription_id, ) subscription.stripe_status = stripeSubscription.status database.session.commit() except Exception as e: logging.warning( f"Could not update stripe subscription status: {e}") else: logging( "Refusing to update subscription status since Stripe connect is not active" )
def stripe_process_event_payment_intent_succeeded(event): """Store suceeded payment_intents as transactions These events will fire both at the begining of a subscription, and also at each successful recuring billing cycle. We use backoff because webhook event order is not guaranteed """ logging.info("Processing payment_intent.succeeded") data = event["data"]["object"] stripe.api_key = get_stripe_secret_key() subscribie_subscription = None # Get the Subscribie subscription id from Stripe subscription metadata try: subscribie_checkout_session_id = data["metadata"][ "subscribie_checkout_session_id"] except KeyError: # There is no metadata on the event if its an upfront payment # So try and get it via the data['invoice'] attribute invoice_id = data["invoice"] invoice = stripe.Invoice.retrieve(stripe_account=event["account"], id=invoice_id) # Fetch subscription via its invoice subscription = stripe.Subscription.retrieve( stripe_account=event["account"], id=invoice.subscription) subscribie_checkout_session_id = subscription.metadata[ "subscribie_checkout_session_id"] except Exception as e: msg = f"Unable to get subscribie_checkout_session_id from event\n{e}" logging.error(msg) return msg, 500 # Locate the Subscribie subscription by its subscribie_checkout_session_id subscribie_subscription = (database.session.query(Subscription).filter_by( subscribie_checkout_session_id=subscribie_checkout_session_id).first()) # Store the transaction in Transaction model # (regardless of if subscription or just one-off plan) if (database.session.query(Transaction).filter_by( external_id=data["id"]).first() is None): transaction = Transaction() transaction.amount = data["amount"] transaction.payment_status = ("paid" if data["status"] == "succeeded" else data["status"]) transaction.external_id = data["id"] transaction.external_src = "stripe" if subscribie_subscription is not None: transaction.person = subscribie_subscription.person transaction.subscription = subscribie_subscription else: print("WARNING: subscribie_subscription not found for this\ payment_intent.succeeded. The metadata was:") print(data["metadata"]) raise Exception database.session.add(transaction) database.session.commit() return "OK", 200
def invoices(): stripe.api_key = get_stripe_secret_key() connect_account = get_stripe_connect_account() invoices = stripe.Invoice.list(stripe_account=connect_account.id) return render_template( "admin/invoices.html", invoices=invoices, datetime=datetime, )
def fetch_stripe_upcoming_invoices(): """Fetch all Stripe upcoming invoices and populate the invoices table""" all_subscriptions = Subscription.query.all() upcoming_invoices = [] stripe.api_key = get_stripe_secret_key() connect_account = get_stripe_connect_account() # Prepare to delete all previous upcomingInvoice UpcomingInvoice.query.delete( ) # don't commit until finished fetching latest collection # noqa for subscription in all_subscriptions: try: log.info( f"Getting upcoming invoice for Stripe subscription: {subscription.stripe_subscription_id}" # noqa ) if subscription.stripe_subscription_id is not None: upcoming_invoice = stripe.Invoice.upcoming( subscription=subscription.stripe_subscription_id, stripe_account=connect_account.id, ) upcoming_invoices.append(upcoming_invoice) # Store the UpcomingInvoice upcomingInvoice = UpcomingInvoice() upcomingInvoice.subscription = subscription upcomingInvoice.stripe_invoice_id = None upcomingInvoice.stripe_subscription_id = ( subscription.stripe_subscription_id) upcomingInvoice.stripe_invoice_status = upcoming_invoice.status upcomingInvoice.stripe_amount_due = upcoming_invoice.amount_due upcomingInvoice.stripe_amount_paid = upcoming_invoice.amount_paid upcomingInvoice.stripe_next_payment_attempt = ( upcoming_invoice.next_payment_attempt) upcomingInvoice.stripe_currency = upcoming_invoice.currency database.session.add(upcomingInvoice) except stripe.error.InvalidRequestError as e: log.error( f"Cannot get stripe subscription id: {subscription.stripe_subscription_id}, {e}" # noqa ) except Exception as e: log.error( f"Error checking for upcoming invoice for {subscription.id}, {e}" ) database.session.commit()
def upcoming_invoice(self): """Return the upcoming invoice (if exists) associated with this Subscription""" stripe.api_key = get_stripe_secret_key() stripe_connect_account_id = get_stripe_connect_account_id() if self.stripe_subscription_id is not None: try: upcoming_invoice = stripe.Invoice.upcoming( subscription=self.stripe_subscription_id, stripe_account=stripe_connect_account_id, ) return upcoming_invoice except stripe.error.InvalidRequestError as e: print( f"Cannot get stripe subscription id: {self.stripe_subscription_id}" ) print(e) return None
def stripe_subscription_active(self): if self.stripe_subscription_id is not None: stripe.api_key = get_stripe_secret_key() connect_account = get_stripe_connect_account() try: subscription = stripe.Subscription.retrieve( stripe_account=connect_account.id, id=self.stripe_subscription_id) if subscription.pause_collection is not None: return False elif subscription.status == "active": return True except stripe.error.InvalidRequestError as e: logging.error("Could not get stripe subscription status") logging.error(e) return False
def get_subscription_status(stripe_external_id: str) -> str: status_on_error = "Unknown" if stripe_external_id is None: return status_on_error try: stripe.api_key = get_stripe_secret_key() connect_account = get_stripe_connect_account() subscription = stripe.Subscription.retrieve( stripe_account=connect_account.id, id=stripe_external_id) if subscription.pause_collection is not None: return "paused" else: return "active" except stripe.error.InvalidRequestError as e: print(e) return status_on_error except ValueError as e: print(e) return status_on_error
def pause_stripe_subscription(subscription_id: str): """Pause a Stripe subscription""" stripe.api_key = get_stripe_secret_key() connect_account_id = get_stripe_connect_account_id() try: stripe.Subscription.modify( subscription_id, stripe_account=connect_account_id, pause_collection={"behavior": "void"}, ) flash("Subscription paused") except Exception as e: flash("Error pausing subscription") print(e) if "goback" in request.args: return redirect(request.referrer) return jsonify(message="Subscription paused", subscription_id=subscription_id)
def resume_stripe_subscription(subscription_id): """Resume a Stripe subscription""" stripe.api_key = get_stripe_secret_key() connect_account_id = get_stripe_connect_account_id() try: stripe.Subscription.modify( subscription_id, stripe_account=connect_account_id, pause_collection= "", # passing empty string unpauses the subscription ) flash("Subscription resumed") except Exception as e: flash("Error resuming subscription") print(e) if "goback" in request.args: return redirect(request.referrer) return jsonify(message="Subscription resumed", subscription_id=subscription_id)
def update_stripe_subscription_statuses(): """Update Stripe subscriptions with their current status by querying Stripe api""" stripe.api_key = get_stripe_secret_key() connect_account = get_stripe_connect_account() if stripe.api_key is None: log.error( "Stripe api key not set refusing to update subscription statuses") if connect_account is None: log.error( "Stripe connect account not set. Refusing to update subscription statuses" ) if stripe_connect_active(): try: stripeSubscriptions = stripe.Subscription.list( stripe_account=connect_account.id, limit=100) for stripeSubscription in stripeSubscriptions.auto_paging_iter(): subscription = (database.session.query(Subscription).where( Subscription.stripe_subscription_id == stripeSubscription.id).first()) if subscription: subscription.stripe_status = stripeSubscription.status log.info(subscription.stripe_status) log.info(subscription.stripe_subscription_id) database.session.commit() else: log.warning( "subscription is in stripe but not in the subscribie database" ) except Exception as e: log.warning(f"Could not update stripe subscription status: {e}") else: log.warning( "Refusing to update subscription status since Stripe connect is not active" )
def stripe_connect(): account = None stripe_express_dashboard_url = None stripe.api_key = get_stripe_secret_key() payment_provider = PaymentProvider.query.first() try: account = get_stripe_connect_account() if account is not None and account.charges_enabled and account.payouts_enabled: payment_provider.stripe_active = True else: payment_provider.stripe_active = False except ( stripe.error.PermissionError, stripe.error.InvalidRequestError, AttributeError, ) as e: print(e) account = None # Setup Stripe webhook endpoint if it dosent already exist if account: try: stripe_express_dashboard_url = stripe.Account.create_login_link( account.id).url except stripe.error.InvalidRequestError: stripe_express_dashboard_url = None database.session.commit() return render_template( "admin/settings/stripe/stripe_connect.html", stripe_onboard_path=url_for("admin.stripe_onboarding"), account=account, payment_provider=payment_provider, stripe_express_dashboard_url=stripe_express_dashboard_url, )
def stripe_create_checkout_session(): data = request.json plan = Plan.query.filter_by(uuid=session["plan"]).first() person = Person.query.get(session["person_id"]) charge = {} charge["sell_price"] = plan.sell_price charge["interval_amount"] = plan.interval_amount charge["currency"] = "GBP" session["subscribie_checkout_session_id"] = str(uuid4()) payment_method_types = ["card"] success_url = url_for("views.instant_payment_complete", _external=True, plan=plan.uuid) cancel_url = url_for("views.order_summary", _external=True) stripe.api_key = get_stripe_secret_key() metadata = { "person_uuid": person.uuid, "plan_uuid": session["plan"], "chosen_option_ids": json.dumps(session.get("chosen_option_ids", None)), "package": session.get("package", None), "subscribie_checkout_session_id": session.get("subscribie_checkout_session_id", None), } # Add note to seller if present directly on payment metadata if session.get("note_to_seller", False): metadata["note_to_seller"] = session.get("note_to_seller") if plan.requirements.subscription: subscription_data = { "application_fee_percent": 1.25, "metadata": { "person_uuid": person.uuid, "plan_uuid": session["plan"], "chosen_option_ids": json.dumps(session.get("chosen_option_ids", None)), # noqa "package": session.get("package", None), "subscribie_checkout_session_id": session.get("subscribie_checkout_session_id", None), }, } # Add note to seller if present on subscription_data metadata if session.get("note_to_seller", False): subscription_data["metadata"]["note_to_seller"] = session.get( "note_to_seller") # Add trial period if present if plan.days_before_first_charge and plan.days_before_first_charge > 0: subscription_data[ "trial_period_days"] = plan.days_before_first_charge if plan.requirements.instant_payment: payment_intent_data = { "application_fee_amount": 20, "metadata": metadata } if plan.requirements.subscription: mode = "subscription" else: mode = "payment" # Build line_items array depending on plan requirements line_items = [] # Add line item for instant_payment if required if plan.requirements.instant_payment: # Append "Up-front fee" to product name if also a subscription plan_name = plan.title if plan.requirements.subscription: plan_name = "(Up-front fee) " + plan_name line_items.append({ "price_data": { "currency": charge["currency"], "product_data": { "name": plan_name, }, "unit_amount": charge["sell_price"], }, "quantity": 1, }) if plan.requirements.subscription: line_items.append({ "price_data": { "recurring": { "interval": format_to_stripe_interval(plan.interval_unit) }, "currency": charge["currency"], "product_data": { "name": plan.title, }, "unit_amount": charge["interval_amount"], }, "quantity": 1, }) # Create Stripe checkout session for payment or subscription mode if plan.requirements.subscription: stripe_session = stripe.checkout.Session.create( stripe_account=data["account_id"], payment_method_types=payment_method_types, line_items=line_items, mode=mode, metadata=metadata, customer_email=person.email, success_url=success_url, cancel_url=cancel_url, subscription_data=subscription_data, ) return jsonify(id=stripe_session.id) elif plan.requirements.instant_payment: stripe_session = stripe.checkout.Session.create( stripe_account=data["account_id"], payment_method_types=payment_method_types, line_items=line_items, mode=mode, metadata=metadata, customer_email=person.email, success_url=success_url, cancel_url=cancel_url, payment_intent_data=payment_intent_data, ) return jsonify(id=stripe_session.id)
def stripe_process_event_payment_intent_succeeded(event): """Store suceeded payment_intents as transactions Stripe sends Subscribie events of different types. This metod processes the payment_intent_succeeded event. This event will fire both at the begining of a subscription, and also at each successful recuring billing cycle. We use backoff because webhook event order from Stripe is not guaranteed, for example a `payment_intent_succeeded` event can be received before a `checkout.session.completed` event. If that happens, then the associated subscription may not be created yet in Subscribie database. Therefore the @backoff.on_exception allows processing of the payment_intent_succeeded event to retry until the checkout.session.completed event is processed. If the backoff fails (max_tries is exceeded) then Stripe will retry the event at a later time. See also - https://stripe.com/docs/api/events/types#event_types-checkout.session.completed - https://stripe.com/docs/api/events/types#event_types-payment_intent.succeeded """ log.info("Processing payment_intent.succeeded") data = event["data"]["object"] stripe.api_key = get_stripe_secret_key() subscribie_subscription = None # Get the Subscribie subscription id from Stripe subscription metadata try: subscribie_checkout_session_id = data["metadata"][ "subscribie_checkout_session_id"] except KeyError: # There is no subscribie metadata on the event if its an upfront payment # So try and get it via the data['invoice'] attribute invoice_id = data["invoice"] # Stripe payment_intent invoice attribute may be null if # the payment intent is an instant charge (meaning not part of a # subscrtiption or plan). if invoice_id is not None: invoice = stripe.Invoice.retrieve(stripe_account=event["account"], id=invoice_id) # Fetch subscription via its invoice subscription = stripe.Subscription.retrieve( stripe_account=event["account"], id=invoice.subscription) subscribie_checkout_session_id = subscription.metadata[ "subscribie_checkout_session_id"] else: # If instance charge (not a subscription) # there is no checkout session (instant charge) subscribie_checkout_session_id = data["id"] except Exception as e: msg = f"Unable to get subscribie_checkout_session_id from event\n{e}" log.error(msg) return msg, 500 # Locate the Subscribie subscription by its subscribie_checkout_session_id subscribie_subscription = (database.session.query(Subscription).filter_by( subscribie_checkout_session_id=subscribie_checkout_session_id).first()) # Store the transaction in Transaction model # (regardless of if subscription or just one-off plan) if (database.session.query(Transaction).filter_by( external_id=data["id"]).first() is None): transaction = Transaction() transaction.amount = data["amount"] transaction.payment_status = ("paid" if data["status"] == "succeeded" else data["status"]) transaction.external_id = data["id"] transaction.external_src = "stripe" if subscribie_subscription is not None: transaction.person = subscribie_subscription.person transaction.subscription = subscribie_subscription elif data["metadata"] == {}: log.warn(f"Empty metadata: {data}") return "Empty metadata", 422 else: log.error("subscribie_subscription not found for this\ payment_intent.succeeded. The metadata was:") log.error(data["metadata"]) raise Exception database.session.add(transaction) database.session.commit() return "OK", 200
def create_subscription( email=None, package=None, chosen_option_ids=None, subscribie_checkout_session_id=None, stripe_external_id=None, stripe_subscription_id=None, ) -> Subscription: """Create subscription model Note: A subscription model is also created if a plan only has one up_front payment (no recuring subscription). This allows the storing of chosen options againt their plan choice. Chosen option ids may be passed via webhook or through session """ log.info("Creating Subscription model if needed") subscription = None # Initalize subscription model to None # Store Subscription against Person locally if email is None: email = session["email"] if package is None: package = session["package"] person = database.session.query(Person).filter_by(email=email).one() # subscribie_checkout_session_id can be passed by stripe metadata (webhook) or # via session (e.g. when session only with no up-front payment) if subscribie_checkout_session_id is None: subscribie_checkout_session_id = session.get( "subscribie_checkout_session_id", None) log.info( f"subscribie_checkout_session_id is: {subscribie_checkout_session_id}") # Verify Subscription not already created (e.g. stripe payment webhook) # another hook or mandate only payment may have already created the Subscription # model, if so, fetch it via its subscribie_checkout_session_id if subscribie_checkout_session_id is not None: subscription = (Subscription.query.filter_by( subscribie_checkout_session_id=subscribie_checkout_session_id). filter(Subscription.person.has(email=email)).first()) if subscription is None: log.info( "No existing subscription model found, creating Subscription model" ) # Create new subscription model subscription = Subscription( sku_uuid=package, person=person, subscribie_checkout_session_id=subscribie_checkout_session_id, stripe_external_id=stripe_external_id, stripe_subscription_id=stripe_subscription_id, ) # Add chosen options (if any) if chosen_option_ids is None: chosen_option_ids = session.get("chosen_option_ids", None) if chosen_option_ids: log.info( f"Applying chosen_option_ids to subscription: {chosen_option_ids}" ) chosen_options = [] for option_id in chosen_option_ids: log.info(f"Locating option id: {option_id}") option = Option.query.get(option_id) # Store as ChosenOption because options may change after the order # has processed. This preserves integrity of the actual chosen options chosen_option = ChosenOption() if option is not None: chosen_option.option_title = option.title chosen_option.choice_group_title = option.choice_group.title chosen_option.choice_group_id = ( option.choice_group.id ) # Used for grouping latest choice chosen_options.append(chosen_option) else: log.error( f"Failed to get Open from session option_id: {option_id}" ) subscription.chosen_options = chosen_options else: log.info("No chosen_option_ids were found or applied.") database.session.add(subscription) database.session.commit() session["subscription_uuid"] = subscription.uuid # If subscription plan has cancel_at set, modify Stripe subscription # charge_at property stripe.api_key = get_stripe_secret_key() connect_account_id = get_stripe_connect_account_id() if subscription.plan.cancel_at: cancel_at = subscription.plan.cancel_at try: stripe.Subscription.modify( sid=subscription.stripe_subscription_id, stripe_account=connect_account_id, cancel_at=cancel_at, ) subscription.stripe_cancel_at = cancel_at database.session.commit() except Exception as e: # noqa log.error("Could not set cancel_at: {e}") newSubscriberEmailNotification() return subscription
def account(): "A subscribers account home screen" stripe.api_key = get_stripe_secret_key() stripe_connect_account_id = get_stripe_connect_account_id() stripe_publishable_key = get_stripe_publishable_key() stripe_default_payment_method = None stripe_session = None # Get subscribers first subscription to determine stripe customer id # excluding one-off plans. subscription = ( database.session.query(Subscription) .execution_options(include_archived=True) .join(Person, Subscription.person_id == Person.id) .join(Plan, Subscription.sku_uuid == Plan.uuid) .join(PlanRequirements, Plan.id == PlanRequirements.plan_id) .where(Person.email == session["subscriber_id"]) .where(PlanRequirements.subscription == 1) .order_by(Subscription.id.desc()) .first() ) if subscription: try: stripe_subscription = stripe.Subscription.retrieve( subscription.stripe_subscription_id, stripe_account=stripe_connect_account_id, ) stripe_customer_id = stripe_subscription.customer stripe_customer = stripe.Customer.retrieve( stripe_customer_id, stripe_account=stripe_connect_account_id ) stripe_session = stripe.checkout.Session.create( stripe_account=stripe_connect_account_id, payment_method_types=["card"], mode="setup", customer=stripe_customer_id, setup_intent_data={ "metadata": { "subscription_id": stripe_subscription.id, }, }, success_url=url_for("subscriber.account", _external=True) + "?stripe_session_id={CHECKOUT_SESSION_ID}", cancel_url=url_for("subscriber.account", _external=True), ) if request.args.get("stripe_session_id"): # Process stripe update payment request # Get Stripe checkout session stripe_session = stripe.checkout.Session.retrieve( request.args.get("stripe_session_id"), stripe_account=stripe_connect_account_id, ) # Get setup_intent id from stripe session stripe_setup_intent_id = stripe_session.setup_intent stripe_setup_intent = stripe.SetupIntent.retrieve( stripe_setup_intent_id, stripe_account=stripe_connect_account_id ) # Update default payment method stripe.Customer.modify( stripe_customer.id, stripe_account=stripe_connect_account_id, invoice_settings={ "default_payment_method": stripe_setup_intent.payment_method }, ) flash("Default payment method updated") return redirect(url_for("subscriber.account")) # Try to get existing default payment method if stripe_customer.invoice_settings.default_payment_method: stripe_default_payment_method = stripe.PaymentMethod.retrieve( stripe_customer.invoice_settings.default_payment_method, stripe_account=stripe_connect_account_id, ) except stripe.error.InvalidRequestError as e: log.error(f"stripe.error.InvalidRequestError: {e}") return render_template( "subscriber/account.html", stripe_session=stripe_session, stripe_publishable_key=stripe_publishable_key, stripe_default_payment_method=stripe_default_payment_method, )
def store_stripe_transaction(stripe_external_id): """Store Stripe invoice payment in transactions table""" stripe.api_key = get_stripe_secret_key() stripe_connect_account_id = get_stripe_connect_account_id() invoice = None # It might be an upcoming invoice or an existing invoice which # is being updated # First try fetching paid invoice try: invoice = stripe.Invoice.retrieve( id=stripe_external_id, stripe_account=stripe_connect_account_id, ) except stripe.error.InvalidRequestError as e: print( f"Cannot get stripe invoice subscription id: {stripe_external_id}") print( "This might be okay. Trying to fetch upcoming invoice with same id..." ) print(e) try: invoice = stripe.Invoice.upcoming( subscription=stripe_external_id, stripe_account=stripe_connect_account_id, ) except stripe.error.InvalidRequestError as e: print(f"Cannot get stripe upcoming invoice subscription id: \ {stripe_external_id}") print(e) raise Exception( f"Cannot locate Stripe subscription invoice {stripe_external_id}" ) # Check if there's an existing payment record in transactions table transaction = Transaction.query.filter_by( external_id=stripe_external_id).first() if transaction is None: # No existing transaction found, so add payment info from Stripe invoice # Note, if the subscription invoice is unsettled, its amount could # potentially change until it's finalized # Store as transaction transaction = Transaction() transaction.amount = invoice.amount_due transaction.payment_status = invoice.status transaction.comment = str(invoice.lines.data[0].metadata) transaction.external_id = stripe_external_id transaction.external_src = "stripe" # Find related subscribie subscription model if exists subscription = Subscription.query.filter_by( stripe_external_id=stripe_external_id).first() if subscription is not None: transaction.subscription = subscription try: transaction.person = subscription.person except AttributeError: # A transaction may not have a person pass # Update payment_status to stripe latest status transaction.payment_status = invoice.status database.session.add(transaction) database.session.commit() # Save/update transaction in transactions table return transaction
def invoices(self): """Get all invoices for a given person Note a person may have zero or more subscriptions, with each subscription having zero or more invoices For Stripe invoices, the stripe customer id is needed, note it is possible (though rare) for one Subscribie customer id to have multiple Stripe customer ids. This is not as issue since we store the Stripe subscription id, and, if needed, can query the Stripe customer id from the Subscription object. See: - this file class "Subscription" with colum "stripe_subscription_id" - https://stripe.com/docs/api/subscriptions/object?lang=python#subscription_object-customer # noqa: E501 """ invoices = [] stripe.api_key = get_stripe_secret_key() stripe_account_id = get_stripe_connect_account_id() # Get Stripe invoices for subscription in self.subscriptions: if subscription.stripe_subscription_id != "": try: stripe_subscription = stripe.Subscription.retrieve( subscription.stripe_subscription_id, stripe_account=stripe_account_id, ) # Get Stripe customer id stripe_customer_id = stripe_subscription.customer # Get Stripe invoices for this customer/subscriber stripe_invoices = stripe.Invoice.list( stripe_account=stripe_account_id, customer=stripe_customer_id, ) # loop over all invoices # See https://stripe.com/docs/api/pagination/auto for invoice in stripe_invoices.auto_paging_iter(): # If invoice is not paid, check for any payment errors if invoice.status != "paid": try: stripe_decline_code = stripe.PaymentIntent.retrieve( invoice.payment_intent, stripe_account=stripe_account_id, ).last_payment_error.decline_code invoice[ "stripe_decline_code"] = stripe_decline_code except Exception as e: log.warning( f"Could not get Stripe Invoice PaymentIntent last_payment_error decline_code: {e}" # noqa: E501 ) else: invoice["stripe_decline_code"] = None invoices.append(invoice) except stripe.error.InvalidRequestError as e: log.error( f"Unable to retrieve stripe subscription by id: {subscription.stripe_subscription_id}. {e}" # noqa: E501 ) else: log.debug( f"Skipping fetching invoice for subscription.uuid {subscription.uuid}" # noqa: E501 ) return invoices