fexed stripe webhook
This commit is contained in:
parent
b53a7f363d
commit
350c699473
2 changed files with 41 additions and 12 deletions
|
|
@ -994,6 +994,25 @@ class BillingObjects:
|
||||||
)
|
)
|
||||||
return created
|
return created
|
||||||
|
|
||||||
|
def ensureActivationBudget(self, mandateId: str, planKey: str) -> Optional[Dict[str, Any]]:
|
||||||
|
"""Idempotent: credit the activation budget only if no SUBSCRIPTION credit exists yet."""
|
||||||
|
poolAccount = self.getMandateAccount(mandateId)
|
||||||
|
if not poolAccount:
|
||||||
|
return self.creditSubscriptionBudget(mandateId, planKey, periodLabel="Erstaktivierung")
|
||||||
|
|
||||||
|
existing = self.db.getRecordset(
|
||||||
|
BillingTransaction,
|
||||||
|
recordFilter={
|
||||||
|
"accountId": poolAccount["id"],
|
||||||
|
"transactionType": TransactionTypeEnum.CREDIT.value,
|
||||||
|
"referenceType": ReferenceTypeEnum.SUBSCRIPTION.value,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if existing:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.creditSubscriptionBudget(mandateId, planKey, periodLabel="Erstaktivierung")
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Workflow Cost Query
|
# Workflow Cost Query
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
|
|
|
||||||
|
|
@ -273,10 +273,8 @@ def verifyCheckout(
|
||||||
):
|
):
|
||||||
"""Verify a Stripe Checkout Session and activate the subscription if paid.
|
"""Verify a Stripe Checkout Session and activate the subscription if paid.
|
||||||
|
|
||||||
This is the synchronous counterpart to the checkout.session.completed webhook.
|
Idempotent: if the webhook already processed the session, returns success.
|
||||||
It's called by the frontend immediately after returning from Stripe to handle
|
Called by the frontend immediately after returning from Stripe.
|
||||||
environments where webhooks may be delayed or unavailable (e.g. localhost dev).
|
|
||||||
The logic is idempotent — if the webhook already processed the session, this is a no-op.
|
|
||||||
"""
|
"""
|
||||||
mandateId = _resolveMandateId(context)
|
mandateId = _resolveMandateId(context)
|
||||||
if not mandateId:
|
if not mandateId:
|
||||||
|
|
@ -294,7 +292,6 @@ def verifyCheckout(
|
||||||
payStatus = session.get("payment_status")
|
payStatus = session.get("payment_status")
|
||||||
if session.get("status") != "complete":
|
if session.get("status") != "complete":
|
||||||
return {"status": "pending", "message": "Checkout not yet completed"}
|
return {"status": "pending", "message": "Checkout not yet completed"}
|
||||||
# Subscription checkouts with trial / $0 first period use no_payment_required, not paid.
|
|
||||||
if payStatus not in ("paid", "no_payment_required"):
|
if payStatus not in ("paid", "no_payment_required"):
|
||||||
return {"status": "pending", "message": "Checkout not yet completed"}
|
return {"status": "pending", "message": "Checkout not yet completed"}
|
||||||
|
|
||||||
|
|
@ -306,18 +303,31 @@ def verifyCheckout(
|
||||||
try:
|
try:
|
||||||
_handleSubscriptionCheckoutCompleted(session, f"verify-{data.sessionId}")
|
_handleSubscriptionCheckoutCompleted(session, f"verify-{data.sessionId}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(
|
logger.warning(
|
||||||
"verifyCheckout: handler failed for session %s mandate %s: %s",
|
"verifyCheckout: handler raised for session %s mandate %s: %s",
|
||||||
data.sessionId,
|
data.sessionId,
|
||||||
mandateId,
|
mandateId,
|
||||||
e,
|
e,
|
||||||
)
|
)
|
||||||
raise HTTPException(
|
|
||||||
status_code=500,
|
|
||||||
detail="Subscription-Aktivierung nach Checkout fehlgeschlagen. Bitte erneut versuchen oder Support informieren.",
|
|
||||||
) from e
|
|
||||||
|
|
||||||
return {"status": "activated", "message": "Subscription activated"}
|
from modules.serviceCenter.services.serviceSubscription.mainServiceSubscription import (
|
||||||
|
getService as getSubscriptionService,
|
||||||
|
)
|
||||||
|
from modules.datamodels.datamodelSubscription import OPERATIVE_STATUSES
|
||||||
|
|
||||||
|
subService = getSubscriptionService(context.user, mandateId)
|
||||||
|
operative = subService.getOperativeSubscription(mandateId)
|
||||||
|
if operative and operative.get("status") in [s.value for s in OPERATIVE_STATUSES]:
|
||||||
|
planKey = operative.get("planKey", "")
|
||||||
|
if planKey:
|
||||||
|
try:
|
||||||
|
from modules.interfaces.interfaceDbBilling import _getRootInterface as _getBillingRoot
|
||||||
|
_getBillingRoot().ensureActivationBudget(mandateId, planKey)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.warning("verifyCheckout: ensureActivationBudget failed: %s", ex)
|
||||||
|
return {"status": "activated", "message": "Subscription activated"}
|
||||||
|
|
||||||
|
return {"status": "pending", "message": "Subscription activation pending — webhook may still be processing."}
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue