@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
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* The durable workflow's identity. Stable and explicit: the engine registry is keyed by
|
|
4
|
+
* name, an in-flight run records the name+version it started on, and a name that changed
|
|
5
|
+
* between deploys would strand runs mid-flight.
|
|
6
|
+
*/
|
|
7
|
+
export const WEBHOOK_WORKFLOW_NAME = 'payments-webhook';
|
|
8
|
+
export const WEBHOOK_WORKFLOW_VERSION = '1';
|
|
1
9
|
/**
|
|
2
10
|
* Runs webhook events through the {@link WebhookProcessor}. In `auto` mode (the default)
|
|
3
11
|
* it tries to import `@adonis-agora/durable` lazily — only when the app has it installed
|
|
@@ -10,9 +18,20 @@ export class WebhookDispatcher {
|
|
|
10
18
|
#maxAttempts;
|
|
11
19
|
#baseDelayMs;
|
|
12
20
|
#maxDelayMs;
|
|
13
|
-
#
|
|
21
|
+
#engine;
|
|
22
|
+
#registered = false;
|
|
14
23
|
#durableChecked = false;
|
|
15
24
|
#durableAvailable;
|
|
25
|
+
#durableEngine;
|
|
26
|
+
/**
|
|
27
|
+
* Work that has been ACCEPTED but is not finished: durable runs the engine has taken but
|
|
28
|
+
* not yet executed, workflow bodies currently running, and in-process background retries.
|
|
29
|
+
*
|
|
30
|
+
* Kept so {@link WebhookDispatcher.flushWebhooks} can answer "is anything still in
|
|
31
|
+
* flight?" — see there for why a test cannot answer it any other way.
|
|
32
|
+
*/
|
|
33
|
+
#accepted = 0;
|
|
34
|
+
#inFlight = new Set();
|
|
16
35
|
constructor(options) {
|
|
17
36
|
this.#processor = options.processor;
|
|
18
37
|
this.#mode = options.mode ?? 'auto';
|
|
@@ -20,15 +39,82 @@ export class WebhookDispatcher {
|
|
|
20
39
|
this.#baseDelayMs = options.retries?.baseDelayMs ?? 500;
|
|
21
40
|
this.#maxDelayMs = options.retries?.maxDelayMs ?? 30_000;
|
|
22
41
|
this.#durableAvailable = options.durableAvailable;
|
|
42
|
+
this.#durableEngine = options.durableEngine;
|
|
23
43
|
}
|
|
24
44
|
/**
|
|
25
45
|
* Dispatch a webhook event for processing. Resolves once the event is accepted (durable)
|
|
26
46
|
* or fully processed (in-process).
|
|
27
47
|
*/
|
|
28
48
|
async dispatch(event) {
|
|
49
|
+
const { runId } = await this.#dispatchOne(event);
|
|
50
|
+
return runId !== undefined ? { runId } : {};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Dispatch a whole DELIVERY: one event, or the several a batched envelope carried.
|
|
54
|
+
*
|
|
55
|
+
* **The loop lives here, not in the route.** Everything it has to get right — the ledger
|
|
56
|
+
* claim, the retry policy, which backend runs the work — is already this class's job; the
|
|
57
|
+
* route's job is to turn the outcome into a status code. It also makes the batch testable
|
|
58
|
+
* without standing up an HTTP context.
|
|
59
|
+
*
|
|
60
|
+
* **Sequential, deliberately.** Two events in one envelope routinely touch the same row
|
|
61
|
+
* (an Adyen AUTHORISATION and its CAPTURE; a Pix and its devolução), and running them
|
|
62
|
+
* concurrently would race the ledger claim and the payment upsert against each other,
|
|
63
|
+
* which is how you get a `paid` row overwritten by the `pending` that preceded it. The
|
|
64
|
+
* gateway's order is the order the money moved in, so it is the order they run in.
|
|
65
|
+
*
|
|
66
|
+
* **One failure does not cancel its siblings.** Event 2 of 4 throwing says nothing about
|
|
67
|
+
* events 3 and 4 — they are different payments, and refusing to process them because a
|
|
68
|
+
* neighbour failed loses money for a reason unrelated to them. So every event is attempted
|
|
69
|
+
* and the failures are COLLECTED. The caller is then responsible for the other half:
|
|
70
|
+
* reporting a non-2xx so the gateway redelivers, since a 200 tells it the failed one is
|
|
71
|
+
* done and it is never sent again. The redelivery re-runs only the failed event — the
|
|
72
|
+
* ledger claims a `failed` row again, while the ones that succeeded answer `null` and skip.
|
|
73
|
+
*/
|
|
74
|
+
async dispatchAll(events) {
|
|
75
|
+
const list = Array.isArray(events) ? events : [events];
|
|
76
|
+
const failures = [];
|
|
77
|
+
let dispatched = 0;
|
|
78
|
+
for (const event of list) {
|
|
79
|
+
try {
|
|
80
|
+
const { error } = await this.#dispatchOne(event);
|
|
81
|
+
if (error !== undefined)
|
|
82
|
+
failures.push({ event, error });
|
|
83
|
+
else
|
|
84
|
+
dispatched += 1;
|
|
85
|
+
}
|
|
86
|
+
catch (thrown) {
|
|
87
|
+
// The durable/queue path throws rather than reporting (an unreachable engine, a
|
|
88
|
+
// workflow that will not build). Caught here for the same reason as above: the
|
|
89
|
+
// remaining events still deserve their attempt.
|
|
90
|
+
failures.push({ event, error: asError(thrown) });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { total: list.length, dispatched, failures };
|
|
94
|
+
}
|
|
95
|
+
/** One event through the active backend. Durable/queue may throw; in-process reports. */
|
|
96
|
+
async #dispatchOne(event) {
|
|
29
97
|
if (await this.#useDurable()) {
|
|
30
|
-
const
|
|
31
|
-
|
|
98
|
+
const engine = await this.#resolveEngine();
|
|
99
|
+
this.#registerWorkflow(engine);
|
|
100
|
+
// A FRESH run id per delivery, deliberately. `engine.start` is idempotent by run id
|
|
101
|
+
// and returns the prior run's state for a repeat — so a deterministic id derived from
|
|
102
|
+
// the event would make the gateway's redelivery of a FAILED event a silent no-op,
|
|
103
|
+
// which is the one case redelivery exists for. Deduplication of an already-processed
|
|
104
|
+
// event is the ledger's job (the claim answers `null` and the run skips), and it
|
|
105
|
+
// makes that decision from the row's state rather than from the id alone.
|
|
106
|
+
const runId = randomUUID();
|
|
107
|
+
// Counted BEFORE the start and cleared when the workflow body begins, so the window
|
|
108
|
+
// between "the engine took it" and "the engine ran it" is not a window in which
|
|
109
|
+
// `flushWebhooks()` reports quiet. That window is the entire bug it exists for.
|
|
110
|
+
this.#accepted += 1;
|
|
111
|
+
try {
|
|
112
|
+
await engine.start(WEBHOOK_WORKFLOW_NAME, { event }, runId);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
this.#accepted -= 1;
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
32
118
|
return { runId };
|
|
33
119
|
}
|
|
34
120
|
return this.#processWithRetry(event);
|
|
@@ -38,8 +124,8 @@ export class WebhookDispatcher {
|
|
|
38
124
|
return this.#mode;
|
|
39
125
|
}
|
|
40
126
|
/**
|
|
41
|
-
* Decide whether durable handles this event. `auto`
|
|
42
|
-
*
|
|
127
|
+
* Decide whether durable handles this event. `auto` resolves the engine once and caches
|
|
128
|
+
* the answer; `durable` forces it (throwing when missing); `in-process` never.
|
|
43
129
|
*/
|
|
44
130
|
async #useDurable() {
|
|
45
131
|
if (this.#mode === 'in-process')
|
|
@@ -48,51 +134,64 @@ export class WebhookDispatcher {
|
|
|
48
134
|
return true;
|
|
49
135
|
// 'auto'
|
|
50
136
|
if (this.#durableChecked)
|
|
51
|
-
return this.#
|
|
137
|
+
return this.#engine !== undefined;
|
|
52
138
|
this.#durableChecked = true;
|
|
53
139
|
try {
|
|
54
140
|
if (this.#durableAvailable && !(await this.#durableAvailable())) {
|
|
55
|
-
this.#
|
|
141
|
+
this.#engine = undefined;
|
|
56
142
|
return false;
|
|
57
143
|
}
|
|
58
|
-
await
|
|
59
|
-
this.#workflow = await this.#buildWorkflow();
|
|
144
|
+
this.#engine = await this.#resolveEngine();
|
|
60
145
|
return true;
|
|
61
146
|
}
|
|
62
147
|
catch {
|
|
63
|
-
this.#
|
|
148
|
+
this.#engine = undefined;
|
|
64
149
|
return false;
|
|
65
150
|
}
|
|
66
151
|
}
|
|
67
152
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
153
|
+
* The app's durable engine, from the seam the provider wires.
|
|
154
|
+
*
|
|
155
|
+
* There is no fallback that imports `@adonis-agora/durable` and builds its own: an engine
|
|
156
|
+
* is bound to a store and a transport that only the app has configured, and a second one
|
|
157
|
+
* would persist runs nothing ever executes.
|
|
70
158
|
*/
|
|
71
|
-
async #
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
async run(_ctx, input) {
|
|
79
|
-
await processor.process(input.event);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
return workflow;
|
|
83
|
-
}
|
|
84
|
-
async #resolveDurable() {
|
|
85
|
-
const workflow = this.#workflow ?? (await this.#buildWorkflow());
|
|
86
|
-
if (!workflow) {
|
|
87
|
-
throw new Error('[payments] billing.durable is set to true/auto but @adonis-agora/durable is not installed. ' +
|
|
88
|
-
'Install it (`node ace add @adonis-agora/durable`) or set billing.durable to false.');
|
|
159
|
+
async #resolveEngine() {
|
|
160
|
+
if (this.#engine)
|
|
161
|
+
return this.#engine;
|
|
162
|
+
if (!this.#durableEngine) {
|
|
163
|
+
throw new Error('[payments] billing.dispatcher is durable but no durable engine was provided. ' +
|
|
164
|
+
'The payments provider wires this from the container — install @adonis-agora/durable ' +
|
|
165
|
+
'and register its provider, or set billing.dispatcher to "in-process".');
|
|
89
166
|
}
|
|
90
|
-
|
|
167
|
+
this.#engine = await this.#durableEngine();
|
|
168
|
+
return this.#engine;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Register the webhook workflow on the engine. Idempotent, and it has to happen before
|
|
172
|
+
* the first `start`: the engine resolves a run by looking its NAME up in the registry and
|
|
173
|
+
* throws `workflow payments-webhook is not registered` otherwise.
|
|
174
|
+
*/
|
|
175
|
+
#registerWorkflow(engine) {
|
|
176
|
+
if (this.#registered)
|
|
177
|
+
return;
|
|
178
|
+
const processor = this.#processor;
|
|
179
|
+
engine.register(WEBHOOK_WORKFLOW_NAME, WEBHOOK_WORKFLOW_VERSION, async (_ctx, input) => {
|
|
180
|
+
this.#accepted = Math.max(0, this.#accepted - 1);
|
|
181
|
+
await this.#track(processor.process(input.event));
|
|
182
|
+
});
|
|
183
|
+
this.#registered = true;
|
|
91
184
|
}
|
|
92
185
|
/**
|
|
93
186
|
* In-process fallback: run through the processor, retrying with exponential backoff on
|
|
94
187
|
* failure (up to `#maxAttempts`). Retries run in the background — the first attempt is
|
|
95
188
|
* awaited so the webhook endpoint can respond.
|
|
189
|
+
*
|
|
190
|
+
* The failure is REPORTED as well as retried. The background retry lives and dies with
|
|
191
|
+
* this process, so a crash between the failure and the retry loses the event outright;
|
|
192
|
+
* the gateway's own redelivery is the only durable retry there is, and it only happens if
|
|
193
|
+
* the route answers non-2xx. Both run, and the ledger keeps that safe: whichever attempt
|
|
194
|
+
* claims the `failed` row first does the work, and the other short-circuits on the claim.
|
|
96
195
|
*/
|
|
97
196
|
async #processWithRetry(event) {
|
|
98
197
|
try {
|
|
@@ -102,8 +201,8 @@ export class WebhookDispatcher {
|
|
|
102
201
|
catch (error) {
|
|
103
202
|
// Retry in the background (don't block the webhook response). The processor itself
|
|
104
203
|
// publishes `webhook.failed` on the diagnostics channel.
|
|
105
|
-
void this.#retry(event, 2);
|
|
106
|
-
return {};
|
|
204
|
+
void this.#track(this.#retry(event, 2));
|
|
205
|
+
return { error: asError(error) };
|
|
107
206
|
}
|
|
108
207
|
}
|
|
109
208
|
async #retry(event, attempt) {
|
|
@@ -118,8 +217,62 @@ export class WebhookDispatcher {
|
|
|
118
217
|
await this.#retry(event, attempt + 1);
|
|
119
218
|
}
|
|
120
219
|
}
|
|
220
|
+
/** Record a promise as in-flight until it settles, and hand it back unchanged. */
|
|
221
|
+
#track(promise) {
|
|
222
|
+
const tracked = promise.finally(() => {
|
|
223
|
+
this.#inFlight.delete(tracked);
|
|
224
|
+
});
|
|
225
|
+
// A rejection here is somebody else's to report (`dispatchAll` collects it, the retry
|
|
226
|
+
// loop swallows it); this handler only exists so tracking never adds an unhandled
|
|
227
|
+
// rejection of its own.
|
|
228
|
+
tracked.catch(() => { });
|
|
229
|
+
this.#inFlight.add(tracked);
|
|
230
|
+
return promise;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Resolve when nothing this dispatcher accepted is still being processed.
|
|
234
|
+
*
|
|
235
|
+
* **The asymmetry this fixes.** In `in-process` mode `dispatchAll` resolves when the event
|
|
236
|
+
* has been PROCESSED, so a test can assert straight after it. In `durable` mode it
|
|
237
|
+
* resolves when the event has been ACCEPTED — the run executes out of band — so the same
|
|
238
|
+
* assertion races the workflow. Every app that pairs `billing.dispatcher` with durable
|
|
239
|
+
* writes the same polling `waitFor(() => ...)` helper to paper over it, and then cannot
|
|
240
|
+
* write a NEGATIVE assertion at all: "nothing was granted" against a poll is a timed
|
|
241
|
+
* sleep, which is slow when it passes and lies when the machine is loaded.
|
|
242
|
+
*
|
|
243
|
+
* `await dispatcher.flushWebhooks()` is that assertion made possible: after it, an
|
|
244
|
+
* unchanged row means the webhook genuinely changed nothing.
|
|
245
|
+
*
|
|
246
|
+
* It waits for the background in-process RETRIES too, which have exactly the same problem
|
|
247
|
+
* — the first attempt is awaited and the retries are not.
|
|
248
|
+
*
|
|
249
|
+
* Only reaches work this process accepted. A worker-role deployment runs the events in
|
|
250
|
+
* another process, and no in-process await can see those; the timeout says so rather than
|
|
251
|
+
* hanging.
|
|
252
|
+
*/
|
|
253
|
+
async flushWebhooks(options = {}) {
|
|
254
|
+
const timeoutMs = options.timeoutMs ?? 5_000;
|
|
255
|
+
const deadline = Date.now() + timeoutMs;
|
|
256
|
+
while (this.#accepted > 0 || this.#inFlight.size > 0) {
|
|
257
|
+
if (Date.now() > deadline) {
|
|
258
|
+
throw new Error([
|
|
259
|
+
`[payments] flushWebhooks timed out after ${timeoutMs}ms with`,
|
|
260
|
+
`${this.#accepted + this.#inFlight.size} webhook(s) still in flight.`,
|
|
261
|
+
'If the events are processed by a separate worker process, no in-process wait can',
|
|
262
|
+
'observe them — assert against the ledger instead.',
|
|
263
|
+
].join(' '));
|
|
264
|
+
}
|
|
265
|
+
// Yield to whatever is running rather than polling on a timer: an in-process retry is
|
|
266
|
+
// on a real `setTimeout` and a durable run may be a microtask away.
|
|
267
|
+
await Promise.race([...this.#inFlight, sleep(1)]).catch(() => { });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
121
270
|
}
|
|
122
271
|
function sleep(ms) {
|
|
123
272
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
124
273
|
}
|
|
274
|
+
/** A thrown value as an `Error` — a handler may throw a string, and the message is the point. */
|
|
275
|
+
function asError(thrown) {
|
|
276
|
+
return thrown instanceof Error ? thrown : new Error(String(thrown));
|
|
277
|
+
}
|
|
125
278
|
//# sourceMappingURL=webhook_dispatcher.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook_dispatcher.js","sourceRoot":"","sources":["../../../src/billing/webhook_dispatcher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"webhook_dispatcher.js","sourceRoot":"","sources":["../../../src/billing/webhook_dispatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,13 +5,20 @@ import type { WebhookEvent } from '../types.js';
|
|
|
5
5
|
* them. Shared constant so a typo in one driver can't silently degrade to the no-op
|
|
6
6
|
* branch.
|
|
7
7
|
*/
|
|
8
|
-
export declare const WEBHOOK_EVENT_TYPES: readonly [
|
|
8
|
+
export declare const WEBHOOK_EVENT_TYPES: readonly ['payment.succeeded', 'payment.failed', 'payment.refunded', 'payment.disputed', 'payment.dispute_warning', 'payment.dispute_closed', 'payment.updated', 'subscription.created', 'subscription.updated', 'subscription.canceled'];
|
|
9
9
|
export type WebhookEventType = (typeof WEBHOOK_EVENT_TYPES)[number];
|
|
10
10
|
/** Shape the built-in sync handlers expect from a payment event's `data`. */
|
|
11
11
|
export interface PaymentWebhookData {
|
|
12
12
|
gatewayId: string;
|
|
13
13
|
amount: number;
|
|
14
14
|
currency: string;
|
|
15
|
+
/**
|
|
16
|
+
* Why it failed, in the gateway's own words. Optional because most gateways send nothing
|
|
17
|
+
* useful — and declared here because `PaymentFailedPayload` has carried a `reason` since
|
|
18
|
+
* the bus was written while the processor never published one, so a subscriber could not
|
|
19
|
+
* see it even on the gateways that do normalize it.
|
|
20
|
+
*/
|
|
21
|
+
reason?: string;
|
|
15
22
|
customerId?: string;
|
|
16
23
|
subscriptionId?: string;
|
|
17
24
|
/**
|
|
@@ -20,6 +27,36 @@ export interface PaymentWebhookData {
|
|
|
20
27
|
* digging into `event.raw` — the reason {@link ChargeInput.externalReference} exists.
|
|
21
28
|
*/
|
|
22
29
|
externalReference?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The payment's CURRENT status at the gateway, as a {@link import('../types.js').BillingStatus}
|
|
32
|
+
* value (`'paid'`, `'refunded'`, `'disputed'`, `'canceled'`, `'failed'`, `'pending'`,
|
|
33
|
+
* `'authorized'`).
|
|
34
|
+
*
|
|
35
|
+
* Only `payment.updated` reads it, and it is the reason that event can do anything at all:
|
|
36
|
+
* the other five payment events state their outcome in their TYPE, while an update says
|
|
37
|
+
* only "this payment changed" and the new state is on the payload. Optional, because most
|
|
38
|
+
* drivers do not normalize one — the built-in sync leaves the stored status alone when it
|
|
39
|
+
* is absent rather than guessing.
|
|
40
|
+
*/
|
|
41
|
+
status?: string;
|
|
42
|
+
/**
|
|
43
|
+
* When the gateway settled the charge, ISO-8601 — the gateway's OWN date, never the
|
|
44
|
+
* webhook's arrival time.
|
|
45
|
+
*
|
|
46
|
+
* `revenue()` windows on this, so inventing it moves historic money into the current month.
|
|
47
|
+
* Optional: a driver that cannot read a settlement date must send nothing, and the
|
|
48
|
+
* processor then leaves whatever is stored alone.
|
|
49
|
+
*/
|
|
50
|
+
paidAt?: string;
|
|
51
|
+
/**
|
|
52
|
+
* How much has been refunded so far, in the SAME integer minor units as `amount`.
|
|
53
|
+
*
|
|
54
|
+
* The field a PARTIAL refund needs and nothing else has: a R$10 refund on a R$100 charge is
|
|
55
|
+
* `amount: 10000, refundedAmount: 1000` and the status stays `paid`. Overwriting `amount`
|
|
56
|
+
* with the refunded figure — the only alternative before this existed — erased R$90 of
|
|
57
|
+
* revenue. NEVER divide.
|
|
58
|
+
*/
|
|
59
|
+
refundedAmount?: number;
|
|
23
60
|
/** Gateway-echoed metadata, when the gateway includes it in the webhook payload. */
|
|
24
61
|
metadata?: Record<string, unknown>;
|
|
25
62
|
}
|
|
@@ -38,6 +75,35 @@ export interface SubscriptionWebhookData {
|
|
|
38
75
|
* checks it before writing a row so a malformed event can't produce garbage rows.
|
|
39
76
|
*/
|
|
40
77
|
export declare function isPaymentWebhookData(data: unknown): data is PaymentWebhookData;
|
|
78
|
+
/**
|
|
79
|
+
* A dispute event's `data`. Deliberately looser than {@link PaymentWebhookData}: a dispute
|
|
80
|
+
* alert does not always carry an amount. Stripe's early fraud warning object has no amount
|
|
81
|
+
* or currency at all — it names a charge and a fraud type — and refusing it for that would
|
|
82
|
+
* throw away the earliest warning the library gets.
|
|
83
|
+
*/
|
|
84
|
+
export interface DisputeWebhookData {
|
|
85
|
+
/** The PAYMENT's gateway id, not the dispute's — the row this is about. */
|
|
86
|
+
gatewayId: string;
|
|
87
|
+
disputeId?: string;
|
|
88
|
+
reason?: string;
|
|
89
|
+
/** When the window to respond closes. The whole value of a warning. */
|
|
90
|
+
actionableUntil?: string;
|
|
91
|
+
outcome?: 'won' | 'lost' | 'canceled' | 'expired';
|
|
92
|
+
amount?: number;
|
|
93
|
+
currency?: string;
|
|
94
|
+
/**
|
|
95
|
+
* The `externalReference` the app set on the disputed charge, when the driver has it. The
|
|
96
|
+
* gateways that build a dispute event out of the payment resource carry it (Asaas nests
|
|
97
|
+
* `chargeback` on the payment and spreads the payment's fields — see the Asaas driver's
|
|
98
|
+
* `#disputeExtras`), and an app routing a chargeback back to its own order needs it as
|
|
99
|
+
* much as a `payment.succeeded` handler does. Optional because a gateway whose dispute is
|
|
100
|
+
* a standalone object — Stripe's early fraud warning names only a charge — has nothing to
|
|
101
|
+
* put here.
|
|
102
|
+
*/
|
|
103
|
+
externalReference?: string;
|
|
104
|
+
}
|
|
105
|
+
/** Guard: is this event's `data` shaped like a dispute event? */
|
|
106
|
+
export declare function isDisputeWebhookData(data: unknown): data is DisputeWebhookData;
|
|
41
107
|
/** Guard: is this event's `data` shaped like a subscription event? */
|
|
42
108
|
export declare function isSubscriptionWebhookData(data: unknown): data is SubscriptionWebhookData;
|
|
43
109
|
/** Assert a webhook event's type is one of the canonical types (or a passthrough). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook_events.d.ts","sourceRoot":"","sources":["../../../src/billing/webhook_events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"webhook_events.d.ts","sourceRoot":"","sources":["../../../src/billing/webhook_events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,YAC9B,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAOlB,yBAAyB,EACzB,wBAAwB,EACxB,iBAAiB,EACjB,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,CACf,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,kFAAkF;AAClF,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,kBAAkB,CAQ9E;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;OAQG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,iEAAiE;AACjE,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,kBAAkB,CAG9E;AAED,sEAAsE;AACtE,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,uBAAuB,CAIxF;AAED,sFAAsF;AACtF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,gBAAgB,CAEzE;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAC5B,KAAK,EAAE,YAAY,GAClB,YAAY,CAAC,OAAO,CAAC,GAAG;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAEpD"}
|
|
@@ -8,6 +8,15 @@ export const WEBHOOK_EVENT_TYPES = [
|
|
|
8
8
|
'payment.succeeded',
|
|
9
9
|
'payment.failed',
|
|
10
10
|
'payment.refunded',
|
|
11
|
+
'payment.disputed',
|
|
12
|
+
// A dispute has two more moments than the one `payment.disputed` names. The warning
|
|
13
|
+
// arrives BEFORE a chargeback exists — Stripe's early fraud warning, Adyen's fraud
|
|
14
|
+
// notification, an Ethoca/Verifi alert — while refunding is still cheaper than losing.
|
|
15
|
+
// The close carries the outcome. Both are canonical because the deadline they carry is
|
|
16
|
+
// the only thing that makes a dispute actionable, and a deadline nothing normalizes is a
|
|
17
|
+
// deadline every app re-derives from a different gateway's payload.
|
|
18
|
+
'payment.dispute_warning',
|
|
19
|
+
'payment.dispute_closed',
|
|
11
20
|
'payment.updated',
|
|
12
21
|
'subscription.created',
|
|
13
22
|
'subscription.updated',
|
|
@@ -26,6 +35,12 @@ export function isPaymentWebhookData(data) {
|
|
|
26
35
|
typeof d.amount === 'number' &&
|
|
27
36
|
typeof d.currency === 'string');
|
|
28
37
|
}
|
|
38
|
+
/** Guard: is this event's `data` shaped like a dispute event? */
|
|
39
|
+
export function isDisputeWebhookData(data) {
|
|
40
|
+
if (typeof data !== 'object' || data === null)
|
|
41
|
+
return false;
|
|
42
|
+
return typeof data.gatewayId === 'string';
|
|
43
|
+
}
|
|
29
44
|
/** Guard: is this event's `data` shaped like a subscription event? */
|
|
30
45
|
export function isSubscriptionWebhookData(data) {
|
|
31
46
|
if (typeof data !== 'object' || data === null)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook_events.js","sourceRoot":"","sources":["../../../src/billing/webhook_events.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,mBAAmB;IACnB,gBAAgB;IAChB,kBAAkB;IAClB,iBAAiB;IACjB,sBAAsB;IACtB,sBAAsB;IACtB,uBAAuB;CACf,CAAC;
|
|
1
|
+
{"version":3,"file":"webhook_events.js","sourceRoot":"","sources":["../../../src/billing/webhook_events.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,mBAAmB;IACnB,gBAAgB;IAChB,kBAAkB;IAClB,kBAAkB;IAClB,oFAAoF;IACpF,mFAAmF;IACnF,uFAAuF;IACvF,uFAAuF;IACvF,yFAAyF;IACzF,oEAAoE;IACpE,yBAAyB;IACzB,wBAAwB;IACxB,iBAAiB;IACjB,sBAAsB;IACtB,sBAAsB;IACtB,uBAAuB;CACf,CAAC;AAoEX;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAa;IAChD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC5D,MAAM,CAAC,GAAG,IAA+B,CAAC;IAC1C,OAAO,CACL,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;QAC/B,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;QAC5B,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAC/B,CAAC;AACJ,CAAC;AA8BD,iEAAiE;AACjE,MAAM,UAAU,oBAAoB,CAAC,IAAa;IAChD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC5D,OAAO,OAAQ,IAAgC,CAAC,SAAS,KAAK,QAAQ,CAAC;AACzE,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,yBAAyB,CAAC,IAAa;IACrD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC5D,MAAM,CAAC,GAAG,IAA+B,CAAC;IAC1C,OAAO,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ,CAAC;AAC7E,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAQ,mBAAyC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACnE,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,cAAc,CAC5B,KAAmB;IAEnB,OAAO,KAA2D,CAAC;AACrE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook_processor.d.ts","sourceRoot":"","sources":["../../../src/billing/webhook_processor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"webhook_processor.d.ts","sourceRoot":"","sources":["../../../src/billing/webhook_processor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQvD;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE3E,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,YAAY,CAAC;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC3C;AAiBD;;;;;;;;;GASG;AACH,qBAAa,gBAAgB;;IAK3B,YAAY,OAAO,EAAE,uBAAuB,EAI3C;IAED;;;OAGG;IACG,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CA2CnD;CAudF"}
|