summaryrefslogtreecommitdiff
path: root/internal/billing/stripe.go
diff options
context:
space:
mode:
authorChia <Chia@93.nz>2026-08-06 15:58:57 +1200
committerChia <Chia@93.nz>2026-08-06 15:58:57 +1200
commit3f702084d20b3c3a3ea916f3110e99b22bda60b3 (patch)
tree517f76c51025ce1ee085ea4898c60f799e5c37ea /internal/billing/stripe.go
parent41e322c53d7b4b796eb377d0df9c29ecd10ba431 (diff)
feat: complete commercial developer workflowspublish-commercial-control-plane
Add tenant-safe usage observability, prepaid billing controls, API key lifecycle management, Embeddings metering, configurable billing alerts, and resilient provider health propagation. Harden Stripe failure handling, migrations, readiness, and the authenticated control-plane UI with end-to-end verification evidence.
Diffstat (limited to '')
-rw-r--r--internal/billing/stripe.go71
1 files changed, 40 insertions, 31 deletions
diff --git a/internal/billing/stripe.go b/internal/billing/stripe.go
index 032eb02..ea4b7ea 100644
--- a/internal/billing/stripe.go
+++ b/internal/billing/stripe.go
@@ -29,6 +29,35 @@ func (s *Service) CreateCheckout(ctx context.Context, input CheckoutInput) (Chec
if err != nil {
return CheckoutResult{}, err
}
+ params := s.checkoutSessionParams(orderID, input)
+ customerID, err := s.ensureStripeCustomer(ctx, input.TenantID)
+ if err != nil {
+ return CheckoutResult{}, s.failCheckoutCreation(ctx, orderID, err)
+ }
+ if customerID != "" {
+ params.Customer = stripe.String(customerID)
+ } else {
+ params.CustomerCreation = stripe.String(string(stripe.CheckoutSessionCustomerCreationAlways))
+ if strings.TrimSpace(input.CustomerEmail) != "" {
+ params.CustomerEmail = stripe.String(strings.TrimSpace(input.CustomerEmail))
+ }
+ }
+ session, err := s.createStripeCheckout(ctx, params)
+ if err != nil {
+ return CheckoutResult{}, s.failCheckoutCreation(ctx, orderID, fmt.Errorf("create Stripe Checkout Session: %w", err))
+ }
+ if session == nil || session.ID == "" || session.URL == "" {
+ return CheckoutResult{}, s.failCheckoutCreation(ctx, orderID, errors.New("Stripe returned an incomplete Checkout Session"))
+ }
+ if _, err := s.db.Exec(ctx, `
+ UPDATE topup_orders SET stripe_session_id = $2, checkout_url = $3
+ WHERE id = $1`, orderID, session.ID, session.URL); err != nil {
+ return CheckoutResult{}, s.failCheckoutCreation(ctx, orderID, fmt.Errorf("persist Stripe Checkout Session: %w", err))
+ }
+ return CheckoutResult{OrderID: orderID, SessionID: session.ID, URL: session.URL}, nil
+}
+
+func (s *Service) checkoutSessionParams(orderID string, input CheckoutInput) *stripe.CheckoutSessionCreateParams {
params := &stripe.CheckoutSessionCreateParams{
Mode: stripe.String("payment"),
ClientReferenceID: stripe.String(orderID),
@@ -58,43 +87,23 @@ func (s *Service) CreateCheckout(ctx context.Context, input CheckoutInput) (Chec
},
}},
}
- 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 {
- params.CustomerCreation = stripe.String(string(stripe.CheckoutSessionCustomerCreationAlways))
- if strings.TrimSpace(input.CustomerEmail) != "" {
- params.CustomerEmail = stripe.String(strings.TrimSpace(input.CustomerEmail))
- }
- }
if s.stripeAutomaticTax {
params.AutomaticTax = &stripe.CheckoutSessionCreateAutomaticTaxParams{Enabled: stripe.Bool(true)}
params.TaxIDCollection = &stripe.CheckoutSessionCreateTaxIDCollectionParams{Enabled: stripe.Bool(true)}
params.LineItems[0].PriceData.ProductData.TaxCode = stripe.String(s.stripeProductTaxCode)
}
params.SetIdempotencyKey("aigw_topup_" + orderID)
- session, err := s.createStripeCheckout(ctx, params)
- 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(fmt.Errorf("create Stripe Checkout Session: %w", err), fmt.Errorf("mark top-up order failed: %w", updateErr))
- }
- return CheckoutResult{}, fmt.Errorf("create Stripe Checkout Session: %w", err)
- }
- if session.ID == "" || session.URL == "" {
- return CheckoutResult{}, errors.New("Stripe returned an incomplete Checkout Session")
- }
- if _, err := s.db.Exec(ctx, `
- UPDATE topup_orders SET stripe_session_id = $2, checkout_url = $3
- WHERE id = $1`, orderID, session.ID, session.URL); err != nil {
- return CheckoutResult{}, fmt.Errorf("persist Stripe Checkout Session: %w", err)
- }
- return CheckoutResult{OrderID: orderID, SessionID: session.ID, URL: session.URL}, nil
+ return params
+}
+
+func (s *Service) failCheckoutCreation(ctx context.Context, orderID string, cause error) error {
+ _, updateErr := s.db.Exec(ctx, `UPDATE topup_orders
+ SET status='failed', reconciliation_status='unknown', reconciliation_error=$2
+ WHERE id=$1 AND status='pending'`, orderID, truncateError(cause))
+ if updateErr != nil {
+ return errors.Join(cause, fmt.Errorf("mark top-up order failed: %w", updateErr))
+ }
+ return cause
}
func checkoutReturnURL(raw, orderID string, includeStripeSession bool) string {