@adonis-agora/payments 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2375 -0
- package/dist/assets/spa/assets/index-B7rU8eIL.css +2 -0
- package/dist/assets/spa/assets/index-BcM2VH5k.js +10 -0
- package/dist/assets/spa/assets/index-BcM2VH5k.js.map +1 -0
- package/dist/assets/spa/index.html +20 -0
- package/dist/commands/main.d.ts +4 -3
- package/dist/commands/main.d.ts.map +1 -1
- package/dist/commands/main.js +9 -2
- package/dist/commands/main.js.map +1 -1
- package/dist/commands/make_billable.d.ts.map +1 -1
- package/dist/commands/make_billable.js +1 -1
- package/dist/commands/make_billable.js.map +1 -1
- package/dist/commands/make_webhook_handler.d.ts.map +1 -1
- package/dist/commands/make_webhook_handler.js +1 -1
- package/dist/commands/make_webhook_handler.js.map +1 -1
- package/dist/commands/payments_health.d.ts +31 -0
- package/dist/commands/payments_health.d.ts.map +1 -0
- package/dist/commands/payments_health.js +113 -0
- package/dist/commands/payments_health.js.map +1 -0
- package/dist/commands/payments_sync.d.ts +18 -4
- package/dist/commands/payments_sync.d.ts.map +1 -1
- package/dist/commands/payments_sync.js +136 -40
- package/dist/commands/payments_sync.js.map +1 -1
- package/dist/commands/payments_webhook.d.ts.map +1 -1
- package/dist/commands/payments_webhook.js.map +1 -1
- package/dist/configure.d.ts +12 -5
- package/dist/configure.d.ts.map +1 -1
- package/dist/configure.js +18 -5
- package/dist/configure.js.map +1 -1
- package/dist/providers/dashboard_provider.d.ts +97 -0
- package/dist/providers/dashboard_provider.d.ts.map +1 -0
- package/dist/providers/dashboard_provider.js +430 -0
- package/dist/providers/dashboard_provider.js.map +1 -0
- package/dist/providers/payments_client_provider.d.ts +41 -0
- package/dist/providers/payments_client_provider.d.ts.map +1 -0
- package/dist/providers/payments_client_provider.js +95 -0
- package/dist/providers/payments_client_provider.js.map +1 -0
- package/dist/providers/payments_provider.d.ts +35 -0
- package/dist/providers/payments_provider.d.ts.map +1 -1
- package/dist/providers/payments_provider.js +250 -36
- package/dist/providers/payments_provider.js.map +1 -1
- package/dist/src/billing/billing_health.d.ts +85 -0
- package/dist/src/billing/billing_health.d.ts.map +1 -0
- package/dist/src/billing/billing_health.js +155 -0
- package/dist/src/billing/billing_health.js.map +1 -0
- package/dist/src/billing/billing_overview.d.ts +4 -3
- package/dist/src/billing/billing_overview.d.ts.map +1 -1
- package/dist/src/billing/billing_overview.js +14 -5
- package/dist/src/billing/billing_overview.js.map +1 -1
- package/dist/src/billing/billing_store.d.ts +634 -5
- package/dist/src/billing/billing_store.d.ts.map +1 -1
- package/dist/src/billing/billing_store.js +18 -1
- package/dist/src/billing/billing_store.js.map +1 -1
- package/dist/src/billing/index.d.ts +4 -3
- package/dist/src/billing/index.d.ts.map +1 -1
- package/dist/src/billing/index.js +2 -1
- package/dist/src/billing/index.js.map +1 -1
- package/dist/src/billing/list_query.d.ts +16 -0
- package/dist/src/billing/list_query.d.ts.map +1 -0
- package/dist/src/billing/list_query.js +28 -0
- package/dist/src/billing/list_query.js.map +1 -0
- package/dist/src/billing/lucid_billing_store.d.ts +122 -4
- package/dist/src/billing/lucid_billing_store.d.ts.map +1 -1
- package/dist/src/billing/lucid_billing_store.js +811 -13
- package/dist/src/billing/lucid_billing_store.js.map +1 -1
- package/dist/src/billing/mixins/index.d.ts +5 -2
- package/dist/src/billing/mixins/index.d.ts.map +1 -1
- package/dist/src/billing/mixins/index.js +5 -2
- package/dist/src/billing/mixins/index.js.map +1 -1
- package/dist/src/billing/mixins/with_audit_event.d.ts +48 -0
- package/dist/src/billing/mixins/with_audit_event.d.ts.map +1 -0
- package/dist/src/billing/mixins/with_audit_event.js +77 -0
- package/dist/src/billing/mixins/with_audit_event.js.map +1 -0
- package/dist/src/billing/mixins/with_billable.d.ts.map +1 -1
- package/dist/src/billing/mixins/with_customer.d.ts +39 -0
- package/dist/src/billing/mixins/with_customer.d.ts.map +1 -0
- package/dist/src/billing/mixins/with_customer.js +68 -0
- package/dist/src/billing/mixins/with_customer.js.map +1 -0
- package/dist/src/billing/mixins/with_dispute.d.ts +68 -0
- package/dist/src/billing/mixins/with_dispute.d.ts.map +1 -0
- package/dist/src/billing/mixins/with_dispute.js +83 -0
- package/dist/src/billing/mixins/with_dispute.js.map +1 -0
- package/dist/src/billing/mixins/with_payment.d.ts +42 -0
- package/dist/src/billing/mixins/with_payment.d.ts.map +1 -1
- package/dist/src/billing/mixins/with_payment.js +29 -3
- package/dist/src/billing/mixins/with_payment.js.map +1 -1
- package/dist/src/billing/mixins/with_subscription.d.ts +7 -0
- package/dist/src/billing/mixins/with_subscription.d.ts.map +1 -1
- package/dist/src/billing/mixins/with_subscription.js +15 -1
- package/dist/src/billing/mixins/with_subscription.js.map +1 -1
- package/dist/src/billing/mixins/with_usage_event.d.ts +7 -0
- package/dist/src/billing/mixins/with_usage_event.d.ts.map +1 -1
- package/dist/src/billing/mixins/with_usage_event.js +15 -1
- package/dist/src/billing/mixins/with_usage_event.js.map +1 -1
- package/dist/src/billing/mixins/with_webhook_event.d.ts +18 -0
- package/dist/src/billing/mixins/with_webhook_event.d.ts.map +1 -1
- package/dist/src/billing/mixins/with_webhook_event.js +18 -1
- package/dist/src/billing/mixins/with_webhook_event.js.map +1 -1
- package/dist/src/billing/resolve_dispatch.d.ts +22 -0
- package/dist/src/billing/resolve_dispatch.d.ts.map +1 -0
- package/dist/src/billing/resolve_dispatch.js +44 -0
- package/dist/src/billing/resolve_dispatch.js.map +1 -0
- package/dist/src/billing/resolve_store.d.ts +18 -0
- package/dist/src/billing/resolve_store.d.ts.map +1 -0
- package/dist/src/billing/resolve_store.js +35 -0
- package/dist/src/billing/resolve_store.js.map +1 -0
- package/dist/src/billing/schema.d.ts +108 -0
- package/dist/src/billing/schema.d.ts.map +1 -0
- package/dist/src/billing/schema.js +350 -0
- package/dist/src/billing/schema.js.map +1 -0
- package/dist/src/billing/webhook_dispatcher.d.ts +95 -16
- package/dist/src/billing/webhook_dispatcher.d.ts.map +1 -1
- package/dist/src/billing/webhook_dispatcher.js +186 -33
- package/dist/src/billing/webhook_dispatcher.js.map +1 -1
- package/dist/src/billing/webhook_events.d.ts +67 -1
- package/dist/src/billing/webhook_events.d.ts.map +1 -1
- package/dist/src/billing/webhook_events.js +15 -0
- package/dist/src/billing/webhook_events.js.map +1 -1
- package/dist/src/billing/webhook_processor.d.ts.map +1 -1
- package/dist/src/billing/webhook_processor.js +325 -5
- package/dist/src/billing/webhook_processor.js.map +1 -1
- package/dist/src/client/define_config.d.ts +150 -0
- package/dist/src/client/define_config.d.ts.map +1 -0
- package/dist/src/client/define_config.js +98 -0
- package/dist/src/client/define_config.js.map +1 -0
- package/dist/src/client/handlers.d.ts +71 -0
- package/dist/src/client/handlers.d.ts.map +1 -0
- package/dist/src/client/handlers.js +119 -0
- package/dist/src/client/handlers.js.map +1 -0
- package/dist/src/client/index.d.ts +15 -0
- package/dist/src/client/index.d.ts.map +1 -0
- package/dist/src/client/index.js +12 -0
- package/dist/src/client/index.js.map +1 -0
- package/dist/src/client/statuses.d.ts +38 -0
- package/dist/src/client/statuses.d.ts.map +1 -0
- package/dist/src/client/statuses.js +42 -0
- package/dist/src/client/statuses.js.map +1 -0
- package/dist/src/dashboard/actions.d.ts +143 -0
- package/dist/src/dashboard/actions.d.ts.map +1 -0
- package/dist/src/dashboard/actions.js +112 -0
- package/dist/src/dashboard/actions.js.map +1 -0
- package/dist/src/dashboard/auth.d.ts +136 -0
- package/dist/src/dashboard/auth.d.ts.map +1 -0
- package/dist/src/dashboard/auth.js +152 -0
- package/dist/src/dashboard/auth.js.map +1 -0
- package/dist/src/dashboard/define_config.d.ts +80 -0
- package/dist/src/dashboard/define_config.d.ts.map +1 -0
- package/dist/src/dashboard/define_config.js +83 -0
- package/dist/src/dashboard/define_config.js.map +1 -0
- package/dist/src/dashboard/handlers.d.ts +281 -0
- package/dist/src/dashboard/handlers.d.ts.map +1 -0
- package/dist/src/dashboard/handlers.js +960 -0
- package/dist/src/dashboard/handlers.js.map +1 -0
- package/dist/src/dashboard/index.d.ts +17 -0
- package/dist/src/dashboard/index.d.ts.map +1 -0
- package/dist/src/dashboard/index.js +13 -0
- package/dist/src/dashboard/index.js.map +1 -0
- package/dist/src/dashboard/login_page.d.ts +23 -0
- package/dist/src/dashboard/login_page.d.ts.map +1 -0
- package/dist/src/dashboard/login_page.js +170 -0
- package/dist/src/dashboard/login_page.js.map +1 -0
- package/dist/src/dashboard/period.d.ts +31 -0
- package/dist/src/dashboard/period.d.ts.map +1 -0
- package/dist/src/dashboard/period.js +44 -0
- package/dist/src/dashboard/period.js.map +1 -0
- package/dist/src/dashboard/session_cookie.d.ts +42 -0
- package/dist/src/dashboard/session_cookie.d.ts.map +1 -0
- package/dist/src/dashboard/session_cookie.js +97 -0
- package/dist/src/dashboard/session_cookie.js.map +1 -0
- package/dist/src/dashboard/spa.d.ts +24 -0
- package/dist/src/dashboard/spa.d.ts.map +1 -0
- package/dist/src/dashboard/spa.js +52 -0
- package/dist/src/dashboard/spa.js.map +1 -0
- package/dist/src/define_config.d.ts +245 -10
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js +97 -1
- package/dist/src/define_config.js.map +1 -1
- package/dist/src/diagnostics.d.ts +287 -7
- package/dist/src/diagnostics.d.ts.map +1 -1
- package/dist/src/diagnostics.js +317 -1
- package/dist/src/diagnostics.js.map +1 -1
- package/dist/src/driver.d.ts +139 -5
- package/dist/src/driver.d.ts.map +1 -1
- package/dist/src/drivers/abacate.d.ts +13 -3
- package/dist/src/drivers/abacate.d.ts.map +1 -1
- package/dist/src/drivers/abacate.js +112 -19
- package/dist/src/drivers/abacate.js.map +1 -1
- package/dist/src/drivers/adyen.d.ts +164 -0
- package/dist/src/drivers/adyen.d.ts.map +1 -0
- package/dist/src/drivers/adyen.js +678 -0
- package/dist/src/drivers/adyen.js.map +1 -0
- package/dist/src/drivers/asaas.d.ts +53 -3
- package/dist/src/drivers/asaas.d.ts.map +1 -1
- package/dist/src/drivers/asaas.js +364 -29
- package/dist/src/drivers/asaas.js.map +1 -1
- package/dist/src/drivers/dodo.d.ts +135 -0
- package/dist/src/drivers/dodo.d.ts.map +1 -0
- package/dist/src/drivers/dodo.js +864 -0
- package/dist/src/drivers/dodo.js.map +1 -0
- package/dist/src/drivers/efi.d.ts +98 -0
- package/dist/src/drivers/efi.d.ts.map +1 -0
- package/dist/src/drivers/efi.js +628 -0
- package/dist/src/drivers/efi.js.map +1 -0
- package/dist/src/drivers/infinitepay.d.ts +156 -0
- package/dist/src/drivers/infinitepay.d.ts.map +1 -0
- package/dist/src/drivers/infinitepay.js +321 -0
- package/dist/src/drivers/infinitepay.js.map +1 -0
- package/dist/src/drivers/lemonsqueezy.d.ts +125 -0
- package/dist/src/drivers/lemonsqueezy.d.ts.map +1 -0
- package/dist/src/drivers/lemonsqueezy.js +632 -0
- package/dist/src/drivers/lemonsqueezy.js.map +1 -0
- package/dist/src/drivers/mercadopago.d.ts +97 -0
- package/dist/src/drivers/mercadopago.d.ts.map +1 -0
- package/dist/src/drivers/mercadopago.js +844 -0
- package/dist/src/drivers/mercadopago.js.map +1 -0
- package/dist/src/drivers/mollie.d.ts +125 -0
- package/dist/src/drivers/mollie.d.ts.map +1 -0
- package/dist/src/drivers/mollie.js +812 -0
- package/dist/src/drivers/mollie.js.map +1 -0
- package/dist/src/drivers/paddle.d.ts +148 -0
- package/dist/src/drivers/paddle.d.ts.map +1 -0
- package/dist/src/drivers/paddle.js +738 -0
- package/dist/src/drivers/paddle.js.map +1 -0
- package/dist/src/drivers/pagarme.d.ts +97 -0
- package/dist/src/drivers/pagarme.d.ts.map +1 -0
- package/dist/src/drivers/pagarme.js +829 -0
- package/dist/src/drivers/pagarme.js.map +1 -0
- package/dist/src/drivers/pagbank.d.ts +57 -0
- package/dist/src/drivers/pagbank.d.ts.map +1 -0
- package/dist/src/drivers/pagbank.js +499 -0
- package/dist/src/drivers/pagbank.js.map +1 -0
- package/dist/src/drivers/paypal.d.ts +120 -0
- package/dist/src/drivers/paypal.d.ts.map +1 -0
- package/dist/src/drivers/paypal.js +891 -0
- package/dist/src/drivers/paypal.js.map +1 -0
- package/dist/src/drivers/polar.d.ts +118 -0
- package/dist/src/drivers/polar.d.ts.map +1 -0
- package/dist/src/drivers/polar.js +604 -0
- package/dist/src/drivers/polar.js.map +1 -0
- package/dist/src/drivers/razorpay.d.ts +103 -0
- package/dist/src/drivers/razorpay.d.ts.map +1 -0
- package/dist/src/drivers/razorpay.js +867 -0
- package/dist/src/drivers/razorpay.js.map +1 -0
- package/dist/src/drivers/shared.d.ts +33 -0
- package/dist/src/drivers/shared.d.ts.map +1 -0
- package/dist/src/drivers/shared.js +37 -0
- package/dist/src/drivers/shared.js.map +1 -0
- package/dist/src/drivers/square.d.ts +133 -0
- package/dist/src/drivers/square.d.ts.map +1 -0
- package/dist/src/drivers/square.js +905 -0
- package/dist/src/drivers/square.js.map +1 -0
- package/dist/src/drivers/stripe.d.ts +41 -4
- package/dist/src/drivers/stripe.d.ts.map +1 -1
- package/dist/src/drivers/stripe.js +823 -26
- package/dist/src/drivers/stripe.js.map +1 -1
- package/dist/src/drivers/woovi.d.ts +11 -4
- package/dist/src/drivers/woovi.d.ts.map +1 -1
- package/dist/src/drivers/woovi.js +162 -29
- package/dist/src/drivers/woovi.js.map +1 -1
- package/dist/src/ensure_customer.d.ts +34 -1
- package/dist/src/ensure_customer.d.ts.map +1 -1
- package/dist/src/ensure_customer.js +29 -4
- package/dist/src/ensure_customer.js.map +1 -1
- package/dist/src/hooks/webhook_handlers.d.ts.map +1 -1
- package/dist/src/hooks/webhook_handlers.js.map +1 -1
- package/dist/src/http.d.ts +37 -3
- package/dist/src/http.d.ts.map +1 -1
- package/dist/src/http.js +69 -5
- package/dist/src/http.js.map +1 -1
- package/dist/src/index.d.ts +54 -26
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +29 -12
- package/dist/src/index.js.map +1 -1
- package/dist/src/invoice/drivers/asaas.d.ts.map +1 -1
- package/dist/src/invoice/drivers/asaas.js.map +1 -1
- package/dist/src/invoice/drivers/enotas.d.ts.map +1 -1
- package/dist/src/invoice/drivers/enotas.js.map +1 -1
- package/dist/src/invoice/drivers/focus.d.ts.map +1 -1
- package/dist/src/invoice/drivers/focus.js.map +1 -1
- package/dist/src/invoice/drivers/plugnotas.d.ts.map +1 -1
- package/dist/src/invoice/drivers/plugnotas.js.map +1 -1
- package/dist/src/invoice/drivers/tecnospeed.d.ts.map +1 -1
- package/dist/src/invoice/drivers/tecnospeed.js.map +1 -1
- package/dist/src/invoice/emit_invoice.d.ts.map +1 -1
- package/dist/src/invoice/emit_invoice.js +31 -11
- package/dist/src/invoice/emit_invoice.js.map +1 -1
- package/dist/src/invoice/invoice_manager.d.ts.map +1 -1
- package/dist/src/money.d.ts +23 -5
- package/dist/src/money.d.ts.map +1 -1
- package/dist/src/money.js +72 -7
- package/dist/src/money.js.map +1 -1
- package/dist/src/payments_manager.d.ts +8 -1
- package/dist/src/payments_manager.d.ts.map +1 -1
- package/dist/src/payments_manager.js +168 -6
- package/dist/src/payments_manager.js.map +1 -1
- package/dist/src/services/main.d.ts +38 -0
- package/dist/src/services/main.d.ts.map +1 -1
- package/dist/src/services/main.js +130 -0
- package/dist/src/services/main.js.map +1 -1
- package/dist/src/telescope/index.d.ts +2 -0
- package/dist/src/telescope/index.d.ts.map +1 -1
- package/dist/src/telescope/index.js +1 -0
- package/dist/src/telescope/index.js.map +1 -1
- package/dist/src/telescope/payments_watcher.d.ts +10 -4
- package/dist/src/telescope/payments_watcher.d.ts.map +1 -1
- package/dist/src/telescope/payments_watcher.js +66 -14
- package/dist/src/telescope/payments_watcher.js.map +1 -1
- package/dist/src/telescope/register.d.ts +62 -0
- package/dist/src/telescope/register.d.ts.map +1 -0
- package/dist/src/telescope/register.js +48 -0
- package/dist/src/telescope/register.js.map +1 -0
- package/dist/src/testing/fake_payments.d.ts +62 -0
- package/dist/src/testing/fake_payments.d.ts.map +1 -0
- package/dist/src/testing/fake_payments.js +82 -0
- package/dist/src/testing/fake_payments.js.map +1 -0
- package/dist/src/testing/fake_payments_driver.d.ts +1 -1
- package/dist/src/testing/fake_payments_driver.d.ts.map +1 -1
- package/dist/src/testing/fake_payments_driver.js.map +1 -1
- package/dist/src/testing/in_memory_billing_store.d.ts +151 -2
- package/dist/src/testing/in_memory_billing_store.d.ts.map +1 -1
- package/dist/src/testing/in_memory_billing_store.js +517 -4
- package/dist/src/testing/in_memory_billing_store.js.map +1 -1
- package/dist/src/testing/index.d.ts +3 -2
- package/dist/src/testing/index.d.ts.map +1 -1
- package/dist/src/testing/index.js +1 -0
- package/dist/src/testing/index.js.map +1 -1
- package/dist/src/testing/mutable_clock.d.ts.map +1 -1
- package/dist/src/types.d.ts +161 -5
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +30 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/webhook_handlers.d.ts +96 -2
- package/dist/src/webhook_handlers.d.ts.map +1 -1
- package/dist/src/webhook_handlers.js +91 -10
- package/dist/src/webhook_handlers.js.map +1 -1
- package/dist/src/webhook_security.d.ts +94 -3
- package/dist/src/webhook_security.d.ts.map +1 -1
- package/dist/src/webhook_security.js +173 -9
- package/dist/src/webhook_security.js.map +1 -1
- package/dist/stubs/config/payments.stub +13 -0
- package/dist/stubs/config/payments_client.stub +42 -0
- package/dist/stubs/config/payments_dashboard.stub +38 -0
- package/dist/stubs/database/migrations/create_billing_tables.stub +35 -77
- package/dist/stubs/main.js.map +1 -1
- package/dist/stubs/make/webhook_handler/main.stub +29 -11
- package/package.json +67 -32
|
@@ -0,0 +1,864 @@
|
|
|
1
|
+
import { publishPaymentDiagnostics, publishRefundDiagnostics, publishSubscriptionDiagnostics, } from '../diagnostics.js';
|
|
2
|
+
import { headerValue, httpRequest, isNotFound } from '../http.js';
|
|
3
|
+
import { emitInvoiceIfRequested } from '../invoice/emit_invoice.js';
|
|
4
|
+
import { verifyStandardWebhookSignature } from '../webhook_security.js';
|
|
5
|
+
import { requireCredential, requireCurrency } from './shared.js';
|
|
6
|
+
/**
|
|
7
|
+
* Dodo Payments driver — merchant-of-record billing for SaaS (dodopayments.com), written
|
|
8
|
+
* against the REST API with `fetch`; the `dodopayments` peer dependency is deliberately
|
|
9
|
+
* not used.
|
|
10
|
+
*
|
|
11
|
+
* Dodo is the legal seller of your digital products: it registers for, calculates and
|
|
12
|
+
* remits sales tax/VAT worldwide, and pays you net. That is a business arrangement the
|
|
13
|
+
* library does not model — it only changes which driver you configure.
|
|
14
|
+
*
|
|
15
|
+
* The consequence for the driver is that **every charge names a product you created in
|
|
16
|
+
* Dodo**. There is no amount-only endpoint anywhere in the API, so `charge()` and
|
|
17
|
+
* `createCheckout()` require a product id, and an arbitrary `amount` is only honored on a
|
|
18
|
+
* product with Pay What You Want enabled.
|
|
19
|
+
*/
|
|
20
|
+
export class DodoDriver {
|
|
21
|
+
provider = 'dodo';
|
|
22
|
+
/**
|
|
23
|
+
* `allowed_payment_method_types` is the field that restricts the hosted checkout, and
|
|
24
|
+
* Dodo's supported-methods table reaches well past cards: wallets (Apple/Google/Amazon
|
|
25
|
+
* Pay, Cash App, Revolut Pay, WeChat Pay), bank redirects (iDEAL, Bancontact, EPS,
|
|
26
|
+
* Multibanco, BLIK, Satispay), direct debit (SEPA, ACH), BNPL (Klarna,
|
|
27
|
+
* Afterpay/Clearpay, Billie), UPI and Pix. Every one of those now has a **category**
|
|
28
|
+
* name in the contract, so they stop being unroutable.
|
|
29
|
+
*
|
|
30
|
+
* Boleto appears in the raw processor-level enum but not in Dodo's table, so it is not
|
|
31
|
+
* advertised; `voucher` has no Dodo equivalent at all. Note Dodo's own wording: naming a
|
|
32
|
+
* method never *guarantees* it — eligibility still depends on the customer's country and
|
|
33
|
+
* your merchant settings, which is why {@link DodoDriver.#allowedMethods} keeps the card
|
|
34
|
+
* fallbacks Dodo tells you to keep.
|
|
35
|
+
*/
|
|
36
|
+
supportedMethods = [
|
|
37
|
+
'credit_card',
|
|
38
|
+
'debit_card',
|
|
39
|
+
'pix',
|
|
40
|
+
'upi',
|
|
41
|
+
'wallet',
|
|
42
|
+
'bank_transfer',
|
|
43
|
+
'bank_debit',
|
|
44
|
+
'bnpl',
|
|
45
|
+
'undefined',
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* `disputes: false` even though Dodo forwards the whole chargeback lifecycle over
|
|
49
|
+
* webhooks. The capability gates the *API* half — `findDispute` and
|
|
50
|
+
* `submitDisputeEvidence` — and Dodo's own dispute reference says evidence "must occur
|
|
51
|
+
* within this window **through the Dodo dashboard**". There is no representment endpoint
|
|
52
|
+
* to call, so a driver that claimed one would be lying about the only part that matters.
|
|
53
|
+
*/
|
|
54
|
+
capabilities = { refunds: true, invoices: true, subscriptions: true, disputes: false };
|
|
55
|
+
#baseUrl;
|
|
56
|
+
#bearerToken;
|
|
57
|
+
#currency;
|
|
58
|
+
#billingCountry;
|
|
59
|
+
#webhookKey;
|
|
60
|
+
#invoiceCtx;
|
|
61
|
+
constructor(ctx, config) {
|
|
62
|
+
this.#invoiceCtx = ctx;
|
|
63
|
+
this.#bearerToken = requireCredential({
|
|
64
|
+
driver: 'dodo',
|
|
65
|
+
option: 'apiKey',
|
|
66
|
+
env: 'DODO_PAYMENTS_API_KEY',
|
|
67
|
+
value: config.apiKey,
|
|
68
|
+
});
|
|
69
|
+
this.#currency = requireCurrency('dodo', config.currency);
|
|
70
|
+
const sandbox = config.sandbox ?? process.env.NODE_ENV !== 'production';
|
|
71
|
+
this.#baseUrl = sandbox ? 'https://test.dodopayments.com' : 'https://live.dodopayments.com';
|
|
72
|
+
this.#billingCountry = config.billingCountry ?? process.env.DODO_PAYMENTS_BILLING_COUNTRY;
|
|
73
|
+
this.#webhookKey = config.webhookKey ?? process.env.DODO_PAYMENTS_WEBHOOK_KEY;
|
|
74
|
+
}
|
|
75
|
+
// ── Customers ────────────────────────────────────────────────────────────────────────
|
|
76
|
+
async createCustomer(input) {
|
|
77
|
+
this.#refuseIdempotencyKey(input.idempotencyKey, 'createCustomer');
|
|
78
|
+
if (!input.email || !input.name) {
|
|
79
|
+
throw new Error('[payments] Dodo Payments requires both `email` and `name` to create a customer.');
|
|
80
|
+
}
|
|
81
|
+
const data = await this.#request('/customers', {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
body: {
|
|
84
|
+
email: input.email,
|
|
85
|
+
name: input.name,
|
|
86
|
+
...(input.metadata !== undefined ? { metadata: input.metadata } : {}),
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
return this.#mapCustomer(data);
|
|
90
|
+
}
|
|
91
|
+
async findCustomer(customerId) {
|
|
92
|
+
try {
|
|
93
|
+
const data = await this.#request(`/customers/${encodeURIComponent(customerId)}`);
|
|
94
|
+
return this.#mapCustomer(data);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
if (isNotFound(error))
|
|
98
|
+
return null;
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async updateCustomer(customerId, input) {
|
|
103
|
+
if (input.taxId !== undefined) {
|
|
104
|
+
// A Dodo customer has no tax id; a tax id belongs to the individual purchase
|
|
105
|
+
// (`tax_id` on a payment/subscription for a B2B sale). Accepting it here would drop
|
|
106
|
+
// it silently.
|
|
107
|
+
throw new Error('[payments] Dodo Payments customers carry no tax id — pass it per purchase instead (B2B `tax_id` on the payment).');
|
|
108
|
+
}
|
|
109
|
+
const data = await this.#request(`/customers/${encodeURIComponent(customerId)}`, {
|
|
110
|
+
method: 'PATCH',
|
|
111
|
+
body: {
|
|
112
|
+
...(input.email !== undefined ? { email: input.email } : {}),
|
|
113
|
+
...(input.name !== undefined ? { name: input.name } : {}),
|
|
114
|
+
...(input.metadata !== undefined ? { metadata: input.metadata } : {}),
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
return this.#mapCustomer(data);
|
|
118
|
+
}
|
|
119
|
+
// ── Payments ─────────────────────────────────────────────────────────────────────────
|
|
120
|
+
/**
|
|
121
|
+
* `POST /payments` — Dodo marks this endpoint deprecated in favour of checkout sessions,
|
|
122
|
+
* but it is the only call that hands back a payment id up front, which is what this
|
|
123
|
+
* contract returns. {@link DodoDriver.createCheckout} uses the newer `/checkouts` route.
|
|
124
|
+
*
|
|
125
|
+
* The payment is created **pending**: the response carries a `payment_link` the customer
|
|
126
|
+
* still has to pay on. Settlement arrives as a `payment.succeeded` webhook.
|
|
127
|
+
*/
|
|
128
|
+
async charge(input) {
|
|
129
|
+
this.#refuseIdempotencyKey(input.idempotencyKey, 'charge');
|
|
130
|
+
const productId = this.#requireProductId(input.metadata);
|
|
131
|
+
const body = {
|
|
132
|
+
product_cart: [
|
|
133
|
+
{
|
|
134
|
+
product_id: productId,
|
|
135
|
+
quantity: input.metadata?.quantity ?? 1,
|
|
136
|
+
// Honored only when the Dodo product has Pay What You Want enabled; on a
|
|
137
|
+
// fixed-price product Dodo ignores it and charges the product's price.
|
|
138
|
+
amount: input.amount,
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
customer: this.#resolveCustomer(input.customerId, input.customer),
|
|
142
|
+
billing: this.#resolveBilling(input.metadata),
|
|
143
|
+
billing_currency: (input.currency ?? this.#currency).toUpperCase(),
|
|
144
|
+
payment_link: true,
|
|
145
|
+
...(input.metadata?.returnUrl !== undefined ? { return_url: input.metadata.returnUrl } : {}),
|
|
146
|
+
...(this.#allowedMethods(input.method) !== undefined
|
|
147
|
+
? { allowed_payment_method_types: this.#allowedMethods(input.method) }
|
|
148
|
+
: {}),
|
|
149
|
+
metadata: this.#metadataWithReference(input),
|
|
150
|
+
};
|
|
151
|
+
const data = await this.#request('/payments', { method: 'POST', body });
|
|
152
|
+
const payment = this.#mapCreatedPayment(data, input);
|
|
153
|
+
await emitInvoiceIfRequested(this.#invoiceCtx, input, payment, this);
|
|
154
|
+
publishPaymentDiagnostics(payment);
|
|
155
|
+
return payment;
|
|
156
|
+
}
|
|
157
|
+
async findPayment(gatewayId) {
|
|
158
|
+
try {
|
|
159
|
+
const data = await this.#request(`/payments/${encodeURIComponent(gatewayId)}`);
|
|
160
|
+
return this.#mapPayment(data);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (isNotFound(error))
|
|
164
|
+
return null;
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* `POST /refunds` has no top-level amount: a partial refund is expressed per line item
|
|
170
|
+
* (`items[].item_id` = the product or addon id, plus its own amount), which this
|
|
171
|
+
* contract's single `amount` cannot address. So a full refund works and a partial one is
|
|
172
|
+
* refused, rather than quietly refunding everything.
|
|
173
|
+
*
|
|
174
|
+
* `options.idempotencyKey` is REFUSED, not ignored: Dodo documents no deduplication
|
|
175
|
+
* mechanism, so accepting the key would turn a retry guarantee into a second refund.
|
|
176
|
+
*/
|
|
177
|
+
async refund(paymentGatewayId, amount, options) {
|
|
178
|
+
this.#refuseIdempotencyKey(options?.idempotencyKey, 'refund');
|
|
179
|
+
if (amount !== undefined) {
|
|
180
|
+
throw new Error('[payments] Dodo Payments refunds a partial amount per line item, not per payment — ' +
|
|
181
|
+
'`POST /refunds` takes `items[{ item_id, amount }]`, which needs the product id of the line to refund. ' +
|
|
182
|
+
'Call `refund(paymentId)` with no amount for a full refund, or use the Dodo API directly for a partial one.');
|
|
183
|
+
}
|
|
184
|
+
const data = await this.#request('/refunds', {
|
|
185
|
+
method: 'POST',
|
|
186
|
+
body: { payment_id: paymentGatewayId },
|
|
187
|
+
});
|
|
188
|
+
const refund = {
|
|
189
|
+
id: data.refund_id,
|
|
190
|
+
gatewayId: data.refund_id,
|
|
191
|
+
provider: this.provider,
|
|
192
|
+
amount: {
|
|
193
|
+
amount: data.amount ?? 0,
|
|
194
|
+
currency: (data.currency ?? this.#currency).toLowerCase(),
|
|
195
|
+
},
|
|
196
|
+
status: data.status === 'succeeded'
|
|
197
|
+
? 'succeeded'
|
|
198
|
+
: data.status === 'failed'
|
|
199
|
+
? 'failed'
|
|
200
|
+
: // `review` is a refund Dodo has not decided on yet — not settled, not refused.
|
|
201
|
+
'pending',
|
|
202
|
+
createdAt: data.created_at,
|
|
203
|
+
};
|
|
204
|
+
publishRefundDiagnostics(refund);
|
|
205
|
+
return refund;
|
|
206
|
+
}
|
|
207
|
+
// ── Checkout ─────────────────────────────────────────────────────────────────────────
|
|
208
|
+
async createCheckout(input) {
|
|
209
|
+
this.#refuseIdempotencyKey(input.idempotencyKey, 'createCheckout');
|
|
210
|
+
const productId = input.planId ?? input.metadata?.productId;
|
|
211
|
+
if (!productId) {
|
|
212
|
+
throw new Error('[payments] Dodo Payments checkout needs a product — pass the Dodo product id as `planId` (or `metadata.productId`). ' +
|
|
213
|
+
'Every price lives on a product; there is no ad-hoc line item.');
|
|
214
|
+
}
|
|
215
|
+
const metadata = this.#metadataWithReference(input);
|
|
216
|
+
const data = await this.#request('/checkouts', {
|
|
217
|
+
method: 'POST',
|
|
218
|
+
body: {
|
|
219
|
+
product_cart: [{ product_id: productId, quantity: 1, amount: input.amount }],
|
|
220
|
+
return_url: input.successUrl,
|
|
221
|
+
...(input.cancelUrl !== undefined ? { cancel_url: input.cancelUrl } : {}),
|
|
222
|
+
billing_currency: (input.currency ?? this.#currency).toUpperCase(),
|
|
223
|
+
...(input.customerId !== undefined ? { customer: { customer_id: input.customerId } } : {}),
|
|
224
|
+
...(input.trialDays !== undefined
|
|
225
|
+
? { subscription_data: { trial_period_days: input.trialDays } }
|
|
226
|
+
: {}),
|
|
227
|
+
...(Object.keys(metadata).length > 0 ? { metadata } : {}),
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
return {
|
|
231
|
+
id: data.session_id,
|
|
232
|
+
gatewayId: data.session_id,
|
|
233
|
+
provider: this.provider,
|
|
234
|
+
url: data.checkout_url ?? '',
|
|
235
|
+
// `/checkouts` returns no status; a session that was just created is open.
|
|
236
|
+
status: 'open',
|
|
237
|
+
amount: { amount: input.amount, currency: (input.currency ?? this.#currency).toLowerCase() },
|
|
238
|
+
...(input.customerId !== undefined ? { customerId: input.customerId } : {}),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
// ── Subscriptions ────────────────────────────────────────────────────────────────────
|
|
242
|
+
async createSubscription(input) {
|
|
243
|
+
this.#refuseIdempotencyKey(input.idempotencyKey, 'createSubscription');
|
|
244
|
+
if (input.amount !== undefined) {
|
|
245
|
+
throw new Error('[payments] Dodo Payments takes the recurring price from the product, so `amount` cannot be set on a subscription. ' +
|
|
246
|
+
'Create the product with the price you want and pass its id as `planId`.');
|
|
247
|
+
}
|
|
248
|
+
if (input.cycle !== undefined) {
|
|
249
|
+
throw new Error('[payments] Dodo Payments takes the billing interval from the product, so `cycle` cannot be set on a subscription. ' +
|
|
250
|
+
'Create a product with the interval you want and pass its id as `planId`.');
|
|
251
|
+
}
|
|
252
|
+
if (input.card !== undefined) {
|
|
253
|
+
throw new Error('[payments] Dodo Payments has no tokenized-card input — as a merchant of record it collects the card on its own checkout. ' +
|
|
254
|
+
'Use the `payment_link` the subscription returns, or `createCheckout({ planId })`.');
|
|
255
|
+
}
|
|
256
|
+
const data = await this.#request('/subscriptions', {
|
|
257
|
+
method: 'POST',
|
|
258
|
+
body: {
|
|
259
|
+
product_id: input.planId,
|
|
260
|
+
quantity: input.metadata?.quantity ?? 1,
|
|
261
|
+
customer: this.#resolveCustomer(input.customerId, input.customer),
|
|
262
|
+
billing: this.#resolveBilling(input.metadata),
|
|
263
|
+
billing_currency: this.#currency.toUpperCase(),
|
|
264
|
+
payment_link: true,
|
|
265
|
+
...(input.trialDays !== undefined ? { trial_period_days: input.trialDays } : {}),
|
|
266
|
+
...(this.#allowedMethods(input.method) !== undefined
|
|
267
|
+
? { allowed_payment_method_types: this.#allowedMethods(input.method) }
|
|
268
|
+
: {}),
|
|
269
|
+
metadata: this.#metadataWithReference(input),
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
const subscription = this.#mapCreatedSubscription(data, input);
|
|
273
|
+
publishSubscriptionDiagnostics(subscription, 'subscription.created');
|
|
274
|
+
return subscription;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Cancelling is a `PATCH`, and Dodo separates the two meanings: `status: 'cancelled'`
|
|
278
|
+
* ends it now, `cancel_at_next_billing_date: true` lets the paid period run out. There
|
|
279
|
+
* is no `DELETE /subscriptions/{id}`.
|
|
280
|
+
*/
|
|
281
|
+
async cancelSubscription(subscriptionGatewayId, options) {
|
|
282
|
+
const data = await this.#request(`/subscriptions/${encodeURIComponent(subscriptionGatewayId)}`, {
|
|
283
|
+
method: 'PATCH',
|
|
284
|
+
body: options?.atPeriodEnd === false
|
|
285
|
+
? { status: 'cancelled', cancel_reason: 'cancelled_by_merchant' }
|
|
286
|
+
: { cancel_at_next_billing_date: true },
|
|
287
|
+
});
|
|
288
|
+
const subscription = this.#mapSubscription(data);
|
|
289
|
+
publishSubscriptionDiagnostics(subscription, 'subscription.canceled');
|
|
290
|
+
return subscription;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* What a customer pays is decided by the product, so the only real change is a plan
|
|
294
|
+
* switch — `POST /subscriptions/{id}/change-plan`, expressed here as
|
|
295
|
+
* `metadata.productId`. Everything else in `UpdateSubscriptionInput` that Dodo has no
|
|
296
|
+
* field for is refused instead of dropped.
|
|
297
|
+
*/
|
|
298
|
+
async updateSubscription(subscriptionGatewayId, input) {
|
|
299
|
+
this.#refuseIdempotencyKey(input.idempotencyKey, 'updateSubscription');
|
|
300
|
+
if (input.amount !== undefined) {
|
|
301
|
+
throw new Error('[payments] Dodo Payments has no subscription amount to update — the price belongs to the product. ' +
|
|
302
|
+
'Switch plans with `updateSubscription(id, { metadata: { productId: <new dodo product id> } })`.');
|
|
303
|
+
}
|
|
304
|
+
if (input.description !== undefined) {
|
|
305
|
+
throw new Error('[payments] Dodo Payments subscriptions carry no description — the product supplies the name shown to the customer.');
|
|
306
|
+
}
|
|
307
|
+
const id = encodeURIComponent(subscriptionGatewayId);
|
|
308
|
+
const { productId, quantity, prorationBillingMode, effectiveAt, ...metadata } = (input.metadata ?? {});
|
|
309
|
+
if (productId !== undefined) {
|
|
310
|
+
// change-plan answers with the payment link for the difference, not the subscription,
|
|
311
|
+
// so the updated subscription is read back to satisfy the contract.
|
|
312
|
+
await this.#request(`/subscriptions/${id}/change-plan`, {
|
|
313
|
+
method: 'POST',
|
|
314
|
+
body: {
|
|
315
|
+
product_id: productId,
|
|
316
|
+
quantity: quantity ?? 1,
|
|
317
|
+
proration_billing_mode: prorationBillingMode ?? 'prorated_immediately',
|
|
318
|
+
...(effectiveAt !== undefined ? { effective_at: effectiveAt } : {}),
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
return this.#mapSubscription(await this.#request(`/subscriptions/${id}`));
|
|
322
|
+
}
|
|
323
|
+
if (Object.keys(metadata).length === 0) {
|
|
324
|
+
throw new Error('[payments] Nothing to update on a Dodo Payments subscription. Pass `metadata.productId` to switch plans, ' +
|
|
325
|
+
'or other `metadata` keys to store your own data on it.');
|
|
326
|
+
}
|
|
327
|
+
const data = await this.#request(`/subscriptions/${id}`, {
|
|
328
|
+
method: 'PATCH',
|
|
329
|
+
body: { metadata },
|
|
330
|
+
});
|
|
331
|
+
return this.#mapSubscription(data);
|
|
332
|
+
}
|
|
333
|
+
async findSubscription(gatewayId) {
|
|
334
|
+
try {
|
|
335
|
+
const data = await this.#request(`/subscriptions/${encodeURIComponent(gatewayId)}`);
|
|
336
|
+
return this.#mapSubscription(data);
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
if (isNotFound(error))
|
|
340
|
+
return null;
|
|
341
|
+
throw error;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// ── Invoices ─────────────────────────────────────────────────────────────────────────
|
|
345
|
+
/**
|
|
346
|
+
* Dodo issues an invoice per payment (it is the merchant of record, so the invoice is
|
|
347
|
+
* its own). `invoice_url` on the payment is the PDF, which saves a call to
|
|
348
|
+
* `GET /invoices/payments/{id}`.
|
|
349
|
+
*/
|
|
350
|
+
async listInvoices(customerId) {
|
|
351
|
+
const data = await this.#request(`/payments?customer_id=${encodeURIComponent(customerId)}&page_size=100`);
|
|
352
|
+
return (data.items ?? []).map((payment) => ({
|
|
353
|
+
id: payment.invoice_id ?? payment.payment_id,
|
|
354
|
+
gatewayId: payment.payment_id,
|
|
355
|
+
provider: this.provider,
|
|
356
|
+
customerId,
|
|
357
|
+
...(payment.subscription_id ? { subscriptionId: payment.subscription_id } : {}),
|
|
358
|
+
status: payment.status === 'succeeded'
|
|
359
|
+
? 'paid'
|
|
360
|
+
: payment.status === 'failed' || payment.status === 'cancelled'
|
|
361
|
+
? 'canceled'
|
|
362
|
+
: 'pending',
|
|
363
|
+
amount: { amount: payment.total_amount, currency: payment.currency.toLowerCase() },
|
|
364
|
+
createdAt: payment.created_at,
|
|
365
|
+
...(payment.invoice_url ? { hostedPdfUrl: payment.invoice_url } : {}),
|
|
366
|
+
...(payment.invoice_id ? { number: payment.invoice_id } : {}),
|
|
367
|
+
payload: payment,
|
|
368
|
+
}));
|
|
369
|
+
}
|
|
370
|
+
// ── Webhooks ─────────────────────────────────────────────────────────────────────────
|
|
371
|
+
/**
|
|
372
|
+
* Whether a delivery to `POST /payments/webhook/:provider` can be authenticated.
|
|
373
|
+
*
|
|
374
|
+
* `parseWebhook` already refuses without the key — declaring it moves the refusal to boot,
|
|
375
|
+
* where it is a config error rather than a lost webhook.
|
|
376
|
+
*/
|
|
377
|
+
get webhookVerification() {
|
|
378
|
+
return this.#webhookKey !== undefined ? 'configured' : 'unconfigured';
|
|
379
|
+
}
|
|
380
|
+
parseWebhook(rawBody, headers) {
|
|
381
|
+
if (!this.#webhookKey) {
|
|
382
|
+
throw new Error('[payments] Dodo Payments webhook processing requires a webhook key. Set `DODO_PAYMENTS_WEBHOOK_KEY` env or pass `webhookKey` to `payments.dodo()`.');
|
|
383
|
+
}
|
|
384
|
+
const id = this.#verifiedEventId(rawBody, headers, this.#webhookKey);
|
|
385
|
+
const payload = JSON.parse(rawBody);
|
|
386
|
+
return {
|
|
387
|
+
id,
|
|
388
|
+
provider: this.provider,
|
|
389
|
+
type: this.#mapWebhookType(payload.type, payload.data ?? {}),
|
|
390
|
+
createdAt: payload.timestamp ?? new Date().toISOString(),
|
|
391
|
+
data: this.#mapWebhookData(payload.type, payload.data ?? {}),
|
|
392
|
+
raw: payload,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Verify the delivery and return the event id, which for Standard Webhooks lives in the
|
|
397
|
+
* `webhook-id` header rather than the body — it is what the idempotency ledger
|
|
398
|
+
* deduplicates on, and what Dodo's own docs tell you to use.
|
|
399
|
+
*
|
|
400
|
+
* `keyEncoding: 'base64'` is the Dodo-specific half: strip `whsec_` and base64-decode
|
|
401
|
+
* the rest, the spec default. (Polar, on the same spec, uses the raw string.)
|
|
402
|
+
*/
|
|
403
|
+
#verifiedEventId(rawBody, headers, secret) {
|
|
404
|
+
const id = headerValue(headers, 'webhook-id');
|
|
405
|
+
if (id === undefined ||
|
|
406
|
+
headerValue(headers, 'webhook-timestamp') === undefined ||
|
|
407
|
+
headerValue(headers, 'webhook-signature') === undefined) {
|
|
408
|
+
throw new Error('[payments] Missing Standard Webhooks headers on Dodo Payments request (webhook-id, webhook-timestamp, webhook-signature).');
|
|
409
|
+
}
|
|
410
|
+
if (!verifyStandardWebhookSignature({ rawBody, headers, secret, keyEncoding: 'base64' })) {
|
|
411
|
+
throw new Error('[payments] Invalid Dodo Payments webhook signature.');
|
|
412
|
+
}
|
|
413
|
+
return id;
|
|
414
|
+
}
|
|
415
|
+
// ── Mappers ──────────────────────────────────────────────────────────────────────────
|
|
416
|
+
#mapCustomer(data) {
|
|
417
|
+
return {
|
|
418
|
+
id: data.customer_id,
|
|
419
|
+
email: data.email,
|
|
420
|
+
name: data.name,
|
|
421
|
+
...(data.metadata !== undefined && Object.keys(data.metadata).length > 0
|
|
422
|
+
? { metadata: data.metadata }
|
|
423
|
+
: {}),
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
#mapCreatedPayment(data, input) {
|
|
427
|
+
const result = {
|
|
428
|
+
id: data.payment_id,
|
|
429
|
+
gatewayId: data.payment_id,
|
|
430
|
+
provider: this.provider,
|
|
431
|
+
// The create response carries no currency, so the one the charge was billed in is
|
|
432
|
+
// the only truthful answer.
|
|
433
|
+
amount: {
|
|
434
|
+
amount: data.total_amount,
|
|
435
|
+
currency: (input.currency ?? this.#currency).toLowerCase(),
|
|
436
|
+
},
|
|
437
|
+
// Nothing is settled yet — the customer still has to pay on `payment_link`.
|
|
438
|
+
status: 'pending',
|
|
439
|
+
customerId: data.customer.customer_id,
|
|
440
|
+
payload: data,
|
|
441
|
+
createdAt: new Date().toISOString(),
|
|
442
|
+
};
|
|
443
|
+
if (data.payment_link)
|
|
444
|
+
result.hostedUrl = data.payment_link;
|
|
445
|
+
return result;
|
|
446
|
+
}
|
|
447
|
+
#mapPayment(data) {
|
|
448
|
+
const statusMap = {
|
|
449
|
+
succeeded: 'paid',
|
|
450
|
+
failed: 'failed',
|
|
451
|
+
cancelled: 'canceled',
|
|
452
|
+
processing: 'pending',
|
|
453
|
+
requires_customer_action: 'pending',
|
|
454
|
+
requires_merchant_action: 'pending',
|
|
455
|
+
requires_payment_method: 'pending',
|
|
456
|
+
requires_confirmation: 'pending',
|
|
457
|
+
// Funds are held on the card and NOTHING has been captured — money has not moved,
|
|
458
|
+
// but it is a great deal more than `pending`, which is what this used to say.
|
|
459
|
+
requires_capture: 'authorized',
|
|
460
|
+
// NOT `authorized`: part of the authorization HAS been captured, so claiming
|
|
461
|
+
// "nothing captured" would be as wrong as `pending` was understated. The API
|
|
462
|
+
// reference does not say how much settled, so `pending` remains the reading that
|
|
463
|
+
// does not claim a figure arrived.
|
|
464
|
+
partially_captured: 'pending',
|
|
465
|
+
partially_captured_and_capturable: 'pending',
|
|
466
|
+
};
|
|
467
|
+
const result = {
|
|
468
|
+
id: data.payment_id,
|
|
469
|
+
gatewayId: data.payment_id,
|
|
470
|
+
provider: this.provider,
|
|
471
|
+
amount: { amount: data.total_amount, currency: data.currency.toLowerCase() },
|
|
472
|
+
status: data.refund_status === 'full' ? 'refunded' : (statusMap[data.status ?? ''] ?? 'pending'),
|
|
473
|
+
customerId: data.customer.customer_id,
|
|
474
|
+
payload: data,
|
|
475
|
+
createdAt: data.created_at,
|
|
476
|
+
};
|
|
477
|
+
const method = this.#mapMethodToType(data.payment_method_type ?? data.payment_method);
|
|
478
|
+
if (method !== undefined && method !== 'unknown')
|
|
479
|
+
result.method = method;
|
|
480
|
+
if (data.subscription_id)
|
|
481
|
+
result.subscriptionId = data.subscription_id;
|
|
482
|
+
if (data.payment_link)
|
|
483
|
+
result.hostedUrl = data.payment_link;
|
|
484
|
+
// Dodo has no settlement timestamp; `updated_at` is when it last moved, which for a
|
|
485
|
+
// succeeded payment is when it succeeded.
|
|
486
|
+
if (data.status === 'succeeded')
|
|
487
|
+
result.paidAt = data.updated_at ?? data.created_at;
|
|
488
|
+
return result;
|
|
489
|
+
}
|
|
490
|
+
#mapCreatedSubscription(data, input) {
|
|
491
|
+
return {
|
|
492
|
+
id: data.subscription_id,
|
|
493
|
+
gatewayId: data.subscription_id,
|
|
494
|
+
provider: this.provider,
|
|
495
|
+
customerId: data.customer.customer_id,
|
|
496
|
+
// The create response has no status. A Dodo subscription starts `pending` until its
|
|
497
|
+
// first payment clears, and `trialing` would claim a trial that may not exist.
|
|
498
|
+
status: 'incomplete',
|
|
499
|
+
planId: input.planId,
|
|
500
|
+
amount: { amount: data.recurring_pre_tax_amount, currency: this.#currency.toLowerCase() },
|
|
501
|
+
payload: data,
|
|
502
|
+
createdAt: new Date().toISOString(),
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
#mapSubscription(data) {
|
|
506
|
+
const statusMap = {
|
|
507
|
+
pending: 'incomplete',
|
|
508
|
+
active: 'active',
|
|
509
|
+
// `on_hold` is a subscription whose payment did not go through.
|
|
510
|
+
on_hold: 'past_due',
|
|
511
|
+
// A paused Dodo subscription still exists and will bill again — but it is not
|
|
512
|
+
// billing NOW, so reporting it as `active` entitled a subscriber who is not paying.
|
|
513
|
+
paused: 'paused',
|
|
514
|
+
cancelled: 'canceled',
|
|
515
|
+
failed: 'canceled',
|
|
516
|
+
expired: 'ended',
|
|
517
|
+
};
|
|
518
|
+
return {
|
|
519
|
+
id: data.subscription_id,
|
|
520
|
+
gatewayId: data.subscription_id,
|
|
521
|
+
provider: this.provider,
|
|
522
|
+
customerId: data.customer.customer_id,
|
|
523
|
+
status: statusMap[data.status] ?? 'active',
|
|
524
|
+
planId: data.product_id,
|
|
525
|
+
amount: {
|
|
526
|
+
amount: data.recurring_pre_tax_amount,
|
|
527
|
+
currency: data.currency.toLowerCase(),
|
|
528
|
+
},
|
|
529
|
+
...(data.cancelled_at ? { endsAt: data.cancelled_at } : {}),
|
|
530
|
+
...(data.expires_at && !data.cancelled_at ? { endsAt: data.expires_at } : {}),
|
|
531
|
+
...(data.previous_billing_date ? { currentPeriodStart: data.previous_billing_date } : {}),
|
|
532
|
+
...(data.next_billing_date ? { currentPeriodEnd: data.next_billing_date } : {}),
|
|
533
|
+
payload: data,
|
|
534
|
+
createdAt: data.created_at,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
#mapWebhookType(event, data) {
|
|
538
|
+
// The dispute family is normalized off the `Dispute` payload, and a `dispute.*` event
|
|
539
|
+
// whose body is not one cannot supply an outcome — `payment.dispute_closed` without an
|
|
540
|
+
// outcome makes the processor throw, on purpose. So an unrecognizable body degrades to
|
|
541
|
+
// `payment.updated` rather than producing an event the processor will reject.
|
|
542
|
+
if (event.startsWith('dispute.')) {
|
|
543
|
+
return data.payload_type === 'Dispute' ? this.#disputeType(event) : 'payment.updated';
|
|
544
|
+
}
|
|
545
|
+
switch (event) {
|
|
546
|
+
case 'payment.succeeded':
|
|
547
|
+
return 'payment.succeeded';
|
|
548
|
+
case 'payment.failed':
|
|
549
|
+
case 'payment.cancelled':
|
|
550
|
+
return 'payment.failed';
|
|
551
|
+
case 'payment.processing':
|
|
552
|
+
return 'payment.updated';
|
|
553
|
+
case 'refund.succeeded':
|
|
554
|
+
return 'payment.refunded';
|
|
555
|
+
// Dodo has no `subscription.created`: `active` is the first event, fired once the
|
|
556
|
+
// subscription's first payment clears.
|
|
557
|
+
case 'subscription.active':
|
|
558
|
+
return 'subscription.created';
|
|
559
|
+
case 'subscription.updated':
|
|
560
|
+
case 'subscription.renewed':
|
|
561
|
+
case 'subscription.plan_changed':
|
|
562
|
+
case 'subscription.on_hold':
|
|
563
|
+
case 'subscription.paused':
|
|
564
|
+
case 'subscription.unpaused':
|
|
565
|
+
case 'subscription.update_payment_method':
|
|
566
|
+
return 'subscription.updated';
|
|
567
|
+
case 'subscription.cancelled':
|
|
568
|
+
case 'subscription.expired':
|
|
569
|
+
case 'subscription.failed':
|
|
570
|
+
return 'subscription.canceled';
|
|
571
|
+
default:
|
|
572
|
+
// refund.failed, license_key.*, payout.*, credit.*, dunning.*,
|
|
573
|
+
// abandoned_checkout.*, entitlement_grant.* — passed through so an app handler can
|
|
574
|
+
// subscribe to them by their Dodo name.
|
|
575
|
+
return event;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* The dispute lifecycle → the canonical dispute types.
|
|
580
|
+
*
|
|
581
|
+
* Dodo is a merchant of record, which usually means the chargeback is the MoR's to
|
|
582
|
+
* fight and you hear about it as a balance line. Dodo is the exception, and its own
|
|
583
|
+
* dispute reference is explicit about both halves: **"Cardholder initiates dispute;
|
|
584
|
+
* funds are held"** on `dispute.opened`, and **"You have 10 days to respond after a
|
|
585
|
+
* dispute opens"**, with the evidence submitted by you.
|
|
586
|
+
*
|
|
587
|
+
* So `dispute.opened` is a real `payment.disputed` — the money is already gone — and
|
|
588
|
+
* there is no funds-untouched warning to map. Dodo carries a `dispute_stage`
|
|
589
|
+
* (`pre_dispute` → `dispute` → `pre_arbitration`), but its reference states the funds
|
|
590
|
+
* hold for `dispute.opened` without qualifying it by stage, so the driver does NOT
|
|
591
|
+
* downgrade a `pre_dispute` open to `payment.dispute_warning`. Guessing that Dodo's
|
|
592
|
+
* `pre_dispute` behaves like a Stripe inquiry would leave a row saying `paid` over
|
|
593
|
+
* money Dodo says it has already held.
|
|
594
|
+
*/
|
|
595
|
+
#disputeType(event) {
|
|
596
|
+
if (event === 'dispute.opened')
|
|
597
|
+
return 'payment.disputed';
|
|
598
|
+
// Evidence submitted, network reviewing. Movement inside an open dispute, not a
|
|
599
|
+
// resolution of it.
|
|
600
|
+
if (event === 'dispute.challenged')
|
|
601
|
+
return 'payment.updated';
|
|
602
|
+
return this.#disputeOutcome(event) === undefined ? 'payment.updated' : 'payment.dispute_closed';
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* The outcome each closing event reports, in Dodo's own words.
|
|
606
|
+
*
|
|
607
|
+
* - `dispute.won` — "Resolved in your favor; funds retained".
|
|
608
|
+
* - `dispute.lost` — "Resolved for cardholder; funds returned". Visa RDR auto-refunds
|
|
609
|
+
* arrive here too, flagged `is_resolved_by_rdr: true` on the payload.
|
|
610
|
+
* - `dispute.accepted` — "Dispute accepted without contest; funds returned". You chose
|
|
611
|
+
* not to defend, so the cardholder keeps the money: a loss, not a cancellation.
|
|
612
|
+
* - `dispute.expired` — "Response window closed without resolution", which Dodo's own
|
|
613
|
+
* table glosses as "typically resolves against you". `expired` rather than `lost`,
|
|
614
|
+
* because nothing was decided — the clock ran out.
|
|
615
|
+
* - `dispute.cancelled` — "Dispute withdrawn; no action needed". `canceled` does not
|
|
616
|
+
* return the row to `paid` on its own, which is deliberate: a withdrawn dispute is
|
|
617
|
+
* not an acquirer returning funds.
|
|
618
|
+
*/
|
|
619
|
+
#disputeOutcome(event) {
|
|
620
|
+
switch (event) {
|
|
621
|
+
case 'dispute.won':
|
|
622
|
+
return 'won';
|
|
623
|
+
case 'dispute.lost':
|
|
624
|
+
case 'dispute.accepted':
|
|
625
|
+
return 'lost';
|
|
626
|
+
case 'dispute.expired':
|
|
627
|
+
return 'expired';
|
|
628
|
+
case 'dispute.cancelled':
|
|
629
|
+
return 'canceled';
|
|
630
|
+
default:
|
|
631
|
+
return undefined;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Normalize the event body onto the shapes the billing layer's built-in sync expects,
|
|
636
|
+
* using the `payload_type` discriminator Dodo puts on every `data` object.
|
|
637
|
+
* `externalReference` comes back out of `metadata.external_reference`.
|
|
638
|
+
*/
|
|
639
|
+
#mapWebhookData(event, data) {
|
|
640
|
+
const payloadType = data.payload_type;
|
|
641
|
+
if (payloadType === 'Payment') {
|
|
642
|
+
const payment = data;
|
|
643
|
+
return {
|
|
644
|
+
gatewayId: payment.payment_id,
|
|
645
|
+
amount: payment.total_amount,
|
|
646
|
+
currency: (payment.currency ?? this.#currency).toLowerCase(),
|
|
647
|
+
customerId: payment.customer?.customer_id,
|
|
648
|
+
...(payment.subscription_id ? { subscriptionId: payment.subscription_id } : {}),
|
|
649
|
+
...this.#referenceFrom(payment.metadata),
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
if (payloadType === 'Refund') {
|
|
653
|
+
const refund = data;
|
|
654
|
+
// Keyed by the PAYMENT, not the refund: the payment is the row an app handler holds.
|
|
655
|
+
return {
|
|
656
|
+
gatewayId: refund.payment_id,
|
|
657
|
+
amount: refund.amount ?? 0,
|
|
658
|
+
currency: (refund.currency ?? this.#currency).toLowerCase(),
|
|
659
|
+
customerId: refund.customer?.customer_id,
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
if (payloadType === 'Dispute') {
|
|
663
|
+
const dispute = data;
|
|
664
|
+
const outcome = this.#disputeOutcome(event);
|
|
665
|
+
// Keyed by the PAYMENT, not the dispute: the payment is the row whose status has to
|
|
666
|
+
// move to `disputed`, and the amount/currency are what the processor writes back.
|
|
667
|
+
//
|
|
668
|
+
// No `actionableUntil`. Dodo's ten-day clock is real and it is YOURS — but Dodo
|
|
669
|
+
// sends no deadline field: the dispute object is `dispute_id`, `payment_id`,
|
|
670
|
+
// `business_id`, `amount`, `currency`, `dispute_status`, `dispute_stage`,
|
|
671
|
+
// `created_at`, `remarks`, `payment_provider`, `is_resolved_by_rdr` and nothing
|
|
672
|
+
// else. Deriving `created_at + 10 days` would put a date this driver invented into
|
|
673
|
+
// the one field an operator is meant to trust, so the deadline stays documented
|
|
674
|
+
// rather than fabricated.
|
|
675
|
+
return {
|
|
676
|
+
gatewayId: dispute.payment_id,
|
|
677
|
+
amount: Number(dispute.amount) || 0,
|
|
678
|
+
currency: (dispute.currency ?? this.#currency).toLowerCase(),
|
|
679
|
+
disputeId: dispute.dispute_id,
|
|
680
|
+
...(dispute.remarks ? { reason: dispute.remarks } : {}),
|
|
681
|
+
...(outcome !== undefined ? { outcome } : {}),
|
|
682
|
+
...(dispute.dispute_stage !== undefined ? { disputeStage: dispute.dispute_stage } : {}),
|
|
683
|
+
...(dispute.dispute_status !== undefined ? { disputeStatus: dispute.dispute_status } : {}),
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
if (payloadType === 'Subscription') {
|
|
687
|
+
const subscription = data;
|
|
688
|
+
const mapped = this.#mapSubscription(subscription);
|
|
689
|
+
return {
|
|
690
|
+
gatewayId: mapped.gatewayId,
|
|
691
|
+
customerId: mapped.customerId,
|
|
692
|
+
status: mapped.status,
|
|
693
|
+
planId: mapped.planId,
|
|
694
|
+
...(mapped.endsAt !== undefined ? { endsAt: mapped.endsAt } : {}),
|
|
695
|
+
...this.#referenceFrom(subscription.metadata),
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
return data;
|
|
699
|
+
}
|
|
700
|
+
#referenceFrom(metadata) {
|
|
701
|
+
const reference = metadata?.external_reference;
|
|
702
|
+
return {
|
|
703
|
+
...(typeof reference === 'string' ? { externalReference: reference } : {}),
|
|
704
|
+
...(metadata !== undefined && Object.keys(metadata).length > 0 ? { metadata } : {}),
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
// ── Request helpers ──────────────────────────────────────────────────────────────────
|
|
708
|
+
/**
|
|
709
|
+
* Dodo echoes `metadata` on the payment/subscription and on every webhook about it,
|
|
710
|
+
* which makes it the only place `externalReference` survives the round trip. Values must
|
|
711
|
+
* be scalars, so anything else is dropped by Dodo rather than by this driver.
|
|
712
|
+
*/
|
|
713
|
+
#metadataWithReference(input) {
|
|
714
|
+
// The keys this driver reads as arguments are consumed here, not forwarded, so they
|
|
715
|
+
// don't turn up as stray metadata on the Dodo record.
|
|
716
|
+
const { productId: _productId, product_id: _snakeProductId, quantity: _quantity, returnUrl: _returnUrl, billing: _billing, billingCountry: _billingCountry, ...rest } = input.metadata ?? {};
|
|
717
|
+
return {
|
|
718
|
+
...rest,
|
|
719
|
+
...(input.externalReference !== undefined
|
|
720
|
+
? { external_reference: input.externalReference }
|
|
721
|
+
: {}),
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Dodo documents **no request deduplication** — no `Idempotency-Key` header, no
|
|
726
|
+
* request-id field; its only idempotency guidance is the `webhook-id` header on the
|
|
727
|
+
* receiving side. So a key handed to this driver is refused rather than accepted and
|
|
728
|
+
* dropped: silently dropping it turns a caller's retry guarantee into a second charge.
|
|
729
|
+
*/
|
|
730
|
+
#refuseIdempotencyKey(key, operation) {
|
|
731
|
+
if (key === undefined)
|
|
732
|
+
return;
|
|
733
|
+
throw new Error(`[payments] Dodo Payments has no idempotency mechanism, so \`idempotencyKey\` cannot be honoured on ${operation}(). Its API deduplicates nothing (the \`webhook-id\` header only covers events it sends you), and a retried request performs the operation a second time — deduplicate on your side before calling.`);
|
|
734
|
+
}
|
|
735
|
+
#requireProductId(metadata) {
|
|
736
|
+
const productId = metadata?.productId ?? metadata?.product_id;
|
|
737
|
+
if (typeof productId !== 'string' || productId === '') {
|
|
738
|
+
throw new Error('[payments] Dodo Payments has no amount-only charge — every payment names a product you created in Dodo. ' +
|
|
739
|
+
'Pass `metadata.productId`, and enable Pay What You Want on that product if the amount varies per charge.');
|
|
740
|
+
}
|
|
741
|
+
return productId;
|
|
742
|
+
}
|
|
743
|
+
/** `POST /payments` and `POST /subscriptions` both demand a billing address country. */
|
|
744
|
+
#resolveBilling(metadata) {
|
|
745
|
+
const billing = metadata?.billing;
|
|
746
|
+
const country = billing?.country ??
|
|
747
|
+
metadata?.billingCountry ??
|
|
748
|
+
this.#billingCountry;
|
|
749
|
+
if (!country) {
|
|
750
|
+
throw new Error('[payments] Dodo Payments requires a billing country (ISO 3166-1 alpha-2) on every payment. ' +
|
|
751
|
+
'Pass `metadata.billingCountry` (or a full `metadata.billing` address), or set `billingCountry` on `payments.dodo()`.');
|
|
752
|
+
}
|
|
753
|
+
return { ...(billing ?? {}), country };
|
|
754
|
+
}
|
|
755
|
+
#resolveCustomer(customerId, customer) {
|
|
756
|
+
if (customerId !== undefined)
|
|
757
|
+
return { customer_id: customerId };
|
|
758
|
+
if (customer?.email !== undefined) {
|
|
759
|
+
return {
|
|
760
|
+
email: customer.email,
|
|
761
|
+
...(customer.name !== undefined ? { name: customer.name } : {}),
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
throw new Error('[payments] Dodo Payments needs a customer — pass `customerId`, or `customer.email` to create one at checkout.');
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Canonical method → the `allowed_payment_method_types` entries that restrict the hosted
|
|
768
|
+
* checkout to it. Dodo's own guidance is to always leave the card methods available as a
|
|
769
|
+
* fallback, because a checkout whose every listed method is unavailable simply fails.
|
|
770
|
+
*/
|
|
771
|
+
#allowedMethods(method) {
|
|
772
|
+
switch (method) {
|
|
773
|
+
case 'credit_card':
|
|
774
|
+
return ['credit'];
|
|
775
|
+
case 'debit_card':
|
|
776
|
+
return ['debit'];
|
|
777
|
+
// Pix additionally requires `billing_currency: BRL` and a Brazilian billing country;
|
|
778
|
+
// Dodo rejects the payment otherwise.
|
|
779
|
+
case 'pix':
|
|
780
|
+
return ['pix', 'credit', 'debit'];
|
|
781
|
+
// The category methods below keep `credit`/`debit` alongside them for the reason
|
|
782
|
+
// Dodo documents: a checkout whose every listed method is unavailable in the buyer's
|
|
783
|
+
// country simply fails, and a category is a set of local methods by definition.
|
|
784
|
+
case 'upi':
|
|
785
|
+
return ['upi_collect', 'credit', 'debit'];
|
|
786
|
+
case 'wallet':
|
|
787
|
+
return [
|
|
788
|
+
'apple_pay',
|
|
789
|
+
'google_pay',
|
|
790
|
+
'amazon_pay',
|
|
791
|
+
'cashapp',
|
|
792
|
+
'revolut_pay',
|
|
793
|
+
'credit',
|
|
794
|
+
'debit',
|
|
795
|
+
];
|
|
796
|
+
case 'bank_transfer':
|
|
797
|
+
return ['ideal', 'bancontact_card', 'eps', 'multibanco', 'blik', 'credit', 'debit'];
|
|
798
|
+
case 'bank_debit':
|
|
799
|
+
return ['sepa', 'ach', 'credit', 'debit'];
|
|
800
|
+
case 'bnpl':
|
|
801
|
+
return ['klarna', 'afterpay_clearpay', 'credit', 'debit'];
|
|
802
|
+
default:
|
|
803
|
+
return undefined;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* The instrument Dodo reports back on a payment, onto the contract's method categories.
|
|
808
|
+
* Only `card`, `pix` and `boleto` had names before, so an iDEAL or Klarna payment came
|
|
809
|
+
* back `unknown` — every method Dodo can produce now lands in a category.
|
|
810
|
+
*/
|
|
811
|
+
#mapMethodToType(method) {
|
|
812
|
+
switch (method) {
|
|
813
|
+
case 'card':
|
|
814
|
+
case 'credit':
|
|
815
|
+
case 'debit':
|
|
816
|
+
case 'visa':
|
|
817
|
+
case 'mastercard':
|
|
818
|
+
return 'card';
|
|
819
|
+
case 'pix':
|
|
820
|
+
return 'pix';
|
|
821
|
+
case 'boleto':
|
|
822
|
+
return 'boleto';
|
|
823
|
+
case 'upi':
|
|
824
|
+
case 'upi_collect':
|
|
825
|
+
case 'upi_intent':
|
|
826
|
+
return 'upi';
|
|
827
|
+
case 'apple_pay':
|
|
828
|
+
case 'google_pay':
|
|
829
|
+
case 'amazon_pay':
|
|
830
|
+
case 'cashapp':
|
|
831
|
+
case 'revolut_pay':
|
|
832
|
+
case 'we_chat_pay':
|
|
833
|
+
case 'payco':
|
|
834
|
+
case 'naver_pay':
|
|
835
|
+
case 'kakao_pay':
|
|
836
|
+
return 'wallet';
|
|
837
|
+
case 'ideal':
|
|
838
|
+
case 'bancontact_card':
|
|
839
|
+
case 'eps':
|
|
840
|
+
case 'multibanco':
|
|
841
|
+
case 'blik':
|
|
842
|
+
case 'satispay':
|
|
843
|
+
return 'bank_transfer';
|
|
844
|
+
case 'sepa':
|
|
845
|
+
case 'ach':
|
|
846
|
+
return 'bank_debit';
|
|
847
|
+
case 'klarna':
|
|
848
|
+
case 'afterpay_clearpay':
|
|
849
|
+
case 'billie':
|
|
850
|
+
return 'bnpl';
|
|
851
|
+
default:
|
|
852
|
+
return 'unknown';
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
async #request(path, options = {}) {
|
|
856
|
+
return httpRequest(path, {
|
|
857
|
+
baseUrl: this.#baseUrl,
|
|
858
|
+
...(options.method !== undefined ? { method: options.method } : {}),
|
|
859
|
+
...(options.body !== undefined ? { body: options.body } : {}),
|
|
860
|
+
bearerToken: this.#bearerToken,
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
//# sourceMappingURL=dodo.js.map
|