Payment methods catalog
Mercentia ships a single canonical method dictionary (payment_methods_catalog) shared across every payment provider, plus an eligibility table (payment_method_eligibility) that says exactly which (provider × method × country × currency) tuples are valid.
The non-negotiable rule:
The shopper sees ONLY the methods they can actually pay with. A US shopper never sees UPI. An Indian shopper never sees Affirm. The eligibility filter is non-negotiable.
Browsing the catalog
- Dashboard — Settings → Methods catalog (
/settings/payments/methods). Filter by category or region, expand any method to see every (provider × country × currency) combo that enables it. - API —
GET /api/v1/payment-providers/catalogreturns the same data as JSON.
Schema
payment_methods_catalog
method_key VARCHAR(40) PRIMARY KEY -- 'apple_pay', 'pix', 'klarna'
display_name VARCHAR(80)
category VARCHAR(20) -- 'card', 'wallet', 'bank_redirect',
-- 'bnpl', 'crypto', 'cash_on_delivery',
-- 'invoice', 'direct_debit'
needs_sdk BOOLEAN -- true → renders via Stripe.js / Adyen Web Drop-in
region_label VARCHAR(20) -- 'global', 'eu', 'us', 'uk', 'india',
-- 'sea', 'latam', 'au'
payment_method_eligibility
provider VARCHAR(40) -- 'stripe', 'razorpay', 'adyen',
-- 'square', 'paypal', 'klarna'
method_key VARCHAR(40) -- FK → payment_methods_catalog
customer_country VARCHAR(2) -- ISO-2 or '*' for any
currency VARCHAR(3) -- ISO-4217
min_amount_minor INTEGER -- e.g. 3500 = £35 Klarna floor
max_amount_minor INTEGER
provider_method_code VARCHAR(40) -- 'klarna', 'scheme', 'directEbanking_DE' etc.
is_active BOOLEAN
UNIQUE(provider, method_key, customer_country, currency)
How a method ends up on a shopper’s checkout
The routePaymentMethods() function (apps/gateway/src/services/payments/methodRouter.ts) applies four filters in order:
- Installation enabled + connected — provider exists for the store with
status='connected'. - Currency supported — the installation’s
supported_currenciesarray contains the cart currency. - Eligibility — at least one
(provider, method_key, customer_country, currency)row exists;customer_country='*'is a wildcard. - Merchant config — the installation’s
enabled_methodsarray contains the method.
The router then groups by installation (provider) and sorts within each block: local-region methods first, then card → wallet → bank_redirect → bnpl → direct_debit → cash_on_delivery, then alphabetical.
Methods that are shadowed by a higher-priority installation are emitted in ineligible with the reason — useful for the dashboard’s “test eligibility” tool.
Currently supported methods (2026)
Cards & wallets
| Method | Providers | Region |
|---|---|---|
card |
stripe, razorpay, adyen, square | global |
apple_pay |
stripe, adyen, square | global |
google_pay |
stripe, square | global |
paypal |
paypal | global |
cashapp |
stripe | us |
revolut_pay |
stripe | uk |
wechat_pay |
stripe | global |
alipay |
stripe | global |
mb_way |
adyen | eu |
grabpay |
adyen | sea |
dana |
adyen | sea |
ovo |
adyen | sea |
paytm, phonepe |
razorpay | india |
BNPL
| Method | Providers | Region |
|---|---|---|
klarna |
stripe, adyen | global (GB, US, DE, FR, IT, ES, NL, BE, PL, SE, NO, FI, DK, AT, CH, IE, PT) |
afterpay_clearpay |
stripe | global (GB, US, AU) |
affirm |
stripe | us |
zip |
stripe | us / au |
Bank redirects
| Method | Providers | Region |
|---|---|---|
ideal |
stripe, adyen | eu (NL) |
bancontact |
stripe, adyen | eu (BE) |
sofort |
adyen | eu (DE/AT) |
giropay |
adyen | eu (DE — note: deprecated by Giropay 2024; rows kept for legacy carts) |
blik |
stripe, adyen | eu (PL) |
eps |
stripe, adyen | eu (AT) |
p24 |
stripe, adyen | eu (PL) |
multibanco |
stripe, adyen | eu (PT) |
upi |
razorpay | india |
netbanking |
razorpay | india |
emi |
razorpay | india |
fpx |
adyen | sea (MY) |
promptpay |
adyen | sea (TH) |
pix |
stripe, adyen | latam (BR) |
webpay |
— | latam (placeholder; provider integration pending) |
payid |
adyen | au |
interac |
square | us (CA) |
Direct debit
| Method | Providers | Region |
|---|---|---|
sepa_debit |
stripe | eu |
ach_debit |
stripe | us |
becs_debit |
stripe | au |
Cash & invoice
| Method | Providers | Region |
|---|---|---|
boleto |
stripe, adyen | latam (BR) |
oxxo |
stripe, adyen | latam (MX) |
cash_on_delivery |
— | global (off-platform; merchant flips order to “paid on delivery”) |
Adding a new method
- Add a migration that inserts the row into
payment_methods_catalog. - In the same migration, insert every
(provider, method_key, customer_country, currency)eligibility row. - If the method has a
regionLabelnot in['global','eu','us','uk','india','sea','latam','au'], updateregionsFor()inmethodRouter.tsso the local-method sort still works. - If the method needs an SDK (Stripe.js Payment Element, Adyen Drop-in), confirm
needs_sdk = TRUEand verify the storefront dispatcher inapps/storefront/components/checkout/PaymentMethods.vuealready routes the method key to the right SDK mount. - Add a test case to
methodRouter.test.tscovering at least one (country, currency) combination. - Update the table above in this doc.
Adding a new provider
Adding a brand-new provider (not just a method) is a bigger lift:
- Extend the
PaymentProviderNameenum in both the schema andmethodRouter.ts. - Implement the provider’s adapter (
apps/gateway/src/services/payments/providers/<provider>.ts) covering: connect, charge, refund, webhook verify, webhook dispatch. - Add a row to the
providerenum inpayment_provider_installations. - Seed eligibility rows in a new migration.
- Add a dashboard provider tile (
apps/dashboard/pages/settings/payments/providers.vue). - Wire the webhook dispatcher in
apps/gateway/src/routes/payment-providers.ts.
Migration history
- 0070 — initial 22-method catalog (Stripe global + Razorpay India + Adyen EU + Square US + PayPal).
- 0107 — 2026 international expansion: 20 new method keys (Pix, Boleto, OXXO, Cash App, Zip, Revolut Pay, GrabPay, FPX, PromptPay, DANA, OVO, WeChat Pay, Alipay, RuPay, Multibanco, MB Way, BECS Direct Debit, PayID, Interac), 70+ new eligibility rows, Klarna eligibility expanded from 3 countries (GB/US/DE) to 13.
Related
- Wallet express checkout — Apple Pay / Google Pay / Link one-tap.
- Express checkout (saved-card) — non-wallet one-tap.
- BNPL eligibility —
@mercentia/bnpl-eligibilitypackage — per-cart BNPL gating with floor + ceiling + risk signals.