From 3be09679369a16453fb8d5024ce8e27bf42c92d2 Mon Sep 17 00:00:00 2001 From: ValueOn AG Date: Tue, 31 Mar 2026 00:15:47 +0200 Subject: [PATCH] fixed changed customer in stripe --- .../mainServiceSubscription.py | 62 ++++++++++++++++--- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/modules/serviceCenter/services/serviceSubscription/mainServiceSubscription.py b/modules/serviceCenter/services/serviceSubscription/mainServiceSubscription.py index 944da4f7..55bff123 100644 --- a/modules/serviceCenter/services/serviceSubscription/mainServiceSubscription.py +++ b/modules/serviceCenter/services/serviceSubscription/mainServiceSubscription.py @@ -283,14 +283,34 @@ class SubscriptionService: subscriptionData["trial_end"] = trialEndTs self._interface.updateFields(subRecord["id"], {"effectiveFrom": periodEnd.isoformat()}) - session = stripe.checkout.Session.create( - mode="subscription", - customer=stripeCustomerId, - line_items=lineItems, - success_url=successUrl, - cancel_url=cancelUrl, - subscription_data=subscriptionData, - ) + session = None + for attempt in range(2): + try: + session = stripe.checkout.Session.create( + mode="subscription", + customer=stripeCustomerId, + line_items=lineItems, + success_url=successUrl, + cancel_url=cancelUrl, + subscription_data=subscriptionData, + ) + break + except Exception as e: + if attempt == 0 and self._isStripeMissingCustomerError(e): + logger.warning( + "Stripe reports missing customer %s for mandate %s — " + "clearing stored stripeCustomerId (wrong account, deleted customer, or copied DB).", + stripeCustomerId, + mandateId, + ) + self._clearStoredStripeCustomerId(mandateId) + stripeCustomerId = self._resolveStripeCustomer(mandateId) + if not stripeCustomerId: + raise ValueError( + f"Could not recreate Stripe customer for mandate {mandateId}" + ) from e + continue + raise if not session or not session.url: raise ValueError("Stripe Checkout Session creation failed") @@ -298,6 +318,32 @@ class SubscriptionService: logger.info("Checkout session %s created for mandate %s, plan %s", session.id, mandateId, plan.planKey) return session.url + @staticmethod + def _isStripeMissingCustomerError(exc: BaseException) -> bool: + code = getattr(exc, "code", None) + param = getattr(exc, "param", None) + if code == "resource_missing" and param == "customer": + return True + body = getattr(exc, "json_body", None) + if isinstance(body, dict): + err = body.get("error") + if isinstance(err, dict): + return err.get("code") == "resource_missing" and err.get("param") == "customer" + return False + + def _clearStoredStripeCustomerId(self, mandateId: str) -> None: + try: + from modules.interfaces.interfaceDbBilling import getInterface as getBillingInterface + + billingIf = getBillingInterface(self.currentUser, mandateId) + settings = billingIf.getSettings(mandateId) + if not settings or not settings.get("stripeCustomerId"): + return + billingIf.updateSettings(settings["id"], {"stripeCustomerId": None}) + logger.info("Cleared stripeCustomerId on billing settings for mandate %s", mandateId) + except Exception as e: + logger.error("Failed to clear stripeCustomerId for mandate %s: %s", mandateId, e) + def _resolveStripeCustomer(self, mandateId: str) -> Optional[str]: try: from modules.interfaces.interfaceDbBilling import getInterface as getBillingInterface