Skip to content

feat(payment): harden Stripe Checkout top-ups - #367

Merged
Laisky merged 13 commits into
mainfrom
feat/stripe-checkout-2026
Aug 19, 2026
Merged

feat(payment): harden Stripe Checkout top-ups#367
Laisky merged 13 commits into
mainfrom
feat/stripe-checkout-2026

Conversation

@Laisky

@Laisky Laisky commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Summary

Supersedes #355 with a hardened implementation built from its head branch.

This preserves the original goal—optional USD top-ups through Stripe Checkout—while tightening the payment lifecycle and completing behavior-oriented coverage.

What changed

  • validate and convert USD amounts to exact Stripe minor units; reject more than two decimal places
  • require a trusted HTTPS public origin, allowing HTTP only for loopback development with sk_test_*
  • scope Stripe idempotency keys and pending session placeholders by user and request
  • reject retries when immutable order terms or lifecycle state no longer match
  • never expose a Checkout URL until the local order is durably bound
  • expire the Stripe Checkout Session on local binding failure
  • cap raw webhook payloads at 64 KiB and verify signatures without coupling delivery to the SDK API-version constant
  • return retryable failures for unknown paid sessions instead of silently acknowledging an uncredited payment
  • resolve the controller/option.go conflict against current main while retaining generic secret redaction

Behavior tests

  • exact cents conversion (19.991999)
  • rejection of fractional cents, minimum violations, non-finite values, and maximum overflow
  • trusted-origin and HTTPS policy, including loopback test-mode behavior
  • user-scoped idempotency identity and retry drift rejection
  • frontend exact amount submission and minimum enforcement
  • frontend paid-return polling, explicit credited state, and balance/history refresh

Validation

GitHub Actions is the source of truth for the full repository test matrix because this change was authored through the connected GitHub workspace. The PR remains draft until all required checks are green.

Closes the implementation goal of #355.

Summary by CodeRabbit

  • New Features

    • Added integrated Stripe Checkout for USD balance top-ups.
    • Added configurable minimum top-up amounts and Stripe readiness status.
    • Added payment history with processing, paid, failed, and canceled statuses.
    • Added automatic payment confirmation and balance updates after checkout.
    • Preserved redemption-code top-ups alongside the new payment flow.
  • Bug Fixes

    • Improved validation for payment amounts, trusted checkout URLs, duplicate requests, and payment mismatches.
    • Added safer handling for canceled, delayed, or repeated payment notifications.

paxaq and others added 11 commits July 17, 2026 22:20
Introduce Stripe Checkout sessions for freeform USD top-ups, webhook
credit with idempotent payment_orders, config/admin options, and modern
TopUp UI. Disabled when STRIPE_SECRET_KEY is unset.
Address PR review: credit quota in the same transaction as marking paid,
use stripe client.New instead of package-level Key, expose stripe_enabled
and min_topup_usd on /api/status, and harden Modern TopUpPage (dynamic min
presets, hide Stripe when disabled, string-only logs, a11y/responsive).
- Optimistic pending→paid claim (RowsAffected) to prevent double settle
- Limit Stripe webhook body to 1 MiB
- Gate stripe_enabled on both secret and webhook secret
- Allow cent precision (step 0.01) on top-up amount input
Address Laisky PR feedback for production-safe top-up:
- Hide Stripe secrets from OptionMap/GetOptions (Key/APIKey sensitive)
- StripeReady gate: secret + webhook + trusted public base URL
- STRIPE_PUBLIC_BASE_URL; no request Host for live return URLs
- Local order + request_id before Checkout; Stripe Idempotency-Key
- Success/cancel via /topup?stripe= query; order status poll API
- Settlement: reload on empty claim, require user RowsAffected,
  amount/currency check, event id dedupe, unknown session 2xx
- Payment order history with immutable amount_cents
- Unit tests for settle idempotency, amount mismatch, missing user
… user

