@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
package/dist/configure.d.ts
CHANGED
|
@@ -2,11 +2,18 @@ import type Configure from '@adonisjs/core/commands/configure';
|
|
|
2
2
|
/**
|
|
3
3
|
* `node ace configure @adonis-agora/payments` — auto-wires the package:
|
|
4
4
|
*
|
|
5
|
-
* 1. registers the payments service provider in `adonisrc.ts
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* 1. registers the payments service provider in `adonisrc.ts`, plus the optional
|
|
6
|
+
* dashboard provider;
|
|
7
|
+
* 2. registers the Assembler `init` hook that generates the `app/payment_handlers/`
|
|
8
|
+
* barrel (discovery works without it — the provider falls back to a runtime scan —
|
|
9
|
+
* but the barrel removes that scan from boot);
|
|
10
|
+
* 3. publishes `config/payments.ts`, `config/payments_dashboard.ts` and
|
|
11
|
+
* `config/payments_client.ts` (the browser status endpoint — off unless enabled);
|
|
12
|
+
* 4. publishes ONE Lucid migration for the billing tables, which most apps can delete: the
|
|
13
|
+
* library creates its own schema on first use (`billing.autoCreateSchema`, on by
|
|
14
|
+
* default), so the file is there for apps that would rather run the DDL explicitly. Both
|
|
15
|
+
* paths call the same `createBillingTables`;
|
|
16
|
+
* 5. registers the env validations for the payment providers.
|
|
10
17
|
*/
|
|
11
18
|
export declare function configure(command: Configure): Promise<void>;
|
|
12
19
|
//# sourceMappingURL=configure.d.ts.map
|
package/dist/configure.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAC;AAG/D
|
|
1
|
+
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAC;AAG/D;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,SAAS,iBA+BjD"}
|
package/dist/configure.js
CHANGED
|
@@ -2,21 +2,33 @@ import { stubsRoot } from './stubs/main.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* `node ace configure @adonis-agora/payments` — auto-wires the package:
|
|
4
4
|
*
|
|
5
|
-
* 1. registers the payments service provider in `adonisrc.ts
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* 1. registers the payments service provider in `adonisrc.ts`, plus the optional
|
|
6
|
+
* dashboard provider;
|
|
7
|
+
* 2. registers the Assembler `init` hook that generates the `app/payment_handlers/`
|
|
8
|
+
* barrel (discovery works without it — the provider falls back to a runtime scan —
|
|
9
|
+
* but the barrel removes that scan from boot);
|
|
10
|
+
* 3. publishes `config/payments.ts`, `config/payments_dashboard.ts` and
|
|
11
|
+
* `config/payments_client.ts` (the browser status endpoint — off unless enabled);
|
|
12
|
+
* 4. publishes ONE Lucid migration for the billing tables, which most apps can delete: the
|
|
13
|
+
* library creates its own schema on first use (`billing.autoCreateSchema`, on by
|
|
14
|
+
* default), so the file is there for apps that would rather run the DDL explicitly. Both
|
|
15
|
+
* paths call the same `createBillingTables`;
|
|
16
|
+
* 5. registers the env validations for the payment providers.
|
|
10
17
|
*/
|
|
11
18
|
export async function configure(command) {
|
|
12
19
|
const codemods = await command.createCodemods();
|
|
13
20
|
// biome-ignore lint/suspicious/noExplicitAny: `createCodemods` types the callback; matching media.
|
|
14
21
|
await codemods.updateRcFile((rcFile) => {
|
|
15
22
|
rcFile.addProvider('@adonis-agora/payments/payments_provider');
|
|
23
|
+
rcFile.addProvider('@adonis-agora/payments/dashboard_provider');
|
|
24
|
+
rcFile.addProvider('@adonis-agora/payments/payments_client_provider');
|
|
16
25
|
rcFile.addCommand('@adonis-agora/payments/commands');
|
|
26
|
+
rcFile.addAssemblerHook('init', '@adonis-agora/payments/hooks/webhook_handlers');
|
|
17
27
|
});
|
|
18
28
|
const stubs = stubsRoot();
|
|
19
29
|
await codemods.makeUsingStub(stubs, 'config/payments.stub', {});
|
|
30
|
+
await codemods.makeUsingStub(stubs, 'config/payments_dashboard.stub', {});
|
|
31
|
+
await codemods.makeUsingStub(stubs, 'config/payments_client.stub', {});
|
|
20
32
|
await codemods.makeUsingStub(stubs, 'database/migrations/create_billing_tables.stub', {});
|
|
21
33
|
await codemods.defineEnvValidations({
|
|
22
34
|
leadingComment: 'PAYMENTS_',
|
|
@@ -28,6 +40,7 @@ export async function configure(command) {
|
|
|
28
40
|
ASAAS_API_KEY: 'Env.schema.string.optional()',
|
|
29
41
|
WOOVI_APP_ID: 'Env.schema.string.optional()',
|
|
30
42
|
FOCUS_NFE_TOKEN: 'Env.schema.string.optional()',
|
|
43
|
+
PAYMENTS_DASHBOARD_TOKEN: 'Env.schema.string.optional()',
|
|
31
44
|
},
|
|
32
45
|
});
|
|
33
46
|
}
|
package/dist/configure.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configure.js","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C
|
|
1
|
+
{"version":3,"file":"configure.js","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAkB;IAChD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC;IAEhD,mGAAmG;IACnG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC,MAAW,EAAE,EAAE;QAC1C,MAAM,CAAC,WAAW,CAAC,0CAA0C,CAAC,CAAC;QAC/D,MAAM,CAAC,WAAW,CAAC,2CAA2C,CAAC,CAAC;QAChE,MAAM,CAAC,WAAW,CAAC,iDAAiD,CAAC,CAAC;QACtE,MAAM,CAAC,UAAU,CAAC,iCAAiC,CAAC,CAAC;QACrD,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,+CAA+C,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,QAAQ,CAAC,aAAa,CAAC,KAAK,EAAE,sBAAsB,EAAE,EAAE,CAAC,CAAC;IAChE,MAAM,QAAQ,CAAC,aAAa,CAAC,KAAK,EAAE,gCAAgC,EAAE,EAAE,CAAC,CAAC;IAC1E,MAAM,QAAQ,CAAC,aAAa,CAAC,KAAK,EAAE,6BAA6B,EAAE,EAAE,CAAC,CAAC;IACvE,MAAM,QAAQ,CAAC,aAAa,CAAC,KAAK,EAAE,gDAAgD,EAAE,EAAE,CAAC,CAAC;IAE1F,MAAM,QAAQ,CAAC,oBAAoB,CAAC;QAClC,cAAc,EAAE,WAAW;QAC3B,SAAS,EAAE;YACT,UAAU,EAAE,8BAA8B;YAC1C,qBAAqB,EAAE,8BAA8B;YACrD,eAAe,EAAE,8BAA8B;YAC/C,kBAAkB,EAAE,8BAA8B;YAClD,aAAa,EAAE,8BAA8B;YAC7C,YAAY,EAAE,8BAA8B;YAC5C,eAAe,EAAE,8BAA8B;YAC/C,wBAAwB,EAAE,8BAA8B;SACzD;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { ApplicationService } from '@adonisjs/core/types';
|
|
2
|
+
/**
|
|
3
|
+
* Mounts the payments dashboard into an AdonisJS app: the `@adonis-agora/payments-dashboard` React
|
|
4
|
+
* SPA plus a JSON API over the resolved {@link BillingStore}, served under a configurable path.
|
|
5
|
+
*
|
|
6
|
+
* Reads never touch a gateway. The two ACTIONS do — a refund calls the driver that took the
|
|
7
|
+
* payment, and a retry re-runs a failed webhook event through the app's own handlers — so both are
|
|
8
|
+
* `POST` (a refund reachable by URL is a refund a crawler can trigger) and both go through the same
|
|
9
|
+
* {@link enforce} guard as every read.
|
|
10
|
+
*
|
|
11
|
+
* Routes (relative to the configured `path`, default `/payments`):
|
|
12
|
+
* - `GET /` -> the dashboard SPA's `index.html`
|
|
13
|
+
* - `GET /assets/:file` -> the SPA's hashed JS/CSS bundle
|
|
14
|
+
* - `GET /api/health` -> `billingHealth()` — what needs attention today
|
|
15
|
+
* - `GET /api/overview` -> `billingOverview()` metrics for a period
|
|
16
|
+
* - `GET /api/payments` -> a page of `billing_payments` (status/provider filters, plus
|
|
17
|
+
* `?reference=` / `?gatewayId=` / `?customerId=` lookups)
|
|
18
|
+
* - `GET /api/payments/:gatewayId` -> one payment: current state, owner, disputes, the ledger
|
|
19
|
+
* rows naming it, and who refunded it
|
|
20
|
+
* - `GET /api/customers` -> a page of `billing_customers` — the owner mapping that ties
|
|
21
|
+
* a `cus_…` back to an app user
|
|
22
|
+
* - `GET /api/audit` -> the trail: refunds issued here, disputes resolved here, and
|
|
23
|
+
* the gateway deliveries this endpoint REFUSED
|
|
24
|
+
* - `GET /api/disputes` -> a page of `billing_disputes`; `?dueWithin=<hours>` for the
|
|
25
|
+
* open windows closing soonest
|
|
26
|
+
* - `GET /api/subscriptions` -> a page of `billing_subscriptions` (status/provider filters)
|
|
27
|
+
* - `GET /api/webhook-events` -> a page of `billing_webhook_events` (status/provider/type)
|
|
28
|
+
* - `GET /api/providers` -> the gateways and event types this install has data for
|
|
29
|
+
* - `POST /api/payments/:gatewayId/refund` -> refund through the payment's own gateway
|
|
30
|
+
* - `POST /api/disputes/:gatewayId/resolve` -> record how a dispute ended, and who said so
|
|
31
|
+
* - `POST /api/webhook-events/:gatewayEventId/retry` -> re-run a failed event's handlers
|
|
32
|
+
*
|
|
33
|
+
* Plus, when `dashboardAuth` is configured, `GET|POST <path>/login`, `POST <path>/session` and
|
|
34
|
+
* `GET <path>/logout`.
|
|
35
|
+
*
|
|
36
|
+
* With `enabled: false` this provider registers NOTHING — not the SPA, not the API, not the auth
|
|
37
|
+
* routes. The dashboard is then completely absent from the app's route table.
|
|
38
|
+
*/
|
|
39
|
+
export default class PaymentsDashboardProvider {
|
|
40
|
+
protected app: ApplicationService;
|
|
41
|
+
constructor(app: ApplicationService);
|
|
42
|
+
/** Warn once so a throwing `login`/`session` hook doesn't spam the logs on every failed attempt. */
|
|
43
|
+
private warnedOnHookThrow;
|
|
44
|
+
boot(): Promise<void>;
|
|
45
|
+
private registerRoutes;
|
|
46
|
+
/**
|
|
47
|
+
* The two write actions, built once and reused.
|
|
48
|
+
*
|
|
49
|
+
* Both need the app's `PaymentsManager` — the refund needs the driver that took the payment, and
|
|
50
|
+
* the retry needs that driver to rebuild the normalized event from the stored payload. Neither is
|
|
51
|
+
* fatal when it is missing: the ports are simply absent and the handlers answer `503` with a
|
|
52
|
+
* sentence saying so.
|
|
53
|
+
*
|
|
54
|
+
* Only a successful build is cached. A failure here usually means "the app has not finished
|
|
55
|
+
* booting", and caching THAT would disable both buttons for the life of the process.
|
|
56
|
+
*/
|
|
57
|
+
private actionsCache;
|
|
58
|
+
private actions;
|
|
59
|
+
/** Webhook handlers from `config.billing.handlers` plus `app/payment_handlers/` (build-time
|
|
60
|
+
* barrel, runtime scan as fallback) — the same two sources the payments provider merges. */
|
|
61
|
+
private resolveHandlers;
|
|
62
|
+
private discoverHandlers;
|
|
63
|
+
/** Mount the React SPA at `config.path` plus its hashed asset bundle. */
|
|
64
|
+
private registerSpaRoutes;
|
|
65
|
+
/**
|
|
66
|
+
* Mount the built-in `dashboardAuth` endpoints under `basePath`. All are public (no guard): they
|
|
67
|
+
* create/destroy the session the {@link enforce} guard checks for.
|
|
68
|
+
*
|
|
69
|
+
* - `GET <base>/login` -> the server-rendered login page (Mode B only).
|
|
70
|
+
* - `POST <base>/login` -> verifies credentials via the host `login` hook and mints the cookie.
|
|
71
|
+
* - `POST <base>/session` -> Mode A: verifies the HOST APP's own auth (off the raw request) via
|
|
72
|
+
* the `session` hook and mints the cookie. `204` on success, uniform `401` on denial.
|
|
73
|
+
* - `GET <base>/logout` -> clears the cookie and redirects to the login page (Mode B) or the
|
|
74
|
+
* dashboard root (Mode A only).
|
|
75
|
+
*/
|
|
76
|
+
private registerAuthRoutes;
|
|
77
|
+
private warnHookThrow;
|
|
78
|
+
/**
|
|
79
|
+
* Run the guards for a dashboard resource. Composes the `authorize` hook (bearer token/custom)
|
|
80
|
+
* with the optional `dashboardAuth` session guard — BOTH must pass:
|
|
81
|
+
*
|
|
82
|
+
* 1. `authorize` fails -> `403`.
|
|
83
|
+
* 2. `dashboardAuth` configured AND no valid session -> for a `page` request, redirect `302` to
|
|
84
|
+
* the login page (Mode B) carrying a sanitized `returnTo`, or a `401` notice (Mode A only);
|
|
85
|
+
* for an `api` request, `401 { error: 'unauthorized', auth: { modes } }`.
|
|
86
|
+
*
|
|
87
|
+
* Returns `allowed: false` (and has already written the response) when the request must
|
|
88
|
+
* short-circuit. On success it also carries WHO passed the gate: the session's user, which
|
|
89
|
+
* the write handlers record against a refund or a dispute resolution. `undefined` when this
|
|
90
|
+
* deployment configures no `dashboardAuth` — there is genuinely nobody to name, and inventing
|
|
91
|
+
* one would be worse than an honest "unattributed".
|
|
92
|
+
*/
|
|
93
|
+
private enforce;
|
|
94
|
+
private readSessionCookie;
|
|
95
|
+
private writeSessionCookie;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=dashboard_provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard_provider.d.ts","sourceRoot":"","sources":["../../providers/dashboard_provider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAqB,MAAM,sBAAsB,CAAC;AAiElF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,CAAC,OAAO,OAAO,yBAAyB;IAChC,SAAS,CAAC,GAAG,EAAE,kBAAkB;IAA7C,YAAsB,GAAG,EAAE,kBAAkB,EAAI;IAEjD,oGAAoG;IACpG,OAAO,CAAC,iBAAiB,CAAS;IAE5B,IAAI,kBAaT;IAED,OAAO,CAAC,cAAc;IA2FtB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,YAAY,CAAwC;YAE9C,OAAO;IAqCrB;iGAC6F;YAC/E,eAAe;YAef,gBAAgB;IAa9B,yEAAyE;IACzE,OAAO,CAAC,iBAAiB;IAgDzB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,kBAAkB;YAwDZ,aAAa;IAQ3B;;;;;;;;;;;;;;OAcG;YACW,OAAO;IA+CrB,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,kBAAkB;CAU3B"}
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { basename, resolve as resolvePath } from 'node:path';
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
|
+
import { WebhookProcessor } from '../src/billing/webhook_processor.js';
|
|
5
|
+
import { createRefundAction, createReplayAction } from '../src/dashboard/actions.js';
|
|
6
|
+
import { performLogin, performSession, readSession, SESSION_COOKIE_NAME, } from '../src/dashboard/auth.js';
|
|
7
|
+
import { resolveConfig, } from '../src/dashboard/define_config.js';
|
|
8
|
+
import { auditEvents, customers, disputes, health, overview, paymentDetail, payments, providers, refundPayment, resolveDispute, retryWebhookEvent, subscriptions, webhookEvents, } from '../src/dashboard/handlers.js';
|
|
9
|
+
import { renderLoginPage } from '../src/dashboard/login_page.js';
|
|
10
|
+
import { contentTypeFor, renderIndexHtml } from '../src/dashboard/spa.js';
|
|
11
|
+
import { PaymentsManager } from '../src/payments_manager.js';
|
|
12
|
+
import { getBillingStore } from '../src/services/main.js';
|
|
13
|
+
import { discoverWebhookHandlers, loadWebhookHandlersFromBarrel, resolveWebhookHandler, } from '../src/webhook_handlers.js';
|
|
14
|
+
/**
|
|
15
|
+
* Directory of the built SPA (`dist/assets/spa`, copied in by `copy:spa` — see `package.json`),
|
|
16
|
+
* relative to this compiled provider (`dist/providers`). Resolved lazily (not at module scope) for
|
|
17
|
+
* the same reason `@adonis-agora/durable`'s dashboard provider resolves its SPA directory lazily:
|
|
18
|
+
* `fileURLToPath` throws on any non-`file:` `import.meta.url`, which would make this provider
|
|
19
|
+
* unimportable under a test runner that doesn't use real file URLs.
|
|
20
|
+
*
|
|
21
|
+
* NOTE this reads BUILT ASSETS FROM DISK. The provider never imports `@adonis-agora/payments-dashboard`
|
|
22
|
+
* — that package is a devDependency of this one purely so the workspace build orders them.
|
|
23
|
+
*/
|
|
24
|
+
let spaDir;
|
|
25
|
+
function spaDirectory() {
|
|
26
|
+
spaDir ??= fileURLToPath(new URL('../assets/spa/', import.meta.url));
|
|
27
|
+
return spaDir;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Mounts the payments dashboard into an AdonisJS app: the `@adonis-agora/payments-dashboard` React
|
|
31
|
+
* SPA plus a JSON API over the resolved {@link BillingStore}, served under a configurable path.
|
|
32
|
+
*
|
|
33
|
+
* Reads never touch a gateway. The two ACTIONS do — a refund calls the driver that took the
|
|
34
|
+
* payment, and a retry re-runs a failed webhook event through the app's own handlers — so both are
|
|
35
|
+
* `POST` (a refund reachable by URL is a refund a crawler can trigger) and both go through the same
|
|
36
|
+
* {@link enforce} guard as every read.
|
|
37
|
+
*
|
|
38
|
+
* Routes (relative to the configured `path`, default `/payments`):
|
|
39
|
+
* - `GET /` -> the dashboard SPA's `index.html`
|
|
40
|
+
* - `GET /assets/:file` -> the SPA's hashed JS/CSS bundle
|
|
41
|
+
* - `GET /api/health` -> `billingHealth()` — what needs attention today
|
|
42
|
+
* - `GET /api/overview` -> `billingOverview()` metrics for a period
|
|
43
|
+
* - `GET /api/payments` -> a page of `billing_payments` (status/provider filters, plus
|
|
44
|
+
* `?reference=` / `?gatewayId=` / `?customerId=` lookups)
|
|
45
|
+
* - `GET /api/payments/:gatewayId` -> one payment: current state, owner, disputes, the ledger
|
|
46
|
+
* rows naming it, and who refunded it
|
|
47
|
+
* - `GET /api/customers` -> a page of `billing_customers` — the owner mapping that ties
|
|
48
|
+
* a `cus_…` back to an app user
|
|
49
|
+
* - `GET /api/audit` -> the trail: refunds issued here, disputes resolved here, and
|
|
50
|
+
* the gateway deliveries this endpoint REFUSED
|
|
51
|
+
* - `GET /api/disputes` -> a page of `billing_disputes`; `?dueWithin=<hours>` for the
|
|
52
|
+
* open windows closing soonest
|
|
53
|
+
* - `GET /api/subscriptions` -> a page of `billing_subscriptions` (status/provider filters)
|
|
54
|
+
* - `GET /api/webhook-events` -> a page of `billing_webhook_events` (status/provider/type)
|
|
55
|
+
* - `GET /api/providers` -> the gateways and event types this install has data for
|
|
56
|
+
* - `POST /api/payments/:gatewayId/refund` -> refund through the payment's own gateway
|
|
57
|
+
* - `POST /api/disputes/:gatewayId/resolve` -> record how a dispute ended, and who said so
|
|
58
|
+
* - `POST /api/webhook-events/:gatewayEventId/retry` -> re-run a failed event's handlers
|
|
59
|
+
*
|
|
60
|
+
* Plus, when `dashboardAuth` is configured, `GET|POST <path>/login`, `POST <path>/session` and
|
|
61
|
+
* `GET <path>/logout`.
|
|
62
|
+
*
|
|
63
|
+
* With `enabled: false` this provider registers NOTHING — not the SPA, not the API, not the auth
|
|
64
|
+
* routes. The dashboard is then completely absent from the app's route table.
|
|
65
|
+
*/
|
|
66
|
+
export default class PaymentsDashboardProvider {
|
|
67
|
+
app;
|
|
68
|
+
constructor(app) {
|
|
69
|
+
this.app = app;
|
|
70
|
+
}
|
|
71
|
+
/** Warn once so a throwing `login`/`session` hook doesn't spam the logs on every failed attempt. */
|
|
72
|
+
warnedOnHookThrow = false;
|
|
73
|
+
async boot() {
|
|
74
|
+
const config = resolveConfig(this.app.config.get('payments_dashboard', {}));
|
|
75
|
+
if (!config.enabled)
|
|
76
|
+
return;
|
|
77
|
+
// Route registration can't happen synchronously in `boot()`: the router singleton isn't
|
|
78
|
+
// committed until the app's "booted" hooks run, which fire strictly AFTER every provider's
|
|
79
|
+
// own `boot()`. Same note `@adonis-agora/durable`'s dashboard provider carries.
|
|
80
|
+
await this.app.booted(async () => {
|
|
81
|
+
const router = await this.app.container.make('router');
|
|
82
|
+
this.registerRoutes(router, config);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
registerRoutes(router, config) {
|
|
86
|
+
const apiBase = `${config.path}/api`;
|
|
87
|
+
// Built-in `dashboardAuth` (Mode A `session` and/or Mode B `login`, opt-in). Registered ONLY
|
|
88
|
+
// when configured. These endpoints are public (behind NEITHER guard): they MINT the session
|
|
89
|
+
// the guard checks for.
|
|
90
|
+
if (config.dashboardAuth) {
|
|
91
|
+
this.registerAuthRoutes(router, config, config.dashboardAuth);
|
|
92
|
+
}
|
|
93
|
+
this.registerSpaRoutes(router, config, apiBase);
|
|
94
|
+
const json = (handler, needsActions = false) => {
|
|
95
|
+
return async (ctx) => {
|
|
96
|
+
const guard = await this.enforce(config, ctx, 'api');
|
|
97
|
+
if (!guard.allowed)
|
|
98
|
+
return;
|
|
99
|
+
let store;
|
|
100
|
+
try {
|
|
101
|
+
store = getBillingStore();
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
// The billing layer is off (or the app hasn't booted it). That is a deployment state,
|
|
105
|
+
// not a bug in the request — say so with a 503 rather than a stack-trace-shaped 500.
|
|
106
|
+
return ctx.response.status(503).json({
|
|
107
|
+
error: error instanceof Error ? error.message : 'billing store unavailable',
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const deps = {
|
|
112
|
+
store,
|
|
113
|
+
currency: config.currency,
|
|
114
|
+
// WHO is asking. The guard has already verified a signed session carrying a user;
|
|
115
|
+
// passing it through is what lets a refund and a dispute resolution be recorded
|
|
116
|
+
// against a person instead of against nobody. Absent when this deployment
|
|
117
|
+
// configures no `dashboardAuth` — recorded as "unattributed", never invented.
|
|
118
|
+
...(guard.actor !== undefined ? { actor: guard.actor } : {}),
|
|
119
|
+
// Resolved lazily and only for the routes that need it: a console whose payments
|
|
120
|
+
// manager is unreachable must still serve every read.
|
|
121
|
+
...(needsActions ? { actions: await this.actions(store) } : {}),
|
|
122
|
+
};
|
|
123
|
+
const result = await handler(deps, toApiRequest(ctx));
|
|
124
|
+
return ctx.response.status(result.status).json(result.body);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
return ctx.response
|
|
128
|
+
.status(500)
|
|
129
|
+
.json({ error: error instanceof Error ? error.message : 'internal error' });
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
router.get(`${apiBase}/health`, json(health)).as('payments_dashboard.health');
|
|
134
|
+
router.get(`${apiBase}/overview`, json(overview)).as('payments_dashboard.overview');
|
|
135
|
+
router.get(`${apiBase}/payments`, json(payments)).as('payments_dashboard.payments');
|
|
136
|
+
// Registered AFTER the collection route and before the refund `POST`, and the ordering is
|
|
137
|
+
// only cosmetic here — `/payments` and `/payments/:gatewayId` cannot shadow each other — but
|
|
138
|
+
// it keeps the read routes reading top-down.
|
|
139
|
+
router
|
|
140
|
+
.get(`${apiBase}/payments/:gatewayId`, json(paymentDetail))
|
|
141
|
+
.as('payments_dashboard.payments.show');
|
|
142
|
+
router.get(`${apiBase}/customers`, json(customers)).as('payments_dashboard.customers');
|
|
143
|
+
router.get(`${apiBase}/audit`, json(auditEvents)).as('payments_dashboard.audit');
|
|
144
|
+
// There is still no "fight" or "accept" route, on purpose: whether to submit evidence or
|
|
145
|
+
// refund is a business rule that stays in the app's code. The `resolve` POST below is a
|
|
146
|
+
// different thing — it records an outcome that already happened at the gateway.
|
|
147
|
+
router.get(`${apiBase}/disputes`, json(disputes)).as('payments_dashboard.disputes');
|
|
148
|
+
router
|
|
149
|
+
.get(`${apiBase}/subscriptions`, json(subscriptions))
|
|
150
|
+
.as('payments_dashboard.subscriptions');
|
|
151
|
+
router
|
|
152
|
+
.get(`${apiBase}/webhook-events`, json(webhookEvents))
|
|
153
|
+
.as('payments_dashboard.webhook_events');
|
|
154
|
+
router.get(`${apiBase}/providers`, json(providers)).as('payments_dashboard.providers');
|
|
155
|
+
// The console's only WRITE routes. `POST` is load-bearing, not stylistic: registering either of
|
|
156
|
+
// these as a `GET` would put "refund this customer" behind a link a prefetcher can follow.
|
|
157
|
+
router
|
|
158
|
+
.post(`${apiBase}/payments/:gatewayId/refund`, json(refundPayment, true))
|
|
159
|
+
.as('payments_dashboard.payments.refund');
|
|
160
|
+
router
|
|
161
|
+
.post(`${apiBase}/webhook-events/:gatewayEventId/retry`, json(retryWebhookEvent, true))
|
|
162
|
+
.as('payments_dashboard.webhook_events.retry');
|
|
163
|
+
// No `needsActions`: this one calls no gateway. It records how a dispute ENDED — the loop a
|
|
164
|
+
// gateway that publishes no lost-dispute event never closes on its own — so it stays
|
|
165
|
+
// available on a deployment whose payments manager is unreachable.
|
|
166
|
+
router
|
|
167
|
+
.post(`${apiBase}/disputes/:gatewayId/resolve`, json(resolveDispute))
|
|
168
|
+
.as('payments_dashboard.disputes.resolve');
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* The two write actions, built once and reused.
|
|
172
|
+
*
|
|
173
|
+
* Both need the app's `PaymentsManager` — the refund needs the driver that took the payment, and
|
|
174
|
+
* the retry needs that driver to rebuild the normalized event from the stored payload. Neither is
|
|
175
|
+
* fatal when it is missing: the ports are simply absent and the handlers answer `503` with a
|
|
176
|
+
* sentence saying so.
|
|
177
|
+
*
|
|
178
|
+
* Only a successful build is cached. A failure here usually means "the app has not finished
|
|
179
|
+
* booting", and caching THAT would disable both buttons for the life of the process.
|
|
180
|
+
*/
|
|
181
|
+
actionsCache;
|
|
182
|
+
async actions(store) {
|
|
183
|
+
if (this.actionsCache)
|
|
184
|
+
return this.actionsCache;
|
|
185
|
+
let manager;
|
|
186
|
+
try {
|
|
187
|
+
manager = await this.app.container.make(PaymentsManager);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return {};
|
|
191
|
+
}
|
|
192
|
+
// The retry re-runs the app's OWN handlers, not just the built-in store sync — a retry that
|
|
193
|
+
// skipped them would silently do half the job, which is worse than no button. They are
|
|
194
|
+
// resolved the same way `payments_provider.ts` resolves them (config entries plus the
|
|
195
|
+
// `app/payment_handlers/` convention).
|
|
196
|
+
const config = this.app.config.get('payments', {});
|
|
197
|
+
const handlers = await this.resolveHandlers(config.billing?.handlers);
|
|
198
|
+
const processor = new WebhookProcessor({
|
|
199
|
+
store,
|
|
200
|
+
...(handlers !== undefined ? { handlers } : {}),
|
|
201
|
+
});
|
|
202
|
+
const actions = {
|
|
203
|
+
refund: createRefundAction((provider) => manager.driver(provider)),
|
|
204
|
+
replayWebhook: createReplayAction({
|
|
205
|
+
store,
|
|
206
|
+
// The event is rebuilt from the ledger's own `payload` + `normalized` columns and run
|
|
207
|
+
// straight through the processor — no driver, no `parseWebhook`, so no signature to
|
|
208
|
+
// re-verify from headers nobody kept. That is what makes a Stripe or Adyen row
|
|
209
|
+
// replayable at all. A row recorded before the `normalized` column existed is reported
|
|
210
|
+
// to the operator as `422`, ledger row untouched.
|
|
211
|
+
process: (event) => processor.process(event),
|
|
212
|
+
}),
|
|
213
|
+
};
|
|
214
|
+
this.actionsCache = actions;
|
|
215
|
+
return actions;
|
|
216
|
+
}
|
|
217
|
+
/** Webhook handlers from `config.billing.handlers` plus `app/payment_handlers/` (build-time
|
|
218
|
+
* barrel, runtime scan as fallback) — the same two sources the payments provider merges. */
|
|
219
|
+
async resolveHandlers(configHandlers) {
|
|
220
|
+
const handlers = {};
|
|
221
|
+
if (configHandlers) {
|
|
222
|
+
for (const [type, entry] of Object.entries(configHandlers)) {
|
|
223
|
+
handlers[type] = await resolveWebhookHandler(entry, this.app.container);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
for (const discovered of await this.discoverHandlers()) {
|
|
227
|
+
handlers[discovered.type] = await resolveWebhookHandler(discovered.entry, this.app.container);
|
|
228
|
+
}
|
|
229
|
+
return Object.keys(handlers).length > 0 ? handlers : undefined;
|
|
230
|
+
}
|
|
231
|
+
async discoverHandlers() {
|
|
232
|
+
try {
|
|
233
|
+
const path = this.app.makePath('.adonisjs/payments/webhook_handlers.js');
|
|
234
|
+
const mod = (await import(pathToFileURL(path).href));
|
|
235
|
+
if (mod.webhookHandlers)
|
|
236
|
+
return loadWebhookHandlersFromBarrel(mod.webhookHandlers);
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// No generated barrel — fall through to the runtime scan.
|
|
240
|
+
}
|
|
241
|
+
return discoverWebhookHandlers(this.app.makePath('app/payment_handlers'));
|
|
242
|
+
}
|
|
243
|
+
/** Mount the React SPA at `config.path` plus its hashed asset bundle. */
|
|
244
|
+
registerSpaRoutes(router, config, apiBase) {
|
|
245
|
+
const indexPath = config.path === '' ? '/' : config.path;
|
|
246
|
+
router
|
|
247
|
+
.get(indexPath, async (ctx) => {
|
|
248
|
+
if (!(await this.enforce(config, ctx, 'page')).allowed)
|
|
249
|
+
return;
|
|
250
|
+
let html;
|
|
251
|
+
try {
|
|
252
|
+
html = await readFile(resolvePath(spaDirectory(), 'index.html'), 'utf8');
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return ctx.response
|
|
256
|
+
.status(404)
|
|
257
|
+
.send('The payments dashboard SPA is not built. Run `pnpm --filter @adonis-agora/payments-dashboard build` (or the package build) to emit dist/spa.');
|
|
258
|
+
}
|
|
259
|
+
return ctx.response
|
|
260
|
+
.header('content-type', 'text/html; charset=utf-8')
|
|
261
|
+
.header('cache-control', 'no-store, must-revalidate')
|
|
262
|
+
.send(renderIndexHtml(html, config.path, apiBase, config.currency));
|
|
263
|
+
})
|
|
264
|
+
.as('payments_dashboard.index');
|
|
265
|
+
router
|
|
266
|
+
.get(`${config.path}/assets/:file`, async (ctx) => {
|
|
267
|
+
if (!(await this.enforce(config, ctx, 'page')).allowed)
|
|
268
|
+
return;
|
|
269
|
+
const file = basename(String(ctx.params.file));
|
|
270
|
+
const root = resolvePath(spaDirectory(), 'assets');
|
|
271
|
+
const assetPath = resolvePath(root, file);
|
|
272
|
+
if (!assetPath.startsWith(root))
|
|
273
|
+
return ctx.response.status(404).send('');
|
|
274
|
+
let bytes;
|
|
275
|
+
try {
|
|
276
|
+
bytes = await readFile(assetPath);
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
return ctx.response.status(404).send('');
|
|
280
|
+
}
|
|
281
|
+
return ctx.response
|
|
282
|
+
.header('content-type', contentTypeFor(file))
|
|
283
|
+
.header('cache-control', 'public, max-age=31536000, immutable')
|
|
284
|
+
.send(bytes);
|
|
285
|
+
})
|
|
286
|
+
.as('payments_dashboard.assets');
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Mount the built-in `dashboardAuth` endpoints under `basePath`. All are public (no guard): they
|
|
290
|
+
* create/destroy the session the {@link enforce} guard checks for.
|
|
291
|
+
*
|
|
292
|
+
* - `GET <base>/login` -> the server-rendered login page (Mode B only).
|
|
293
|
+
* - `POST <base>/login` -> verifies credentials via the host `login` hook and mints the cookie.
|
|
294
|
+
* - `POST <base>/session` -> Mode A: verifies the HOST APP's own auth (off the raw request) via
|
|
295
|
+
* the `session` hook and mints the cookie. `204` on success, uniform `401` on denial.
|
|
296
|
+
* - `GET <base>/logout` -> clears the cookie and redirects to the login page (Mode B) or the
|
|
297
|
+
* dashboard root (Mode A only).
|
|
298
|
+
*/
|
|
299
|
+
registerAuthRoutes(router, config, auth) {
|
|
300
|
+
const loginPath = `${config.path}/login`;
|
|
301
|
+
const sessionPath = `${config.path}/session`;
|
|
302
|
+
const logoutPath = `${config.path}/logout`;
|
|
303
|
+
if (auth.login) {
|
|
304
|
+
router
|
|
305
|
+
.get(loginPath, async (ctx) => {
|
|
306
|
+
ctx.response.header('content-type', 'text/html; charset=utf-8');
|
|
307
|
+
ctx.response.header('cache-control', 'no-store, must-revalidate');
|
|
308
|
+
return ctx.response.send(renderLoginPage(config.path));
|
|
309
|
+
})
|
|
310
|
+
.as('payments_dashboard.login.page');
|
|
311
|
+
router
|
|
312
|
+
.post(loginPath, async (ctx) => {
|
|
313
|
+
const outcome = await performLogin(auth, ctx.request.body(), config.path);
|
|
314
|
+
if (outcome.kind === 'bad-request') {
|
|
315
|
+
return ctx.response.status(400).json({ error: outcome.message });
|
|
316
|
+
}
|
|
317
|
+
if (outcome.kind === 'unauthorized') {
|
|
318
|
+
await this.warnHookThrow(outcome.hookError);
|
|
319
|
+
return ctx.response.status(401).json({ error: outcome.message });
|
|
320
|
+
}
|
|
321
|
+
this.writeSessionCookie(ctx, auth, outcome.cookieValue);
|
|
322
|
+
return ctx.response.status(200).json({ redirectTo: outcome.redirectTo });
|
|
323
|
+
})
|
|
324
|
+
.as('payments_dashboard.login.submit');
|
|
325
|
+
}
|
|
326
|
+
if (auth.session) {
|
|
327
|
+
router
|
|
328
|
+
.post(sessionPath, async (ctx) => {
|
|
329
|
+
const outcome = await performSession(auth, ctx.request.request);
|
|
330
|
+
if (outcome.kind === 'unauthorized') {
|
|
331
|
+
await this.warnHookThrow(outcome.hookError);
|
|
332
|
+
return ctx.response.status(401).json({ error: 'unauthorized' });
|
|
333
|
+
}
|
|
334
|
+
this.writeSessionCookie(ctx, auth, outcome.cookieValue);
|
|
335
|
+
return ctx.response.status(204).send('');
|
|
336
|
+
})
|
|
337
|
+
.as('payments_dashboard.session.mint');
|
|
338
|
+
}
|
|
339
|
+
router
|
|
340
|
+
.get(logoutPath, async (ctx) => {
|
|
341
|
+
ctx.response.clearCookie(SESSION_COOKIE_NAME, { path: '/' });
|
|
342
|
+
return ctx.response.redirect().toPath(auth.login ? loginPath : config.path || '/');
|
|
343
|
+
})
|
|
344
|
+
.as('payments_dashboard.logout');
|
|
345
|
+
}
|
|
346
|
+
async warnHookThrow(hookError) {
|
|
347
|
+
if (hookError === undefined || this.warnedOnHookThrow)
|
|
348
|
+
return;
|
|
349
|
+
this.warnedOnHookThrow = true;
|
|
350
|
+
const message = hookError instanceof Error ? hookError.message : String(hookError);
|
|
351
|
+
const logger = await this.app.container.make('logger');
|
|
352
|
+
logger.warn(`dashboardAuth login/session hook threw; treating as denial. ${message}`);
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Run the guards for a dashboard resource. Composes the `authorize` hook (bearer token/custom)
|
|
356
|
+
* with the optional `dashboardAuth` session guard — BOTH must pass:
|
|
357
|
+
*
|
|
358
|
+
* 1. `authorize` fails -> `403`.
|
|
359
|
+
* 2. `dashboardAuth` configured AND no valid session -> for a `page` request, redirect `302` to
|
|
360
|
+
* the login page (Mode B) carrying a sanitized `returnTo`, or a `401` notice (Mode A only);
|
|
361
|
+
* for an `api` request, `401 { error: 'unauthorized', auth: { modes } }`.
|
|
362
|
+
*
|
|
363
|
+
* Returns `allowed: false` (and has already written the response) when the request must
|
|
364
|
+
* short-circuit. On success it also carries WHO passed the gate: the session's user, which
|
|
365
|
+
* the write handlers record against a refund or a dispute resolution. `undefined` when this
|
|
366
|
+
* deployment configures no `dashboardAuth` — there is genuinely nobody to name, and inventing
|
|
367
|
+
* one would be worse than an honest "unattributed".
|
|
368
|
+
*/
|
|
369
|
+
async enforce(config, ctx, mode) {
|
|
370
|
+
const allowed = await config.authorize(ctx);
|
|
371
|
+
if (!allowed) {
|
|
372
|
+
if (!ctx.response.getHeader('location')) {
|
|
373
|
+
ctx.response.status(403).json({ error: 'forbidden' });
|
|
374
|
+
}
|
|
375
|
+
return { allowed: false };
|
|
376
|
+
}
|
|
377
|
+
const auth = config.dashboardAuth;
|
|
378
|
+
if (!auth)
|
|
379
|
+
return { allowed: true };
|
|
380
|
+
const session = readSession(auth, this.readSessionCookie(ctx));
|
|
381
|
+
// The display name when the host supplied one, the stable id otherwise. `sub` alone is
|
|
382
|
+
// unambiguous but often unreadable; a name alone is readable and not unique.
|
|
383
|
+
if (session) {
|
|
384
|
+
return {
|
|
385
|
+
allowed: true,
|
|
386
|
+
actor: session.name !== undefined ? `${session.name} <${session.sub}>` : session.sub,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
if (mode === 'page') {
|
|
390
|
+
if (auth.login) {
|
|
391
|
+
const returnTo = ctx.request.url(true);
|
|
392
|
+
ctx.response.redirect().withQs('returnTo', returnTo).toPath(`${config.path}/login`);
|
|
393
|
+
return { allowed: false };
|
|
394
|
+
}
|
|
395
|
+
// Mode A only: there's no login page to send the browser to — this deployment expects the
|
|
396
|
+
// host app to mint a session via `POST <path>/session` before ever navigating here.
|
|
397
|
+
ctx.response
|
|
398
|
+
.status(401)
|
|
399
|
+
.header('content-type', 'text/html; charset=utf-8')
|
|
400
|
+
.header('cache-control', 'no-store, must-revalidate')
|
|
401
|
+
.send('<!doctype html><html><body style="font-family:ui-monospace,monospace;background:#09090b;color:#e7e7ea;display:flex;align-items:center;justify-content:center;height:100vh;margin:0"><p>Open this console from your application.</p></body></html>');
|
|
402
|
+
return { allowed: false };
|
|
403
|
+
}
|
|
404
|
+
ctx.response.status(401).json({ error: 'unauthorized', auth: { modes: auth.modes } });
|
|
405
|
+
return { allowed: false };
|
|
406
|
+
}
|
|
407
|
+
readSessionCookie(ctx) {
|
|
408
|
+
const value = ctx.request.plainCookie(SESSION_COOKIE_NAME, undefined, false);
|
|
409
|
+
return typeof value === 'string' && value !== '' ? value : undefined;
|
|
410
|
+
}
|
|
411
|
+
writeSessionCookie(ctx, auth, value) {
|
|
412
|
+
ctx.response.plainCookie(SESSION_COOKIE_NAME, value, {
|
|
413
|
+
httpOnly: true,
|
|
414
|
+
sameSite: 'lax',
|
|
415
|
+
secure: ctx.request.secure(),
|
|
416
|
+
path: '/',
|
|
417
|
+
maxAge: Math.floor(auth.ttlMs / 1000),
|
|
418
|
+
encode: false,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/** Adapt an AdonisJS `HttpContext` to the framework-light {@link ApiRequest}. */
|
|
423
|
+
function toApiRequest(ctx) {
|
|
424
|
+
return {
|
|
425
|
+
params: ctx.params,
|
|
426
|
+
query: ctx.request.qs(),
|
|
427
|
+
body: ctx.request.body(),
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
//# sourceMappingURL=dashboard_provider.js.map
|