@adonis-agora/payments 0.2.0 → 0.3.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 +2347 -0
- package/dist/assets/spa/assets/index-CT5Zon6P.css +1 -0
- package/dist/assets/spa/assets/index-CvEbJEnq.js +41 -0
- package/dist/assets/spa/assets/index-CvEbJEnq.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/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/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 +2 -1
- package/dist/src/billing/index.d.ts.map +1 -1
- package/dist/src/billing/index.js +1 -0
- 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 +3 -0
- package/dist/src/billing/mixins/index.d.ts.map +1 -1
- package/dist/src/billing/mixins/index.js +3 -0
- 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_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 +16 -0
- package/dist/src/client/index.d.ts.map +1 -0
- package/dist/src/client/index.js +13 -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 +18 -0
- package/dist/src/dashboard/index.d.ts.map +1 -0
- package/dist/src/dashboard/index.js +14 -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 +244 -9
- 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 +12 -2
- 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 +52 -2
- 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 +40 -3
- package/dist/src/drivers/stripe.d.ts.map +1 -1
- package/dist/src/drivers/stripe.js +806 -25
- package/dist/src/drivers/stripe.js.map +1 -1
- package/dist/src/drivers/woovi.d.ts +10 -3
- 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/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 +40 -10
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +22 -4
- package/dist/src/index.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/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 +65 -13
- 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/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 +2 -1
- 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/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/make/webhook_handler/main.stub +29 -11
- package/package.json +60 -25
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { resolveConfig, } from '../src/client/define_config.js';
|
|
2
|
+
import { paymentStatus } from '../src/client/handlers.js';
|
|
3
|
+
import { getBillingStore } from '../src/services/main.js';
|
|
4
|
+
/**
|
|
5
|
+
* Mounts the browser-facing status endpoint from `config/payments_client.ts`.
|
|
6
|
+
*
|
|
7
|
+
* One route, and only one:
|
|
8
|
+
*
|
|
9
|
+
* - `GET <path>/status?reference=<reference>` -> `{ status, amount, currency, paidAt }`
|
|
10
|
+
*
|
|
11
|
+
* It exists because a Pix QR (or a boleto) is not paid until the gateway's webhook says so,
|
|
12
|
+
* seconds to days later, and every app that takes Pix otherwise hand-writes the same
|
|
13
|
+
* polling loop in its checkout page. It does NOT wrap any gateway's card SDK — Stripe,
|
|
14
|
+
* Mercado Pago and Adyen ship their own, and eighteen of those is a surface this package
|
|
15
|
+
* could not keep honest.
|
|
16
|
+
*
|
|
17
|
+
* Disabled by default. With `enabled: false` (the default) this provider registers NOTHING:
|
|
18
|
+
* unlike the dashboard, which is an operator console behind an admin gate, this endpoint is
|
|
19
|
+
* reachable by every logged-in browser, so an app takes it on deliberately.
|
|
20
|
+
*
|
|
21
|
+
* Registered separately from `payments_provider` for the same reason the dashboard is: the
|
|
22
|
+
* webhook route lives at `/payments/webhook/:provider`, and a guard mounted over that prefix
|
|
23
|
+
* is how a gateway delivery ends up answering `403`. This one mounts under
|
|
24
|
+
* `/payments/client` and touches nothing above it.
|
|
25
|
+
*/
|
|
26
|
+
export default class PaymentsClientProvider {
|
|
27
|
+
app;
|
|
28
|
+
constructor(app) {
|
|
29
|
+
this.app = app;
|
|
30
|
+
}
|
|
31
|
+
/** Warn once, not once per poll — a denied checkout page can retry for a while. */
|
|
32
|
+
warnedOnDeny = false;
|
|
33
|
+
async boot() {
|
|
34
|
+
const config = resolveConfig(this.app.config.get('payments_client', {}));
|
|
35
|
+
if (!config.enabled)
|
|
36
|
+
return;
|
|
37
|
+
// Registration can't happen synchronously in `boot()`: the router singleton isn't
|
|
38
|
+
// committed until the app's "booted" hooks run, which fire after every provider's `boot()`.
|
|
39
|
+
await this.app.booted(async () => {
|
|
40
|
+
const router = await this.app.container.make('router');
|
|
41
|
+
this.registerRoutes(router, config);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
registerRoutes(router, config) {
|
|
45
|
+
router
|
|
46
|
+
.get(`${config.path}/status`, async (ctx) => {
|
|
47
|
+
// A payment status is per-caller and changes under you. Nothing between the browser
|
|
48
|
+
// and here may keep a copy — least of all a shared proxy, which would hand one
|
|
49
|
+
// customer's settled charge to the next caller with the same URL.
|
|
50
|
+
ctx.response.header('cache-control', 'no-store');
|
|
51
|
+
let store;
|
|
52
|
+
try {
|
|
53
|
+
store = getBillingStore();
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
// The billing layer is off, or the app has not booted it. A deployment state, not
|
|
57
|
+
// a bad request — say so rather than emit a stack-trace-shaped 500.
|
|
58
|
+
return ctx.response.status(503).json({
|
|
59
|
+
error: error instanceof Error ? error.message : 'billing store unavailable',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const reference = ctx.request.qs().reference;
|
|
63
|
+
const result = await paymentStatus({
|
|
64
|
+
store,
|
|
65
|
+
config,
|
|
66
|
+
onDeny: (reason) => {
|
|
67
|
+
void this.warnOnDeny(reason);
|
|
68
|
+
},
|
|
69
|
+
}, ctx, typeof reference === 'string' ? reference : undefined);
|
|
70
|
+
return ctx.response.status(result.status).json(result.body);
|
|
71
|
+
})
|
|
72
|
+
.as('payments_client.status');
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Surface the developer-facing denial reason once.
|
|
76
|
+
*
|
|
77
|
+
* The browser only ever sees `403`. The single most likely cause — the app never called
|
|
78
|
+
* `ensureCustomer({ store, owner })`, so `billing_customers` is empty and the default
|
|
79
|
+
* guard can prove nothing — is otherwise indistinguishable from a real denial, and an
|
|
80
|
+
* install would sit there refusing every poll with no clue why.
|
|
81
|
+
*/
|
|
82
|
+
async warnOnDeny(reason) {
|
|
83
|
+
if (this.warnedOnDeny)
|
|
84
|
+
return;
|
|
85
|
+
this.warnedOnDeny = true;
|
|
86
|
+
try {
|
|
87
|
+
const logger = await this.app.container.make('logger');
|
|
88
|
+
logger.warn(reason);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// No logger (a container this bare is a test harness, not a running app).
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=payments_client_provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payments_client_provider.js","sourceRoot":"","sources":["../../providers/payments_client_provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
1
2
|
import type { ApplicationService } from '@adonisjs/core/types';
|
|
3
|
+
import type { BillingStore } from '../src/billing/billing_store.js';
|
|
4
|
+
import { WebhookDispatcher } from '../src/billing/webhook_dispatcher.js';
|
|
5
|
+
import { PaymentsManager } from '../src/payments_manager.js';
|
|
2
6
|
/**
|
|
3
7
|
* Wires `@adonis-agora/payments` into the AdonisJS application: binds a singleton
|
|
4
8
|
* {@link PaymentsManager} built from `config/payments.ts`, wires the optional Lucid
|
|
@@ -11,5 +15,36 @@ export default class PaymentsProvider {
|
|
|
11
15
|
constructor(app: ApplicationService);
|
|
12
16
|
register(): void;
|
|
13
17
|
boot(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Wire the typed `payments` Telescope watcher, if telescope is in this app and nobody has
|
|
20
|
+
* already wired one.
|
|
21
|
+
*
|
|
22
|
+
* In `ready()`, not `boot()`, and that is the load-bearing part: `start/` files run before
|
|
23
|
+
* `ready`, so an app that registers its own `PaymentsWatcher` — the way the docs used to
|
|
24
|
+
* say to — has already claimed the payments channels by the time this looks, and this
|
|
25
|
+
* stands down. Doing it in `boot()` would race that and record every payment event twice
|
|
26
|
+
* for exactly the apps that followed the instructions.
|
|
27
|
+
*
|
|
28
|
+
* There is no config option. Every one that was drafted described something the code can
|
|
29
|
+
* detect: the claims registry answers "I wire my own", and a failed import answers "I do
|
|
30
|
+
* not use telescope".
|
|
31
|
+
*/
|
|
32
|
+
ready(): Promise<void>;
|
|
33
|
+
/** Release the watcher's channel claims and subscriptions on shutdown. */
|
|
34
|
+
shutdown(): Promise<void>;
|
|
14
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* One webhook delivery, from raw body to HTTP status — the body of the mounted
|
|
38
|
+
* `POST /payments/webhook/:provider` route.
|
|
39
|
+
*
|
|
40
|
+
* Exported so the delivery can be tested for what it actually promises the gateway (the
|
|
41
|
+
* status code IS the retry instruction) without standing up an application, a router and a
|
|
42
|
+
* container around it.
|
|
43
|
+
*/
|
|
44
|
+
export declare function handleWebhookDelivery(ctx: HttpContext, deps: {
|
|
45
|
+
manager: PaymentsManager;
|
|
46
|
+
dispatcher?: WebhookDispatcher | undefined;
|
|
47
|
+
/** Where a REFUSED delivery is recorded. Absent when the billing layer is off. */
|
|
48
|
+
store?: Pick<BillingStore, 'recordAuditEvent'> | undefined;
|
|
49
|
+
}): Promise<unknown>;
|
|
15
50
|
//# sourceMappingURL=payments_provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payments_provider.d.ts","sourceRoot":"","sources":["../../providers/payments_provider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"payments_provider.d.ts","sourceRoot":"","sources":["../../providers/payments_provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAqB,MAAM,sBAAsB,CAAC;AAElF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAQpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAczE,OAAO,EAAE,eAAe,EAAkB,MAAM,4BAA4B,CAAC;AAa7E;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;;IAKvB,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAE7C,QAAQ;IA2BF,IAAI;IA6BV;;;;;;;;;;;;;OAaG;IACG,KAAK;IAKX,0EAA0E;IACpE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAgLhC;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE;IACJ,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC3C,kFAAkF;IAClF,KAAK,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,kBAAkB,CAAC,GAAG,SAAS,CAAC;CAC5D,GACA,OAAO,CAAC,OAAO,CAAC,CAqGlB"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { pathToFileURL } from 'node:url';
|
|
2
|
+
import { AUDIT_ACTIONS } from '../src/billing/billing_store.js';
|
|
2
3
|
import { LucidBillingStore } from '../src/billing/lucid_billing_store.js';
|
|
4
|
+
import { assertRoleIsDispatchable, resolveDispatchMode, resolveRole, } from '../src/billing/resolve_dispatch.js';
|
|
5
|
+
import { isInjectableAsLucid, resolveBillingStore } from '../src/billing/resolve_store.js';
|
|
3
6
|
import { WebhookDispatcher } from '../src/billing/webhook_dispatcher.js';
|
|
4
7
|
import { WebhookProcessor } from '../src/billing/webhook_processor.js';
|
|
8
|
+
import { newPaymentsTraceId, publishWebhookVerification, runWithPaymentsTrace, webhookVerificationOutcome, } from '../src/diagnostics.js';
|
|
5
9
|
import { InvoiceManager, resolveInvoiceProviders } from '../src/invoice/invoice_manager.js';
|
|
6
10
|
import { PaymentsManager, resolveDrivers } from '../src/payments_manager.js';
|
|
7
|
-
import { setPayments } from '../src/services/main.js';
|
|
8
|
-
import { discoverWebhookHandlers, loadWebhookHandlersFromBarrel, resolveWebhookHandler, } from '../src/webhook_handlers.js';
|
|
11
|
+
import { setBillingStore, setPayments, setWebhookDispatcher } from '../src/services/main.js';
|
|
12
|
+
import { assertWebhookHandlerTypes, discoverWebhookHandlers, loadWebhookHandlersFromBarrel, resolveWebhookHandler, } from '../src/webhook_handlers.js';
|
|
13
|
+
import { assertWebhookVerification } from '../src/webhook_security.js';
|
|
9
14
|
/**
|
|
10
15
|
* Wires `@adonis-agora/payments` into the AdonisJS application: binds a singleton
|
|
11
16
|
* {@link PaymentsManager} built from `config/payments.ts`, wires the optional Lucid
|
|
@@ -15,6 +20,8 @@ import { discoverWebhookHandlers, loadWebhookHandlersFromBarrel, resolveWebhookH
|
|
|
15
20
|
export default class PaymentsProvider {
|
|
16
21
|
app;
|
|
17
22
|
#webhook;
|
|
23
|
+
/** Held for the webhook route: a REFUSED delivery is filed here, having no ledger row. */
|
|
24
|
+
#store;
|
|
18
25
|
constructor(app) {
|
|
19
26
|
this.app = app;
|
|
20
27
|
}
|
|
@@ -40,6 +47,8 @@ export default class PaymentsProvider {
|
|
|
40
47
|
return manager;
|
|
41
48
|
});
|
|
42
49
|
}
|
|
50
|
+
/** Kept so `shutdown()` can release the channel claims it took. */
|
|
51
|
+
#telescopeWatcher;
|
|
43
52
|
async boot() {
|
|
44
53
|
await this.app.booted(async () => {
|
|
45
54
|
const config = this.app.config.get('payments', {});
|
|
@@ -50,25 +59,108 @@ export default class PaymentsProvider {
|
|
|
50
59
|
}
|
|
51
60
|
// Resolve the manager here so `setPayments` runs and `getPayments()` works for
|
|
52
61
|
// services that read it during boot — after every provider has registered.
|
|
53
|
-
await this.app.container.make(PaymentsManager);
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
const manager = await this.app.container.make(PaymentsManager);
|
|
63
|
+
// Before the route goes up, not after: a driver with an empty webhook-credential slot
|
|
64
|
+
// verifies nothing, and the route it would be mounted behind is reachable from the
|
|
65
|
+
// public internet. Fail closed at boot, the way the dashboard does on a missing session
|
|
66
|
+
// secret.
|
|
67
|
+
assertWebhookVerification(manager.drivers, {
|
|
68
|
+
...(config.allowUnverifiedWebhooks !== undefined
|
|
69
|
+
? { allowUnverifiedWebhooks: config.allowUnverifiedWebhooks }
|
|
70
|
+
: {}),
|
|
71
|
+
});
|
|
72
|
+
// A 'worker' process consumes what the api half enqueued; it must not also
|
|
73
|
+
// advertise an endpoint gateways could deliver to.
|
|
74
|
+
if (resolveRole(config) !== 'worker') {
|
|
75
|
+
const router = await this.app.container.make('router');
|
|
76
|
+
this.#registerWebhookRoute(router);
|
|
77
|
+
}
|
|
56
78
|
});
|
|
57
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Wire the typed `payments` Telescope watcher, if telescope is in this app and nobody has
|
|
82
|
+
* already wired one.
|
|
83
|
+
*
|
|
84
|
+
* In `ready()`, not `boot()`, and that is the load-bearing part: `start/` files run before
|
|
85
|
+
* `ready`, so an app that registers its own `PaymentsWatcher` — the way the docs used to
|
|
86
|
+
* say to — has already claimed the payments channels by the time this looks, and this
|
|
87
|
+
* stands down. Doing it in `boot()` would race that and record every payment event twice
|
|
88
|
+
* for exactly the apps that followed the instructions.
|
|
89
|
+
*
|
|
90
|
+
* There is no config option. Every one that was drafted described something the code can
|
|
91
|
+
* detect: the claims registry answers "I wire my own", and a failed import answers "I do
|
|
92
|
+
* not use telescope".
|
|
93
|
+
*/
|
|
94
|
+
async ready() {
|
|
95
|
+
const { registerPaymentsWatcher } = await import('../src/telescope/register.js');
|
|
96
|
+
this.#telescopeWatcher = await registerPaymentsWatcher(this.app);
|
|
97
|
+
}
|
|
98
|
+
/** Release the watcher's channel claims and subscriptions on shutdown. */
|
|
99
|
+
async shutdown() {
|
|
100
|
+
this.#telescopeWatcher?.dispose();
|
|
101
|
+
this.#telescopeWatcher = undefined;
|
|
102
|
+
}
|
|
58
103
|
async #buildBillingLayer(config) {
|
|
59
|
-
const
|
|
60
|
-
const
|
|
104
|
+
const mode = resolveDispatchMode(config);
|
|
105
|
+
const role = resolveRole(config);
|
|
106
|
+
assertRoleIsDispatchable(role, mode);
|
|
107
|
+
const store = await this.#resolveBillingStore(config);
|
|
108
|
+
this.#store = store;
|
|
109
|
+
let handlers;
|
|
110
|
+
if (role === 'api') {
|
|
111
|
+
// The handlers run on the worker, so resolving them here would scan the folder and
|
|
112
|
+
// build services this process never calls. The TYPES are still checked, from config
|
|
113
|
+
// alone: a misspelled key is a config error on both halves of the deployment, and the
|
|
114
|
+
// api process is usually the one a developer boots first.
|
|
115
|
+
assertWebhookHandlerTypes(Object.keys(config.billing?.handlers ?? {}).map((type) => ({
|
|
116
|
+
type,
|
|
117
|
+
source: 'billing.handlers',
|
|
118
|
+
})), {
|
|
119
|
+
...(config.billing?.passthroughEvents !== undefined
|
|
120
|
+
? { passthroughEvents: config.billing.passthroughEvents }
|
|
121
|
+
: {}),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
handlers = await this.#resolveAllHandlers(config.billing?.handlers, config.billing?.passthroughEvents);
|
|
126
|
+
}
|
|
61
127
|
const processor = new WebhookProcessor({
|
|
62
128
|
store,
|
|
63
129
|
...(handlers !== undefined ? { handlers } : {}),
|
|
64
130
|
});
|
|
65
|
-
|
|
131
|
+
const dispatcher = new WebhookDispatcher({
|
|
66
132
|
processor,
|
|
67
|
-
mode
|
|
133
|
+
mode,
|
|
68
134
|
// In 'auto', durable is used only when its engine is resolvable in the app
|
|
69
135
|
// (i.e. the durable provider is registered) — not merely installed.
|
|
70
136
|
durableAvailable: () => this.#isDurableAvailable(),
|
|
137
|
+
// The engine itself, not just "is it there": the dispatcher has to REGISTER the
|
|
138
|
+
// webhook workflow on the app's engine before it can start a run on it.
|
|
139
|
+
durableEngine: () => this.#resolveDurableEngine(),
|
|
71
140
|
});
|
|
141
|
+
this.#webhook = dispatcher;
|
|
142
|
+
// Published so tests can reach it: `flushWebhooks()` from
|
|
143
|
+
// `@adonis-agora/payments/testing` awaits the in-flight processing that durable mode
|
|
144
|
+
// deliberately does not await. Bound in the container too, for an app that prefers
|
|
145
|
+
// `@inject()` over the service accessor.
|
|
146
|
+
setWebhookDispatcher(dispatcher);
|
|
147
|
+
this.app.container.singleton(WebhookDispatcher, () => dispatcher);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* The billing store from `config.billing.store`, defaulting to Lucid over the
|
|
151
|
+
* published tables.
|
|
152
|
+
*
|
|
153
|
+
* The resolved store is published on `services/main` (which reaches any store) and,
|
|
154
|
+
* when it is the Lucid one, bound in the container so a service can `@inject()` it
|
|
155
|
+
* like any other dependency.
|
|
156
|
+
*/
|
|
157
|
+
async #resolveBillingStore(config) {
|
|
158
|
+
const store = await resolveBillingStore(config);
|
|
159
|
+
setBillingStore(store);
|
|
160
|
+
if (isInjectableAsLucid(store)) {
|
|
161
|
+
this.app.container.singleton(LucidBillingStore, () => store);
|
|
162
|
+
}
|
|
163
|
+
return store;
|
|
72
164
|
}
|
|
73
165
|
/**
|
|
74
166
|
* Merge webhook handlers from the two sources:
|
|
@@ -76,15 +168,33 @@ export default class PaymentsProvider {
|
|
|
76
168
|
* 2. the conventions folder `app/payment_handlers/` (build-time barrel, runtime scan
|
|
77
169
|
* as fallback) — the durable-style discovery.
|
|
78
170
|
*/
|
|
79
|
-
async #resolveAllHandlers(configHandlers) {
|
|
171
|
+
async #resolveAllHandlers(configHandlers, passthroughEvents) {
|
|
172
|
+
const discovered = await this.#discoverFolderHandlers();
|
|
173
|
+
// Validated BEFORE anything is wired, so the app never boots half-registered. A handler
|
|
174
|
+
// for an event type that will never be delivered is a silent no-op — the ledger records
|
|
175
|
+
// the delivery as processed and the grant simply never happens — and two handlers for
|
|
176
|
+
// one type mean the second overwrote the first in the map below.
|
|
177
|
+
const registrations = [
|
|
178
|
+
...Object.keys(configHandlers ?? {}).map((type) => ({
|
|
179
|
+
type,
|
|
180
|
+
source: 'billing.handlers',
|
|
181
|
+
})),
|
|
182
|
+
...discovered.map((entry) => ({
|
|
183
|
+
type: entry.type,
|
|
184
|
+
source: entry.source ?? 'app/payment_handlers',
|
|
185
|
+
})),
|
|
186
|
+
];
|
|
187
|
+
assertWebhookHandlerTypes(registrations, {
|
|
188
|
+
...(passthroughEvents !== undefined ? { passthroughEvents } : {}),
|
|
189
|
+
});
|
|
80
190
|
const handlers = {};
|
|
81
191
|
if (configHandlers) {
|
|
82
192
|
for (const [type, entry] of Object.entries(configHandlers)) {
|
|
83
193
|
handlers[type] = await resolveWebhookHandler(entry, this.app.container);
|
|
84
194
|
}
|
|
85
195
|
}
|
|
86
|
-
for (const
|
|
87
|
-
handlers[
|
|
196
|
+
for (const entry of discovered) {
|
|
197
|
+
handlers[entry.type] = await resolveWebhookHandler(entry.entry, this.app.container);
|
|
88
198
|
}
|
|
89
199
|
return Object.keys(handlers).length > 0 ? handlers : undefined;
|
|
90
200
|
}
|
|
@@ -113,43 +223,147 @@ export default class PaymentsProvider {
|
|
|
113
223
|
/** Whether the app has a resolvable durable workflow engine. */
|
|
114
224
|
async #isDurableAvailable() {
|
|
115
225
|
try {
|
|
116
|
-
|
|
117
|
-
await this.app.container.make(WorkflowEngine);
|
|
226
|
+
await this.#resolveDurableEngine();
|
|
118
227
|
return true;
|
|
119
228
|
}
|
|
120
229
|
catch {
|
|
121
230
|
return false;
|
|
122
231
|
}
|
|
123
232
|
}
|
|
124
|
-
/**
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
233
|
+
/**
|
|
234
|
+
* The app's durable engine, from the container.
|
|
235
|
+
*
|
|
236
|
+
* Deliberately the app's own instance and never a fresh one: the engine is bound to the
|
|
237
|
+
* store and transport the app configured, and a second engine would persist runs that
|
|
238
|
+
* nothing picks up.
|
|
239
|
+
*/
|
|
240
|
+
async #resolveDurableEngine() {
|
|
241
|
+
const { WorkflowEngine } = await import('@adonis-agora/durable');
|
|
242
|
+
return (await this.app.container.make(WorkflowEngine));
|
|
132
243
|
}
|
|
244
|
+
/** Map `billing.durable` (`'auto' | boolean`) onto the dispatcher's mode. */
|
|
133
245
|
#registerWebhookRoute(router) {
|
|
134
246
|
router
|
|
135
247
|
.post('/payments/webhook/:provider', async (ctx) => {
|
|
136
248
|
const manager = await this.app.container.make(PaymentsManager);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (this.#webhook) {
|
|
143
|
-
await this.#webhook.dispatch(event);
|
|
144
|
-
}
|
|
145
|
-
return ctx.response.status(200).json({ received: true });
|
|
146
|
-
}
|
|
147
|
-
catch (error) {
|
|
148
|
-
const message = error instanceof Error ? error.message : 'invalid webhook';
|
|
149
|
-
return ctx.response.status(400).json({ error: message });
|
|
150
|
-
}
|
|
249
|
+
return handleWebhookDelivery(ctx, {
|
|
250
|
+
manager,
|
|
251
|
+
dispatcher: this.#webhook,
|
|
252
|
+
...(this.#store !== undefined ? { store: this.#store } : {}),
|
|
253
|
+
});
|
|
151
254
|
})
|
|
152
255
|
.as('payments.webhook');
|
|
153
256
|
}
|
|
154
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* One webhook delivery, from raw body to HTTP status — the body of the mounted
|
|
260
|
+
* `POST /payments/webhook/:provider` route.
|
|
261
|
+
*
|
|
262
|
+
* Exported so the delivery can be tested for what it actually promises the gateway (the
|
|
263
|
+
* status code IS the retry instruction) without standing up an application, a router and a
|
|
264
|
+
* container around it.
|
|
265
|
+
*/
|
|
266
|
+
export async function handleWebhookDelivery(ctx, deps) {
|
|
267
|
+
const provider = String(ctx.params.provider);
|
|
268
|
+
// ONE trace per delivery ATTEMPT, shared by every event the delivery carries — not one
|
|
269
|
+
// trace per event.
|
|
270
|
+
//
|
|
271
|
+
// The trace answers "what happened to this HTTP request", and the facts it correlates are
|
|
272
|
+
// request-scoped: the body that arrived, the signature check over it, the response the
|
|
273
|
+
// gateway got. Minting a trace per event would leave the single `webhook.verification`
|
|
274
|
+
// report attached to one arbitrary event and orphan the rest, and it would erase the one
|
|
275
|
+
// thing a batch makes worth knowing — that these four events arrived together, so a
|
|
276
|
+
// failure that hit all four is one delivery's problem and not four payments' problem.
|
|
277
|
+
//
|
|
278
|
+
// Per-event identity is not lost by sharing: `webhook.received`/`processed`/`failed` each
|
|
279
|
+
// carry the event's own `id` and `type`, and the processor publishes them once per event.
|
|
280
|
+
// So a delivery of four events is four lifecycle triples under one traceId, which is
|
|
281
|
+
// exactly what happened — never one event's worth of noise.
|
|
282
|
+
//
|
|
283
|
+
// (A redelivery gets its own id, which is what you want when the question is "why did the
|
|
284
|
+
// retry behave differently".)
|
|
285
|
+
const frame = { traceId: newPaymentsTraceId(), provider };
|
|
286
|
+
return runWithPaymentsTrace(frame, async () => {
|
|
287
|
+
const startedAt = Date.now();
|
|
288
|
+
// The catch below also sees failures from parsing, which happen before the delivery was
|
|
289
|
+
// authenticated — one delivery must not report two outcomes.
|
|
290
|
+
let verificationPublished = false;
|
|
291
|
+
try {
|
|
292
|
+
const driver = deps.manager.driver(provider);
|
|
293
|
+
const rawBody = ctx.request.raw() ?? '';
|
|
294
|
+
// Awaited: `parseWebhook` may be async. A gateway whose callback carries only an
|
|
295
|
+
// id (Mollie) has to fetch the payment to know what happened, and that fetch is
|
|
296
|
+
// also the only thing authenticating the call.
|
|
297
|
+
//
|
|
298
|
+
// May be ONE event or SEVERAL: Adyen's `notificationItems` and Efí's `pix` are both
|
|
299
|
+
// lists. `dispatchAll` takes either, so no driver is forced to wrap its single event
|
|
300
|
+
// in an array.
|
|
301
|
+
const parsed = await driver.parseWebhook(rawBody, ctx.request.headers());
|
|
302
|
+
// Reported by the shared `webhook_security` helpers during parseWebhook. No
|
|
303
|
+
// report means nothing verified through them: the driver used its own SDK, or
|
|
304
|
+
// it has no webhook credential configured and skipped the check entirely.
|
|
305
|
+
publishWebhookVerification({
|
|
306
|
+
provider,
|
|
307
|
+
...webhookVerificationOutcome(frame),
|
|
308
|
+
durationMs: Date.now() - startedAt,
|
|
309
|
+
});
|
|
310
|
+
verificationPublished = true;
|
|
311
|
+
if (deps.dispatcher) {
|
|
312
|
+
const result = await deps.dispatcher.dispatchAll(parsed);
|
|
313
|
+
if (result.failures.length > 0) {
|
|
314
|
+
// A 2xx tells the gateway "done, never send this again". Answering it over an
|
|
315
|
+
// event that failed is how a payment is lost for good — Adyen queues a failed
|
|
316
|
+
// delivery for up to 30 days of retries and Efí makes 9 attempts, and both of
|
|
317
|
+
// those only ever start if the response is not a 2xx. So a delivery with ANY
|
|
318
|
+
// failed event is a failed delivery.
|
|
319
|
+
//
|
|
320
|
+
// The events that DID succeed are not re-run by that redelivery: their ledger
|
|
321
|
+
// rows are `processed`, so the claim answers `null` and they skip. The gateway
|
|
322
|
+
// resending all four costs one redelivery and re-runs exactly the one that failed.
|
|
323
|
+
const failed = result.failures.map((failure) => failure.event.id);
|
|
324
|
+
return ctx.response.status(500).json({
|
|
325
|
+
received: true,
|
|
326
|
+
// What the delivery actually did, rather than a bare error — an operator reading
|
|
327
|
+
// the gateway's failed-delivery log needs to know the other three landed.
|
|
328
|
+
processed: result.dispatched,
|
|
329
|
+
failed,
|
|
330
|
+
error: result.failures[0]?.error.message ?? 'webhook processing failed',
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return ctx.response.status(200).json({ received: true });
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
const message = error instanceof Error ? error.message : 'invalid webhook';
|
|
338
|
+
// A throw out of `parseWebhook` is a REJECTED delivery — a bad signature, an
|
|
339
|
+
// unparsable body, an unknown provider — and the reason is the single most-wanted
|
|
340
|
+
// fact when a gateway reports failing callbacks. It stays a 400: unlike a processing
|
|
341
|
+
// failure, redelivering it would fail identically, and a forged batch must not be
|
|
342
|
+
// answered with an invitation to send it again.
|
|
343
|
+
if (!verificationPublished) {
|
|
344
|
+
publishWebhookVerification({
|
|
345
|
+
provider,
|
|
346
|
+
...webhookVerificationOutcome(frame, message),
|
|
347
|
+
durationMs: Date.now() - startedAt,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
// A rejected delivery is the ONLY webhook outcome that leaves no ledger row — it is
|
|
351
|
+
// refused before an event exists to record. That is why a rotated webhook token looks
|
|
352
|
+
// exactly like a quiet gateway from the inside: zero events, zero failures, every
|
|
353
|
+
// health check green. The audit row is what the `rejected_deliveries` check counts.
|
|
354
|
+
//
|
|
355
|
+
// Best-effort on purpose: the delivery is already being refused, and failing to file
|
|
356
|
+
// the note must not change the status the gateway sees. The store itself returns
|
|
357
|
+
// `null` when the install predates the table.
|
|
358
|
+
await deps.store
|
|
359
|
+
?.recordAuditEvent({
|
|
360
|
+
action: AUDIT_ACTIONS.webhookRejected,
|
|
361
|
+
provider,
|
|
362
|
+
message,
|
|
363
|
+
})
|
|
364
|
+
.catch(() => { });
|
|
365
|
+
return ctx.response.status(400).json({ error: message });
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
}
|
|
155
369
|
//# sourceMappingURL=payments_provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payments_provider.js","sourceRoot":"","sources":["../../providers/payments_provider.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"payments_provider.js","sourceRoot":"","sources":["../../providers/payments_provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { type BillingStore, type DisputeListItem, type WebhookEventBreakdownLine } from './billing_store.js';
|
|
2
|
+
export interface BillingHealthOptions {
|
|
3
|
+
/** Milliseconds an event may sit in `received` before it counts as stuck. Default 15 min. */
|
|
4
|
+
stuckAfter?: number;
|
|
5
|
+
/** Milliseconds a `pending` payment may age before it counts as unconfirmed. Default 2 h. */
|
|
6
|
+
unconfirmedAfter?: number;
|
|
7
|
+
/** The window `failed` events are counted over. Default 24 h. */
|
|
8
|
+
failedWithin?: number;
|
|
9
|
+
/**
|
|
10
|
+
* How far ahead to look for a dispute whose evidence window is about to close. Default 72 h.
|
|
11
|
+
*
|
|
12
|
+
* In milliseconds like every other threshold here, though the store's read takes hours —
|
|
13
|
+
* the conversion happens once, at the call.
|
|
14
|
+
*/
|
|
15
|
+
disputeDueWithin?: number;
|
|
16
|
+
/** The window rejected deliveries are counted over. Default 24 h. */
|
|
17
|
+
rejectedWithin?: number;
|
|
18
|
+
/** Overridable clock — the tests pass a fixed instant. Defaults to now. */
|
|
19
|
+
now?: Date;
|
|
20
|
+
}
|
|
21
|
+
export interface BillingHealthCheck {
|
|
22
|
+
key: 'stuck_webhooks' | 'failed_webhooks' | 'unconfirmed_payments' | 'disputes_due' | 'open_disputes' | 'rejected_deliveries';
|
|
23
|
+
label: string;
|
|
24
|
+
count: number;
|
|
25
|
+
/** `true` when `count` is zero — every check here is a "should be nothing" check. */
|
|
26
|
+
healthy: boolean;
|
|
27
|
+
/** What a non-zero count means, and what to do about it. */
|
|
28
|
+
hint: string;
|
|
29
|
+
}
|
|
30
|
+
export interface BillingHealth {
|
|
31
|
+
/** `false` when any check is non-zero — the exit code `payments:health` returns. */
|
|
32
|
+
healthy: boolean;
|
|
33
|
+
checkedAt: Date;
|
|
34
|
+
checks: BillingHealthCheck[];
|
|
35
|
+
/** Which provider/event pairs make up `failed_webhooks`, worst first. */
|
|
36
|
+
failures: WebhookEventBreakdownLine[];
|
|
37
|
+
/**
|
|
38
|
+
* The disputes behind `disputes_due`, soonest deadline first — WHICH windows are closing,
|
|
39
|
+
* not just how many. Capped at {@link DISPUTE_DEADLINE_SAMPLE}; `disputes_due.count` is the
|
|
40
|
+
* full number, so a report can name twenty and still say there are fifty.
|
|
41
|
+
*/
|
|
42
|
+
deadlines: DisputeListItem[];
|
|
43
|
+
/**
|
|
44
|
+
* The disputes behind `open_disputes`, OLDEST first — the ones nobody has answered, whether or
|
|
45
|
+
* not a deadline was ever published for them. Capped at {@link OPEN_DISPUTE_SAMPLE}.
|
|
46
|
+
*
|
|
47
|
+
* Overlaps `deadlines` on purpose: a dispute that has both a deadline and no answer belongs in
|
|
48
|
+
* both lists, and suppressing it from one of them to keep them disjoint would make the
|
|
49
|
+
* deadline-free check quietly incomplete on exactly the install it exists for.
|
|
50
|
+
*/
|
|
51
|
+
openDisputes: DisputeListItem[];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The six operational questions about a billing install that nothing else answers, asked
|
|
55
|
+
* through the store instead of by hand against the tables.
|
|
56
|
+
*
|
|
57
|
+
* Each is a silent failure — the kind where the endpoint keeps returning `200` and revenue
|
|
58
|
+
* quietly stops landing:
|
|
59
|
+
*
|
|
60
|
+
* - **Stuck events**: claimed in the ledger, never finished. Almost always the worker the
|
|
61
|
+
* dispatcher depends on (`durable:work` / `queue:listen`) is not running.
|
|
62
|
+
* - **Failed events**: the handler threw and retries were exhausted, so the effect that
|
|
63
|
+
* event described — the grant, the activation — never happened.
|
|
64
|
+
* - **Unconfirmed charges**: created and never confirmed. This is what a webhook endpoint
|
|
65
|
+
* that stopped being reachable looks like from the inside, and nothing errors.
|
|
66
|
+
* - **Closing dispute windows**: an open chargeback whose evidence deadline is inside the
|
|
67
|
+
* next three days. This is the only check here that alerts on a CLOCK rather than on a
|
|
68
|
+
* failure — nothing is broken, and the money is lost anyway if the window closes
|
|
69
|
+
* unanswered, by default rather than on the merits.
|
|
70
|
+
* - **Open disputes**: the same money, without the clock. `disputes_due` can only ever see a
|
|
71
|
+
* dispute whose gateway PUBLISHED a deadline, and most do not — Asaas' comes from
|
|
72
|
+
* `chargeback.deadlineToSendDisputeDocuments`, which no published webhook example contains.
|
|
73
|
+
* On such an install the deadline check is zero forever while a chargeback sits open with the
|
|
74
|
+
* money already pulled back, and the report says healthy. This one asks the question the
|
|
75
|
+
* deadline cannot.
|
|
76
|
+
* - **Rejected deliveries**: gateway calls the endpoint REFUSED — a bad signature, an unparsable
|
|
77
|
+
* body, a provider nobody configured. They never become ledger rows, so a rotated webhook
|
|
78
|
+
* token used to look exactly like a quiet week: zero events, zero failures, every check green.
|
|
79
|
+
* `unconfirmed_payments` eventually notices, but only for charges the app itself created;
|
|
80
|
+
* refunds, chargebacks and dispute closures produce no pending payment and simply vanish.
|
|
81
|
+
*
|
|
82
|
+
* Pure store reads, no gateway calls — safe to run on a schedule and to alert on.
|
|
83
|
+
*/
|
|
84
|
+
export declare function billingHealth(store: BillingStore, options?: BillingHealthOptions): Promise<BillingHealth>;
|
|
85
|
+
//# sourceMappingURL=billing_health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing_health.d.ts","sourceRoot":"","sources":["../../../src/billing/billing_health.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC/B,MAAM,oBAAoB,CAAC;AAkC5B,MAAM,WAAW,oBAAoB;IACnC,6FAA6F;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EACC,gBAAgB,GAChB,iBAAiB,GACjB,sBAAsB,GACtB,cAAc,GACd,eAAe,GACf,qBAAqB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,qFAAqF;IACrF,OAAO,EAAE,OAAO,CAAC;IACjB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,oFAAoF;IACpF,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,yEAAyE;IACzE,QAAQ,EAAE,yBAAyB,EAAE,CAAC;IACtC;;;;OAIG;IACH,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B;;;;;;;OAOG;IACH,YAAY,EAAE,eAAe,EAAE,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,aAAa,CAAC,CA6FxB"}
|