diff options
| author | Chia <Chia@93.nz> | 2026-08-06 09:29:41 +1200 |
|---|---|---|
| committer | Chia <Chia@93.nz> | 2026-08-06 09:32:46 +1200 |
| commit | 41e322c53d7b4b796eb377d0df9c29ecd10ba431 (patch) | |
| tree | c730526150e55e39b822d5197e4a20318ecaa449 /internal/billing/stripe.go | |
| parent | eadb2ffe85c43cf6fc741c9823cd28eedb4a844c (diff) | |
feat: complete commercial control plane, billing, auth, and model catalog
- add PostgreSQL control-plane persistence with Redis-degraded hot reload
- implement prepaid balance, usage ledger, Stripe top-up and reconciliation
- add registration, email verification, password reset, invitations and RBAC
- support TOTP, Passkey MFA, device sessions, quotas and rate limits
- add tenant billing profiles, audit logs and operational readiness checks
- build authenticated admin console, Quickstart, Playground and usage analytics
- add public model catalog with pricing, filtering and cost estimation
- support OpenAI Responses providers and provider health failover
- validate real upstream usage reporting and balance settlement
Diffstat (limited to 'internal/billing/stripe.go')
| -rw-r--r-- | internal/billing/stripe.go | 25 |
1 files changed, 23 insertions, 2 deletions
diff --git a/internal/billing/stripe.go b/internal/billing/stripe.go index 59ee9bd..032eb02 100644 --- a/internal/billing/stripe.go +++ b/internal/billing/stripe.go @@ -58,8 +58,13 @@ func (s *Service) CreateCheckout(ctx context.Context, input CheckoutInput) (Chec }, }}, } - var customerID string - _ = s.db.QueryRow(ctx, `SELECT stripe_customer_id FROM stripe_customers WHERE tenant_id=$1`, input.TenantID).Scan(&customerID) + customerID, err := s.ensureStripeCustomer(ctx, input.TenantID) + if err != nil { + if _, updateErr := s.db.Exec(ctx, `UPDATE topup_orders SET status = 'failed' WHERE id = $1 AND status = 'pending'`, orderID); updateErr != nil { + return CheckoutResult{}, errors.Join(err, fmt.Errorf("mark top-up order failed: %w", updateErr)) + } + return CheckoutResult{}, err + } if customerID != "" { params.Customer = stripe.String(customerID) } else { @@ -154,6 +159,7 @@ func (s *Service) processStripeEvent(ctx context.Context, event stripe.Event) er return s.processCheckoutEvent(ctx, event) case stripe.EventTypeChargeSucceeded, stripe.EventTypeChargeUpdated, stripe.EventTypeChargeRefunded, stripe.EventTypeRefundCreated, stripe.EventTypeRefundUpdated, stripe.EventTypeRefundFailed, + stripe.EventTypePaymentIntentSucceeded, stripe.EventTypePaymentIntentPaymentFailed, stripe.EventTypePaymentIntentCanceled, stripe.EventTypeChargeDisputeCreated, stripe.EventTypeChargeDisputeUpdated, stripe.EventTypeChargeDisputeClosed, stripe.EventTypeChargeDisputeFundsWithdrawn, stripe.EventTypeChargeDisputeFundsReinstated, @@ -176,6 +182,9 @@ func (s *Service) processCheckoutEvent(ctx context.Context, event stripe.Event) if err := json.Unmarshal(event.Data.Raw, &session); err != nil { return ErrInvalidAmount } + if session.Metadata["aigw_action"] == autoTopUpAction { + return s.processAutoTopUpSetupEvent(ctx, event, &session) + } if event.ID == "" || session.ID == "" || session.ClientReferenceID == "" { return ErrInvalidAmount } @@ -330,6 +339,18 @@ func (s *Service) processOperationalStripeEvent(ctx context.Context, event strip } } switch event.Type { + case stripe.EventTypePaymentIntentSucceeded, stripe.EventTypePaymentIntentPaymentFailed, stripe.EventTypePaymentIntentCanceled: + var intent stripe.PaymentIntent + if json.Unmarshal(event.Data.Raw, &intent) != nil || intent.ID == "" { + return ErrInvalidAmount + } + if event.Type == stripe.EventTypePaymentIntentSucceeded { + if err := s.applyAutoTopUpPaymentIntentTx(ctx, tx, &intent); err != nil { + return err + } + } else if err := s.applyAutoTopUpPaymentIntentFailureTx(ctx, tx, &intent); err != nil { + return err + } case stripe.EventTypeChargeSucceeded, stripe.EventTypeChargeUpdated, stripe.EventTypeChargeRefunded: var charge stripe.Charge if json.Unmarshal(event.Data.Raw, &charge) != nil || charge.ID == "" { |
