@volter/twin-stripe 2.0.37 → 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
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Stripe's payment_intents operations (docs/contributing/architecture.md, "What an author writes"): each handler over the
|
|
2
|
+
// handler contract's context, computing with the engine (../engine/).
|
|
3
|
+
import type { HandlerContext } from '@volter/world-core';
|
|
4
|
+
import type { Row } from '../engine/common.ts';
|
|
5
|
+
import { PI, TYPES_GIVEN } from '../engine/payment-intents.ts';
|
|
6
|
+
import { asBool, mintClientSecret, nowUnix, validateMoney, verifyMicrodeposits } from '../engine/stripe.ts';
|
|
7
|
+
import { afterCharge, beginBankDebit, cancelAuthorization, captureAuthorization, confirmIntent, confirmOnly, fundFromCashBalance, incrementTooSmall, methodFromData, mintCharge, mintMandate, payInvoiceOf, paymentIntentsLoad, paymentIntentsSend, search } from './shared.ts';
|
|
8
|
+
|
|
9
|
+
export async function PostPaymentIntents(ctx: HandlerContext): Promise<Response> {
|
|
10
|
+
const params = ctx.params;
|
|
11
|
+
const bad = validateMoney(params);
|
|
12
|
+
if (bad) return paymentIntentsSend(ctx, bad);
|
|
13
|
+
// what describes a confirmation needs one: error_on_requires_action, mandate, mandate_data, off_session and return_url
|
|
14
|
+
// are each, in the served spec, a parameter that "can only be used with `confirm=true`" (shared.ts confirmOnly)
|
|
15
|
+
const unconfirmed = confirmOnly(ctx, ['error_on_requires_action', 'mandate', 'mandate_data', 'off_session', 'return_url'], asBool(params.confirm));
|
|
16
|
+
if (unconfirmed) return unconfirmed;
|
|
17
|
+
// automatic_payment_methods: when enabled, Stripe selects eligible methods and answers the
|
|
18
|
+
// normalized { enabled, allow_redirects } object
|
|
19
|
+
const apmIn = params.automatic_payment_methods;
|
|
20
|
+
const apmEnabled = apmIn && typeof apmIn === 'object' ? asBool((apmIn as Record<string, unknown>).enabled) : false;
|
|
21
|
+
const automatic_payment_methods = apmEnabled ? { enabled: true, allow_redirects: ((apmIn as Record<string, unknown>).allow_redirects as string) ?? 'always' } : null;
|
|
22
|
+
// the methods it may be paid with: allowed_payment_method_types in the served version, payment_method_types for a
|
|
23
|
+
// caller pinned to an earlier one (docs.stripe.com/api/payment_intents/create)
|
|
24
|
+
const given = Array.isArray(params.allowed_payment_method_types) ? params.allowed_payment_method_types : params.payment_method_types;
|
|
25
|
+
const payment_method_types = Array.isArray(given) ? given.map(String) : ['card'];
|
|
26
|
+
// `confirm` is an instruction, not a field: `confirm=true` attempts to confirm the intent at once
|
|
27
|
+
// (https://docs.stripe.com/api/payment_intents/create#create_payment_intent-confirm)
|
|
28
|
+
const { automatic_payment_methods: _a, payment_method_types: _p, allowed_payment_method_types: _ap, id: provided, expand: _e, confirm: confirmNow, payment_method_data: data, mandate_data: _mandate, ...rest } = params;
|
|
29
|
+
// a create's payment_method_data makes its method, as a confirm's does (payment-methods.ts methodFromData)
|
|
30
|
+
const made = await methodFromData(ctx, data);
|
|
31
|
+
if (made) rest.payment_method = made;
|
|
32
|
+
// the id is minted first so the client secret can carry it: Stripe.js parses the id back out
|
|
33
|
+
const id = typeof provided === 'string' && provided ? provided : ctx.mint(PI);
|
|
34
|
+
// it waits for a payment method until it has one, then for its confirmation (manifest.ts)
|
|
35
|
+
const hasMethod = typeof rest.payment_method === 'string' && rest.payment_method !== '';
|
|
36
|
+
if (hasMethod) ctx.legal(PI, 'status', 'PostPaymentIntents', 'requires_payment_method', 'requires_confirmation', id);
|
|
37
|
+
const body = await ctx.write(
|
|
38
|
+
PI,
|
|
39
|
+
id,
|
|
40
|
+
{
|
|
41
|
+
object: 'payment_intent',
|
|
42
|
+
created: nowUnix(ctx.occurredAt),
|
|
43
|
+
status: hasMethod ? 'requires_confirmation' : 'requires_payment_method',
|
|
44
|
+
client_secret: mintClientSecret(id, await ctx.secret(`client-secret:${id}`)),
|
|
45
|
+
livemode: false,
|
|
46
|
+
capture_method: 'automatic',
|
|
47
|
+
amount_capturable: 0,
|
|
48
|
+
amount_received: 0,
|
|
49
|
+
next_action: null,
|
|
50
|
+
automatic_payment_methods,
|
|
51
|
+
payment_method_types,
|
|
52
|
+
...(Array.isArray(params.allowed_payment_method_types) ? { allowed_payment_method_types: payment_method_types } : {}),
|
|
53
|
+
payment_method_options: (params.payment_method_options as object) ?? {},
|
|
54
|
+
...rest,
|
|
55
|
+
},
|
|
56
|
+
'payment_intent.create',
|
|
57
|
+
);
|
|
58
|
+
if (Array.isArray(given)) await ctx.record(TYPES_GIVEN, { payment_intent: id }, id);
|
|
59
|
+
if (asBool(confirmNow)) return confirmIntent(ctx, body, {});
|
|
60
|
+
return ctx.reply(ctx.expand(PI, body));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function PostPaymentIntentsIntentCancel(ctx: HandlerContext): Promise<Response> {
|
|
64
|
+
const loaded = paymentIntentsLoad(ctx, 'PostPaymentIntentsIntentCancel');
|
|
65
|
+
if ('answer' in loaded) return loaded.answer;
|
|
66
|
+
const cancellation_reason = typeof ctx.params.cancellation_reason === 'string' ? ctx.params.cancellation_reason : 'requested_by_customer';
|
|
67
|
+
// "For PaymentIntents with a `status` of `requires_capture`, the remaining `amount_capturable` is automatically
|
|
68
|
+
// refunded" (docs.stripe.com/api/payment_intents/cancel): the authorization is released (charges.ts)
|
|
69
|
+
const held = loaded.pi.status === 'requires_capture' && typeof loaded.pi.latest_charge === 'string' ? ctx.get('charge', loaded.pi.latest_charge) : undefined;
|
|
70
|
+
if (held && held.captured === false && held.refunded !== true) await cancelAuthorization(ctx, held);
|
|
71
|
+
return ctx.reply(ctx.expand(PI, await ctx.write(PI, String(loaded.pi.id), { status: 'canceled', cancellation_reason, amount_capturable: 0 }, 'payment_intent.canceled')));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function PostPaymentIntentsIntentConfirm(ctx: HandlerContext): Promise<Response> {
|
|
75
|
+
const loaded = paymentIntentsLoad(ctx, 'PostPaymentIntentsIntentConfirm');
|
|
76
|
+
if ('answer' in loaded) return loaded.answer;
|
|
77
|
+
const given = ctx.params;
|
|
78
|
+
const { expand: _e, payment_method_data: data, mandate_data: _mandate, ...params } = given;
|
|
79
|
+
// a confirm's payment_method_data makes the method it pays with; neither it nor mandate_data is a field of the intent
|
|
80
|
+
const made = await methodFromData(ctx, data);
|
|
81
|
+
return confirmIntent(ctx, loaded.pi, made ? { ...params, payment_method: made } : params);
|
|
82
|
+
}
|
|
83
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Stripe's payment_methods operations (docs/contributing/architecture.md, "What an author writes"): each handler over the
|
|
2
|
+
// handler contract's context, computing with the engine (../engine/).
|
|
3
|
+
import type { HandlerContext } from '@volter/world-core';
|
|
4
|
+
import type { Row } from '../engine/common.ts';
|
|
5
|
+
import { noBilling, PM } from '../engine/payment-methods.ts';
|
|
6
|
+
import { declineFor, paymentMethodSubObject } from '../engine/stripe.ts';
|
|
7
|
+
import { afterSuccessOf, at, attachable, attachPaymentMethod, created, fail, pmMissing } from './shared.ts';
|
|
8
|
+
|
|
9
|
+
export async function GetPaymentMethodsPaymentMethod(ctx: HandlerContext): Promise<Response> {
|
|
10
|
+
const m = ctx.get(PM, at(ctx, 'payment_method'));
|
|
11
|
+
return m ? ctx.reply(m) : pmMissing(ctx, at(ctx, 'payment_method'));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// the raw card number, cvc and bank account number are never echoed: the modeled sub-object is
|
|
15
|
+
// what the PaymentMethod carries, with the decline outcome its card implies, so a later charge
|
|
16
|
+
// naming it only by id declines the same way
|
|
17
|
+
export async function PostPaymentMethods(ctx: HandlerContext): Promise<Response> {
|
|
18
|
+
const params = ctx.params;
|
|
19
|
+
const pmType = typeof params.type === 'string' ? params.type : 'card';
|
|
20
|
+
const { card: _card, sepa_debit: _sepa, us_bank_account: _ach, type: _t, ...rest } = params;
|
|
21
|
+
const sub = paymentMethodSubObject(pmType, params);
|
|
22
|
+
const declineOutcome = pmType === 'card' ? (declineFor(() => undefined, params) ?? null) : null;
|
|
23
|
+
// and what Stripe does after its card succeeds (a dispute, a review), which a later charge naming it by id brings
|
|
24
|
+
const afterSuccess = pmType === 'card' ? afterSuccessOf(ctx, params.card) : undefined;
|
|
25
|
+
// the billing details the create gives are the method's (docs.stripe.com/api/payment_methods/create#create_payment_method-billing_details)
|
|
26
|
+
const given = rest.billing_details && typeof rest.billing_details === 'object' ? (rest.billing_details as Row) : {};
|
|
27
|
+
return ctx.reply(await created(ctx, PM, { ...rest, type: pmType, customer: null, livemode: false, billing_details: { ...noBilling, ...given }, ...sub, _declineOutcome: declineOutcome, ...(afterSuccess ? { _afterSuccess: afterSuccess } : {}) }, {}));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function PostPaymentMethodsPaymentMethodAttach(ctx: HandlerContext): Promise<Response> {
|
|
31
|
+
const id = at(ctx, 'payment_method');
|
|
32
|
+
const customer = typeof ctx.params.customer === 'string' ? ctx.params.customer : '';
|
|
33
|
+
if (!customer) return fail(ctx, 'Missing required param: customer.', 400, 'parameter_missing');
|
|
34
|
+
if (!ctx.get('customer', customer)) return fail(ctx, `No such customer: '${customer}'`, 400, 'resource_missing');
|
|
35
|
+
if (!attachable(ctx, id)) return pmMissing(ctx, id);
|
|
36
|
+
return ctx.reply(await attachPaymentMethod(ctx, id, customer));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function PostPaymentMethodsPaymentMethodDetach(ctx: HandlerContext): Promise<Response> {
|
|
40
|
+
const id = at(ctx, 'payment_method');
|
|
41
|
+
if (!ctx.get(PM, id)) return pmMissing(ctx, id);
|
|
42
|
+
return ctx.reply(await ctx.write(PM, id, { customer: null }, 'payment_method.detach'));
|
|
43
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Stripe's payouts operations (docs/contributing/architecture.md, "What an author writes"): each handler over the
|
|
2
|
+
// handler contract's context, computing with the engine (../engine/).
|
|
3
|
+
import type { HandlerContext } from '@volter/world-core';
|
|
4
|
+
import { inRange } from '../engine/common.ts';
|
|
5
|
+
import { validateMoney } from '../engine/stripe.ts';
|
|
6
|
+
import { account, asOf, at, created, fail, kept, list, newest, noExternalAccount, payoutBank, payoutDefaults, payoutMissing, refusePayout, send, settlePayout, where } from './shared.ts';
|
|
7
|
+
|
|
8
|
+
export async function PostPayouts(ctx: HandlerContext): Promise<Response> {
|
|
9
|
+
const bad = validateMoney(ctx.params);
|
|
10
|
+
if (bad) return send(ctx, bad);
|
|
11
|
+
const acct = account(ctx);
|
|
12
|
+
if (acct && !ctx.get('account', acct)) return fail(ctx, `No such account: '${acct}'`, 400, 'account_invalid');
|
|
13
|
+
const amount = Math.trunc(Number(ctx.params.amount) || 0);
|
|
14
|
+
const currency = String(ctx.params.currency ?? 'usd');
|
|
15
|
+
// a connected account is paid out to its own bank account
|
|
16
|
+
if (acct && !ctx.rows('external_account').some((e) => e.account === acct && (e.currency ?? currency) === currency)) return noExternalAccount(ctx, currency);
|
|
17
|
+
const refused = refusePayout(ctx, amount, currency);
|
|
18
|
+
if (refused) return refused;
|
|
19
|
+
const id = ctx.mint('payout');
|
|
20
|
+
const bt = await settlePayout(ctx, id, -amount, currency);
|
|
21
|
+
return ctx.reply(await created(ctx, 'payout', { ...ctx.params, id }, { status: 'pending', ...payoutDefaults(ctx), balance_transaction: bt, destination: acct ? payoutBank(ctx, acct, currency) : null, ...(acct ? { _account: acct } : {}) }));
|
|
22
|
+
}
|
|
23
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Stripe's prices operations (docs/contributing/architecture.md, "What an author writes"): each handler over the
|
|
2
|
+
// handler contract's context, computing with the engine (../engine/).
|
|
3
|
+
import type { HandlerContext } from '@volter/world-core';
|
|
4
|
+
import type { Row } from '../engine/common.ts';
|
|
5
|
+
import { asBool } from '../engine/stripe.ts';
|
|
6
|
+
import { at, created, fail, list, newest, tieredPrice, where } from './shared.ts';
|
|
7
|
+
|
|
8
|
+
// `type` is not stored (it collides with the tree's discriminator), so `recurring` stands for it
|
|
9
|
+
export async function GetPrices(ctx: HandlerContext): Promise<Response> {
|
|
10
|
+
return list(ctx, 'price', where(ctx, newest(ctx, 'price'), {
|
|
11
|
+
active: (p, v) => asBool(p.active) === asBool(v),
|
|
12
|
+
product: (p, v) => p.product === v,
|
|
13
|
+
currency: (p, v) => p.currency === v,
|
|
14
|
+
type: (p, v) => (v === 'recurring' ? !!p.recurring : !p.recurring),
|
|
15
|
+
lookup_keys: (p, v) => (Array.isArray(v) ? v.map(String) : [String(v)]).includes(String(p.lookup_key)),
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function GetPricesPrice(ctx: HandlerContext): Promise<Response> {
|
|
20
|
+
const p = ctx.get('price', at(ctx, 'price'));
|
|
21
|
+
return p ? ctx.reply(p) : fail(ctx, `No such price: '${at(ctx, 'price')}'`, 404, 'resource_missing');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function PostPrices(ctx: HandlerContext): Promise<Response> {
|
|
25
|
+
const params = ctx.params;
|
|
26
|
+
// the product must exist
|
|
27
|
+
const product = typeof params.product === 'string' ? params.product : '';
|
|
28
|
+
if (!product || !ctx.get('product', product)) return fail(ctx, `No such product: '${product}'`, 400, 'resource_missing');
|
|
29
|
+
// a tiered price needs tiers_mode and tiers, each tier with up_to and a unit or flat amount; it
|
|
30
|
+
// has no single unit_amount, and its last tier's up_to is null (∞)
|
|
31
|
+
const scheme = typeof params.billing_scheme === 'string' ? params.billing_scheme : 'per_unit';
|
|
32
|
+
if (scheme === 'tiered') return tieredPrice(ctx, params);
|
|
33
|
+
// a price that recurs is billed by subscriptions; any other is paid once. Its recurring block answers what the create
|
|
34
|
+
// left out as Stripe does: `usage_type` "Defaults to `licensed`", and the create example's answer
|
|
35
|
+
// (docs.stripe.com/api/prices/create) carries `"interval_count": 1, "trial_period_days": null`; a meter only a metered
|
|
36
|
+
// price names
|
|
37
|
+
if (params.recurring && typeof params.recurring === 'object') {
|
|
38
|
+
const r = params.recurring as Row;
|
|
39
|
+
params.recurring = { ...r, interval_count: r.interval_count !== undefined ? Math.trunc(Number(r.interval_count)) : 1, usage_type: r.usage_type ?? 'licensed', trial_period_days: r.trial_period_days !== undefined ? Math.trunc(Number(r.trial_period_days)) : null, meter: r.meter ?? null };
|
|
40
|
+
}
|
|
41
|
+
return ctx.reply(await created(ctx, 'price', params, { active: true, livemode: false, billing_scheme: 'per_unit', type: params.recurring ? 'recurring' : 'one_time', metadata: {} }));
|
|
42
|
+
}
|
|
@@ -1,154 +1,30 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import { at, created, fail, kept, list, newest, where, type Row } from './shared.ts';
|
|
1
|
+
// Stripe's products operations (docs/contributing/architecture.md, "What an author writes"): each handler over the
|
|
2
|
+
// handler contract's context, computing with the engine (../engine/).
|
|
3
|
+
import type { HandlerContext } from '@volter/world-core';
|
|
4
|
+
import { asBool, nowUnix } from '../engine/stripe.ts';
|
|
5
|
+
import { alreadyAttached, at, created, fail, search, featureMissing, kept, list, newest, pricedProductRefused, productFeature, productMissing, where } from './shared.ts';
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const createProduct: Semantics = async (ctx) =>
|
|
11
|
-
ctx.reply(await created(ctx, 'product', ctx.params, { active: true, livemode: false, images: [], marketing_features: [], metadata: {}, updated: ctx.now() }));
|
|
12
|
-
|
|
13
|
-
const listProducts: Semantics = async (ctx) =>
|
|
14
|
-
list(ctx, 'product', where(ctx, newest(ctx, 'product'), {
|
|
15
|
-
active: (p, v) => asBool(p.active) === asBool(v),
|
|
16
|
-
ids: (p, v) => (Array.isArray(v) ? v.map(String).includes(String(p.id)) : String(p.id) === String(v)),
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
// a product with prices cannot be deleted; Stripe says to deactivate it instead
|
|
20
|
-
/** A product deleted while it has prices: "Deleting a product is only possible if it has no prices associated with it"
|
|
21
|
-
* (docs.stripe.com/api/products/delete). */
|
|
22
|
-
function pricedProductRefused(ctx: SemanticsContext): Response {
|
|
23
|
-
return fail(ctx, 'You cannot delete the product because it has one or more prices. Deactivate the product instead.', 400, 'resource_already_exists');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const deleteProduct: Semantics = async (ctx) => {
|
|
7
|
+
export async function DeleteProductsId(ctx: HandlerContext): Promise<Response> {
|
|
27
8
|
const id = at(ctx, 'id');
|
|
28
9
|
if (!ctx.get('product', id)) return productMissing(ctx, id);
|
|
29
10
|
if (ctx.rows('price').some((p) => p.product === id)) return pricedProductRefused(ctx);
|
|
30
11
|
await ctx.write('product', id, { deleted: true }, 'product.delete');
|
|
31
12
|
return ctx.reply({ id, object: 'product', deleted: true });
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
// ── features: an entitlements feature granted to a product, at most once ──
|
|
35
|
-
|
|
36
|
-
/** A feature attached to a product that already has it. */
|
|
37
|
-
function alreadyAttached(ctx: SemanticsContext): Response {
|
|
38
|
-
return fail(ctx, 'This feature is already attached to the product.', 400, 'resource_already_exists');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const attachFeature: Semantics = async (ctx) => {
|
|
42
|
-
const product = at(ctx, 'product');
|
|
43
|
-
if (!ctx.get('product', product)) return productMissing(ctx, product);
|
|
44
|
-
const feature = typeof ctx.params.entitlement_feature === 'string' ? ctx.params.entitlement_feature : '';
|
|
45
|
-
if (!feature) return fail(ctx, 'Missing required param: entitlement_feature.', 400, 'parameter_missing');
|
|
46
|
-
const feat = ctx.get('entitlements_feature', feature);
|
|
47
|
-
if (!feat) return fail(ctx, `No such feature: '${feature}'`, 400, 'resource_missing');
|
|
48
|
-
if (ctx.rowsRaw('product_feature').some((pf) => pf._product === product && pf._feature === feature)) return alreadyAttached(ctx);
|
|
49
|
-
// Stripe answers the feature itself as the product feature's entitlement_feature (docs.stripe.com/api/product-feature/object)
|
|
50
|
-
return ctx.reply(
|
|
51
|
-
await created(ctx, 'product_feature', { _product: product, _feature: feature }, {
|
|
52
|
-
livemode: false,
|
|
53
|
-
entitlement_feature: { id: feature, object: 'entitlements.feature', name: feat.name, lookup_key: feat.lookup_key, active: feat.active, metadata: feat.metadata ?? {}, livemode: false },
|
|
54
|
-
}),
|
|
55
|
-
);
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const features: Semantics = async (ctx) => {
|
|
59
|
-
const product = at(ctx, 'product');
|
|
60
|
-
if (!ctx.get('product', product)) return productMissing(ctx, product);
|
|
61
|
-
return list(ctx, 'product_feature', newest(ctx, 'product_feature').filter((pf) => kept(ctx, 'product_feature', pf, '_product') === product));
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const productFeature = (ctx: SemanticsContext): Row | undefined => {
|
|
65
|
-
const pf = ctx.get('product_feature', at(ctx, 'id'));
|
|
66
|
-
// the product a feature is attached to is its address, not a field Stripe answers
|
|
67
|
-
return pf && kept(ctx, 'product_feature', pf, '_product') === at(ctx, 'product') ? pf : undefined;
|
|
68
|
-
};
|
|
69
|
-
const featureMissing = (ctx: SemanticsContext): Response => fail(ctx, `No such product feature: '${at(ctx, 'id')}'`, 404, 'resource_missing');
|
|
70
|
-
|
|
71
|
-
const feature: Semantics = async (ctx) => {
|
|
72
|
-
const pf = productFeature(ctx);
|
|
73
|
-
return pf ? ctx.reply(pf) : featureMissing(ctx);
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const detachFeature: Semantics = async (ctx) => {
|
|
77
|
-
if (!productFeature(ctx)) return featureMissing(ctx);
|
|
78
|
-
const id = at(ctx, 'id');
|
|
79
|
-
// when it came off the product: a subscription keeps it until its next period (semantics/entitlements.ts)
|
|
80
|
-
await ctx.write('product_feature', id, { deleted: true, _removed_at: Number(ctx.now()) }, 'product_feature.delete');
|
|
81
|
-
return ctx.reply({ id, object: 'product_feature', deleted: true });
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
// ── prices ──
|
|
85
|
-
|
|
86
|
-
/** A tiered price: tiers_mode and tiers, each tier with up_to and a unit or flat amount; no single unit_amount, and
|
|
87
|
-
* the last tier's up_to null (∞) (docs.stripe.com/products-prices/pricing-models#tiered-pricing). */
|
|
88
|
-
async function tieredPrice(ctx: SemanticsContext, params: Row): Promise<Response> {
|
|
89
|
-
const mode = typeof params.tiers_mode === 'string' ? params.tiers_mode : '';
|
|
90
|
-
if (mode !== 'graduated' && mode !== 'volume') return fail(ctx, 'Missing required param: tiers_mode (graduated or volume).', 400, 'parameter_missing');
|
|
91
|
-
const rawTiers = Array.isArray(params.tiers) ? (params.tiers as Row[]) : [];
|
|
92
|
-
if (rawTiers.length === 0) return fail(ctx, 'Missing required param: tiers.', 400, 'parameter_missing');
|
|
93
|
-
const tiers = rawTiers.map((t) => {
|
|
94
|
-
const upToRaw = t.up_to;
|
|
95
|
-
return {
|
|
96
|
-
up_to: upToRaw === 'inf' || upToRaw === undefined || upToRaw === null ? null : Math.trunc(Number(upToRaw)),
|
|
97
|
-
unit_amount: t.unit_amount !== undefined ? Math.trunc(Number(t.unit_amount)) : null,
|
|
98
|
-
unit_amount_decimal: t.unit_amount !== undefined ? String(Math.trunc(Number(t.unit_amount))) : null,
|
|
99
|
-
flat_amount: t.flat_amount !== undefined ? Math.trunc(Number(t.flat_amount)) : null,
|
|
100
|
-
flat_amount_decimal: t.flat_amount !== undefined ? String(Math.trunc(Number(t.flat_amount))) : null,
|
|
101
|
-
};
|
|
102
|
-
});
|
|
103
|
-
if (tiers.some((t) => t.unit_amount === null && t.flat_amount === null)) return fail(ctx, 'Each tier must specify a unit_amount or flat_amount.', 400, 'parameter_invalid_empty');
|
|
104
|
-
const { tiers: _t, ...rest } = params;
|
|
105
|
-
return ctx.reply(await created(ctx, 'price', { ...rest, billing_scheme: 'tiered', tiers_mode: mode, tiers, unit_amount: null }, { active: true, livemode: false, type: rest.recurring ? 'recurring' : 'one_time', metadata: {} }));
|
|
106
13
|
}
|
|
107
14
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// the product must exist
|
|
111
|
-
const product = typeof params.product === 'string' ? params.product : '';
|
|
112
|
-
if (!product || !ctx.get('product', product)) return fail(ctx, `No such product: '${product}'`, 400, 'resource_missing');
|
|
113
|
-
// a tiered price needs tiers_mode and tiers, each tier with up_to and a unit or flat amount; it
|
|
114
|
-
// has no single unit_amount, and its last tier's up_to is null (∞)
|
|
115
|
-
const scheme = typeof params.billing_scheme === 'string' ? params.billing_scheme : 'per_unit';
|
|
116
|
-
if (scheme === 'tiered') return tieredPrice(ctx, params);
|
|
117
|
-
// a price that recurs is billed by subscriptions; any other is paid once. Its recurring block answers what the create
|
|
118
|
-
// left out as Stripe does: `usage_type` "Defaults to `licensed`", and the create example's answer
|
|
119
|
-
// (docs.stripe.com/api/prices/create) carries `"interval_count": 1, "trial_period_days": null`; a meter only a metered
|
|
120
|
-
// price names
|
|
121
|
-
if (params.recurring && typeof params.recurring === 'object') {
|
|
122
|
-
const r = params.recurring as Row;
|
|
123
|
-
params.recurring = { ...r, interval_count: r.interval_count !== undefined ? Math.trunc(Number(r.interval_count)) : 1, usage_type: r.usage_type ?? 'licensed', trial_period_days: r.trial_period_days !== undefined ? Math.trunc(Number(r.trial_period_days)) : null, meter: r.meter ?? null };
|
|
124
|
-
}
|
|
125
|
-
return ctx.reply(await created(ctx, 'price', params, { active: true, livemode: false, billing_scheme: 'per_unit', type: params.recurring ? 'recurring' : 'one_time', metadata: {} }));
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
// `type` is not stored (it collides with the tree's discriminator), so `recurring` stands for it
|
|
129
|
-
const listPrices: Semantics = async (ctx) =>
|
|
130
|
-
list(ctx, 'price', where(ctx, newest(ctx, 'price'), {
|
|
15
|
+
export async function GetProducts(ctx: HandlerContext): Promise<Response> {
|
|
16
|
+
return list(ctx, 'product', where(ctx, newest(ctx, 'product'), {
|
|
131
17
|
active: (p, v) => asBool(p.active) === asBool(v),
|
|
132
|
-
|
|
133
|
-
currency: (p, v) => p.currency === v,
|
|
134
|
-
type: (p, v) => (v === 'recurring' ? !!p.recurring : !p.recurring),
|
|
135
|
-
lookup_keys: (p, v) => (Array.isArray(v) ? v.map(String) : [String(v)]).includes(String(p.lookup_key)),
|
|
18
|
+
ids: (p, v) => (Array.isArray(v) ? v.map(String).includes(String(p.id)) : String(p.id) === String(v)),
|
|
136
19
|
}));
|
|
20
|
+
}
|
|
137
21
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
};
|
|
22
|
+
export async function PostProducts(ctx: HandlerContext): Promise<Response> {
|
|
23
|
+
return ctx.reply(await created(ctx, 'product', ctx.params, { active: true, livemode: false, images: [], marketing_features: [], metadata: {}, updated: nowUnix(ctx.occurredAt) }));
|
|
24
|
+
}
|
|
142
25
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
GetProductsProductFeatures: features,
|
|
149
|
-
GetProductsProductFeaturesId: feature,
|
|
150
|
-
DeleteProductsProductFeaturesId: detachFeature,
|
|
151
|
-
PostPrices: createPrice,
|
|
152
|
-
GetPrices: listPrices,
|
|
153
|
-
GetPricesPrice: retrievePrice,
|
|
154
|
-
};
|
|
26
|
+
/** `GET /v1/products/search?query=`: the products Stripe's search language selects (Ponder's catalog probes). */
|
|
27
|
+
// source: spec:GetProductsSearch "Search for products you’ve previously created using Stripe’s"
|
|
28
|
+
export async function GetProductsSearch(ctx: HandlerContext): Promise<Response> {
|
|
29
|
+
return search(ctx, 'product');
|
|
30
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Stripe's promotion_codes operations (docs/contributing/architecture.md, "What an author writes"): each handler over the
|
|
2
|
+
// handler contract's context, computing with the engine (../engine/).
|
|
3
|
+
import type { HandlerContext } from '@volter/world-core';
|
|
4
|
+
import { asBool } from '../engine/stripe.ts';
|
|
5
|
+
import { created, fail, list, newest, where } from './shared.ts';
|
|
6
|
+
|
|
7
|
+
export async function GetPromotionCodes(ctx: HandlerContext): Promise<Response> {
|
|
8
|
+
return list(ctx, 'promotion_code', where(ctx, newest(ctx, 'promotion_code'), {
|
|
9
|
+
code: (p, v) => p.code === v,
|
|
10
|
+
active: (p, v) => asBool(p.active) === asBool(v),
|
|
11
|
+
coupon: (p, v) => p.coupon === v,
|
|
12
|
+
customer: (p, v) => p.customer === v,
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// a code the caller does not give is derived from the promotion code's id
|
|
17
|
+
export async function PostPromotionCodes(ctx: HandlerContext): Promise<Response> {
|
|
18
|
+
// what it promotes: promotion[type]=coupon and promotion[coupon] (2025-09-30.clover,
|
|
19
|
+
// docs.stripe.com/changelog/clover/2025-09-30/polymorphic-coupon), or the top-level coupon a caller pinned to an
|
|
20
|
+
// earlier version sends; the twin keeps the coupon's id on the code for its rules
|
|
21
|
+
const given = ctx.params;
|
|
22
|
+
const { promotion, coupon: legacy, ...params } = given;
|
|
23
|
+
const promo = promotion && typeof promotion === 'object' ? (promotion as Record<string, unknown>) : undefined;
|
|
24
|
+
if (promo && promo.type !== 'coupon') return fail(ctx, 'Invalid promotion[type]: must be coupon.', 400, 'parameter_invalid_string_enum');
|
|
25
|
+
const coupon = typeof promo?.coupon === 'string' ? promo.coupon : typeof legacy === 'string' ? legacy : '';
|
|
26
|
+
if (!coupon) return fail(ctx, promo ? 'Missing required param: promotion[coupon].' : 'Missing required param: promotion.', 400, 'parameter_missing');
|
|
27
|
+
if (!ctx.get('coupon', coupon)) return fail(ctx, `No such coupon: '${coupon}'`, 400, 'resource_missing');
|
|
28
|
+
const id = ctx.mint('promotion_code');
|
|
29
|
+
const code = typeof params.code === 'string' && params.code ? params.code : `TWIN${id.toUpperCase().replace(/[^A-Z0-9]/g, '')}`;
|
|
30
|
+
return ctx.reply(
|
|
31
|
+
await created(ctx, 'promotion_code', { ...params, coupon, promotion: { type: 'coupon', coupon }, id, code }, {
|
|
32
|
+
active: params.active !== undefined ? asBool(params.active) : true,
|
|
33
|
+
customer: typeof params.customer === 'string' ? params.customer : null,
|
|
34
|
+
expires_at: params.expires_at !== undefined ? Number(params.expires_at) : null,
|
|
35
|
+
max_redemptions: params.max_redemptions !== undefined ? Number(params.max_redemptions) : null,
|
|
36
|
+
times_redeemed: 0, livemode: false, metadata: {}, restrictions: { first_time_transaction: false, minimum_amount: null, minimum_amount_currency: null },
|
|
37
|
+
}),
|
|
38
|
+
);
|
|
39
|
+
}
|
package/src/semantics/radar.ts
CHANGED
|
@@ -1,85 +1,16 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import { at, created, fail, list, newest } from './shared.ts';
|
|
1
|
+
// Stripe's radar operations (docs/contributing/architecture.md, "What an author writes"): each handler over the
|
|
2
|
+
// handler contract's context, computing with the engine (../engine/).
|
|
3
|
+
import type { HandlerContext } from '@volter/world-core';
|
|
4
|
+
import { VL, VLI } from '../engine/radar.ts';
|
|
5
|
+
import { at, created, fail, list, listMissing, newest, radarSyncItems } from './shared.ts';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
const id = at(ctx, 'review');
|
|
9
|
-
const r = ctx.get('review', id);
|
|
10
|
-
if (!r) return fail(ctx, `No such review: '${id}'`, 404, 'resource_missing');
|
|
11
|
-
const refused = ctx.legal('review', 'open', 'PostReviewsReviewApprove', r.open === true, undefined, id);
|
|
12
|
-
if (refused) return ctx.refuse(refused);
|
|
13
|
-
return ctx.reply(await ctx.write('review', id, { open: false, closed_reason: 'approved', reason: 'approved' }, 'review.closed'));
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const VL = 'radar.value_list';
|
|
17
|
-
const VLI = 'radar.value_list_item';
|
|
18
|
-
const listMissing = (ctx: SemanticsContext, id: string, status = 404): Response => fail(ctx, `No such radar value list: '${id}'`, status, 'resource_missing');
|
|
19
|
-
|
|
20
|
-
const createList: Semantics = async (ctx) => {
|
|
21
|
-
const params = ctx.params;
|
|
22
|
-
if (params.alias === undefined || params.alias === '') return fail(ctx, 'Missing required param: alias.', 400, 'parameter_missing');
|
|
23
|
-
if (params.name === undefined || params.name === '') return fail(ctx, 'Missing required param: name.', 400, 'parameter_missing');
|
|
24
|
-
const made = await created(ctx, VL, params, {
|
|
25
|
-
item_type: typeof params.item_type === 'string' ? params.item_type : 'string', livemode: false, metadata: {},
|
|
26
|
-
created_by: 'twin', list_items: { object: 'list', data: [], has_more: false, url: '' },
|
|
27
|
-
});
|
|
28
|
-
await syncItems(ctx, String(made.id));
|
|
29
|
-
return ctx.reply(ctx.get(VL, String(made.id))!);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/** A value list's list_items, "List of items contained within this value list" (the served spec), kept in step with
|
|
33
|
-
* its items, at the address the fixture names (/v1/radar/value_list_items?value_list=…). */
|
|
34
|
-
async function syncItems(ctx: SemanticsContext, listId: string): Promise<void> {
|
|
35
|
-
const data = newest(ctx, VLI).filter((i) => i.value_list === listId);
|
|
36
|
-
await ctx.write(VL, listId, { list_items: { object: 'list', data, has_more: false, url: `/v1/radar/value_list_items?value_list=${listId}` } }, 'radar.value_list.items_synced');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const updateList: Semantics = async (ctx) => {
|
|
40
|
-
const id = at(ctx, 'value_list');
|
|
41
|
-
if (!ctx.row(VL, id, { withDeleted: true })) return listMissing(ctx, id);
|
|
42
|
-
return ctx.reply(await ctx.write(VL, id, ctx.params, 'radar.value_list.updated'));
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const deleteList: Semantics = async (ctx) => {
|
|
46
|
-
const id = at(ctx, 'value_list');
|
|
47
|
-
if (!ctx.row(VL, id, { withDeleted: true })) return listMissing(ctx, id);
|
|
48
|
-
await ctx.write(VL, id, { deleted: true }, 'radar.value_list.deleted');
|
|
49
|
-
return ctx.reply({ id, object: 'radar.value_list', deleted: true });
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const addItem: Semantics = async (ctx) => {
|
|
7
|
+
export async function PostRadarValueListItems(ctx: HandlerContext): Promise<Response> {
|
|
53
8
|
const listId = typeof ctx.params.value_list === 'string' ? ctx.params.value_list : '';
|
|
54
9
|
if (!listId) return fail(ctx, 'Missing required param: value_list.', 400, 'parameter_missing');
|
|
55
10
|
if (!ctx.row(VL, listId, { withDeleted: true })) return listMissing(ctx, listId, 400);
|
|
56
11
|
if (ctx.params.value === undefined || ctx.params.value === '') return fail(ctx, 'Missing required param: value.', 400, 'parameter_missing');
|
|
57
12
|
const item = await created(ctx, VLI, { value_list: listId, value: ctx.params.value }, { livemode: false, created_by: 'twin' });
|
|
58
|
-
await
|
|
13
|
+
await radarSyncItems(ctx, listId);
|
|
59
14
|
return ctx.reply(item);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const items: Semantics = async (ctx) => {
|
|
63
|
-
const listId = typeof ctx.params.value_list === 'string' ? ctx.params.value_list : '';
|
|
64
|
-
if (!listId) return fail(ctx, 'Missing required param: value_list.', 400, 'parameter_missing');
|
|
65
|
-
return list(ctx, VLI, newest(ctx, VLI).filter((i) => i.value_list === listId));
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const removeItem: Semantics = async (ctx) => {
|
|
69
|
-
const id = at(ctx, 'item');
|
|
70
|
-
const was = ctx.row(VLI, id, { withDeleted: true });
|
|
71
|
-
if (!was) return fail(ctx, `No such radar value list item: '${id}'`, 404, 'resource_missing');
|
|
72
|
-
await ctx.write(VLI, id, { deleted: true }, 'radar.value_list_item.deleted');
|
|
73
|
-
if (typeof was.value_list === 'string' && ctx.get(VL, was.value_list)) await syncItems(ctx, was.value_list);
|
|
74
|
-
return ctx.reply({ id, object: 'radar.value_list_item', deleted: true });
|
|
75
|
-
};
|
|
15
|
+
}
|
|
76
16
|
|
|
77
|
-
export const radar: Record<string, Semantics> = {
|
|
78
|
-
PostReviewsReviewApprove: approve,
|
|
79
|
-
PostRadarValueLists: createList,
|
|
80
|
-
PostRadarValueListsValueList: updateList,
|
|
81
|
-
DeleteRadarValueListsValueList: deleteList,
|
|
82
|
-
PostRadarValueListItems: addItem,
|
|
83
|
-
GetRadarValueListItems: items,
|
|
84
|
-
DeleteRadarValueListItemsItem: removeItem,
|
|
85
|
-
};
|