@volter/twin-stripe 2.0.38 → 3.0.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/README.md +82 -207
- package/dist/generated/pack-facts.json +94 -0
- package/dist/spec/SOURCE.md +14 -0
- package/dist/spec/fixtures3.json.gz +0 -0
- package/dist/spec/openapi.json.gz +0 -0
- package/dist/spec/patches.json +34 -0
- package/dist/spec/sources.json +378 -0
- package/dist/src/cli.js +12 -30
- package/dist/src/engine/after-payment.d.ts +9 -0
- package/dist/src/engine/after-payment.js +28 -0
- package/dist/src/engine/apps-secrets.d.ts +1 -0
- package/dist/src/engine/apps-secrets.js +3 -0
- package/dist/src/engine/balance.d.ts +5 -0
- package/dist/src/engine/balance.js +15 -0
- package/dist/src/engine/billing.d.ts +5 -0
- package/dist/src/engine/billing.js +6 -0
- package/dist/src/engine/charges.d.ts +6 -0
- package/dist/src/engine/charges.js +13 -0
- package/dist/src/engine/checkout.d.ts +24 -0
- package/dist/src/engine/checkout.js +30 -0
- package/dist/src/engine/common.d.ts +19 -0
- package/dist/src/engine/common.js +32 -0
- package/dist/src/{screens → engine}/connect-settings.d.ts +1 -7
- package/dist/src/engine/connect-settings.js +26 -0
- package/dist/src/engine/connect.d.ts +66 -0
- package/dist/src/engine/connect.js +189 -0
- package/dist/src/engine/coupons.d.ts +0 -0
- package/dist/src/engine/coupons.js +5 -0
- package/dist/src/engine/credit-notes.d.ts +1 -0
- package/dist/src/engine/credit-notes.js +5 -0
- package/dist/src/engine/customers.d.ts +12 -0
- package/dist/src/engine/customers.js +30 -0
- package/dist/src/engine/display.d.ts +14 -0
- package/dist/src/engine/display.js +16 -0
- package/dist/src/engine/disputes.d.ts +0 -0
- package/dist/src/engine/disputes.js +9 -0
- package/dist/src/engine/entitlements.d.ts +1 -0
- package/dist/src/engine/entitlements.js +4 -0
- package/dist/src/engine/ephemeral-keys.d.ts +0 -0
- package/dist/src/engine/ephemeral-keys.js +4 -0
- package/dist/src/engine/files.d.ts +18 -0
- package/dist/src/engine/files.js +33 -0
- package/dist/src/engine/front.d.ts +5 -0
- package/dist/src/engine/front.js +25 -0
- package/dist/src/engine/invoices.d.ts +24 -0
- package/dist/src/engine/invoices.js +57 -0
- package/dist/src/engine/issuing.d.ts +35 -0
- package/dist/src/engine/issuing.js +45 -0
- package/dist/src/engine/ledger.d.ts +9 -0
- package/dist/src/engine/ledger.js +9 -0
- package/dist/src/engine/oauth.d.ts +4 -0
- package/dist/src/engine/oauth.js +25 -0
- package/dist/src/engine/onboarding.d.ts +13 -0
- package/dist/src/engine/onboarding.js +42 -0
- package/dist/src/{stripe-params.d.ts → engine/params.d.ts} +20 -3
- package/dist/src/{stripe-params.js → engine/params.js} +7 -60
- package/dist/src/engine/payment-intents.d.ts +10 -0
- package/dist/src/engine/payment-intents.js +27 -0
- package/dist/src/engine/payment-links.d.ts +1 -0
- package/dist/src/engine/payment-links.js +5 -0
- package/dist/src/engine/payment-methods.d.ts +16 -0
- package/dist/src/engine/payment-methods.js +22 -0
- package/dist/src/engine/plans.d.ts +6 -0
- package/dist/src/engine/plans.js +26 -0
- package/dist/src/engine/platform.d.ts +39 -0
- package/dist/src/engine/platform.js +34 -0
- package/dist/src/engine/products.d.ts +0 -0
- package/dist/src/engine/products.js +4 -0
- package/dist/src/engine/radar.d.ts +2 -0
- package/dist/src/engine/radar.js +5 -0
- package/dist/src/engine/refunds.d.ts +1 -0
- package/dist/src/engine/refunds.js +28 -0
- package/dist/src/engine/renewals.d.ts +36 -0
- package/dist/src/engine/renewals.js +65 -0
- package/dist/src/engine/setup-intents.d.ts +1 -0
- package/dist/src/engine/setup-intents.js +4 -0
- package/dist/src/{stripe-twin.d.ts → engine/stripe.d.ts} +11 -39
- package/dist/src/{stripe-twin.js → engine/stripe.js} +25 -310
- package/dist/src/engine/subscription-schedules.d.ts +3 -0
- package/dist/src/engine/subscription-schedules.js +1 -0
- package/dist/src/engine/subscriptions.d.ts +12 -0
- package/dist/src/engine/subscriptions.js +31 -0
- package/dist/src/engine/tax.d.ts +7 -0
- package/dist/src/engine/tax.js +28 -0
- package/dist/src/engine/terminal.d.ts +4 -0
- package/dist/src/engine/terminal.js +20 -0
- package/dist/src/engine/test-clocks.d.ts +5 -0
- package/dist/src/engine/test-clocks.js +9 -0
- package/dist/src/engine/tokens.d.ts +7 -0
- package/dist/src/engine/tokens.js +20 -0
- package/dist/src/engine/transfers.d.ts +2 -0
- package/dist/src/engine/transfers.js +1 -0
- package/dist/src/engine/treasury.d.ts +23 -0
- package/dist/src/engine/treasury.js +45 -0
- package/dist/src/{stripe-version.js → engine/version.js} +4 -3
- package/dist/src/engine/webhook-endpoints.d.ts +1 -0
- package/dist/src/engine/webhook-endpoints.js +12 -0
- package/dist/src/engine/webhooks.d.ts +31 -0
- package/dist/src/engine/webhooks.js +68 -0
- package/dist/src/fetch.d.ts +2 -0
- package/dist/src/fetch.js +26 -0
- package/dist/src/generated/surface.gen.json +1 -1
- package/dist/src/generated/ui.gen.json +1 -1
- package/dist/src/index.d.ts +5 -14
- package/dist/src/index.js +8 -75
- package/dist/src/manifest.js +232 -758
- package/dist/src/screens/billing-portal.d.ts +3 -0
- package/dist/src/screens/{portal.js → billing-portal.js} +28 -54
- package/dist/src/screens/checkout.d.ts +2 -31
- package/dist/src/screens/checkout.js +32 -121
- package/dist/src/screens/connect-oauth.d.ts +3 -27
- package/dist/src/screens/connect-oauth.js +51 -132
- package/dist/src/screens/connect-onboarding.d.ts +3 -0
- package/dist/src/screens/{onboarding.js → connect-onboarding.js} +36 -102
- package/dist/src/screens/dashboard.d.ts +3 -0
- package/dist/src/screens/dashboard.js +117 -0
- package/dist/src/screens/shared.d.ts +27 -0
- package/dist/src/screens/shared.js +87 -0
- package/dist/src/screens/stripe-js.d.ts +3 -0
- package/dist/src/screens/stripe-js.js +135 -0
- package/dist/src/semantics/account_links.d.ts +2 -0
- package/dist/src/semantics/account_links.js +22 -0
- package/dist/src/semantics/accounts.d.ts +4 -0
- package/dist/src/semantics/accounts.js +38 -0
- package/dist/src/semantics/apps.d.ts +4 -0
- package/dist/src/semantics/{apps-secrets.js → apps.js} +14 -26
- package/dist/src/semantics/around.d.ts +2 -0
- package/dist/src/semantics/around.js +16 -0
- package/dist/src/semantics/balance.d.ts +2 -11
- package/dist/src/semantics/balance.js +3 -194
- package/dist/src/semantics/balance_transactions.d.ts +2 -0
- package/dist/src/semantics/balance_transactions.js +15 -0
- package/dist/src/semantics/billing_portal.d.ts +5 -0
- package/dist/src/semantics/billing_portal.js +42 -0
- package/dist/src/semantics/charges.d.ts +3 -28
- package/dist/src/semantics/charges.js +5 -202
- package/dist/src/semantics/checkout.d.ts +3 -20
- package/dist/src/semantics/checkout.js +8 -261
- package/dist/src/semantics/clock.d.ts +2 -0
- package/dist/src/semantics/clock.js +21 -0
- package/dist/src/semantics/coupons.d.ts +2 -6
- package/dist/src/semantics/coupons.js +2 -60
- package/dist/src/semantics/customers.d.ts +6 -6
- package/dist/src/semantics/customers.js +27 -387
- package/dist/src/semantics/disputes.d.ts +2 -2
- package/dist/src/semantics/disputes.js +3 -49
- package/dist/src/semantics/doors.d.ts +23 -0
- package/dist/src/semantics/doors.js +73 -0
- package/dist/src/semantics/entitlements.d.ts +2 -2
- package/dist/src/semantics/entitlements.js +5 -50
- package/dist/src/semantics/events.d.ts +12 -0
- package/dist/src/semantics/events.js +36 -0
- package/dist/src/semantics/index.d.ts +2 -0
- package/dist/src/semantics/index.js +107 -0
- package/dist/src/semantics/invoice_payments.d.ts +2 -0
- package/dist/src/semantics/invoice_payments.js +12 -0
- package/dist/src/semantics/invoiceitems.d.ts +3 -0
- package/dist/src/semantics/invoiceitems.js +59 -0
- package/dist/src/semantics/invoices.d.ts +16 -18
- package/dist/src/semantics/invoices.js +118 -444
- package/dist/src/semantics/payment_intents.d.ts +4 -0
- package/dist/src/semantics/payment_intents.js +79 -0
- package/dist/src/semantics/payment_methods.d.ts +5 -0
- package/dist/src/semantics/payment_methods.js +39 -0
- package/dist/src/semantics/payouts.d.ts +2 -0
- package/dist/src/semantics/payouts.js +21 -0
- package/dist/src/semantics/prices.d.ts +4 -0
- package/dist/src/semantics/prices.js +37 -0
- package/dist/src/semantics/products.d.ts +6 -2
- package/dist/src/semantics/products.js +15 -130
- package/dist/src/semantics/promotion_codes.d.ts +3 -0
- package/dist/src/semantics/promotion_codes.js +35 -0
- package/dist/src/semantics/radar.d.ts +2 -2
- package/dist/src/semantics/radar.js +5 -74
- package/dist/src/semantics/refunds.d.ts +3 -9
- package/dist/src/semantics/refunds.js +6 -192
- package/dist/src/semantics/setup_intents.d.ts +3 -0
- package/dist/src/semantics/{setup-intents.js → setup_intents.js} +10 -53
- package/dist/src/semantics/shared.d.ts +822 -47
- package/dist/src/semantics/shared.js +3044 -78
- package/dist/src/semantics/states.d.ts +531 -0
- package/dist/src/semantics/states.js +852 -0
- package/dist/src/semantics/subscription_items.d.ts +5 -0
- package/dist/src/semantics/subscription_items.js +55 -0
- package/dist/src/semantics/subscription_schedules.d.ts +4 -0
- package/dist/src/semantics/subscription_schedules.js +60 -0
- package/dist/src/semantics/subscriptions.d.ts +7 -11
- package/dist/src/semantics/subscriptions.js +44 -415
- package/dist/src/semantics/test_helpers.d.ts +6 -0
- package/dist/src/semantics/{test-clocks.js → test_helpers.js} +14 -40
- package/dist/src/semantics/transfers.d.ts +2 -2
- package/dist/src/semantics/transfers.js +6 -101
- package/dist/src/server.d.ts +7 -0
- package/dist/src/server.js +7 -0
- package/generated/pack-facts.json +94 -0
- package/package.json +10 -20
- package/spec/SOURCE.md +14 -0
- package/spec/fixtures3.json.gz +0 -0
- package/spec/openapi.json.gz +0 -0
- package/spec/patches.json +34 -0
- package/spec/sources.json +378 -0
- package/src/cli.ts +12 -26
- package/src/engine/after-payment.ts +31 -0
- package/src/engine/apps-secrets.ts +4 -0
- package/src/engine/balance.ts +33 -0
- package/src/engine/billing.ts +16 -0
- package/src/engine/charges.ts +19 -0
- package/src/engine/checkout.ts +40 -0
- package/src/engine/common.ts +47 -0
- package/src/engine/connect-settings.ts +23 -0
- package/src/engine/connect.ts +184 -0
- package/src/engine/coupons.ts +5 -0
- package/src/engine/credit-notes.ts +6 -0
- package/src/engine/customers.ts +35 -0
- package/src/engine/display.ts +19 -0
- package/src/engine/disputes.ts +9 -0
- package/src/engine/entitlements.ts +5 -0
- package/src/engine/ephemeral-keys.ts +3 -0
- package/src/engine/files.ts +41 -0
- package/src/engine/front.ts +31 -0
- package/src/engine/invoices.ts +71 -0
- package/src/engine/issuing.ts +61 -0
- package/src/engine/ledger.ts +50 -0
- package/src/engine/oauth.ts +19 -0
- package/src/engine/onboarding.ts +54 -0
- package/src/{stripe-params.ts → engine/params.ts} +8 -56
- package/src/engine/payment-intents.ts +33 -0
- package/src/engine/payment-links.ts +6 -0
- package/src/engine/payment-methods.ts +25 -0
- package/src/engine/plans.ts +42 -0
- package/src/engine/platform.ts +47 -0
- package/src/engine/products.ts +3 -0
- package/src/engine/radar.ts +6 -0
- package/src/engine/refunds.ts +29 -0
- package/src/engine/renewals.ts +73 -0
- package/src/engine/setup-intents.ts +5 -0
- package/src/{stripe-twin.ts → engine/stripe.ts} +28 -323
- package/src/engine/subscription-schedules.ts +10 -0
- package/src/engine/subscriptions.ts +40 -0
- package/src/engine/tax.ts +39 -0
- package/src/engine/terminal.ts +23 -0
- package/src/engine/test-clocks.ts +11 -0
- package/src/engine/tokens.ts +22 -0
- package/src/engine/transfers.ts +23 -0
- package/src/engine/treasury.ts +47 -0
- package/src/{stripe-version.ts → engine/version.ts} +4 -3
- package/src/engine/webhook-endpoints.ts +14 -0
- package/src/engine/webhooks.ts +72 -0
- package/src/fetch.ts +27 -0
- package/src/generated/surface.gen.json +1 -1
- package/src/generated/ui.gen.json +1 -1
- package/src/index.ts +8 -116
- package/src/manifest.ts +234 -786
- package/src/screens/{portal.tsx → billing-portal.tsx} +32 -37
- package/src/screens/checkout.tsx +33 -95
- package/src/screens/connect-oauth.tsx +64 -88
- package/src/screens/{onboarding.tsx → connect-onboarding.tsx} +38 -82
- package/src/screens/dashboard.tsx +198 -0
- package/src/screens/shared.tsx +84 -0
- package/src/screens/stripe-js.ts +170 -0
- package/src/semantics/account_links.ts +21 -0
- package/src/semantics/accounts.ts +43 -0
- package/src/semantics/{apps-secrets.ts → apps.ts} +17 -32
- package/src/semantics/around.ts +20 -0
- package/src/semantics/balance.ts +6 -207
- package/src/semantics/balance_transactions.ts +20 -0
- package/src/semantics/billing_portal.ts +51 -0
- package/src/semantics/charges.ts +12 -211
- package/src/semantics/checkout.ts +13 -268
- package/src/semantics/clock.ts +26 -0
- package/src/semantics/coupons.ts +5 -67
- package/src/semantics/customers.ts +29 -392
- package/src/semantics/disputes.ts +9 -59
- package/src/semantics/doors.ts +80 -0
- package/src/semantics/entitlements.ts +9 -56
- package/src/semantics/events.ts +44 -0
- package/src/semantics/index.ts +111 -0
- package/src/semantics/invoice_payments.ts +18 -0
- package/src/semantics/invoiceitems.ts +59 -0
- package/src/semantics/invoices.ts +109 -443
- package/src/semantics/payment_intents.ts +83 -0
- package/src/semantics/payment_methods.ts +43 -0
- package/src/semantics/payouts.ts +23 -0
- package/src/semantics/prices.ts +42 -0
- package/src/semantics/products.ts +18 -142
- package/src/semantics/promotion_codes.ts +39 -0
- package/src/semantics/radar.ts +8 -77
- package/src/semantics/refunds.ts +9 -212
- package/src/semantics/setup_intents.ts +45 -0
- package/src/semantics/shared.ts +3092 -106
- package/src/semantics/states.ts +883 -0
- package/src/semantics/subscription_items.ts +54 -0
- package/src/semantics/subscription_schedules.ts +66 -0
- package/src/semantics/subscriptions.ts +49 -432
- package/src/semantics/{test-clocks.ts → test_helpers.ts} +23 -47
- package/src/semantics/transfers.ts +11 -131
- package/src/server.ts +8 -0
- package/client/dashboard-api.ts +0 -286
- package/client/stripe-mirror.css +0 -277
- package/client/stripe-mirror.tsx +0 -1559
- package/dist/client/dashboard-api.d.ts +0 -107
- package/dist/client/dashboard-api.js +0 -238
- package/dist/client/dashboard-api.ts +0 -286
- package/dist/client/stripe-mirror.bundle.js +0 -236
- package/dist/client/stripe-mirror.css +0 -277
- package/dist/client/stripe-mirror.d.ts +0 -134
- package/dist/client/stripe-mirror.js +0 -859
- package/dist/client/stripe-mirror.tsx +0 -1559
- package/dist/src/screens/connect-settings.js +0 -103
- package/dist/src/screens/consent-skin.d.ts +0 -4
- package/dist/src/screens/consent-skin.js +0 -18
- package/dist/src/screens/financial-connections.d.ts +0 -5
- package/dist/src/screens/financial-connections.js +0 -90
- package/dist/src/screens/identity.d.ts +0 -5
- package/dist/src/screens/identity.js +0 -86
- package/dist/src/screens/onboarding.d.ts +0 -13
- package/dist/src/screens/portal.d.ts +0 -5
- package/dist/src/screens/public-details.d.ts +0 -5
- package/dist/src/screens/public-details.js +0 -90
- package/dist/src/semantics/after-payment.d.ts +0 -22
- package/dist/src/semantics/after-payment.js +0 -99
- package/dist/src/semantics/apps-secrets.d.ts +0 -2
- package/dist/src/semantics/billing.d.ts +0 -2
- package/dist/src/semantics/billing.js +0 -220
- package/dist/src/semantics/connect.d.ts +0 -5
- package/dist/src/semantics/connect.js +0 -493
- package/dist/src/semantics/credit-notes.d.ts +0 -2
- package/dist/src/semantics/credit-notes.js +0 -172
- package/dist/src/semantics/ephemeral-keys.d.ts +0 -2
- package/dist/src/semantics/ephemeral-keys.js +0 -34
- package/dist/src/semantics/files.d.ts +0 -2
- package/dist/src/semantics/files.js +0 -125
- package/dist/src/semantics/issuing.d.ts +0 -13
- package/dist/src/semantics/issuing.js +0 -575
- package/dist/src/semantics/ledger.d.ts +0 -59
- package/dist/src/semantics/ledger.js +0 -200
- package/dist/src/semantics/payment-intents.d.ts +0 -18
- package/dist/src/semantics/payment-intents.js +0 -452
- package/dist/src/semantics/payment-links.d.ts +0 -2
- package/dist/src/semantics/payment-links.js +0 -133
- package/dist/src/semantics/payment-methods.d.ts +0 -20
- package/dist/src/semantics/payment-methods.js +0 -140
- package/dist/src/semantics/plans.d.ts +0 -5
- package/dist/src/semantics/plans.js +0 -121
- package/dist/src/semantics/platform.d.ts +0 -9
- package/dist/src/semantics/platform.js +0 -206
- package/dist/src/semantics/renewals.d.ts +0 -47
- package/dist/src/semantics/renewals.js +0 -281
- package/dist/src/semantics/setup-intents.d.ts +0 -2
- package/dist/src/semantics/subscription-schedules.d.ts +0 -2
- package/dist/src/semantics/subscription-schedules.js +0 -119
- package/dist/src/semantics/tax.d.ts +0 -2
- package/dist/src/semantics/tax.js +0 -197
- package/dist/src/semantics/terminal.d.ts +0 -5
- package/dist/src/semantics/terminal.js +0 -182
- package/dist/src/semantics/test-clocks.d.ts +0 -6
- package/dist/src/semantics/tokens.d.ts +0 -4
- package/dist/src/semantics/tokens.js +0 -44
- package/dist/src/semantics/treasury.d.ts +0 -2
- package/dist/src/semantics/treasury.js +0 -377
- package/dist/src/semantics/webhook-endpoints.d.ts +0 -3
- package/dist/src/semantics/webhook-endpoints.js +0 -85
- package/dist/src/stripe-budget.d.ts +0 -55
- package/dist/src/stripe-budget.js +0 -155
- package/dist/src/stripe-capabilities.d.ts +0 -3
- package/dist/src/stripe-capabilities.js +0 -5836
- package/dist/src/stripe-conformance.d.ts +0 -43
- package/dist/src/stripe-conformance.js +0 -105
- package/dist/src/stripe-connector.d.ts +0 -161
- package/dist/src/stripe-connector.js +0 -414
- package/dist/src/stripe-emit.d.ts +0 -2
- package/dist/src/stripe-emit.js +0 -145
- package/dist/src/stripe-events.d.ts +0 -93
- package/dist/src/stripe-events.js +0 -392
- package/dist/src/stripe-js.d.ts +0 -4
- package/dist/src/stripe-js.js +0 -70
- package/dist/src/stripe-mirror-ui.d.ts +0 -15
- package/dist/src/stripe-mirror-ui.js +0 -87
- package/dist/src/stripe-perform-harness.d.ts +0 -9
- package/dist/src/stripe-perform-harness.js +0 -26
- package/dist/src/stripe-server.d.ts +0 -33
- package/dist/src/stripe-server.js +0 -429
- package/dist/src/stripe-shared.d.ts +0 -109
- package/dist/src/stripe-shared.js +0 -276
- package/dist/src/stripe-ui-conformance.d.ts +0 -5
- package/dist/src/stripe-ui-conformance.js +0 -79
- package/dist/src/stripe-ui-structure.d.ts +0 -3
- package/dist/src/stripe-ui-structure.js +0 -168
- package/dist/test-fixtures/stripe-known-deviations.json +0 -110
- package/dist/test-fixtures/stripe-openapi-operations.SOURCE.md +0 -14
- package/dist/test-fixtures/stripe-openapi-operations.json +0 -4717
- package/dist/test-fixtures/stripe-schemas.SOURCE.md +0 -35
- package/dist/test-fixtures/stripe-schemas.json +0 -3813
- package/src/screens/connect-settings.tsx +0 -121
- package/src/screens/consent-skin.ts +0 -20
- package/src/screens/financial-connections.tsx +0 -101
- package/src/screens/identity.tsx +0 -96
- package/src/screens/public-details.tsx +0 -105
- package/src/semantics/after-payment.ts +0 -118
- package/src/semantics/billing.ts +0 -216
- package/src/semantics/connect.ts +0 -487
- package/src/semantics/credit-notes.ts +0 -168
- package/src/semantics/ephemeral-keys.ts +0 -34
- package/src/semantics/files.ts +0 -143
- package/src/semantics/issuing.ts +0 -590
- package/src/semantics/ledger.ts +0 -253
- package/src/semantics/payment-intents.ts +0 -463
- package/src/semantics/payment-links.ts +0 -148
- package/src/semantics/payment-methods.ts +0 -145
- package/src/semantics/plans.ts +0 -131
- package/src/semantics/platform.ts +0 -220
- package/src/semantics/renewals.ts +0 -279
- package/src/semantics/setup-intents.ts +0 -87
- package/src/semantics/subscription-schedules.ts +0 -129
- package/src/semantics/tax.ts +0 -220
- package/src/semantics/terminal.ts +0 -195
- package/src/semantics/tokens.ts +0 -52
- package/src/semantics/treasury.ts +0 -383
- package/src/semantics/webhook-endpoints.ts +0 -87
- package/src/stripe-budget.ts +0 -181
- package/src/stripe-capabilities.ts +0 -5651
- package/src/stripe-conformance.ts +0 -124
- package/src/stripe-connector.ts +0 -462
- package/src/stripe-emit.ts +0 -157
- package/src/stripe-events.ts +0 -434
- package/src/stripe-js.ts +0 -70
- package/src/stripe-mirror-ui.ts +0 -90
- package/src/stripe-perform-harness.ts +0 -29
- package/src/stripe-server.ts +0 -393
- package/src/stripe-shared.ts +0 -297
- package/src/stripe-ui-conformance.ts +0 -88
- package/src/stripe-ui-structure.ts +0 -182
- package/test-fixtures/stripe-known-deviations.json +0 -110
- package/test-fixtures/stripe-openapi-operations.json +0 -4717
- package/test-fixtures/stripe-schemas.json +0 -3813
- /package/dist/src/{screens → engine}/industries.d.ts +0 -0
- /package/dist/src/{screens → engine}/industries.js +0 -0
- /package/dist/src/{semantics → engine}/test-cards.d.ts +0 -0
- /package/dist/src/{semantics → engine}/test-cards.js +0 -0
- /package/dist/src/{stripe-version.d.ts → engine/version.d.ts} +0 -0
- /package/src/{screens → engine}/industries.ts +0 -0
- /package/src/{semantics → engine}/test-cards.ts +0 -0
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
// A subscription's invoices over time: the first one Checkout pays, and each renewal. Time's moves are made
|
|
2
|
-
// as the twin's trial end already made them: caught up to the customer's clock (the World's, or its test
|
|
3
|
-
// clock) whenever the twin is asked anything (../stripe-server.ts), each checked against the machines in
|
|
4
|
-
// ../manifest.ts.
|
|
5
|
-
//
|
|
6
|
-
// - Checkout's subscription session pays the first invoice with the card the customer entered, which becomes the
|
|
7
|
-
// subscription's default payment method (docs.stripe.com/payments/checkout/how-checkout-works); nothing is due
|
|
8
|
-
// on a trial's.
|
|
9
|
-
// - At each period's end Stripe drafts a `subscription_cycle` invoice for the next period and advances the
|
|
10
|
-
// period; a trial's end is the first such renewal, and the subscription is active from then
|
|
11
|
-
// (docs.stripe.com/billing/subscriptions/overview#how-payments-work-subscriptions,
|
|
12
|
-
// docs.stripe.com/billing/subscriptions/trials).
|
|
13
|
-
// - About an hour after, Stripe finalizes the draft and charges the subscription's default payment method,
|
|
14
|
-
// else the customer's (docs.stripe.com/invoicing/integration/workflow-transitions#finalized,
|
|
15
|
-
// docs.stripe.com/api/subscriptions/object#subscription_object-default_payment_method).
|
|
16
|
-
// - A declined renewal leaves the invoice open with its attempt counted and the subscription past_due
|
|
17
|
-
// (docs.stripe.com/billing/subscriptions/overview#payment-status); paying the invoice later, through the API
|
|
18
|
-
// (docs.stripe.com/api/invoices/pay) or on the customer portal (docs.stripe.com/customer-management), makes it
|
|
19
|
-
// active again.
|
|
20
|
-
//
|
|
21
|
-
// Where the documentation stops and the twin decides: Stripe retries a failed renewal on its Smart Retries
|
|
22
|
-
// schedule and may then cancel or mark the subscription unpaid; the twin makes no retry (next_payment_attempt
|
|
23
|
-
// is null) and leaves it past_due until the invoice is paid. A paused subscription (pause_collection) is not renewed.
|
|
24
|
-
// - One set to cancel at its period's end is canceled when the period ends, with no renewal
|
|
25
|
-
// (docs.stripe.com/billing/subscriptions/cancel#cancel-at-end-of-cycle).
|
|
26
|
-
import { runAsVendorMove } from '@volter/world-core';
|
|
27
|
-
import { addBillingInterval, applyCouponDiscount, declineFor, mintClientSecret, priceAmount, resolveSubscriptionBillingInterval } from "../stripe-twin.js";
|
|
28
|
-
import { finalizedFields } from "./invoices.js";
|
|
29
|
-
import { mintCharge } from "./payment-intents.js";
|
|
30
|
-
import { at_, created, finder } from "./shared.js";
|
|
31
|
-
const SUB = 'subscription';
|
|
32
|
-
const INV = 'invoice';
|
|
33
|
-
const HOUR = 3600;
|
|
34
|
-
/** The statuses a subscription renews in. */
|
|
35
|
-
const RENEWING = ['trialing', 'active', 'past_due'];
|
|
36
|
-
/** A customer's now: its test clock's frozen time when it is on one (docs.stripe.com/billing/testing/test-clocks), the
|
|
37
|
-
* World's otherwise. */
|
|
38
|
-
export function clockNow(ctx, customer) {
|
|
39
|
-
const clock = typeof customer === 'string' ? ctx.get('customer', customer)?.test_clock : undefined;
|
|
40
|
-
const frozen = typeof clock === 'string' ? ctx.get('test_helpers.test_clock', clock)?.frozen_time : undefined;
|
|
41
|
-
return frozen !== undefined ? Number(frozen) : Number(ctx.now());
|
|
42
|
-
}
|
|
43
|
-
/** What a subscription's invoices charge: its own default payment method, else its customer's. */
|
|
44
|
-
export function payerOf(ctx, sub) {
|
|
45
|
-
return typeof sub.default_payment_method === 'string' && sub.default_payment_method ? sub.default_payment_method : customerDefault(ctx, sub);
|
|
46
|
-
}
|
|
47
|
-
/** The customer's default for invoices, charged when the subscription names none of its own. */
|
|
48
|
-
function customerDefault(ctx, sub) {
|
|
49
|
-
const customer = typeof sub.customer === 'string' ? ctx.get('customer', sub.customer) : undefined;
|
|
50
|
-
const settings = customer?.invoice_settings;
|
|
51
|
-
return typeof settings?.default_payment_method === 'string' && settings.default_payment_method ? settings.default_payment_method : undefined;
|
|
52
|
-
}
|
|
53
|
-
const itemsOf = (sub) => (sub.items?.data) ?? [];
|
|
54
|
-
/** An item's quantity as it bills: 1 when none is set, and 0 when 0 is (a quantity of 0 bills nothing). */
|
|
55
|
-
export function itemQuantity(it) {
|
|
56
|
-
return it.quantity === undefined || it.quantity === null ? 1 : Math.max(0, Math.trunc(Number(it.quantity) || 0));
|
|
57
|
-
}
|
|
58
|
-
/** The subscription's discounts with a `once` coupon's spent: "When a subscription uses a coupon with `duration=once`, the
|
|
59
|
-
* coupon is considered used after the invoice finalizes and is removed from the subscription's `discounts` array"
|
|
60
|
-
* (docs.stripe.com/billing/subscriptions/coupons#coupon-duration). */
|
|
61
|
-
export function unspentDiscounts(discounts) {
|
|
62
|
-
return (discounts ?? []).filter((d) => !(d && typeof d === 'object' && d.coupon?.duration === 'once'));
|
|
63
|
-
}
|
|
64
|
-
/** The coupon ids an invoice's discounts name. */
|
|
65
|
-
const couponsOf = (discounts) => new Set((discounts ?? []).map((d) => {
|
|
66
|
-
const c = d && typeof d === 'object' ? d.coupon : undefined;
|
|
67
|
-
return typeof c === 'string' ? c : c && typeof c === 'object' ? String(c.id ?? '') : '';
|
|
68
|
-
}).filter(Boolean));
|
|
69
|
-
/** A subscription whose invoice has just been finalized: the `once` coupon that invoice took is spent, and a once coupon
|
|
70
|
-
* it did not take (added after it was drafted) stays for the next (unspentDiscounts). */
|
|
71
|
-
export async function spendOnceCoupon(ctx, invoice) {
|
|
72
|
-
const subId = typeof invoice.subscription === 'string' ? invoice.subscription : undefined;
|
|
73
|
-
const sub = subId ? ctx.get(SUB, subId) : undefined;
|
|
74
|
-
if (!sub || !subId)
|
|
75
|
-
return;
|
|
76
|
-
const all = sub.discounts ?? [];
|
|
77
|
-
const kept = keptAfter(all, invoice);
|
|
78
|
-
if (kept.length !== all.length)
|
|
79
|
-
await ctx.write(SUB, subId, { discounts: kept }, 'subscription.discount_spent');
|
|
80
|
-
}
|
|
81
|
-
/** A subscription's discounts after `invoice` finalizes: less the `once` coupons it took. */
|
|
82
|
-
export function keptAfter(discounts, invoice) {
|
|
83
|
-
const taken = couponsOf(invoice.discounts);
|
|
84
|
-
return discounts.filter((d) => !(unspentDiscounts([d]).length === 0 && [...couponsOf([d])].some((c) => taken.has(c))));
|
|
85
|
-
}
|
|
86
|
-
/** A draft invoice for one period of a subscription, its lines the subscription's items and any one-time lines `once`
|
|
87
|
-
* gives it (a Checkout session's one-time prices, "on the initial invoice only", docs.stripe.com/api/checkout/sessions/
|
|
88
|
-
* create#create_checkout_session-line_items), under the subscription's discount. Answers the invoice id. */
|
|
89
|
-
export async function draftSubscriptionInvoice(ctx, sub, o) {
|
|
90
|
-
const id = o.id ?? ctx.mint(INV);
|
|
91
|
-
const currency = String(sub.currency ?? 'usd');
|
|
92
|
-
const lines = itemsOf(sub).map((it, i) => {
|
|
93
|
-
const price = it.price && typeof it.price === 'object' ? it.price : ctx.get('price', String(it.price ?? ''));
|
|
94
|
-
const quantity = itemQuantity(it);
|
|
95
|
-
return {
|
|
96
|
-
id: `il_twin_${id}_${i + 1}`, object: 'line_item', type: 'subscription',
|
|
97
|
-
// a trial's first invoice bills nothing for the trial period (docs.stripe.com/billing/subscriptions/trials)
|
|
98
|
-
amount: o.trial ? 0 : priceAmount(price, quantity), currency, quantity, proration: false,
|
|
99
|
-
price: price ?? null, subscription: sub.id, subscription_item: it.id ?? null, invoice_item: null,
|
|
100
|
-
period: { start: o.start, end: o.end }, description: o.trial ? `Trial period for ${String(ctx.get('product', String(price?.product ?? ''))?.name ?? 'the plan')}` : null,
|
|
101
|
-
};
|
|
102
|
-
});
|
|
103
|
-
if (o.once?.length)
|
|
104
|
-
lines.push(...oneTimeLines(id, sub, o.once, lines.length, o.start, currency));
|
|
105
|
-
const subtotal = lines.reduce((s, l) => s + l.amount, 0);
|
|
106
|
-
// a discount the subscription still carries applies; a `once` coupon is removed once an invoice finalizes
|
|
107
|
-
// (spendOnceCoupon), so it discounts one invoice (docs.stripe.com/billing/subscriptions/coupons#coupon-duration)
|
|
108
|
-
const discount = (sub.discounts ?? [])[0];
|
|
109
|
-
const coupon = discount && typeof discount === 'object' ? discount.coupon : undefined;
|
|
110
|
-
const off = coupon ? applyCouponDiscount(subtotal, coupon) : 0;
|
|
111
|
-
const total = Math.max(0, subtotal - off);
|
|
112
|
-
await created(ctx, INV, { id, customer: sub.customer, subscription: sub.id }, {
|
|
113
|
-
created: o.start, status: 'draft', livemode: false, currency, collection_method: 'charge_automatically',
|
|
114
|
-
billing_reason: o.reason, auto_advance: true, attempt_count: 0, attempted: false, next_payment_attempt: null,
|
|
115
|
-
amount_due: total, amount_paid: 0, amount_remaining: total, amount_overpaid: 0, amount_paid_off_stripe: 0, amount_shipping: 0,
|
|
116
|
-
subtotal, total, starting_balance: 0, post_payment_credit_notes_amount: 0, pre_payment_credit_notes_amount: 0,
|
|
117
|
-
// "The invoice that consumed the coupon still shows the applied discount" (docs.stripe.com/billing/subscriptions/coupons)
|
|
118
|
-
period_start: o.start, period_end: o.end, default_tax_rates: [], discounts: coupon && discount ? [discount] : [],
|
|
119
|
-
lines: { object: 'list', data: lines, has_more: false, total_count: lines.length, url: `/v1/invoices/${id}/lines` },
|
|
120
|
-
automatic_tax: { enabled: false, liability: null, status: null },
|
|
121
|
-
status_transitions: { finalized_at: null, marked_uncollectible_at: null, paid_at: null, voided_at: null },
|
|
122
|
-
issuer: { type: 'self' }, payment_settings: { default_mandate: null, payment_method_options: null, payment_method_types: null },
|
|
123
|
-
payment_intent: null, charge: null,
|
|
124
|
-
});
|
|
125
|
-
return id;
|
|
126
|
-
}
|
|
127
|
-
/** A Checkout session's one-time line items as lines of its subscription's first invoice, numbered after its `after` lines. */
|
|
128
|
-
function oneTimeLines(invoiceId, sub, items, after, at, currency) {
|
|
129
|
-
return items.map((it, i) => {
|
|
130
|
-
const price = it.price && typeof it.price === 'object' ? it.price : null;
|
|
131
|
-
const quantity = itemQuantity(it);
|
|
132
|
-
return {
|
|
133
|
-
id: `il_twin_${invoiceId}_${after + i + 1}`, object: 'line_item', type: 'subscription',
|
|
134
|
-
amount: priceAmount(price ?? undefined, quantity), currency, quantity, proration: false,
|
|
135
|
-
price, subscription: sub.id, subscription_item: null, invoice_item: null,
|
|
136
|
-
period: { start: at, end: at }, description: typeof it.description === 'string' ? it.description : null,
|
|
137
|
-
};
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
/** Finalize a subscription's draft invoice at `at` and charge what its subscription pays with. The move is time's (a
|
|
141
|
-
* renewal), the customer's (the Checkout page they paid on) or the API's (an update that ends a trial, which names the
|
|
142
|
-
* payer, may ask for no attempt, and moves the subscription itself: semantics/subscriptions.ts applyTrialEnd). Answers
|
|
143
|
-
* whether it was paid. */
|
|
144
|
-
export async function collectSubscriptionInvoice(ctx, invoiceId, at, actor, opts = {}) {
|
|
145
|
-
const inv = ctx.get(INV, invoiceId);
|
|
146
|
-
const sub = inv && typeof inv.subscription === 'string' ? ctx.get(SUB, inv.subscription) : undefined;
|
|
147
|
-
if (!inv || !sub || inv.status !== 'draft')
|
|
148
|
-
return false;
|
|
149
|
-
const total = Number(inv.total) || 0;
|
|
150
|
-
const numbered = { ...finalizedFields(ctx, inv, at), status_transitions: { ...inv.status_transitions, finalized_at: at } };
|
|
151
|
-
const op = ctx.call.operation.id;
|
|
152
|
-
// nothing due (a trial's first invoice) is paid as it is finalized; finalizing spends a `once` coupon
|
|
153
|
-
if (total <= 0) {
|
|
154
|
-
if (ctx.legal(INV, 'status', op, 'draft', 'paid', invoiceId, actor))
|
|
155
|
-
return false;
|
|
156
|
-
await spendOnceCoupon(ctx, inv);
|
|
157
|
-
await ctx.write(INV, invoiceId, { ...numbered, status: 'paid', paid: true, auto_advance: false, status_transitions: { ...numbered.status_transitions, paid_at: at } }, 'invoice.paid');
|
|
158
|
-
return true;
|
|
159
|
-
}
|
|
160
|
-
const payer = actor === 'api' ? opts.payer : payerOf(ctx, sub);
|
|
161
|
-
// default_incomplete finalizes the invoice without attempting payment (docs.stripe.com/api/subscriptions/update#update_subscription-payment_behavior)
|
|
162
|
-
const attempt = opts.attempt !== false;
|
|
163
|
-
const decline = payer && attempt ? declineFor(finder(ctx), { payment_method: payer }) : undefined;
|
|
164
|
-
const pays = attempt && !!payer && !decline;
|
|
165
|
-
if (ctx.legal(INV, 'status', op, 'draft', pays ? 'paid' : 'open', invoiceId, actor))
|
|
166
|
-
return false;
|
|
167
|
-
await spendOnceCoupon(ctx, inv);
|
|
168
|
-
const piId = ctx.mint('payment_intent');
|
|
169
|
-
const currency = String(inv.currency ?? 'usd');
|
|
170
|
-
const chargeId = pays ? await mintCharge(ctx, piId, { currency, customer: inv.customer, payment_method: payer, invoice: invoiceId }, total) : null;
|
|
171
|
-
await created(ctx, 'payment_intent', { id: piId, amount: total, currency, customer: inv.customer, invoice: invoiceId, payment_method: pays ? payer : null }, {
|
|
172
|
-
status: pays ? 'succeeded' : 'requires_payment_method', client_secret: mintClientSecret(piId), livemode: false,
|
|
173
|
-
capture_method: 'automatic', amount_capturable: 0, amount_received: pays ? total : 0, next_action: null, latest_charge: chargeId,
|
|
174
|
-
last_payment_error: decline ? { type: 'card_error', code: decline.code, decline_code: decline.decline_code ?? null, message: decline.message, payment_method: { id: payer } } : null,
|
|
175
|
-
automatic_payment_methods: null, payment_method_types: ['card'], payment_method_options: {},
|
|
176
|
-
});
|
|
177
|
-
await ctx.write(INV, invoiceId, {
|
|
178
|
-
...numbered, status: pays ? 'paid' : 'open', paid: pays, attempt_count: attempt ? 1 : 0, attempted: attempt, payment_intent: piId, charge: chargeId,
|
|
179
|
-
amount_paid: pays ? total : 0, amount_remaining: pays ? 0 : total, auto_advance: attempt && !pays, next_payment_attempt: null,
|
|
180
|
-
status_transitions: { ...numbered.status_transitions, paid_at: pays ? at : null },
|
|
181
|
-
}, pays ? 'invoice.paid' : attempt ? 'invoice.payment_failed' : 'invoice.finalized');
|
|
182
|
-
if (actor !== 'api' && !pays && sub.status !== 'past_due' && !ctx.legal(SUB, 'status', op, sub.status, 'past_due', String(sub.id), 'time')) {
|
|
183
|
-
await ctx.write(SUB, String(sub.id), { status: 'past_due' }, 'customer.subscription.updated');
|
|
184
|
-
}
|
|
185
|
-
return pays;
|
|
186
|
-
}
|
|
187
|
-
/** Pay a subscription's open invoice with `paymentMethod` (the customer on the portal, `external`): the charge on the
|
|
188
|
-
* invoice's PaymentIntent, the invoice paid, a past_due subscription active. Answers the decline, if the card is refused. */
|
|
189
|
-
export async function payOpenInvoice(ctx, invoiceId, paymentMethod) {
|
|
190
|
-
const inv = ctx.get(INV, invoiceId);
|
|
191
|
-
if (!inv || inv.status !== 'open')
|
|
192
|
-
return { code: 'invoice_not_open', message: 'This invoice is no longer open.' };
|
|
193
|
-
const decline = declineFor(finder(ctx), { payment_method: paymentMethod });
|
|
194
|
-
if (decline)
|
|
195
|
-
return decline;
|
|
196
|
-
const op = ctx.call.operation.id;
|
|
197
|
-
const refused = ctx.legal(INV, 'status', op, 'open', 'paid', invoiceId, 'external');
|
|
198
|
-
if (refused)
|
|
199
|
-
return { code: refused.code ?? 'invoice_not_open', message: refused.message };
|
|
200
|
-
const total = Number(inv.amount_remaining ?? inv.total) || 0;
|
|
201
|
-
const now = Number(ctx.now());
|
|
202
|
-
const piId = typeof inv.payment_intent === 'string' ? inv.payment_intent : undefined;
|
|
203
|
-
let chargeId = null;
|
|
204
|
-
const pi = piId ? ctx.get('payment_intent', piId) : undefined;
|
|
205
|
-
if (piId && pi && !ctx.legal('payment_intent', 'status', op, pi.status, 'succeeded', piId, 'external')) {
|
|
206
|
-
chargeId = await mintCharge(ctx, piId, { ...pi, payment_method: paymentMethod, invoice: invoiceId }, total);
|
|
207
|
-
await ctx.write('payment_intent', piId, { status: 'succeeded', payment_method: paymentMethod, amount_received: total, last_payment_error: null, latest_charge: chargeId }, 'payment_intent.succeeded');
|
|
208
|
-
}
|
|
209
|
-
await ctx.write(INV, invoiceId, {
|
|
210
|
-
status: 'paid', paid: true, amount_paid: total, amount_remaining: 0, charge: chargeId, auto_advance: false,
|
|
211
|
-
attempt_count: (Number(inv.attempt_count) || 0) + 1, attempted: true,
|
|
212
|
-
status_transitions: { ...inv.status_transitions, paid_at: now },
|
|
213
|
-
}, 'invoice.paid');
|
|
214
|
-
const sub = typeof inv.subscription === 'string' ? ctx.get(SUB, inv.subscription) : undefined;
|
|
215
|
-
if (sub && sub.status === 'past_due' && !ctx.legal(SUB, 'status', op, 'past_due', 'active', String(sub.id), 'external')) {
|
|
216
|
-
await ctx.write(SUB, String(sub.id), { status: 'active' }, 'customer.subscription.updated');
|
|
217
|
-
}
|
|
218
|
-
return undefined;
|
|
219
|
-
}
|
|
220
|
-
/** Time's moves on subscriptions, caught up to each customer's clock in the order they fell due, each made at the
|
|
221
|
-
* moment it was due: a period that ended renews (a trial's end among them) or, set to cancel, cancels; a renewal
|
|
222
|
-
* drafted an hour or more ago is finalized and charged. */
|
|
223
|
-
export async function advanceBilling(ctx, only) {
|
|
224
|
-
// the billing clock is the vendor's own move, wherever a handler reaches it: never the request's caller's
|
|
225
|
-
return runAsVendorMove(() => advanceBillingDue(ctx, only));
|
|
226
|
-
}
|
|
227
|
-
async function advanceBillingDue(ctx, only) {
|
|
228
|
-
const at = at_(ctx);
|
|
229
|
-
for (let guard = 0; guard < 10_000 && (await billingStep(ctx, at, only)); guard++)
|
|
230
|
-
;
|
|
231
|
-
}
|
|
232
|
-
/** The earliest move that has come due, made at its moment; false when none has. */
|
|
233
|
-
async function billingStep(ctx, at, only) {
|
|
234
|
-
// the earliest move that has come due, of any subscription
|
|
235
|
-
let next;
|
|
236
|
-
for (const sub of ctx.rows(SUB)) {
|
|
237
|
-
if (only && !only(sub))
|
|
238
|
-
continue;
|
|
239
|
-
if (!RENEWING.includes(String(sub.status)) || typeof sub.current_period_end !== 'number' || sub.pause_collection)
|
|
240
|
-
continue;
|
|
241
|
-
if (sub.current_period_end <= clockNow(ctx, sub.customer) && (!next || sub.current_period_end < next.t))
|
|
242
|
-
next = { t: sub.current_period_end, kind: 'period', id: String(sub.id) };
|
|
243
|
-
}
|
|
244
|
-
for (const inv of ctx.rows(INV)) {
|
|
245
|
-
if (inv.status !== 'draft' || inv.billing_reason !== 'subscription_cycle' || inv.auto_advance !== true)
|
|
246
|
-
continue;
|
|
247
|
-
const sub = typeof inv.subscription === 'string' ? ctx.get(SUB, inv.subscription) : undefined;
|
|
248
|
-
if (!sub || (only && !only(sub)))
|
|
249
|
-
continue;
|
|
250
|
-
const t = Number(inv.created) + HOUR;
|
|
251
|
-
if (t <= clockNow(ctx, sub.customer) && (!next || t < next.t))
|
|
252
|
-
next = { t, kind: 'collect', id: String(inv.id) };
|
|
253
|
-
}
|
|
254
|
-
if (!next)
|
|
255
|
-
return false;
|
|
256
|
-
const c = await at(next.t);
|
|
257
|
-
if (next.kind === 'collect') {
|
|
258
|
-
await collectSubscriptionInvoice(c, next.id, next.t, 'time');
|
|
259
|
-
return true;
|
|
260
|
-
}
|
|
261
|
-
const id = next.id;
|
|
262
|
-
const sub = c.get(SUB, id);
|
|
263
|
-
const op = c.call.operation.id;
|
|
264
|
-
// one set to cancel at its period's end is canceled then, with no renewal
|
|
265
|
-
// (docs.stripe.com/billing/subscriptions/cancel#cancel-at-end-of-cycle)
|
|
266
|
-
if (sub.cancel_at_period_end === true) {
|
|
267
|
-
if (c.legal(SUB, 'status', op, sub.status, 'canceled', id, 'time'))
|
|
268
|
-
return false;
|
|
269
|
-
await c.write(SUB, id, { status: 'canceled', ended_at: next.t, canceled_at: sub.canceled_at ?? next.t }, 'customer.subscription.deleted');
|
|
270
|
-
return true;
|
|
271
|
-
}
|
|
272
|
-
const start = next.t;
|
|
273
|
-
const { interval, interval_count } = resolveSubscriptionBillingInterval(itemsOf(sub), finder(c));
|
|
274
|
-
const end = addBillingInterval(start, interval, interval_count);
|
|
275
|
-
const trialEnds = sub.status === 'trialing';
|
|
276
|
-
if (trialEnds && c.legal(SUB, 'status', op, 'trialing', 'active', id, 'time'))
|
|
277
|
-
return false;
|
|
278
|
-
const invoiceId = await draftSubscriptionInvoice(c, sub, { start, end, reason: 'subscription_cycle' });
|
|
279
|
-
await c.write(SUB, id, { current_period_start: start, current_period_end: end, latest_invoice: invoiceId, ...(trialEnds ? { status: 'active' } : {}) }, 'customer.subscription.updated');
|
|
280
|
-
return true;
|
|
281
|
-
}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { addBillingInterval, buildSubscriptionItemsList } from "../stripe-twin.js";
|
|
2
|
-
import { at, created, fail, finder } from "./shared.js";
|
|
3
|
-
import { storeItems } from "./subscriptions.js";
|
|
4
|
-
const SS = 'subscription_schedule';
|
|
5
|
-
const scheduleMissing = (ctx, id) => fail(ctx, `No such subscription_schedule: '${id}'`, 404, 'resource_missing');
|
|
6
|
-
/** A schedule's phases as Stripe answers them (the served spec's subscription_schedule_phase_configuration): each
|
|
7
|
-
* starts where the last ended (the first at the schedule's start_date, "now" or a timestamp), and ends at its
|
|
8
|
-
* end_date or after its duration ("duration: interval, interval_count", the served spec's phases[] parameter) on the
|
|
9
|
-
* calendar; its currency is its first price's, each item carries its price, quantity and (empty) discounts. Where the
|
|
10
|
-
* documentation stops and the twin decides: a phase with neither lasts one interval of its first price. */
|
|
11
|
-
function phasesOf(ctx, given, start) {
|
|
12
|
-
let cursor = start;
|
|
13
|
-
return given.map((p) => {
|
|
14
|
-
const items = (Array.isArray(p.items) ? p.items : []).map((it) => ({ price: it.price ?? null, quantity: Math.max(1, Math.trunc(Number(it.quantity) || 1)), discounts: [], metadata: {}, tax_rates: [], billing_thresholds: null }));
|
|
15
|
-
const price = typeof items[0]?.price === 'string' ? ctx.get('price', items[0].price) : undefined;
|
|
16
|
-
const recurring = price?.recurring ?? {};
|
|
17
|
-
const d = p.duration && typeof p.duration === 'object' ? p.duration : undefined;
|
|
18
|
-
const startDate = cursor;
|
|
19
|
-
const end = p.end_date !== undefined && p.end_date !== 'now' ? Number(p.end_date)
|
|
20
|
-
: d ? addBillingInterval(startDate, String(d.interval), Math.max(1, Number(d.interval_count) || 1))
|
|
21
|
-
: addBillingInterval(startDate, String(recurring.interval ?? 'month'), Math.max(1, Number(recurring.interval_count) || 1));
|
|
22
|
-
cursor = end;
|
|
23
|
-
return {
|
|
24
|
-
add_invoice_items: [], application_fee_percent: null, automatic_tax: { enabled: false, liability: null, disabled_reason: null }, billing_cycle_anchor: null,
|
|
25
|
-
billing_thresholds: null, collection_method: null, currency: String(price?.currency ?? 'usd'), default_payment_method: null, default_tax_rates: [],
|
|
26
|
-
description: null, discounts: [], end_date: end, invoice_settings: null, items, metadata: p.metadata && typeof p.metadata === 'object' ? p.metadata : {},
|
|
27
|
-
on_behalf_of: null, proration_behavior: typeof p.proration_behavior === 'string' ? p.proration_behavior : 'create_prorations', start_date: startDate,
|
|
28
|
-
transfer_data: null, trial_end: null,
|
|
29
|
-
};
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
// a schedule starts from a customer or from an existing subscription
|
|
33
|
-
const create = async (ctx) => {
|
|
34
|
-
const params = ctx.params;
|
|
35
|
-
const customer = typeof params.customer === 'string' ? params.customer : '';
|
|
36
|
-
const fromSub = typeof params.from_subscription === 'string' ? params.from_subscription : '';
|
|
37
|
-
if (!customer && !fromSub)
|
|
38
|
-
return fail(ctx, 'Missing required param: customer (or from_subscription).', 400, 'parameter_missing');
|
|
39
|
-
if (customer && !ctx.get('customer', customer))
|
|
40
|
-
return fail(ctx, `No such customer: '${customer}'`, 404, 'resource_missing');
|
|
41
|
-
if (fromSub && !ctx.get('subscription', fromSub))
|
|
42
|
-
return fail(ctx, `No such subscription: '${fromSub}'`, 404, 'resource_missing');
|
|
43
|
-
// start_date is an instruction ("now" or when), not a field of the schedule
|
|
44
|
-
const start = params.start_date !== undefined && params.start_date !== 'now' ? Number(params.start_date) : Number(ctx.now());
|
|
45
|
-
const phases = phasesOf(ctx, Array.isArray(params.phases) ? params.phases : [], start);
|
|
46
|
-
const { phases: _p, start_date: _s, default_settings: givenDefaults, ...rest } = params;
|
|
47
|
-
const defaults = givenDefaults && typeof givenDefaults === 'object' ? givenDefaults : {};
|
|
48
|
-
const schedId = ctx.mint(SS);
|
|
49
|
-
// a schedule for a customer whose first phase starts now starts its subscription at once, on that phase's items
|
|
50
|
-
// (docs.stripe.com/billing/subscriptions/subscription-schedules#managing)
|
|
51
|
-
let subscription = fromSub || null;
|
|
52
|
-
if (!fromSub && phases.length) {
|
|
53
|
-
const now = Number(ctx.now());
|
|
54
|
-
const subId = ctx.mint('subscription');
|
|
55
|
-
const items = buildSubscriptionItemsList(phases[0].items, subId, now, finder(ctx));
|
|
56
|
-
await created(ctx, 'subscription', { customer, id: subId }, {
|
|
57
|
-
status: 'active', livemode: false, currency: 'usd', collection_method: String(params.default_settings?.collection_method ?? 'charge_automatically'),
|
|
58
|
-
cancel_at_period_end: false, start_date: now, billing_cycle_anchor: now, metadata: {}, discounts: [], billing_schedules: [],
|
|
59
|
-
trial_start: null, trial_end: null, current_period_start: now, current_period_end: phases[0].end_date, items, schedule: schedId,
|
|
60
|
-
automatic_tax: { enabled: false, liability: null }, billing_mode: { type: 'classic' }, invoice_settings: { issuer: { type: 'self' } },
|
|
61
|
-
});
|
|
62
|
-
await storeItems(ctx, subId, items.data ?? []);
|
|
63
|
-
subscription = subId;
|
|
64
|
-
}
|
|
65
|
-
return ctx.reply(await created(ctx, SS, { ...rest, id: schedId }, {
|
|
66
|
-
status: 'active', livemode: false, phases, current_phase: phases.length ? { start_date: phases[0].start_date, end_date: phases[0].end_date } : null,
|
|
67
|
-
subscription, released_subscription: null, released_at: null,
|
|
68
|
-
canceled_at: null, completed_at: null, end_behavior: params.end_behavior ?? 'release',
|
|
69
|
-
renewal_interval: null, metadata: {}, billing_mode: { type: 'classic' },
|
|
70
|
-
// "billing_cycle_anchor: Possible values are phase_start or automatic" and invoice_settings are required (the
|
|
71
|
-
// served spec's default settings); the rest answer what the request set, else null
|
|
72
|
-
default_settings: {
|
|
73
|
-
application_fee_percent: defaults.application_fee_percent ?? null, automatic_tax: { enabled: false, liability: null, disabled_reason: null },
|
|
74
|
-
billing_cycle_anchor: typeof defaults.billing_cycle_anchor === 'string' ? defaults.billing_cycle_anchor : 'automatic', billing_thresholds: null,
|
|
75
|
-
collection_method: typeof defaults.collection_method === 'string' ? defaults.collection_method : 'charge_automatically', default_payment_method: defaults.default_payment_method ?? null,
|
|
76
|
-
description: defaults.description ?? null, invoice_settings: { account_tax_ids: null, days_until_due: null, issuer: { type: 'self' } }, on_behalf_of: null, transfer_data: null,
|
|
77
|
-
},
|
|
78
|
-
// a schedule made from a subscription is its customer's
|
|
79
|
-
customer: customer || String(ctx.get('subscription', fromSub)?.customer ?? ''),
|
|
80
|
-
}));
|
|
81
|
-
};
|
|
82
|
-
function end(kind) {
|
|
83
|
-
return async (ctx) => {
|
|
84
|
-
const id = at(ctx, 'schedule');
|
|
85
|
-
const sched = ctx.get(SS, id);
|
|
86
|
-
if (!sched)
|
|
87
|
-
return scheduleMissing(ctx, id);
|
|
88
|
-
const refused = ctx.legal(SS, 'status', kind === 'cancel' ? 'PostSubscriptionSchedulesScheduleCancel' : 'PostSubscriptionSchedulesScheduleRelease', sched.status, undefined, id);
|
|
89
|
-
if (refused)
|
|
90
|
-
return ctx.refuse(refused);
|
|
91
|
-
const now = ctx.now();
|
|
92
|
-
const subId = typeof sched.subscription === 'string' ? sched.subscription : undefined;
|
|
93
|
-
const sub = subId ? ctx.get('subscription', subId) : undefined;
|
|
94
|
-
// "Cancels a subscription schedule and its associated subscription immediately (if the subscription schedule has an
|
|
95
|
-
// active subscription)" (docs.stripe.com/api/subscription_schedules/cancel); releasing "will remove its subscription
|
|
96
|
-
// property and set the subscription's ID to the released_subscription property", leaving the subscription in place
|
|
97
|
-
// (docs.stripe.com/api/subscription_schedules/release)
|
|
98
|
-
if (kind === 'cancel' && subId && sub && sub.status !== 'canceled') {
|
|
99
|
-
ctx.legal('subscription', 'status', 'PostSubscriptionSchedulesScheduleCancel', String(sub.status), 'canceled', subId);
|
|
100
|
-
await ctx.write('subscription', subId, { status: 'canceled', canceled_at: now, ended_at: now }, 'subscription.cancel');
|
|
101
|
-
}
|
|
102
|
-
if (kind === 'release' && subId && sub)
|
|
103
|
-
await ctx.write('subscription', subId, { schedule: null }, 'subscription.update');
|
|
104
|
-
const fields = kind === 'cancel'
|
|
105
|
-
? { status: 'canceled', canceled_at: now, current_phase: null }
|
|
106
|
-
: { status: 'released', released_at: now, released_subscription: subId ?? null, subscription: null, current_phase: null };
|
|
107
|
-
return ctx.reply(await ctx.write(SS, id, fields, `subscription_schedule.${kind}`));
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
const retrieve = async (ctx) => {
|
|
111
|
-
const s = ctx.get(SS, at(ctx, 'schedule'));
|
|
112
|
-
return s ? ctx.reply(s) : scheduleMissing(ctx, at(ctx, 'schedule'));
|
|
113
|
-
};
|
|
114
|
-
export const subscriptionSchedules = {
|
|
115
|
-
PostSubscriptionSchedules: create,
|
|
116
|
-
PostSubscriptionSchedulesScheduleCancel: end('cancel'),
|
|
117
|
-
PostSubscriptionSchedulesScheduleRelease: end('release'),
|
|
118
|
-
GetSubscriptionSchedulesSchedule: retrieve,
|
|
119
|
-
};
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { asBool, lineItemEntries, paginate } from "../stripe-twin.js";
|
|
2
|
-
import { at, created, fail, list, newest, path, where } from "./shared.js";
|
|
3
|
-
const TWIN_TAX_RATE_PCT = 10;
|
|
4
|
-
const createRate = async (ctx) => {
|
|
5
|
-
const params = ctx.params;
|
|
6
|
-
if (params.display_name === undefined || params.display_name === '')
|
|
7
|
-
return fail(ctx, 'Missing required param: display_name.', 400, 'parameter_missing');
|
|
8
|
-
if (params.percentage === undefined)
|
|
9
|
-
return fail(ctx, 'Missing required param: percentage.', 400, 'parameter_missing');
|
|
10
|
-
if (params.inclusive === undefined)
|
|
11
|
-
return fail(ctx, 'Missing required param: inclusive.', 400, 'parameter_missing');
|
|
12
|
-
return ctx.reply(await created(ctx, 'tax_rate', { ...params, inclusive: asBool(params.inclusive), percentage: Number(params.percentage) }, {
|
|
13
|
-
active: true, livemode: false, metadata: {},
|
|
14
|
-
country: null, state: null, jurisdiction: typeof params.jurisdiction === 'string' ? params.jurisdiction : null,
|
|
15
|
-
jurisdiction_level: null, description: params.description ?? null, display_name: params.display_name,
|
|
16
|
-
tax_type: null,
|
|
17
|
-
}));
|
|
18
|
-
};
|
|
19
|
-
const listRates = async (ctx) => list(ctx, 'tax_rate', where(ctx, newest(ctx, 'tax_rate'), {
|
|
20
|
-
active: (t, v) => asBool(t.active) === asBool(v),
|
|
21
|
-
inclusive: (t, v) => asBool(t.inclusive) === asBool(v),
|
|
22
|
-
}));
|
|
23
|
-
// percentage and inclusive are fixed once created
|
|
24
|
-
const updateRate = async (ctx) => {
|
|
25
|
-
const id = at(ctx, 'tax_rate');
|
|
26
|
-
if (!ctx.get('tax_rate', id))
|
|
27
|
-
return ctx.notFound('tax_rate', id);
|
|
28
|
-
const { percentage: _p, inclusive: _i, ...rest } = ctx.params;
|
|
29
|
-
return ctx.reply(await ctx.write('tax_rate', id, { ...rest, ...('active' in rest ? { active: asBool(rest.active) } : {}) }, 'tax_rate.update'));
|
|
30
|
-
};
|
|
31
|
-
// ── settings: active once a head office is set; the answer has no id ──
|
|
32
|
-
// the defaults' provider: "The tax calculation provider this account uses. Defaults to `stripe` when not using a
|
|
33
|
-
// third-party provider" (the served spec's tax settings defaults); the head office's address answers each line, the
|
|
34
|
-
// lines not given null (the served spec's address: each "(string, nullable)"; the settings page's example answers
|
|
35
|
-
// line2 null)
|
|
36
|
-
const ADDRESS_LINES = ['city', 'country', 'line1', 'line2', 'postal_code', 'state'];
|
|
37
|
-
function settingsBody(stored) {
|
|
38
|
-
const given = (stored?.head_office ?? null);
|
|
39
|
-
const address = given && typeof given.address === 'object' && given.address ? given.address : undefined;
|
|
40
|
-
const headOffice = given ? { ...given, ...(address ? { address: Object.fromEntries(ADDRESS_LINES.map((k) => [k, address[k] ?? null])) } : {}) } : null;
|
|
41
|
-
const active = !!headOffice;
|
|
42
|
-
return {
|
|
43
|
-
object: 'tax.settings', livemode: false,
|
|
44
|
-
defaults: { provider: 'stripe', ...(stored?.defaults ?? { tax_behavior: null, tax_code: null }) }, head_office: headOffice,
|
|
45
|
-
status: active ? 'active' : 'pending',
|
|
46
|
-
status_details: active ? { active: {} } : { pending: { missing_fields: ['head_office'] } },
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
const getSettings = async (ctx) => ctx.reply(settingsBody(ctx.rows('tax.settings')[0]));
|
|
50
|
-
const updateSettings = async (ctx) => {
|
|
51
|
-
const existing = ctx.rows('tax.settings')[0];
|
|
52
|
-
const cur = existing ?? {};
|
|
53
|
-
const defaults = ctx.params.defaults && typeof ctx.params.defaults === 'object' ? ctx.params.defaults : {};
|
|
54
|
-
const curDefaults = cur.defaults ?? {};
|
|
55
|
-
const headOffice = ctx.params.head_office && typeof ctx.params.head_office === 'object' ? ctx.params.head_office : undefined;
|
|
56
|
-
const fields = {
|
|
57
|
-
defaults: {
|
|
58
|
-
tax_behavior: typeof defaults.tax_behavior === 'string' ? defaults.tax_behavior : (curDefaults.tax_behavior ?? null),
|
|
59
|
-
tax_code: typeof defaults.tax_code === 'string' ? defaults.tax_code : (curDefaults.tax_code ?? null),
|
|
60
|
-
},
|
|
61
|
-
head_office: headOffice ?? cur.head_office ?? null,
|
|
62
|
-
livemode: false,
|
|
63
|
-
};
|
|
64
|
-
const body = await ctx.write('tax.settings', existing ? String(existing.id) : 'taxset_twin_1', fields, existing ? 'tax_settings.update' : 'tax_settings.create');
|
|
65
|
-
return ctx.reply(settingsBody(body));
|
|
66
|
-
};
|
|
67
|
-
// ── calculations: each line's tax on top of its amount; no `created`, a tax_date instead ──
|
|
68
|
-
const calcMissing = (ctx, id) => fail(ctx, `No such tax calculation: '${id}'`, 404, 'resource_missing');
|
|
69
|
-
const calculate = async (ctx) => {
|
|
70
|
-
const params = ctx.params;
|
|
71
|
-
if (params.currency === undefined || params.currency === '')
|
|
72
|
-
return fail(ctx, 'Missing required param: currency.', 400, 'parameter_missing');
|
|
73
|
-
const entries = lineItemEntries(params);
|
|
74
|
-
if (entries.length === 0)
|
|
75
|
-
return fail(ctx, 'Missing required param: line_items.', 400, 'parameter_missing');
|
|
76
|
-
const now = Number(ctx.now());
|
|
77
|
-
const id = ctx.mint('tax.calculation');
|
|
78
|
-
const items = entries.map((e, i) => {
|
|
79
|
-
const amount = Math.max(0, Math.trunc(Number(e.amount) || 0));
|
|
80
|
-
return {
|
|
81
|
-
object: 'tax.calculation_line_item', id: `tax_li_${id}_${i + 1}`,
|
|
82
|
-
amount, amount_tax: Math.round((amount * TWIN_TAX_RATE_PCT) / 100), reference: typeof e.reference === 'string' ? e.reference : null,
|
|
83
|
-
tax_behavior: 'exclusive', tax_code: typeof e.tax_code === 'string' ? e.tax_code : 'txcd_99999999',
|
|
84
|
-
quantity: e.quantity === undefined ? 1 : Math.max(1, Math.trunc(Number(e.quantity) || 1)), livemode: false,
|
|
85
|
-
};
|
|
86
|
-
});
|
|
87
|
-
const amountSubtotal = items.reduce((s, it) => s + it.amount, 0);
|
|
88
|
-
const taxExclusive = items.reduce((s, it) => s + it.amount_tax, 0);
|
|
89
|
-
const cd = params.customer_details && typeof params.customer_details === 'object' ? params.customer_details : {};
|
|
90
|
-
return ctx.reply(await created(ctx, 'tax.calculation', {
|
|
91
|
-
id, currency: String(params.currency), amount_total: amountSubtotal + taxExclusive, tax_amount_exclusive: taxExclusive, tax_amount_inclusive: 0,
|
|
92
|
-
customer: typeof params.customer === 'string' ? params.customer : null,
|
|
93
|
-
customer_details: {
|
|
94
|
-
address: cd.address ?? null, address_source: cd.address_source ?? 'shipping', ip_address: cd.ip_address ?? null,
|
|
95
|
-
tax_ids: Array.isArray(cd.tax_ids) ? cd.tax_ids : [], taxability_override: cd.taxability_override ?? 'none',
|
|
96
|
-
},
|
|
97
|
-
line_items: { object: 'list', data: items, has_more: false, url: `/v1/tax/calculations/${id}/line_items` },
|
|
98
|
-
shipping_cost: null, tax_date: now,
|
|
99
|
-
tax_breakdown: taxExclusive > 0 ? [{
|
|
100
|
-
amount: taxExclusive, inclusive: false, taxability_reason: 'standard_rated', taxable_amount: amountSubtotal,
|
|
101
|
-
tax_rate_details: { country: null, percentage_decimal: String(TWIN_TAX_RATE_PCT), state: null, tax_type: 'sales_tax' },
|
|
102
|
-
}] : [],
|
|
103
|
-
expires_at: now + 90 * 24 * 3600, livemode: false,
|
|
104
|
-
}, {}, { timeField: 'tax_date' }));
|
|
105
|
-
};
|
|
106
|
-
/** A page of the line items a stored object carries. */
|
|
107
|
-
function linePage(ctx, holder) {
|
|
108
|
-
const li = holder.line_items;
|
|
109
|
-
const { page, hasMore } = paginate(Array.isArray(li?.data) ? li.data : [], ctx.params);
|
|
110
|
-
return ctx.reply({ object: 'list', url: path(ctx), has_more: hasMore, data: page });
|
|
111
|
-
}
|
|
112
|
-
const calculationLines = async (ctx) => {
|
|
113
|
-
const c = ctx.get('tax.calculation', at(ctx, 'calculation'));
|
|
114
|
-
return c ? linePage(ctx, c) : calcMissing(ctx, at(ctx, 'calculation'));
|
|
115
|
-
};
|
|
116
|
-
// ── registrations ──
|
|
117
|
-
const register = async (ctx) => {
|
|
118
|
-
const params = ctx.params;
|
|
119
|
-
if (params.country === undefined || params.country === '')
|
|
120
|
-
return fail(ctx, 'Missing required param: country.', 400, 'parameter_missing');
|
|
121
|
-
if (params.active_from === undefined)
|
|
122
|
-
return fail(ctx, 'Missing required param: active_from.', 400, 'parameter_missing');
|
|
123
|
-
const now = Number(ctx.now());
|
|
124
|
-
const activeFrom = params.active_from === 'now' ? now : Number(params.active_from) || now;
|
|
125
|
-
return ctx.reply(await created(ctx, 'tax.registration', { ...params, active_from: activeFrom }, {
|
|
126
|
-
status: 'active', expires_at: null, livemode: false,
|
|
127
|
-
country_options: params.country_options && typeof params.country_options === 'object' ? params.country_options : {},
|
|
128
|
-
}));
|
|
129
|
-
};
|
|
130
|
-
// ── transactions: committing a calculation, or reversing a transaction with negated amounts ──
|
|
131
|
-
const TX = 'tax.transaction';
|
|
132
|
-
const txMissing = (ctx, id, status = 404) => fail(ctx, `No such tax transaction: '${id}'`, status, 'resource_missing');
|
|
133
|
-
const txLine = (id, i, l, sign, reversal, type) => ({
|
|
134
|
-
object: 'tax.transaction_line_item', id: `tax_li_${id}_${i + 1}`,
|
|
135
|
-
amount: sign === 1 ? l.amount : -(Number(l.amount) || 0), amount_tax: sign === 1 ? l.amount_tax : -(Number(l.amount_tax) || 0),
|
|
136
|
-
reference: l.reference ?? null, tax_behavior: l.tax_behavior ?? 'exclusive', tax_code: l.tax_code ?? 'txcd_99999999',
|
|
137
|
-
quantity: l.quantity ?? 1, reversal, type, livemode: false, metadata: {},
|
|
138
|
-
});
|
|
139
|
-
const linesOf = (holder) => holder.line_items?.data ?? [];
|
|
140
|
-
const fromCalculation = async (ctx) => {
|
|
141
|
-
const calcId = typeof ctx.params.calculation === 'string' ? ctx.params.calculation : '';
|
|
142
|
-
if (!calcId)
|
|
143
|
-
return fail(ctx, 'Missing required param: calculation.', 400, 'parameter_missing');
|
|
144
|
-
if (ctx.params.reference === undefined || ctx.params.reference === '')
|
|
145
|
-
return fail(ctx, 'Missing required param: reference.', 400, 'parameter_missing');
|
|
146
|
-
const calc = ctx.get('tax.calculation', calcId);
|
|
147
|
-
if (!calc)
|
|
148
|
-
return fail(ctx, `No such tax calculation: '${calcId}'`, 400, 'resource_missing');
|
|
149
|
-
const id = ctx.mint(TX);
|
|
150
|
-
const now = ctx.now();
|
|
151
|
-
return ctx.reply(await created(ctx, TX, {
|
|
152
|
-
id, _stripe_type: 'transaction', reference: ctx.params.reference, currency: calc.currency,
|
|
153
|
-
customer: calc.customer ?? null, customer_details: calc.customer_details ?? {},
|
|
154
|
-
tax_date: now, livemode: false, posted_at: now,
|
|
155
|
-
line_items: { object: 'list', data: linesOf(calc).map((l, i) => txLine(id, i, l, 1, null, 'transaction')), has_more: false, url: `/v1/tax/transactions/${id}/line_items` },
|
|
156
|
-
shipping_cost: calc.shipping_cost ?? null, reversal: null, calculation: calcId, metadata: {},
|
|
157
|
-
}, {}));
|
|
158
|
-
};
|
|
159
|
-
const reversal = async (ctx) => {
|
|
160
|
-
const original = typeof ctx.params.original_transaction === 'string' ? ctx.params.original_transaction : '';
|
|
161
|
-
if (!original)
|
|
162
|
-
return fail(ctx, 'Missing required param: original_transaction.', 400, 'parameter_missing');
|
|
163
|
-
if (ctx.params.reference === undefined || ctx.params.reference === '')
|
|
164
|
-
return fail(ctx, 'Missing required param: reference.', 400, 'parameter_missing');
|
|
165
|
-
const mode = typeof ctx.params.mode === 'string' ? ctx.params.mode : '';
|
|
166
|
-
if (mode !== 'full' && mode !== 'partial')
|
|
167
|
-
return fail(ctx, 'Missing required param: mode (full or partial).', 400, 'parameter_missing');
|
|
168
|
-
const orig = ctx.get(TX, original);
|
|
169
|
-
if (!orig)
|
|
170
|
-
return txMissing(ctx, original, 400);
|
|
171
|
-
const id = ctx.mint(TX);
|
|
172
|
-
const now = ctx.now();
|
|
173
|
-
return ctx.reply(await created(ctx, TX, {
|
|
174
|
-
id, _stripe_type: 'reversal', reference: ctx.params.reference, currency: orig.currency,
|
|
175
|
-
customer: orig.customer ?? null, customer_details: orig.customer_details ?? {},
|
|
176
|
-
tax_date: now, livemode: false, posted_at: now,
|
|
177
|
-
line_items: { object: 'list', data: linesOf(orig).map((l, i) => txLine(id, i, l, -1, { original_line_item: l.id }, 'reversal')), has_more: false, url: `/v1/tax/transactions/${id}/line_items` },
|
|
178
|
-
shipping_cost: null, reversal: { original_transaction: original, mode }, metadata: {},
|
|
179
|
-
}, {}));
|
|
180
|
-
};
|
|
181
|
-
const transactionLines = async (ctx) => {
|
|
182
|
-
const t = ctx.get(TX, at(ctx, 'transaction'));
|
|
183
|
-
return t ? linePage(ctx, t) : txMissing(ctx, at(ctx, 'transaction'));
|
|
184
|
-
};
|
|
185
|
-
export const tax = {
|
|
186
|
-
PostTaxRates: createRate,
|
|
187
|
-
GetTaxRates: listRates,
|
|
188
|
-
PostTaxRatesTaxRate: updateRate,
|
|
189
|
-
GetTaxSettings: getSettings,
|
|
190
|
-
PostTaxSettings: updateSettings,
|
|
191
|
-
PostTaxCalculations: calculate,
|
|
192
|
-
GetTaxCalculationsCalculationLineItems: calculationLines,
|
|
193
|
-
PostTaxRegistrations: register,
|
|
194
|
-
PostTaxTransactionsCreateFromCalculation: fromCalculation,
|
|
195
|
-
PostTaxTransactionsCreateReversal: reversal,
|
|
196
|
-
GetTaxTransactionsTransactionLineItems: transactionLines,
|
|
197
|
-
};
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { Semantics, SemanticsContext } from '@volter/world-core';
|
|
2
|
-
/** Time's settling of top-ups, caught up to the World's clock: a pending top-up succeeds on its expected_availability_date,
|
|
3
|
-
* its funds entered then in the balance it names. */
|
|
4
|
-
export declare function settleTopups(ctx: SemanticsContext): Promise<void>;
|
|
5
|
-
export declare const terminal: Record<string, Semantics>;
|