- Record webhook event ids only after handlers finish successfully so 5xx can retry
- Short-circuit already-processed events via HasStripeWebhookEvent
- Composite unique index on (user_id, request_id)
- Amount/currency mismatch → manual_review without crediting
- Log GetUserEmail failures; i18n status labels; aria-pressed presets
- Concurrent settle test
Resolve conflicts keeping both upstream Resend email support and Stripe
checkout hardening (sensitive options, StripeReady, payment orders).
- Wrap TopUpPage tests in MemoryRouter for useSearchParams
- Guard amount/currency manual_review with pending status claim
- Fail concurrent settle test when retries are exhausted
@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ceaca025-bbaf-48d7-89d6-83c02335a5b7

📥 Commits

Reviewing files that changed from the base of the PR and between f882258 and 60f2d3a.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (16)
  • common/config/config.go
  • controller/misc.go
  • controller/option.go
  • controller/payment_stripe.go
  • controller/payment_stripe_hardening_test.go
  • controller/payment_stripe_ready_test.go
  • go.mod
  • model/main.go
  • model/option.go
  • model/payment_order.go
  • model/payment_order_ordering_test.go
  • model/payment_order_test.go
  • router/api.go
  • web/modern/src/pages/topup/TopUpPage.test.tsx
  • web/modern/src/pages/topup/TopUpPage.tsx
  • web/modern/src/pages/topup/__tests__/TopUpPage.amount.test.tsx

📝 Walkthrough

Walkthrough

The PR adds Stripe USD top-ups with checkout sessions, payment-order persistence, signed webhook settlement, status reporting, API routes, and a rewritten top-up page with payment history and fulfillment polling.

Changes

Stripe top-up integration

Layer / File(s) Summary
Configuration and readiness
common/config/config.go, model/option.go, controller/misc.go, controller/payment_stripe_ready_test.go, model/main.go, controller/option.go, go.mod
Stripe credentials, public URL, minimum top-up settings, readiness reporting, option handling, migrations, request-scoped logging, and the Stripe SDK dependency are added.
Payment-order persistence and settlement
model/payment_order.go, model/payment_order_test.go, model/payment_order_ordering_test.go
Payment orders and webhook events support idempotency, event deduplication, session lookup, transactional quota settlement, terminal statuses, manual review, and concurrent settlement tests.
Checkout and webhook API
controller/payment_stripe.go, router/api.go, controller/payment_stripe_hardening_test.go
Authenticated checkout and order APIs plus a public webhook endpoint validate requests, create or reuse Stripe sessions, bind payment orders, verify signatures, settle payments, and handle terminal events.
Top-up page and tests
web/modern/src/pages/topup/TopUpPage.tsx, web/modern/src/pages/topup/TopUpPage.test.tsx, web/modern/src/pages/topup/__tests__/TopUpPage.amount.test.tsx
The page submits Stripe amounts, loads system status and history, polls returned sessions, displays payment outcomes, and retains redemption and external portal actions. Tests cover routing, validation, cancellation, and successful fulfillment.

Estimated code review effort: 5 (Critical) | ~90 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TopUpPage
  participant StripeAPI
  participant Stripe
  participant PaymentOrder
  participant StripeWebhook
  TopUpPage->>StripeAPI: Submit amount_usd
  StripeAPI->>PaymentOrder: Create or reuse pending order
  StripeAPI->>Stripe: Create Checkout Session
  StripeAPI-->>TopUpPage: Return checkout URL
  Stripe->>StripeWebhook: Send signed payment event
  StripeWebhook->>PaymentOrder: Settle payment and credit quota
  PaymentOrder-->>StripeWebhook: Return acknowledgement
Loading

Possibly related PRs

  • Laisky/one-api#355: Overlaps with the Stripe configuration, controller, payment-order model, routes, migration, status handling, and top-up UI changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/stripe-checkout-2026

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Laisky
Laisky marked this pull request as ready for review August 19, 2026 20:43
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@Laisky
Laisky merged commit 6c6da85 into main Aug 19, 2026
9 of 12 checks passed
@Laisky
Laisky deleted the feat/stripe-checkout-2026 branch August 19, 2026 20:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants