Practitionist / Practitionist/elluminar_web

Dodo Payments provider — Merchant of Record for international sales

Open
#1 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

payments post-mvp v1
Dominant language
TypeScript
Stars
1
Forks
0
Avg merge
17h 2m
Merged PRs (30d)
18

Description

Why / Context

Razorpay handles all MVP payments (INR-first). Post-MVP we add Dodo Payments as Merchant of Record for international expansion (per the locked decision: "Razorpay pre-MVP, Dodo Payments post-MVP"). MoR means Dodo owns global tax/compliance remittance for international sales, which pairs with the Regional/PPP pricing issue. The schema and payment layer were designed provider-agnostic from day one, so this is an adapter implementation, not a migration.

Schema already in place — NO migration required

  • PaymentProviderKind.DODO enum value on Payment.provider / Subscription.provider
  • Payment.providerOrderRef / providerPaymentRef (unique per provider) + raw Json
  • Refund.providerRefundRef
  • SubscriptionPlan.providerRefs Json — add dodo.productId per interval×currency alongside existing razorpay ids
  • UserProviderIdentity(provider: DODO, externalRef) for Dodo customer ids
  • WebhookEvent(provider: DODO, eventRef) — idempotency backbone already handles multi-provider

Implementation plan

  1. Create Dodo merchant account; configure products mirroring SubscriptionPlan intervals and one-time SKUs; store ids in SubscriptionPlan.providerRefs.dodo.
  2. Implement src/lib/payments/dodo.ts satisfying the existing PaymentProvider interface (createCheckout, createSubscription, cancelSubscription, pauseSubscription, refund, verifyWebhook, parseEvent) using the dodopayments Node SDK.
  3. Add /api/webhooks/dodo route handler: verify per Standard Webhooks spec (svix-style signature headers), insert WebhookEvent(provider: DODO), dispatch to the same fulfillment service Razorpay uses (payment.succeeded, subscription.active, subscription.renewed, refund.succeeded, failure events → PAST_DUE).
  4. Provider selection policy in src/lib/payments/index.ts: route by order currency/region (INR → Razorpay, non-INR → Dodo) behind a FeatureFlag('dodo-payments').
  5. Evaluate the official BetterAuth dodopayments plugin (better-auth.com/docs/plugins/dodopayments) for customer mapping/portal; adopt only if it doesn't fight our Subscription tables (decision note in PR).
  6. Customer portal link (Dodo-hosted) on the billing page for Dodo-billed users.
  7. Invoice handling: Dodo is MoR and issues its own invoices for international sales — store Dodo invoice refs in Payment.raw; suppress our GST Invoice generation for MoR payments (flag in Invoice.data).
  8. Subscription migration playbook (docs/payments-dodo.md): existing Razorpay subscribers stay on Razorpay until cancellation; new international signups go to Dodo; write the support runbook.
  9. Unit tests for parseEvent mapping + E2E in Dodo test mode.

Acceptance criteria

  • International (non-INR) checkout completes via Dodo test mode → Order PAID, fulfillment identical to Razorpay path
  • Dodo subscription lifecycle events transition Subscription.status correctly (active → past_due → cancelled)
  • Webhook replay is a no-op (WebhookEvent dedupe)
  • Refund via Dodo updates Refund.status=PROCESSED and writes ledger reversals
  • Razorpay flows unaffected (regression suite green)

Dependencies / sequencing

  • Builds on MVP M7 (commerce core) + M8 (subscriptions)
  • Pairs with issue "Regional/PPP pricing + multi-currency GA" (#21 in plan) — ship this first or together

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the existing PaymentProvider interface and Razorpay flow, then read src/lib/payments/index.ts and implement the adapter in src/lib/payments/dodo.ts. Review the /api/webhooks/dodo route requirements and docs/payments-dodo.md, and add the specified parseEvent unit tests plus Dodo test-mode E2E coverage. Done means international checkout, subscription events, webhook deduplication, refunds, and the Razorpay regression suite all pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, typescript
Domain
api, backend, database, payments
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.