feat(payment): harden Stripe Checkout top-ups - #367
Conversation
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
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (16)
📝 WalkthroughWalkthroughThe 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. ChangesStripe top-up integration
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
Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
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
sk_test_*controller/option.goconflict against currentmainwhile retaining generic secret redactionBehavior tests
19.99→1999)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
Bug Fixes