Payment Architecture — Stripe Connect, Razorpay, 15% Fee, and Why Two Providers #118
LarytheLord
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Related Issue
This discussion provides context for issue #106 (Stripe Connect). It also explains decisions that affect all payment-related work.
The Payment Stack
The platform uses two providers simultaneously:
Both singletons already exist. Both webhook handlers already exist (
app/api/payments/webhooks/stripeandapp/api/payments/webhooks/razorpay). Issue #106 connects Stripe to a real onboarding flow — it is not starting from zero.Why Two Providers
Razorpay is the dominant payment provider in India. Indian bank accounts, UPI, and INR transactions work natively. Stripe's India support has historically been unreliable for domestic payouts. Since Open Paws India Pvt Ltd is based in Bangalore and many interns and clients will be Indian, Razorpay is the practical choice for INR.
Stripe Connect handles everything else. It is the global standard for marketplace payouts. When international clients pay, or when adventurers have international bank accounts, Stripe routes the money.
The routing logic in
lib/payment-provider.ts:The 15% Platform Fee
Every paid quest has a 15% commission taken by the platform.
This is calculated at payment time in
payment-provider.ts. The fee is stored on theTransactionmodel asplatformFeeandplatformFeeRate.Why 15%? The org plan projects ₹1–2 lakh revenue in months 1–3, ₹5–10 lakh by month 6. At 15%, that requires ₹7–13 lakh in GMV in months 1–3. That is achievable with 20 interns completing 1–2 quests each at an average reward of ₹15–20K per quest.
Stripe Connect Express
Adventurers receive payouts via Stripe Connect Express. This is the right product because:
The flow:
stripeOnboardingDone = truesaved to theirAdventurerProfilePayment Timeline in the Quest Lifecycle
The PaymentIntent is created at quest acceptance (not at submission). This creates an escrow-like behaviour — the money is reserved but not transferred until approval.
What Phase 2 Builds vs What Already Exists
Already exists (before issue #106):
lib/stripe.ts— Stripe singleton with lazy initlib/razorpay.ts— Razorpay singletonlib/payment-provider.ts— unified routingPOST /api/payments/webhooks/stripe— handles payment_intent eventsPOST /api/payments/webhooks/razorpay— handles payment.captured eventsPOST /api/payments— records transactions (simulated,txn_${Date.now()})What issue #106 adds:
stripeAccountId+stripeOnboardingDoneonAdventurerProfilestripeCustomerIdonCompanyProfilestripePaymentIntentId+platformFee+platformFeeRateonTransactionPOST /api/payments/stripe/connect— create Express account + return onboarding URLGET /api/payments/stripe/connect/callback— handle Stripe return, mark onboarding donePOST /api/payments/stripe/intent— create PaymentIntent for quest rewardNot in Phase 2:
Simulated Payments Must Still Work
The existing
POST /api/paymentsendpoint uses simulated transactions (txn_${Date.now()}). This must continue to work when Stripe is not configured (i.e., in local development without Stripe env vars).When implementing #106, do not break the fallback path. If
STRIPE_SECRET_KEYis not set, the system should gracefully fall back to the simulated payment model.BOOTCAMP Track Gets No Payment
This is a deliberate architectural decision (Decision #13 in
docs/ARCHITECTURE_DECISIONS.md).Bootcamp students earn XP and portfolio work in Phase 2. No payment. When building #106, gate the Stripe integration:
Do not trigger PaymentIntents or transfers for BOOTCAMP track quest approvals.
Env Vars Required for Payment Work
Get Stripe test keys from dashboard.stripe.com. Never commit real keys.
Beta Was this translation helpful? Give feedback.
All reactions