@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,4 +1,301 @@
|
|
|
1
|
-
import type { BillingPayment, BillingSubscription, BillingUsageEvent, BillingWebhookEvent } from './mixins/index.js';
|
|
1
|
+
import type { BillingCustomer, BillingDispute, BillingPayment, BillingSubscription, BillingUsageEvent, BillingWebhookEvent } from './mixins/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* One `billing_payments` row, normalized for reading.
|
|
4
|
+
*
|
|
5
|
+
* The write side of this SPI is generic over the row type (a Lucid model instance in one
|
|
6
|
+
* implementation, a plain object in the other). A reader — the dashboard, a report — cannot
|
|
7
|
+
* work against `PaymentRow` for that reason, so the LIST side returns this flat, plain
|
|
8
|
+
* shape instead: every implementation normalizes into it, and no caller ever has to know
|
|
9
|
+
* whether a Lucid model or a plain object produced it.
|
|
10
|
+
*
|
|
11
|
+
* `amount` is integer cents, like everywhere else in this package. Format at the edge.
|
|
12
|
+
*/
|
|
13
|
+
export interface PaymentListItem {
|
|
14
|
+
id: string;
|
|
15
|
+
gatewayId: string;
|
|
16
|
+
provider: string;
|
|
17
|
+
status: string;
|
|
18
|
+
/** Integer cents — NEVER divide here. */
|
|
19
|
+
amount: number;
|
|
20
|
+
currency: string;
|
|
21
|
+
customerId: string | null;
|
|
22
|
+
subscriptionId: string | null;
|
|
23
|
+
/**
|
|
24
|
+
* The app's own id for this charge, as the gateway echoed it back.
|
|
25
|
+
*
|
|
26
|
+
* `null` on a payment whose gateway sent none, and on every row an install wrote before
|
|
27
|
+
* it ran an earlier schema.
|
|
28
|
+
*/
|
|
29
|
+
externalReference: string | null;
|
|
30
|
+
/**
|
|
31
|
+
* How much of {@link PaymentListItem.amount} has come back, in the SAME integer minor
|
|
32
|
+
* units — `0` when nothing has, `null` on a row written before an earlier schema ran.
|
|
33
|
+
*
|
|
34
|
+
* Net revenue for a row is `amount - refundedAmount`. It is a separate figure precisely so
|
|
35
|
+
* a PARTIAL refund does not have to be spelled by mangling `amount` or `status`: a R$10
|
|
36
|
+
* refund on a R$100 charge leaves `amount: 10000, refundedAmount: 1000, status: 'paid'`.
|
|
37
|
+
* NEVER divide — format at the edge.
|
|
38
|
+
*/
|
|
39
|
+
refundedAmount: number | null;
|
|
40
|
+
paidAt: Date | null;
|
|
41
|
+
createdAt: Date | null;
|
|
42
|
+
}
|
|
43
|
+
/** One `billing_customers` row, normalized for reading. See {@link PaymentListItem}. */
|
|
44
|
+
export interface CustomerListItem {
|
|
45
|
+
id: string;
|
|
46
|
+
gatewayId: string;
|
|
47
|
+
provider: string;
|
|
48
|
+
ownerType: string | null;
|
|
49
|
+
ownerId: string | null;
|
|
50
|
+
email: string | null;
|
|
51
|
+
name: string | null;
|
|
52
|
+
taxId: string | null;
|
|
53
|
+
createdAt: Date | null;
|
|
54
|
+
}
|
|
55
|
+
/** One `billing_subscriptions` row, normalized for reading. See {@link PaymentListItem}. */
|
|
56
|
+
export interface SubscriptionListItem {
|
|
57
|
+
id: string;
|
|
58
|
+
gatewayId: string;
|
|
59
|
+
provider: string;
|
|
60
|
+
status: string;
|
|
61
|
+
planId: string;
|
|
62
|
+
customerId: string | null;
|
|
63
|
+
trialEndsAt: Date | null;
|
|
64
|
+
endsAt: Date | null;
|
|
65
|
+
createdAt: Date | null;
|
|
66
|
+
}
|
|
67
|
+
/** One `billing_webhook_events` row, normalized for reading. See {@link PaymentListItem}. */
|
|
68
|
+
export interface WebhookEventListItem {
|
|
69
|
+
id: string;
|
|
70
|
+
gatewayEventId: string;
|
|
71
|
+
provider: string;
|
|
72
|
+
type: string;
|
|
73
|
+
status: string;
|
|
74
|
+
/** The handler's failure message when `status === 'failed'`; `null` otherwise. */
|
|
75
|
+
error: string | null;
|
|
76
|
+
createdAt: Date | null;
|
|
77
|
+
updatedAt: Date | null;
|
|
78
|
+
}
|
|
79
|
+
/** One `billing_disputes` row, normalized for reading. See {@link PaymentListItem}. */
|
|
80
|
+
export interface DisputeListItem {
|
|
81
|
+
id: string;
|
|
82
|
+
/** The DISPUTE's own gateway id — or the synthesized one, for a gateway that sends none. */
|
|
83
|
+
gatewayId: string;
|
|
84
|
+
/** The disputed payment's gateway id — the join back to `billing_payments`. */
|
|
85
|
+
paymentGatewayId: string;
|
|
86
|
+
provider: string;
|
|
87
|
+
/** A {@link import('../types.js').DisputeStatus} value. */
|
|
88
|
+
status: string;
|
|
89
|
+
reason: string | null;
|
|
90
|
+
/** Integer minor units, or `null` — an early fraud warning names no money. NEVER divide here. */
|
|
91
|
+
amount: number | null;
|
|
92
|
+
currency: string | null;
|
|
93
|
+
/**
|
|
94
|
+
* The deadline to respond, or `null` when the gateway sent none.
|
|
95
|
+
*
|
|
96
|
+
* `null` means "this gateway told us nothing", never "no hurry": a reader must not treat
|
|
97
|
+
* it as a distant deadline, and {@link BillingStore.listDisputesDueWithin} does not
|
|
98
|
+
* return rows that carry one.
|
|
99
|
+
*/
|
|
100
|
+
evidenceDueBy: Date | null;
|
|
101
|
+
outcome: string | null;
|
|
102
|
+
openedAt: Date | null;
|
|
103
|
+
closedAt: Date | null;
|
|
104
|
+
createdAt: Date | null;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The dispute statuses that still need an answer — everything that is not a resolution.
|
|
108
|
+
*
|
|
109
|
+
* Shared, and exported, because the deadline read is only meaningful against the same set
|
|
110
|
+
* in both store implementations: one of them counting `expired` rows as open would make an
|
|
111
|
+
* alert that never goes quiet, and one of them omitting `warning` would drop the alert that
|
|
112
|
+
* arrives while a refund still prevents the chargeback.
|
|
113
|
+
*/
|
|
114
|
+
export declare const OPEN_DISPUTE_STATUSES: readonly ['warning', 'open', 'under_review'];
|
|
115
|
+
/**
|
|
116
|
+
* "Which windows close soon?" — the one question this table was added to answer.
|
|
117
|
+
*
|
|
118
|
+
* Deliberately not expressible through {@link BillingListQuery}: it filters on a deadline,
|
|
119
|
+
* not on a creation time, and it is the read an alert is built on, so a store that quietly
|
|
120
|
+
* ignored the window would report a healthy zero.
|
|
121
|
+
*/
|
|
122
|
+
export interface DisputeDeadlineQuery {
|
|
123
|
+
/** How far ahead to look, in HOURS. `72` = "everything due in the next three days". */
|
|
124
|
+
withinHours: number;
|
|
125
|
+
/** Overridable clock — the tests pass a fixed instant. Defaults to now. */
|
|
126
|
+
now?: Date;
|
|
127
|
+
/** Exact provider match. Omit for every provider. */
|
|
128
|
+
provider?: string;
|
|
129
|
+
limit?: number;
|
|
130
|
+
offset?: number;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* One line of {@link BillingStore.webhookEventBreakdown} — how many ledger rows matched,
|
|
134
|
+
* grouped by the two fields that identify WHAT is failing: which gateway, and which event.
|
|
135
|
+
*/
|
|
136
|
+
export interface WebhookEventBreakdownLine {
|
|
137
|
+
provider: string;
|
|
138
|
+
type: string;
|
|
139
|
+
count: number;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Filter for the count/breakdown reads — the operational questions ("how many events are
|
|
143
|
+
* stuck?", "what failed in the last day?") that a page of rows cannot answer.
|
|
144
|
+
*
|
|
145
|
+
* Kept separate from {@link BillingListQuery} on purpose: a store that silently ignored a
|
|
146
|
+
* window it did not implement would report a healthy zero, which is the one wrong answer an
|
|
147
|
+
* alert must never get.
|
|
148
|
+
*/
|
|
149
|
+
export interface BillingCountQuery {
|
|
150
|
+
/** Exact status match. Omit for every status. */
|
|
151
|
+
status?: string;
|
|
152
|
+
/** Only rows created strictly BEFORE this instant — "older than", for staleness checks. */
|
|
153
|
+
createdBefore?: Date;
|
|
154
|
+
/** Only rows created at or AFTER this instant — "within the last N", for recency checks. */
|
|
155
|
+
createdAfter?: Date;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Filter + page for {@link BillingStore.listPayments}.
|
|
159
|
+
*
|
|
160
|
+
* The three fields beyond `status`/`provider` all answer the SAME question, which the console
|
|
161
|
+
* could not ask before: "did THIS charge land?" An operator holds one of three ids — the app's
|
|
162
|
+
* own reference for the order, the gateway's id for the payment, or the gateway's id for the
|
|
163
|
+
* customer — and a list that filters only on status makes them page a growing table looking for
|
|
164
|
+
* a string.
|
|
165
|
+
*
|
|
166
|
+
* All three are EXACT matches, not prefixes or substrings. They are join keys, not search terms:
|
|
167
|
+
* a substring match on `externalReference` would let `order-4` return `order-42`, and the whole
|
|
168
|
+
* point of the lookup is that the row it returns is the row that was asked for.
|
|
169
|
+
*/
|
|
170
|
+
export interface PaymentListQuery extends BillingListQuery {
|
|
171
|
+
/**
|
|
172
|
+
* The app's own id for the charge (`ChargeInput.externalReference`), exactly as
|
|
173
|
+
* {@link BillingStore.findPaymentByExternalReference} matches it.
|
|
174
|
+
*
|
|
175
|
+
* Unlike that method this returns every row carrying the reference rather than the newest —
|
|
176
|
+
* an app may reuse a reference across retries, and when it did, "which one landed?" is
|
|
177
|
+
* precisely the question being asked.
|
|
178
|
+
*/
|
|
179
|
+
externalReference?: string;
|
|
180
|
+
/** The gateway's own payment id (`pi_...`, `pay_...`) — what the gateway's dashboard shows. */
|
|
181
|
+
gatewayId?: string;
|
|
182
|
+
/** The gateway's customer id — every payment recorded for one customer. */
|
|
183
|
+
customerId?: string;
|
|
184
|
+
}
|
|
185
|
+
/** Filter + page for {@link BillingStore.listWebhookEvents}. */
|
|
186
|
+
export interface WebhookEventListQuery extends BillingListQuery {
|
|
187
|
+
/**
|
|
188
|
+
* Exact event-type match (`payment.succeeded`, `payment.refunded`, ...). Omit for every type.
|
|
189
|
+
*
|
|
190
|
+
* `status` + `provider` answers "what is failing on Asaas"; without this there is no way to
|
|
191
|
+
* ask "did any refund event arrive at all", which is the question a ledger is read for once
|
|
192
|
+
* a specific charge is in doubt. The type vocabulary is discoverable —
|
|
193
|
+
* {@link BillingStore.webhookEventBreakdown} with no filter enumerates exactly the types this
|
|
194
|
+
* install has actually received.
|
|
195
|
+
*/
|
|
196
|
+
type?: string;
|
|
197
|
+
}
|
|
198
|
+
/** Filter + page for {@link BillingStore.listCustomers}. */
|
|
199
|
+
export interface CustomerListQuery extends BillingListQuery {
|
|
200
|
+
/** The app-side owner type (`'users'`) written by `ensureCustomer`. Exact match. */
|
|
201
|
+
ownerType?: string;
|
|
202
|
+
/** The app-side owner id. Exact match; pair it with `ownerType`. */
|
|
203
|
+
ownerId?: string;
|
|
204
|
+
/** The gateway's customer id — the join key a payment row carries. Exact match. */
|
|
205
|
+
gatewayId?: string;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* "Which disputes are still unanswered?" — regardless of whether anyone told us a deadline.
|
|
209
|
+
*
|
|
210
|
+
* The deliberately deadline-FREE counterpart to {@link DisputeDeadlineQuery}, and it exists
|
|
211
|
+
* because the deadline one is structurally blind on most installs: `evidence_due_by` can only be
|
|
212
|
+
* filled by a gateway that publishes the field, and several of the eighteen drivers here never
|
|
213
|
+
* receive one. On those, `countDisputesDueWithin` returns zero while a chargeback sits open with
|
|
214
|
+
* the money already pulled — a healthy report about an install that is losing money by default.
|
|
215
|
+
*/
|
|
216
|
+
export interface OpenDisputeQuery {
|
|
217
|
+
/** Exact provider match. Omit for every provider. */
|
|
218
|
+
provider?: string;
|
|
219
|
+
limit?: number;
|
|
220
|
+
offset?: number;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* One `billing_audit_events` row, normalized for reading. See {@link PaymentListItem}.
|
|
224
|
+
*
|
|
225
|
+
* The table records the things that HAPPENED to this install that no other table keeps: a human
|
|
226
|
+
* refunding from the console, a human resolving a dispute the gateway will never close, and a
|
|
227
|
+
* delivery the endpoint REJECTED before it ever became a ledger row. Every one of those was
|
|
228
|
+
* previously a diagnostic and nothing else — i.e. a line in a log that has usually rotated away
|
|
229
|
+
* by the time somebody asks.
|
|
230
|
+
*/
|
|
231
|
+
export interface AuditEventListItem {
|
|
232
|
+
id: string;
|
|
233
|
+
/** One of {@link AUDIT_ACTIONS}' values. A free string in the column: an app may add its own. */
|
|
234
|
+
action: string;
|
|
235
|
+
/**
|
|
236
|
+
* WHO did it — the dashboard session's user, as `enforce()` knew them.
|
|
237
|
+
*
|
|
238
|
+
* `null` when nothing authorised it in a human sense: a rejected delivery has no actor, and a
|
|
239
|
+
* console with no `dashboardAuth` configured cannot name one. `null` therefore means
|
|
240
|
+
* "unattributed", never "the system".
|
|
241
|
+
*/
|
|
242
|
+
actor: string | null;
|
|
243
|
+
provider: string | null;
|
|
244
|
+
/** `'payment'` | `'dispute'` | `'webhook'`, or whatever an app records. */
|
|
245
|
+
subjectType: string | null;
|
|
246
|
+
/** The subject's gateway id — the join back to the table it names. */
|
|
247
|
+
subjectId: string | null;
|
|
248
|
+
/** Integer minor units, or `null`. NEVER divide here. */
|
|
249
|
+
amount: number | null;
|
|
250
|
+
currency: string | null;
|
|
251
|
+
/** A human sentence: the refusal reason, the operator's note. */
|
|
252
|
+
message: string | null;
|
|
253
|
+
metadata: Record<string, unknown> | null;
|
|
254
|
+
createdAt: Date | null;
|
|
255
|
+
}
|
|
256
|
+
/** The actions this package records itself. A free string in the column; these are the built-ins. */
|
|
257
|
+
export declare const AUDIT_ACTIONS: {
|
|
258
|
+
/** A refund issued FROM THE CONSOLE. The gateway's own webhook still moves the payment row. */
|
|
259
|
+
readonly refund: 'payment.refunded';
|
|
260
|
+
/** A dispute an operator closed by hand, because the gateway publishes no lost-dispute event. */
|
|
261
|
+
readonly disputeResolved: 'dispute.resolved';
|
|
262
|
+
/** A delivery the webhook endpoint refused: bad signature, unparsable body, unknown provider. */
|
|
263
|
+
readonly webhookRejected: 'webhook.rejected';
|
|
264
|
+
};
|
|
265
|
+
/** Filter + page for {@link BillingStore.listAuditEvents}. */
|
|
266
|
+
export interface AuditEventQuery {
|
|
267
|
+
/** Exact action match. */
|
|
268
|
+
action?: string;
|
|
269
|
+
/** Any of these actions — the timeline reads several at once. Composed with `action` as AND. */
|
|
270
|
+
actions?: readonly string[];
|
|
271
|
+
actor?: string;
|
|
272
|
+
provider?: string;
|
|
273
|
+
subjectType?: string;
|
|
274
|
+
subjectId?: string;
|
|
275
|
+
/** Only rows created at or AFTER this instant — the window a health check counts over. */
|
|
276
|
+
createdAfter?: Date;
|
|
277
|
+
/** Only rows created strictly BEFORE this instant. */
|
|
278
|
+
createdBefore?: Date;
|
|
279
|
+
limit?: number;
|
|
280
|
+
offset?: number;
|
|
281
|
+
}
|
|
282
|
+
/** The same filter without a page — the count a health check alerts on. */
|
|
283
|
+
export type AuditEventCountQuery = Omit<AuditEventQuery, 'limit' | 'offset'>;
|
|
284
|
+
/** Filter + page for the two list queries. `limit`/`offset` are applied after the filter. */
|
|
285
|
+
export interface BillingListQuery {
|
|
286
|
+
/** Exact status match. Omit for every status. */
|
|
287
|
+
status?: string;
|
|
288
|
+
/**
|
|
289
|
+
* Exact provider match. Omit for every provider.
|
|
290
|
+
*
|
|
291
|
+
* Not decorative: with eighteen gateways configurable at once, "what is failing on
|
|
292
|
+
* Asaas" is a different question from "what is failing", and without this the dashboard
|
|
293
|
+
* had to page the whole table and filter in memory.
|
|
294
|
+
*/
|
|
295
|
+
provider?: string;
|
|
296
|
+
limit?: number;
|
|
297
|
+
offset?: number;
|
|
298
|
+
}
|
|
2
299
|
/**
|
|
3
300
|
* The persistence SPI for the billing layer. The Lucid implementation writes through the
|
|
4
301
|
* configured models; an in-memory implementation exists in `src/testing` so the billing
|
|
@@ -7,7 +304,54 @@ import type { BillingPayment, BillingSubscription, BillingUsageEvent, BillingWeb
|
|
|
7
304
|
* Generic over the row types so the in-memory store can use plain objects while the
|
|
8
305
|
* Lucid store returns model instances.
|
|
9
306
|
*/
|
|
10
|
-
export interface BillingStore<SubscriptionRow = BillingSubscription, PaymentRow = BillingPayment, WebhookEventRow = BillingWebhookEvent, UsageEventRow = BillingUsageEvent> {
|
|
307
|
+
export interface BillingStore<SubscriptionRow = BillingSubscription, PaymentRow = BillingPayment, WebhookEventRow = BillingWebhookEvent, UsageEventRow = BillingUsageEvent, CustomerRow = BillingCustomer, DisputeRow = BillingDispute> {
|
|
308
|
+
/**
|
|
309
|
+
* Upsert the mapping between an app-side owner and its gateway customer, keyed by
|
|
310
|
+
* gateway id.
|
|
311
|
+
*
|
|
312
|
+
* Nothing records this for you — the library learns a gateway customer id inside
|
|
313
|
+
* `ensureCustomer`, which takes a store precisely so the mapping is written where it is
|
|
314
|
+
* created. Without it `billing_customers` stays empty and `payments:sync --all` has
|
|
315
|
+
* nothing to reconcile over.
|
|
316
|
+
*/
|
|
317
|
+
saveCustomer(customer: {
|
|
318
|
+
gatewayId: string;
|
|
319
|
+
provider: string;
|
|
320
|
+
ownerType?: string | null;
|
|
321
|
+
ownerId?: string | null;
|
|
322
|
+
email?: string | null;
|
|
323
|
+
name?: string | null;
|
|
324
|
+
taxId?: string | null;
|
|
325
|
+
metadata?: Record<string, unknown>;
|
|
326
|
+
}): Promise<CustomerRow>;
|
|
327
|
+
findCustomerByGatewayId(gatewayId: string): Promise<CustomerRow | null>;
|
|
328
|
+
/**
|
|
329
|
+
* The customer an app-side row holds at one gateway.
|
|
330
|
+
*
|
|
331
|
+
* `provider` is part of the question, not an optional filter: one owner may legitimately
|
|
332
|
+
* have a customer at every configured gateway, and answering without it would return an
|
|
333
|
+
* arbitrary one of them.
|
|
334
|
+
*/
|
|
335
|
+
findCustomerByOwner(ownerType: string, ownerId: string, provider: string): Promise<CustomerRow | null>;
|
|
336
|
+
/**
|
|
337
|
+
* Page through recorded customers, newest first, optionally narrowed to one provider.
|
|
338
|
+
*
|
|
339
|
+
* This is what `payments:sync --all` iterates. Returns the normalized
|
|
340
|
+
* {@link CustomerListItem} so a reader never depends on Lucid.
|
|
341
|
+
*/
|
|
342
|
+
listCustomers(query: CustomerListQuery): Promise<CustomerListItem[]>;
|
|
343
|
+
/**
|
|
344
|
+
* The customer mappings behind a set of gateway customer ids, in one read.
|
|
345
|
+
*
|
|
346
|
+
* The join the payments screen needs and could not do: a payment row carries the GATEWAY's
|
|
347
|
+
* customer id (`cus_…`), and the only thing that ties it back to an app user is
|
|
348
|
+
* `billing_customers.owner_type`/`owner_id`, written by `ensureCustomer`. Resolving that one
|
|
349
|
+
* row at a time would be a query per row of every page.
|
|
350
|
+
*
|
|
351
|
+
* Ids not present simply do not appear — a short result is the answer, not an error. An empty
|
|
352
|
+
* input returns an empty array without touching the database.
|
|
353
|
+
*/
|
|
354
|
+
listCustomersByGatewayIds(gatewayIds: readonly string[]): Promise<CustomerListItem[]>;
|
|
11
355
|
/** Upsert a subscription keyed by gateway id. Returns the stored row. */
|
|
12
356
|
saveSubscription(sub: {
|
|
13
357
|
gatewayId: string;
|
|
@@ -20,7 +364,32 @@ export interface BillingStore<SubscriptionRow = BillingSubscription, PaymentRow
|
|
|
20
364
|
payload?: Record<string, unknown>;
|
|
21
365
|
}): Promise<SubscriptionRow>;
|
|
22
366
|
findSubscriptionByGatewayId(gatewayId: string): Promise<SubscriptionRow | null>;
|
|
23
|
-
/**
|
|
367
|
+
/**
|
|
368
|
+
* Page through subscriptions, newest first, optionally filtered by status.
|
|
369
|
+
*
|
|
370
|
+
* `countActiveSubscriptions()` answers "how many", which is the wrong question on any day
|
|
371
|
+
* something is wrong: the operational ones are WHICH subscriptions are `past_due`, and
|
|
372
|
+
* which are `paused` — a count cannot name a customer to email.
|
|
373
|
+
*/
|
|
374
|
+
listSubscriptions(query: BillingListQuery): Promise<SubscriptionListItem[]>;
|
|
375
|
+
/** How many subscriptions match a status and/or a creation window. */
|
|
376
|
+
countSubscriptions(query: BillingCountQuery): Promise<number>;
|
|
377
|
+
/**
|
|
378
|
+
* Upsert a payment keyed by gateway id. Returns the stored row.
|
|
379
|
+
*
|
|
380
|
+
* `externalReference` left `undefined` does NOT erase a stored one: the first webhook of a
|
|
381
|
+
* charge usually carries the app's reference and a later one (a refund, a dispute) often
|
|
382
|
+
* does not, and blanking it there would throw away the only key
|
|
383
|
+
* {@link BillingStore.findPaymentByExternalReference} can route on. Pass `null` explicitly
|
|
384
|
+
* to clear it. Same rule, same reason, as `saveCustomer`'s owner mapping.
|
|
385
|
+
*
|
|
386
|
+
* **`paidAt` and `refundedAmount` follow exactly the same rule**, and the reason is money.
|
|
387
|
+
* A refund, a chargeback and a dispute close all write this row and none of them carries a
|
|
388
|
+
* settlement date; a store that wrote `undefined` through as `null` destroyed `paid_at` on
|
|
389
|
+
* every one of them, and {@link BillingStore.revenue} filters on `paid_at`. A dispute closed
|
|
390
|
+
* as WON therefore restored `status = 'paid'` with no date and the recovered money dropped
|
|
391
|
+
* out of every windowed revenue figure. Absent means "not stated"; `null` means "clear it".
|
|
392
|
+
*/
|
|
24
393
|
savePayment(payment: {
|
|
25
394
|
gatewayId: string;
|
|
26
395
|
provider: string;
|
|
@@ -29,19 +398,246 @@ export interface BillingStore<SubscriptionRow = BillingSubscription, PaymentRow
|
|
|
29
398
|
currency: string;
|
|
30
399
|
customerId?: string | null;
|
|
31
400
|
subscriptionId?: string | null;
|
|
401
|
+
/** The app's own id for this charge (`ChargeInput.externalReference`), echoed by the gateway. */
|
|
402
|
+
externalReference?: string | null;
|
|
32
403
|
paidAt?: Date | null;
|
|
404
|
+
/**
|
|
405
|
+
* Integer minor units refunded so far — the whole point of the field is that a PARTIAL
|
|
406
|
+
* refund can be recorded without lying about `amount` or `status`. Absent leaves the
|
|
407
|
+
* stored figure alone; `null` clears it.
|
|
408
|
+
*/
|
|
409
|
+
refundedAmount?: number | null;
|
|
33
410
|
payload?: Record<string, unknown>;
|
|
34
411
|
}): Promise<PaymentRow>;
|
|
35
412
|
findPaymentByGatewayId(gatewayId: string): Promise<PaymentRow | null>;
|
|
36
|
-
/**
|
|
413
|
+
/**
|
|
414
|
+
* The payment carrying one app-side reference — the id the APP knows the charge by.
|
|
415
|
+
*
|
|
416
|
+
* The other direction of {@link findPaymentByGatewayId}, and the one an app actually has
|
|
417
|
+
* at hand: a checkout page polls for "order-1042", not for `pi_3Qx...`. Backed by
|
|
418
|
+
* `billing_payments_external_reference_idx`.
|
|
419
|
+
*
|
|
420
|
+
* `null` when no row carries it — including on a payment written before the install ran
|
|
421
|
+
* an earlier schema, whose column is `null` for every row.
|
|
422
|
+
* Ties are broken newest-first: nothing stops an app reusing a reference across retries.
|
|
423
|
+
*/
|
|
424
|
+
findPaymentByExternalReference(reference: string): Promise<PaymentRow | null>;
|
|
425
|
+
/**
|
|
426
|
+
* Page through recorded payments, newest first, optionally filtered by status.
|
|
427
|
+
*
|
|
428
|
+
* A narrow read the aggregates above cannot answer: `revenue()` sums, it does not say
|
|
429
|
+
* WHICH payment failed. Returns the normalized {@link PaymentListItem}, not the
|
|
430
|
+
* implementation's row type, so a reader never depends on Lucid.
|
|
431
|
+
*/
|
|
432
|
+
listPayments(query: PaymentListQuery): Promise<PaymentListItem[]>;
|
|
433
|
+
/**
|
|
434
|
+
* How many payments match a status and/or a creation window.
|
|
435
|
+
*
|
|
436
|
+
* The check this exists for: charges that were created and never confirmed
|
|
437
|
+
* (`{ status: 'pending', createdBefore: twoHoursAgo }`). A webhook endpoint that stops
|
|
438
|
+
* being reachable produces exactly that and nothing else in the system errors.
|
|
439
|
+
*/
|
|
440
|
+
countPayments(query: BillingCountQuery): Promise<number>;
|
|
441
|
+
/**
|
|
442
|
+
* Upsert a dispute keyed by `gatewayId` — the DISPUTE's own id, not the payment's.
|
|
443
|
+
*
|
|
444
|
+
* Absent fields do NOT erase what is already recorded, the same rule (and for a sharper
|
|
445
|
+
* reason) as `savePayment`'s `externalReference`: the event that opens a dispute carries
|
|
446
|
+
* the deadline and the reason, and the event that CLOSES it carries neither. Blanking
|
|
447
|
+
* them on the close would destroy the only record of the window that was answered — and
|
|
448
|
+
* the deadline is the entire reason this table exists. Pass `null` explicitly to clear.
|
|
449
|
+
*
|
|
450
|
+
* Returns `null` — and writes nothing — when the install has no `billing_disputes` table
|
|
451
|
+
* yet, i.e. it upgraded the package before running an earlier schema. The dispute row
|
|
452
|
+
* is ADDITIONAL (the payment row still moves, the diagnostics still publish), so the write
|
|
453
|
+
* is skipped rather than failing every gateway delivery until someone runs the migration.
|
|
454
|
+
*/
|
|
455
|
+
saveDispute(dispute: {
|
|
456
|
+
/** The dispute's own gateway id. See {@link DisputeListItem.gatewayId}. */
|
|
457
|
+
gatewayId: string;
|
|
458
|
+
/** The disputed payment's gateway id. */
|
|
459
|
+
paymentGatewayId: string;
|
|
460
|
+
provider: string;
|
|
461
|
+
/** A {@link import('../types.js').DisputeStatus} value. */
|
|
462
|
+
status: string;
|
|
463
|
+
reason?: string | null;
|
|
464
|
+
/** Integer minor units. */
|
|
465
|
+
amount?: number | null;
|
|
466
|
+
currency?: string | null;
|
|
467
|
+
evidenceDueBy?: Date | null;
|
|
468
|
+
outcome?: string | null;
|
|
469
|
+
/** Defaults to the insert instant, and is never moved by a later update. */
|
|
470
|
+
openedAt?: Date | null;
|
|
471
|
+
closedAt?: Date | null;
|
|
472
|
+
payload?: Record<string, unknown>;
|
|
473
|
+
}): Promise<DisputeRow | null>;
|
|
474
|
+
/** The dispute with this gateway id, or `null`. The idempotency lookup behind the upsert. */
|
|
475
|
+
findDisputeByGatewayId(gatewayId: string): Promise<DisputeRow | null>;
|
|
476
|
+
/**
|
|
477
|
+
* The most recent UNRESOLVED dispute against one payment, or `null`.
|
|
478
|
+
*
|
|
479
|
+
* The processor's answer to an event that carries no dispute id: several gateways send
|
|
480
|
+
* the id when the dispute opens and omit it when it closes, and without this the close
|
|
481
|
+
* would open a second row instead of finishing the first. Resolved disputes are skipped
|
|
482
|
+
* so a fresh chargeback on a previously-lost payment starts its own row.
|
|
483
|
+
*/
|
|
484
|
+
findOpenDisputeByPayment(paymentGatewayId: string): Promise<DisputeRow | null>;
|
|
485
|
+
/**
|
|
486
|
+
* Page through recorded disputes, newest first, `status`/`provider` filters — the same
|
|
487
|
+
* shape as {@link listPayments}, and normalized for the same reason.
|
|
488
|
+
*/
|
|
489
|
+
listDisputes(query: BillingListQuery): Promise<DisputeListItem[]>;
|
|
490
|
+
/** How many disputes match a status and/or a creation window. */
|
|
491
|
+
countDisputes(query: BillingCountQuery): Promise<number>;
|
|
492
|
+
/**
|
|
493
|
+
* The open disputes whose response window closes within `withinHours` — ordered by the
|
|
494
|
+
* deadline, soonest FIRST.
|
|
495
|
+
*
|
|
496
|
+
* The one read that earns the table, and the only list here that is not newest-first: the
|
|
497
|
+
* ordering is the priority order, because a window that closes tomorrow outranks a dispute
|
|
498
|
+
* that arrived today.
|
|
499
|
+
*
|
|
500
|
+
* A deadline that is already PAST is included, not filtered out. It is still open, nothing
|
|
501
|
+
* has answered it, and dropping it the moment it expires would make the alert go quiet at
|
|
502
|
+
* exactly the moment it became true — the operator would read that as resolved.
|
|
503
|
+
*
|
|
504
|
+
* Rows with no `evidenceDueBy` are excluded: a gateway that sent no deadline gives nothing
|
|
505
|
+
* to be late for, and putting them in the list would bury the rows that do have one.
|
|
506
|
+
*/
|
|
507
|
+
listDisputesDueWithin(query: DisputeDeadlineQuery): Promise<DisputeListItem[]>;
|
|
508
|
+
/**
|
|
509
|
+
* The count behind {@link listDisputesDueWithin}, unbounded by any page.
|
|
510
|
+
*
|
|
511
|
+
* Separate from the list on purpose: the health check alerts on this number, and a count
|
|
512
|
+
* derived from a capped page saturates at the cap — which reads as "200 disputes" forever
|
|
513
|
+
* and, worse, would report a healthy zero if the page limit were ever misapplied.
|
|
514
|
+
*/
|
|
515
|
+
countDisputesDueWithin(query: Omit<DisputeDeadlineQuery, 'limit' | 'offset'>): Promise<number>;
|
|
516
|
+
/**
|
|
517
|
+
* Every dispute that still needs an answer, oldest FIRST — deadline or no deadline.
|
|
518
|
+
*
|
|
519
|
+
* The check {@link listDisputesDueWithin} cannot be. That one requires `evidence_due_by`, and
|
|
520
|
+
* the deadline only ever arrives from a gateway that publishes one; on Asaas it comes from
|
|
521
|
+
* `chargeback.deadlineToSendDisputeDocuments`, which no published webhook example even
|
|
522
|
+
* contains. On such an install every deadline read answers zero forever, so a chargeback can
|
|
523
|
+
* be open with the money already pulled back while the report says healthy.
|
|
524
|
+
*
|
|
525
|
+
* Oldest first because with no deadline to sort on, "how long has nobody answered this" is the
|
|
526
|
+
* only priority signal left.
|
|
527
|
+
*/
|
|
528
|
+
listOpenDisputes(query: OpenDisputeQuery): Promise<DisputeListItem[]>;
|
|
529
|
+
/** The unbounded count behind {@link listOpenDisputes} — what a health check alerts on. */
|
|
530
|
+
countOpenDisputes(query: {
|
|
531
|
+
provider?: string;
|
|
532
|
+
}): Promise<number>;
|
|
533
|
+
/**
|
|
534
|
+
* Record a webhook event for idempotency.
|
|
535
|
+
*
|
|
536
|
+
* Returns `null` when this gateway event is already in flight or already
|
|
537
|
+
* processed — that is the guard that makes a gateway redelivery a no-op.
|
|
538
|
+
*
|
|
539
|
+
* An event whose previous attempt **failed** is claimed again and returned,
|
|
540
|
+
* so a retry actually re-runs it. Without that, the first failure would seal
|
|
541
|
+
* the ledger and every retry — in-process or durable — would short-circuit
|
|
542
|
+
* into doing nothing.
|
|
543
|
+
*/
|
|
37
544
|
recordWebhookEvent(event: {
|
|
38
545
|
gatewayEventId: string;
|
|
39
546
|
provider: string;
|
|
40
547
|
type: string;
|
|
41
548
|
payload: Record<string, unknown>;
|
|
549
|
+
/**
|
|
550
|
+
* The NORMALIZED event (`WebhookEvent.data`), stored alongside the raw payload.
|
|
551
|
+
*
|
|
552
|
+
* Without it the dashboard's retry had to rebuild the event by calling `parseWebhook`
|
|
553
|
+
* over the stored payload, which re-verifies a signature computed from headers the
|
|
554
|
+
* ledger never kept — so a retry on Stripe or Adyen answered `422` and only unsigned
|
|
555
|
+
* gateways could replay. Stored once, on the way in, where it is still in hand.
|
|
556
|
+
*
|
|
557
|
+
* Left `undefined` on the re-claim path (a retry), where the row keeps what it has.
|
|
558
|
+
*/
|
|
559
|
+
normalized?: unknown;
|
|
42
560
|
}): Promise<WebhookEventRow | null>;
|
|
43
561
|
markWebhookProcessed(id: string): Promise<void>;
|
|
44
562
|
markWebhookFailed(id: string, error: string): Promise<void>;
|
|
563
|
+
/**
|
|
564
|
+
* Page through the ledger, newest first, optionally filtered by status.
|
|
565
|
+
*
|
|
566
|
+
* `status: 'failed'` is the operationally load-bearing one: a `failed` row means a
|
|
567
|
+
* handler threw and the dispatcher gave up, so the event's effect never happened. It
|
|
568
|
+
* carries the handler's `error`, which is the only place that message survives.
|
|
569
|
+
*/
|
|
570
|
+
listWebhookEvents(query: WebhookEventListQuery): Promise<WebhookEventListItem[]>;
|
|
571
|
+
/**
|
|
572
|
+
* The ledger rows whose stored delivery NAMES one payment — that payment's timeline.
|
|
573
|
+
*
|
|
574
|
+
* `billing_payments` is a single mutable row upserted in place: it holds the current state and
|
|
575
|
+
* no history at all, so "what happened to this charge, and when?" has no answer from the
|
|
576
|
+
* payments table. The ledger does have one — every delivery that moved the row is in it — but
|
|
577
|
+
* nothing links a ledger row to a payment, because the link lives inside the stored payload.
|
|
578
|
+
*
|
|
579
|
+
* So this is a SUBSTRING match over the payload, and both of that decision's costs are real:
|
|
580
|
+
* it is an unindexed scan (bounded by `limit`, newest first, which is why the bound is not
|
|
581
|
+
* optional in spirit even though it has a default), and a gateway id that happens to be a
|
|
582
|
+
* substring of some other identifier in another delivery will match. It is offered anyway
|
|
583
|
+
* because the alternative on the table today is nothing at all. The honest fix is a
|
|
584
|
+
* `payment_gateway_id` column the PROCESSOR fills on the way in; see the changeset.
|
|
585
|
+
*
|
|
586
|
+
* Never returns the payload itself — the same rule the rest of this console follows.
|
|
587
|
+
*/
|
|
588
|
+
listWebhookEventsForPayment(paymentGatewayId: string, query?: {
|
|
589
|
+
limit?: number;
|
|
590
|
+
}): Promise<WebhookEventListItem[]>;
|
|
591
|
+
/**
|
|
592
|
+
* The ledger row for one **gateway** event id — the id the gateway's dashboard shows.
|
|
593
|
+
*
|
|
594
|
+
* This is the first question when a customer paid and nothing happened: did the event
|
|
595
|
+
* arrive at all? `null` means it never reached the processor; a row with status `failed`
|
|
596
|
+
* carries the handler's error.
|
|
597
|
+
*/
|
|
598
|
+
findWebhookEventByGatewayEventId(gatewayEventId: string): Promise<WebhookEventListItem | null>;
|
|
599
|
+
/**
|
|
600
|
+
* How many ledger rows match a status and/or a creation window.
|
|
601
|
+
*
|
|
602
|
+
* `{ status: 'received', createdBefore: fifteenMinutesAgo }` is the one to alert on: an
|
|
603
|
+
* event was claimed and nothing ever finished it, which usually means the worker the
|
|
604
|
+
* dispatcher depends on is not running.
|
|
605
|
+
*/
|
|
606
|
+
countWebhookEvents(query: BillingCountQuery): Promise<number>;
|
|
607
|
+
/**
|
|
608
|
+
* The same count, grouped by provider and event type — "what is failing", not just
|
|
609
|
+
* "how much". Ordered by count, descending.
|
|
610
|
+
*/
|
|
611
|
+
webhookEventBreakdown(query: BillingCountQuery): Promise<WebhookEventBreakdownLine[]>;
|
|
612
|
+
/**
|
|
613
|
+
* Record something that happened which no other table keeps: a console refund, a dispute an
|
|
614
|
+
* operator resolved by hand, a delivery the endpoint rejected.
|
|
615
|
+
*
|
|
616
|
+
* Returns `null` — and writes nothing — when the install has no `billing_audit_events` table
|
|
617
|
+
* yet, i.e. it upgraded the package before running an earlier schema. Exactly the tolerance
|
|
618
|
+
* {@link BillingStore.saveDispute} has, and for the same reason: the audit row is ADDITIONAL,
|
|
619
|
+
* and failing a refund that the gateway already accepted because the note could not be filed
|
|
620
|
+
* would be the worse outcome by a distance.
|
|
621
|
+
*/
|
|
622
|
+
recordAuditEvent(event: {
|
|
623
|
+
action: string;
|
|
624
|
+
/** WHO. `null`/absent means unattributed — see {@link AuditEventListItem.actor}. */
|
|
625
|
+
actor?: string | null;
|
|
626
|
+
provider?: string | null;
|
|
627
|
+
subjectType?: string | null;
|
|
628
|
+
subjectId?: string | null;
|
|
629
|
+
/** Integer minor units. */
|
|
630
|
+
amount?: number | null;
|
|
631
|
+
currency?: string | null;
|
|
632
|
+
message?: string | null;
|
|
633
|
+
metadata?: Record<string, unknown> | null;
|
|
634
|
+
/** Defaults to the insert instant. */
|
|
635
|
+
createdAt?: Date;
|
|
636
|
+
}): Promise<AuditEventListItem | null>;
|
|
637
|
+
/** Page through the audit trail, newest first. Empty on an install with no table yet. */
|
|
638
|
+
listAuditEvents(query: AuditEventQuery): Promise<AuditEventListItem[]>;
|
|
639
|
+
/** How many audit rows match — the count the rejected-delivery check alerts on. */
|
|
640
|
+
countAuditEvents(query: AuditEventCountQuery): Promise<number>;
|
|
45
641
|
/** Record one metered-usage event (a metered subscription's consumption). */
|
|
46
642
|
recordUsage(event: {
|
|
47
643
|
subscriptionId?: string | null;
|
|
@@ -62,11 +658,44 @@ export interface BillingStore<SubscriptionRow = BillingSubscription, PaymentRow
|
|
|
62
658
|
meter: string;
|
|
63
659
|
quantity: number;
|
|
64
660
|
}>>;
|
|
65
|
-
/**
|
|
661
|
+
/**
|
|
662
|
+
* Sum of paid payments within a window, **GROSS** — `amount` on `status = 'paid'` rows,
|
|
663
|
+
* windowed on `paid_at`, with nothing subtracted.
|
|
664
|
+
*
|
|
665
|
+
* A half-refunded charge counts at its FULL value here, because that is what gross means:
|
|
666
|
+
* it answers "how much did we charge and collect in this window". For "how much did we
|
|
667
|
+
* keep", see {@link BillingStore.netRevenue}. Both are legitimate figures and neither is
|
|
668
|
+
* the other's replacement — a screen that shows one without saying which is the screen that
|
|
669
|
+
* misleads. Integer minor units; NEVER divide.
|
|
670
|
+
*/
|
|
66
671
|
revenue(query: {
|
|
67
672
|
from?: Date;
|
|
68
673
|
to?: Date;
|
|
69
674
|
}): Promise<number>;
|
|
675
|
+
/**
|
|
676
|
+
* The same rows and the same window as {@link BillingStore.revenue}, **NET of refunds**:
|
|
677
|
+
* `SUM(amount - COALESCE(refunded_amount, 0))`.
|
|
678
|
+
*
|
|
679
|
+
* The `refunded_amount` column exists precisely so a PARTIAL refund does not have to be
|
|
680
|
+
* spelled by mangling `amount` or `status` — a R$10 refund on a R$100 charge leaves the row
|
|
681
|
+
* `paid` at `amount: 10000, refundedAmount: 1000`. Gross sees R$100 of that row, net sees
|
|
682
|
+
* R$90, and until this existed only gross was reachable, so a partially refunded charge was
|
|
683
|
+
* invisible in every revenue figure the console printed.
|
|
684
|
+
*
|
|
685
|
+
* `COALESCE` is not decoration: `refunded_amount` is `NULL` on every row written before the
|
|
686
|
+
* column existed, and `10000 - NULL` is `NULL` in SQL, which would poison the whole SUM into
|
|
687
|
+
* `NULL` — one legacy row would report zero revenue for the entire window. `NULL` means
|
|
688
|
+
* "nothing came back", so it reads as zero.
|
|
689
|
+
*
|
|
690
|
+
* On an install whose table predates the column, this equals {@link BillingStore.revenue}:
|
|
691
|
+
* no refund was ever recorded, so there is nothing to subtract.
|
|
692
|
+
*
|
|
693
|
+
* Integer minor units, like everything else here. NEVER divide — format at the edge.
|
|
694
|
+
*/
|
|
695
|
+
netRevenue(query: {
|
|
696
|
+
from?: Date;
|
|
697
|
+
to?: Date;
|
|
698
|
+
}): Promise<number>;
|
|
70
699
|
/** Count of active subscriptions (status `'active'`/`'trialing'`). */
|
|
71
700
|
countActiveSubscriptions(): Promise<number>;
|
|
72
701
|
}
|