@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
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// Connect semantics: connected accounts (and the platform's own account), their persons,
|
|
2
|
+
// capabilities, external (payout) accounts, login links, onboarding links and embedded-component
|
|
3
|
+
// sessions. A new connected account is not yet active: charges and payouts are off and its
|
|
4
|
+
// requirements are outstanding, as on Stripe before onboarding. Account retrieve and delete and
|
|
5
|
+
// the person list are the derived core's.
|
|
6
|
+
import { accountRequirements, accountSettings, asBool, PLATFORM_ACCOUNT_ID } from './stripe.ts';
|
|
7
|
+
import { currentlyDue, kindOf } from './onboarding.ts';
|
|
8
|
+
import { externalList, type Row } from './common.ts';
|
|
9
|
+
|
|
10
|
+
// the platform's own account: stored once it is written, otherwise Stripe's default. It "controls itself" (controller
|
|
11
|
+
// type `account`, docs.stripe.com/api/accounts/object), is fully onboarded with nothing due, and takes card payments
|
|
12
|
+
// and transfers. Where the documentation stops and the twin decides: it was created as the World began (2026-01-01),
|
|
13
|
+
// and its bank account, which its automatic payouts reach, is not modelled, so it lists none.
|
|
14
|
+
export const PLATFORM_CREATED = 1_767_225_600;
|
|
15
|
+
/** The platform's own account as Stripe's default has it, before anything is written to it. */
|
|
16
|
+
export const platformAccountDefault = (): Row => ({
|
|
17
|
+
object: 'account', id: PLATFORM_ACCOUNT_ID, type: 'standard', country: 'US', default_currency: 'usd', created: PLATFORM_CREATED,
|
|
18
|
+
charges_enabled: true, payouts_enabled: true, details_submitted: true, email: null, metadata: {},
|
|
19
|
+
capabilities: { card_payments: 'active', transfers: 'active' }, controller: { type: 'account' },
|
|
20
|
+
requirements: { alternatives: [], current_deadline: null, currently_due: [], disabled_reason: null, errors: [], eventually_due: [], past_due: [], pending_verification: [] },
|
|
21
|
+
future_requirements: { alternatives: [], current_deadline: null, currently_due: [], disabled_reason: null, errors: [], eventually_due: [], past_due: [], pending_verification: [] },
|
|
22
|
+
external_accounts: externalList(PLATFORM_ACCOUNT_ID, []), tos_acceptance: { date: null, ip: null, user_agent: null }, business_profile: {},
|
|
23
|
+
settings: accountSettings(undefined), livemode: false,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/** A new Express account owes what its hosted onboarding will ask for (screens/connect-onboarding.tsx; the US sets
|
|
27
|
+
* docs.stripe.com/connect/required-verification-information lists), or, where the twin does not model the set, the
|
|
28
|
+
* generic list. */
|
|
29
|
+
export function expressRequirements(params: Row): Row {
|
|
30
|
+
const kind = kindOf({ country: params.country ?? 'US', business_type: params.business_type ?? 'individual' });
|
|
31
|
+
const base = accountRequirements();
|
|
32
|
+
return kind ? { ...base, currently_due: currentlyDue(kind), eventually_due: currentlyDue(kind) } : base;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The controller an account made with a `type` has: "Each of the three account types maps to values in the
|
|
36
|
+
* `controller` hash", Standard to losses.payments `stripe`, fees.payer `account`, requirement_collection `stripe` and
|
|
37
|
+
* a `full` dashboard, Express to `application`, `application_express`, `stripe` and `express`, Custom to
|
|
38
|
+
* `application`, `application_custom`, `application` and `none`; each "type": "application", "is_controller": true
|
|
39
|
+
* (docs.stripe.com/connect/migrate-to-controller-properties). */
|
|
40
|
+
export function controllerOf(type: string): Row {
|
|
41
|
+
const [losses, payer, collection, dashboard] = type === 'express' ? ['application', 'application_express', 'stripe', 'express']
|
|
42
|
+
: type === 'custom' ? ['application', 'application_custom', 'application', 'none'] : ['stripe', 'account', 'stripe', 'full'];
|
|
43
|
+
return { type: 'application', is_controller: true, losses: { payments: losses }, fees: { payer }, requirement_collection: collection, stripe_dashboard: { type: dashboard } };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** What a Custom account still owes, from what the platform has given for it, as Stripe's requirements endpoint lists it
|
|
47
|
+
* for a US account with no Stripe Dashboard and the full service agreement
|
|
48
|
+
* (docs.stripe.com/_endpoint/get-requirements-for-setups, the data behind
|
|
49
|
+
* docs.stripe.com/connect/required-verification-information). With transfers alone, a company owes business_profile.url,
|
|
50
|
+
* company.name, an external account and tos_acceptance.date and .ip at once (capability_limit_amount -1: paused until
|
|
51
|
+
* given) and company.tax_id before $3,000 of payouts (payout_limit_amount 300000); an individual owes the url, its
|
|
52
|
+
* first and last name, the bank account and the terms at once, and its date of birth and ssn_last_4 before $3,000 of
|
|
53
|
+
* payouts. Requesting card_payments adds business_profile.mcc and the entity's address at once (and an individual's
|
|
54
|
+
* email and ssn_last_4). A company with card_payments owes, at once, business_profile.mcc, its address, that its owners
|
|
55
|
+
* are provided, each owner's name and email, and its representative's name, email, address, title and ssn_last_4; and
|
|
56
|
+
* in time its phone, its statement descriptor and its representative's date of birth and phone (the endpoint's answer
|
|
57
|
+
* for US / dashboard none / full terms / company / card_payments and transfers, saved in scratch as
|
|
58
|
+
* req-custom-company-cards-transfers.json: capability_limit_amount -1 for the first, none for the second). The
|
|
59
|
+
* representative and the owners are the account's persons (relationship.representative, relationship.owner).
|
|
60
|
+
* Where the documentation stops and the twin decides: what has no limit, or is owed before $3,000 of payouts, is
|
|
61
|
+
* eventually_due and blocks nothing (the twin keeps no payout total against it); an owner field is met when every
|
|
62
|
+
* person marked owner has it (none marked: met). */
|
|
63
|
+
export const at_path = (a: Row, path: string): unknown => path.split('.').reduce<unknown>((v, k) => (v && typeof v === 'object' ? (v as Row)[k] : undefined), a);
|
|
64
|
+
export const CARD_COMPANY_NOW = ['business_profile.mcc', 'company.address.line1', 'company.address.city', 'company.address.state', 'company.address.postal_code', 'company.owners_provided', 'owners.first_name', 'owners.last_name', 'owners.email', 'representative.first_name', 'representative.last_name', 'representative.email', 'representative.address.line1', 'representative.address.city', 'representative.address.state', 'representative.address.postal_code', 'representative.relationship.title', 'representative.ssn_last_4'];
|
|
65
|
+
export const CARD_COMPANY_LATER = ['company.phone', 'settings.payments.statement_descriptor', 'representative.dob.day', 'representative.dob.month', 'representative.dob.year', 'representative.phone'];
|
|
66
|
+
export const CARD_INDIVIDUAL_NOW = ['business_profile.mcc', 'individual.address.line1', 'individual.address.city', 'individual.address.state', 'individual.address.postal_code', 'individual.email', 'individual.ssn_last_4'];
|
|
67
|
+
/** A field only card_payments asks for (the transfers capability's requirements leave it out). */
|
|
68
|
+
export const cardOnly = (field: string): boolean => [...CARD_COMPANY_NOW, ...CARD_COMPANY_LATER, ...CARD_INDIVIDUAL_NOW].includes(field);
|
|
69
|
+
export const personHas = (p: Row, field: string): boolean => {
|
|
70
|
+
if (field === 'ssn_last_4') return p.ssn_last_4_provided === true;
|
|
71
|
+
const v = at_path(p, field);
|
|
72
|
+
return v !== undefined && v !== null && v !== '';
|
|
73
|
+
};
|
|
74
|
+
export const met = (a: Row, bank: boolean, field: string, persons: Row[] = []): boolean => {
|
|
75
|
+
if (field === 'external_account') return bank;
|
|
76
|
+
if (field.startsWith('representative.')) {
|
|
77
|
+
const rep = persons.find((p) => (p.relationship as Row | undefined)?.representative === true);
|
|
78
|
+
return !!rep && personHas(rep, field.slice('representative.'.length));
|
|
79
|
+
}
|
|
80
|
+
if (field.startsWith('owners.')) return persons.filter((p) => (p.relationship as Row | undefined)?.owner === true).every((p) => personHas(p, field.slice('owners.'.length)));
|
|
81
|
+
if (field === 'company.tax_id') return at_path(a, 'company.tax_id_provided') === true;
|
|
82
|
+
if (field === 'individual.ssn_last_4') return at_path(a, 'individual.ssn_last_4_provided') === true;
|
|
83
|
+
const v = at_path(a, field);
|
|
84
|
+
return v !== undefined && v !== null && v !== '';
|
|
85
|
+
};
|
|
86
|
+
export function customRequirements(a: Row, bank: boolean, persons: Row[] = []): { now: string[]; later: string[] } {
|
|
87
|
+
const company = a.business_type === 'company';
|
|
88
|
+
const cards = Object.keys((a.capabilities as Row | undefined) ?? {}).includes('card_payments');
|
|
89
|
+
const now = ['business_profile.url', ...(company ? ['company.name'] : ['individual.first_name', 'individual.last_name']), 'external_account', 'tos_acceptance.date', 'tos_acceptance.ip'];
|
|
90
|
+
if (cards) now.push(...(company ? CARD_COMPANY_NOW : CARD_INDIVIDUAL_NOW));
|
|
91
|
+
const later = [...(company ? ['company.tax_id'] : ['individual.dob.day', 'individual.dob.month', 'individual.dob.year', 'individual.ssn_last_4']), ...(cards && company ? CARD_COMPANY_LATER : [])].filter((f) => !now.includes(f));
|
|
92
|
+
return { now: now.filter((f) => !met(a, bank, f, persons)), later: later.filter((f) => !met(a, bank, f, persons)) };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** A request's company or individual as Stripe keeps it: a tax id or SSN given is kept only as provided (the Account
|
|
96
|
+
* object answers company.tax_id_provided and individual.ssn_last_4_provided, never the numbers;
|
|
97
|
+
* docs.stripe.com/api/accounts/object). */
|
|
98
|
+
export function keptKyc(params: Row): Row {
|
|
99
|
+
const out: Row = { ...params };
|
|
100
|
+
if (params.company && typeof params.company === 'object') {
|
|
101
|
+
const { tax_id, ...company } = params.company as Row;
|
|
102
|
+
out.company = { ...company, ...(tax_id !== undefined && tax_id !== '' ? { tax_id_provided: true } : {}) };
|
|
103
|
+
}
|
|
104
|
+
if (params.individual && typeof params.individual === 'object') {
|
|
105
|
+
const { ssn_last_4, id_number, ...individual } = params.individual as Row;
|
|
106
|
+
out.individual = { ...individual, ...(ssn_last_4 !== undefined && ssn_last_4 !== '' ? { ssn_last_4_provided: true } : {}), ...(id_number !== undefined && id_number !== '' ? { id_number_provided: true } : {}) };
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** A person's details as Stripe keeps them: an SSN or ID number given is kept only as provided (the Person object
|
|
112
|
+
* answers ssn_last_4_provided and id_number_provided, never the numbers; docs.stripe.com/api/persons/object). */
|
|
113
|
+
export function keptPerson(params: Row, was: Row = {}): Row {
|
|
114
|
+
const { ssn_last_4, id_number, ...rest } = params;
|
|
115
|
+
const out: Row = { ...rest, ...(ssn_last_4 !== undefined && ssn_last_4 !== '' ? { ssn_last_4_provided: true } : {}), ...(id_number !== undefined && id_number !== '' ? { id_number_provided: true } : {}) };
|
|
116
|
+
// a relationship, address or date of birth given in part is laid over what the person holds
|
|
117
|
+
for (const k of ['relationship', 'address', 'dob'] as const) if (out[k] && typeof out[k] === 'object' && was[k] && typeof was[k] === 'object') out[k] = { ...(was[k] as Row), ...(out[k] as Row) };
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
export const RELATIONSHIP: Row = { director: false, executive: false, owner: false, representative: false, percent_ownership: null, title: null };
|
|
121
|
+
|
|
122
|
+
// ── capabilities: the account's capability map is the truth; requesting one leaves it pending ──
|
|
123
|
+
|
|
124
|
+
/** A capability as its account stands: its status, and the account's requirements that capability asks for (transfers
|
|
125
|
+
* leaves out what only card_payments asks for). Where the documentation stops and the twin decides: the account-wide
|
|
126
|
+
* fields (the bank account, the terms) belong to every capability. */
|
|
127
|
+
export const capabilityBody = (acct: Row, id: string, status: unknown, requested = true, requestedAt: unknown = null): Row => {
|
|
128
|
+
const req = (acct.requirements as Row | undefined) ?? {};
|
|
129
|
+
const mine = (list: unknown): string[] => (Array.isArray(list) ? (list as string[]) : []).filter((f) => id === 'card_payments' || !cardOnly(f));
|
|
130
|
+
const due = mine(req.currently_due);
|
|
131
|
+
return {
|
|
132
|
+
id, object: 'capability', account: acct.id, status, requested, requested_at: requestedAt,
|
|
133
|
+
requirements: { currently_due: due, eventually_due: mine(req.eventually_due), past_due: [], pending_verification: [], errors: [], alternatives: [], current_deadline: null, disabled_reason: due.length ? 'requirements.past_due' : null },
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// a session is identified by its client_secret: the row is kept for traceability, the answer has no id
|
|
138
|
+
/** Every embedded component an account session answers (the served spec requires all of them), each enabled only when
|
|
139
|
+
* the request enables it, with its features: what the request gives, else the spec's stated default, "The default
|
|
140
|
+
* value for this feature is `true`" (external_account_collection); disable_stripe_user_authentication "the opposite of
|
|
141
|
+
* the `external_account_collection` value"; edit_payout_schedule and standard_payouts "Defaults to `true` when
|
|
142
|
+
* `controller.losses.payments` is set to `stripe` for the account, otherwise `false`"; capture_payments, dispute_management
|
|
143
|
+
* and refund_management "true by default"; destination_on_behalf_of_charge_management "false by default";
|
|
144
|
+
* smart_disputes_management "Defaults to the value of `dispute_management`". Where the documentation stops and the twin
|
|
145
|
+
* decides: instant_payouts (described as `enabled` "when Stripe is responsible for negative account balances", typed
|
|
146
|
+
* boolean) is true exactly then, and the Issuing and financial-account features, which state no default, are false. */
|
|
147
|
+
export const SESSION_COMPONENTS: Record<string, string[]> = {
|
|
148
|
+
account_management: ['external_account_collection', 'disable_stripe_user_authentication'],
|
|
149
|
+
account_onboarding: ['external_account_collection', 'disable_stripe_user_authentication'],
|
|
150
|
+
balance_report: [], documents: [], payout_details: [], payout_reconciliation_report: [], payouts_list: [], tax_registrations: [], tax_settings: [],
|
|
151
|
+
balances: ['external_account_collection', 'disable_stripe_user_authentication', 'edit_payout_schedule', 'instant_payouts', 'standard_payouts'],
|
|
152
|
+
payouts: ['external_account_collection', 'disable_stripe_user_authentication', 'edit_payout_schedule', 'instant_payouts', 'standard_payouts'],
|
|
153
|
+
disputes_list: ['capture_payments', 'destination_on_behalf_of_charge_management', 'dispute_management', 'refund_management', 'smart_disputes_management'],
|
|
154
|
+
payment_details: ['capture_payments', 'destination_on_behalf_of_charge_management', 'dispute_management', 'refund_management', 'smart_disputes_management'],
|
|
155
|
+
payments: ['capture_payments', 'destination_on_behalf_of_charge_management', 'dispute_management', 'refund_management', 'smart_disputes_management'],
|
|
156
|
+
payment_disputes: ['destination_on_behalf_of_charge_management', 'dispute_management', 'refund_management', 'smart_disputes_management'],
|
|
157
|
+
financial_account: ['external_account_collection', 'disable_stripe_user_authentication', 'send_money', 'transfer_balance'],
|
|
158
|
+
financial_account_transactions: ['card_spend_dispute_management'],
|
|
159
|
+
instant_payouts_promotion: ['external_account_collection', 'disable_stripe_user_authentication', 'instant_payouts'],
|
|
160
|
+
issuing_card: ['card_management', 'card_spend_dispute_management', 'cardholder_management', 'spend_control_management'],
|
|
161
|
+
issuing_cards_list: ['card_management', 'card_spend_dispute_management', 'cardholder_management', 'disable_stripe_user_authentication', 'spend_control_management'],
|
|
162
|
+
notification_banner: ['external_account_collection', 'disable_stripe_user_authentication'],
|
|
163
|
+
payment_method_settings: ['disable_stripe_user_authentication'],
|
|
164
|
+
};
|
|
165
|
+
export function sessionComponents(account: Row, given: Row): Row {
|
|
166
|
+
const stripeLosses = (((account.controller as Row | undefined)?.losses as Row | undefined)?.payments) === 'stripe';
|
|
167
|
+
const out: Row = {};
|
|
168
|
+
for (const [name, featureNames] of Object.entries(SESSION_COMPONENTS)) {
|
|
169
|
+
const c = given[name] && typeof given[name] === 'object' ? (given[name] as Row) : {};
|
|
170
|
+
const asked = c.features && typeof c.features === 'object' ? (c.features as Row) : {};
|
|
171
|
+
const f: Row = {};
|
|
172
|
+
const val = (k: string, fallback: boolean): boolean => (asked[k] !== undefined ? asBool(asked[k]) : fallback);
|
|
173
|
+
for (const k of featureNames) {
|
|
174
|
+
if (k === 'external_account_collection') f[k] = val(k, true);
|
|
175
|
+
else if (k === 'disable_stripe_user_authentication') f[k] = val(k, !val('external_account_collection', true));
|
|
176
|
+
else if (k === 'edit_payout_schedule' || k === 'standard_payouts' || k === 'instant_payouts') f[k] = val(k, stripeLosses);
|
|
177
|
+
else if (k === 'capture_payments' || k === 'dispute_management' || k === 'refund_management') f[k] = val(k, true);
|
|
178
|
+
else if (k === 'smart_disputes_management') f[k] = val(k, val('dispute_management', true));
|
|
179
|
+
else f[k] = val(k, false);
|
|
180
|
+
}
|
|
181
|
+
out[name] = { enabled: c.enabled !== undefined ? asBool(c.enabled) : false, features: f };
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Coupon and PromotionCode semantics. A coupon is exactly one of percent_off or amount_off (with
|
|
2
|
+
// its currency) over a duration; a promotion code is a customer-facing code for an existing
|
|
3
|
+
// coupon, active until switched off (the machine in ../manifest.ts). Coupon list, retrieve, update
|
|
4
|
+
// and delete and promotion-code retrieve and update (`active`, the machine) are the derived core's.
|
|
5
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Credit note semantics. A credit note credits part of an invoice: pre_payment while the invoice
|
|
2
|
+
// is unpaid (it lowers what is owed), post_payment once paid. The invoice carries the sums of its
|
|
3
|
+
// issued credit notes, re-derived on every change so voiding one returns the invoice to where it
|
|
4
|
+
// was. The machine in ../manifest.ts says a credit note is voided once. The list is the derived core's.
|
|
5
|
+
|
|
6
|
+
export const CN = 'credit_note';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Customer semantics: the customer and what hangs off it (tax ids, the credit-balance ledger, the
|
|
2
|
+
// cash balance, legacy sources, attached payment methods) and the top-level Source object. List,
|
|
3
|
+
// update and delete of a customer, retrieve of a Source and the flat tax-id list are the derived core's.
|
|
4
|
+
import { invoicePrefix } from './invoices.ts';
|
|
5
|
+
import type { Row } from './common.ts';
|
|
6
|
+
|
|
7
|
+
/** What a new customer answers before any invoice: its invoice_prefix and a next_invoice_sequence of 1, a zero balance,
|
|
8
|
+
* no metadata (docs.stripe.com/api/customers/object). */
|
|
9
|
+
export const newCustomer = (id: string): Row => ({ invoice_prefix: invoicePrefix(id), next_invoice_sequence: 1, balance: 0, metadata: {}, invoice_settings: {} });
|
|
10
|
+
|
|
11
|
+
// ── tax ids: a customer's, keyed under it; `type` and `value` are required ──
|
|
12
|
+
|
|
13
|
+
// An EU VAT number is its country's prefix and that country's format (the European Commission's VIES formats,
|
|
14
|
+
// ec.europa.eu/taxation_customs/vies); Stripe refuses one that does not fit as tax_id_invalid
|
|
15
|
+
// (docs.stripe.com/error-codes#tax-id-invalid). The message's wording is the twin's.
|
|
16
|
+
export const EU_VAT: Record<string, RegExp> = {
|
|
17
|
+
AT: /^U\d{8}$/, BE: /^[01]\d{9}$/, BG: /^\d{9,10}$/, CY: /^\d{8}[A-Z]$/, CZ: /^\d{8,10}$/, DE: /^\d{9}$/, DK: /^\d{8}$/,
|
|
18
|
+
EE: /^\d{9}$/, EL: /^\d{9}$/, ES: /^[A-Z0-9]\d{7}[A-Z0-9]$/, FI: /^\d{8}$/, FR: /^[A-Z0-9]{2}\d{9}$/, HR: /^\d{11}$/,
|
|
19
|
+
HU: /^\d{8}$/, IE: /^(\d{7}[A-W][A-I]?|\d[A-Z+*]\d{5}[A-W])$/, IT: /^\d{11}$/, LT: /^(\d{9}|\d{12})$/, LU: /^\d{8}$/,
|
|
20
|
+
LV: /^\d{11}$/, MT: /^\d{8}$/, NL: /^\d{9}B\d{2}$/, PL: /^\d{10}$/, PT: /^\d{9}$/, RO: /^\d{2,10}$/, SE: /^\d{12}$/,
|
|
21
|
+
SI: /^\d{8}$/, SK: /^\d{10}$/, XI: /^(\d{9}|\d{12}|GD\d{3}|HA\d{3})$/,
|
|
22
|
+
};
|
|
23
|
+
export const euVatCountry = (value: string): string | undefined => {
|
|
24
|
+
const v = value.replace(/[\s.-]/g, '').toUpperCase();
|
|
25
|
+
const prefix = v.slice(0, 2);
|
|
26
|
+
const format = EU_VAT[prefix];
|
|
27
|
+
return format && format.test(v.slice(2)) ? (prefix === 'EL' ? 'GR' : prefix) : undefined;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** The tax ID types Stripe checks against a government register: "Stripe automatically validates all Australian
|
|
31
|
+
* Business Numbers (ABNs)", "all European Value-Added-Tax (EU VAT) numbers" (VIES) and "all UK Value-Added-Tax (GB VAT)
|
|
32
|
+
* numbers" (HMRC); a verification's status "is pending, verified, unverified, or unavailable", and "When automatic
|
|
33
|
+
* validation is unavailable, you must manually verify the tax ID" (docs.stripe.com/billing/customer/tax-ids). Any other
|
|
34
|
+
* type answers unavailable; one of these waits pending. */
|
|
35
|
+
export const AUTO_VALIDATED = new Set(['au_abn', 'eu_vat', 'gb_vat']);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// What Stripe's pages show of an account, and where its Connect OAuth connections are kept.
|
|
2
|
+
|
|
3
|
+
type Row = Record<string, any>;
|
|
4
|
+
|
|
5
|
+
/** The name an account shows its customers (Checkout, the customer portal): its public business name,
|
|
6
|
+
* `business_profile.name` ("The customer-facing business name", docs.stripe.com/api/accounts/object), which the
|
|
7
|
+
* operator sets on the Dashboard ("You can change a Checkout page's name by modifying the Business name field",
|
|
8
|
+
* docs.stripe.com/payments/checkout/customization/appearance). Where the documentation stops and the twin decides:
|
|
9
|
+
* with none set it falls back to the Dashboard's account name, `settings.dashboard.display_name` ("used on the Stripe
|
|
10
|
+
* Dashboard to differentiate between accounts", the same object page), and with neither to "Twin Inc.", the name the
|
|
11
|
+
* twin has always given the World's own account. */
|
|
12
|
+
export const PLATFORM_DEFAULT_NAME = 'Twin Inc.';
|
|
13
|
+
/** The bookkeeping type of a connected account's Connect OAuth connection (../screens/connect-oauth.tsx): one row per
|
|
14
|
+
* account, `revoked: true` once deauthorized or its code reused. Read by the account list and the event path too. */
|
|
15
|
+
export const OAUTH_CONNECTIONS = '_oauth_connection';
|
|
16
|
+
export function publicBusinessName(account: Row | undefined): string {
|
|
17
|
+
const named = (v: unknown): string | undefined => (typeof v === 'string' && v.trim() ? v : undefined);
|
|
18
|
+
return named(account?.business_profile?.name) ?? named(account?.settings?.dashboard?.display_name) ?? PLATFORM_DEFAULT_NAME;
|
|
19
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Dispute semantics. A dispute is Stripe's to open, when a charge's cardholder disputes it: the test cards
|
|
2
|
+
// that are disputed open one as their charge succeeds (./after-payment.ts). It is then listed, retrieved,
|
|
3
|
+
// updated with evidence and closed. Closing concedes it (status → lost), the machine's move in
|
|
4
|
+
// ../manifest.ts. Evidence is submitted with the update unless `submit` is false
|
|
5
|
+
// (docs.stripe.com/api/disputes/update#update_dispute-submit), and the bank then decides it: in test mode, evidence whose
|
|
6
|
+
// uncategorized_text is `winning_evidence` wins it and `losing_evidence` loses it (docs.stripe.com/testing#evidence);
|
|
7
|
+
// a won dispute returns the disputed amount. Where the documentation stops and the twin decides: the bank decides at
|
|
8
|
+
// once, and any other evidence leaves the dispute under review. List is the derived core's.
|
|
9
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Entitlements semantics: features (unique by lookup_key) that products grant, and the features a
|
|
2
|
+
// customer is entitled to now, derived from its live subscriptions' products. Feature retrieve is
|
|
3
|
+
// the derived core's.
|
|
4
|
+
|
|
5
|
+
export const FEAT = 'entitlements.feature';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// File, file link, Identity and billing-portal semantics. A file is the uploaded document's object
|
|
2
|
+
// (the twin keeps no bytes); a file link is answered, not kept. An Identity verification session is
|
|
3
|
+
// created here and verified through the twin's own helper outside Stripe's surface; its reports
|
|
4
|
+
// are the derived core's to list and read, as are file list and retrieve, session retrieve and
|
|
5
|
+
// portal-configuration retrieve.
|
|
6
|
+
import type { Row } from './common.ts';
|
|
7
|
+
|
|
8
|
+
/** The type Stripe answers for an uploaded file: "The returned file type (for example, csv, pdf, jpg, or png)"
|
|
9
|
+
* (docs.stripe.com/api/files/object). Where the documentation stops and the twin decides: read from the name's
|
|
10
|
+
* extension (jpeg as jpg), and null for a name without one. */
|
|
11
|
+
export function fileType(name: string): string | null {
|
|
12
|
+
const ext = /\.([a-z0-9]+)$/i.exec(name)?.[1]?.toLowerCase();
|
|
13
|
+
return ext ? (ext === 'jpeg' ? 'jpg' : ext) : null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** The purposes a file link may be made to: "The file's `purpose` must be one of the following: …" (the served
|
|
17
|
+
* spec, POST /v1/file_links `file`). */
|
|
18
|
+
export const LINKABLE = ['business_icon', 'business_logo', 'customer_signature', 'dispute_evidence', 'finance_report_run', 'financial_account_statement', 'identity_document_downloadable', 'issuing_regulatory_reporting', 'pci_document', 'selfie', 'sigma_scheduled_query', 'tax_document_user_upload', 'terminal_android_apk', 'terminal_reader_splashscreen'] as const;
|
|
19
|
+
|
|
20
|
+
export const PBC = 'billing_portal.configuration';
|
|
21
|
+
|
|
22
|
+
/** A portal configuration's features, each filled where the request leaves it: every feature is answered (the served
|
|
23
|
+
* spec requires all five), as the create page's example answers a request that sets only customer_update and
|
|
24
|
+
* invoice_history: payment_method_update disabled, subscription_cancel disabled at_period_end with proration_behavior
|
|
25
|
+
* none and its five default reasons, subscription_update disabled with no allowed updates and proration_behavior none
|
|
26
|
+
* (docs.stripe.com/api/customer_portal/configurations/create); trial_update_behavior "Defaults to a value of
|
|
27
|
+
* `end_trial` if you don't set it during creation" (the served spec). Where the documentation stops and the twin
|
|
28
|
+
* decides: a feature the request leaves out is disabled, and schedule_at_period_end has no conditions. */
|
|
29
|
+
export const PORTAL_FEATURES: Row = {
|
|
30
|
+
customer_update: { allowed_updates: [], enabled: false },
|
|
31
|
+
invoice_history: { enabled: false },
|
|
32
|
+
payment_method_update: { enabled: false, payment_method_configuration: null },
|
|
33
|
+
subscription_cancel: { cancellation_reason: { enabled: false, options: ['too_expensive', 'missing_features', 'switched_service', 'unused', 'other'] }, enabled: false, mode: 'at_period_end', proration_behavior: 'none' },
|
|
34
|
+
subscription_update: { default_allowed_updates: [], enabled: false, proration_behavior: 'none', schedule_at_period_end: { conditions: [] }, trial_update_behavior: 'end_trial' },
|
|
35
|
+
};
|
|
36
|
+
export const filled = (base: unknown, given: unknown): unknown => {
|
|
37
|
+
if (!given || typeof given !== 'object' || Array.isArray(given) || !base || typeof base !== 'object' || Array.isArray(base)) return given === undefined ? base : given;
|
|
38
|
+
const out: Row = { ...(base as Row) };
|
|
39
|
+
for (const [k, v] of Object.entries(given as Row)) out[k] = filled((base as Row)[k], v);
|
|
40
|
+
return out;
|
|
41
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// What a publishable key may call, as data the handlers' shared checks read (../semantics/shared.ts
|
|
2
|
+
// publishableKeyRefused, run by ../semantics/around.ts).
|
|
3
|
+
import { expandOf, render, servesCurrent, withoutEndpointSecret } from './version.ts';
|
|
4
|
+
|
|
5
|
+
type Row = Record<string, unknown>;
|
|
6
|
+
|
|
7
|
+
// ── what a publishable key may call ──
|
|
8
|
+
// "Publishable API key pk_... | Safe to expose: Yes | API key for Stripe.js, Elements, and mobile SDKs. It can identify
|
|
9
|
+
// your account and create tokens or PaymentMethods from payment details, but it can't perform sensitive operations such
|
|
10
|
+
// as creating charges or reading account data" (docs.stripe.com/keys). The served spec names the calls a publishable key
|
|
11
|
+
// makes with an object's client secret: "You can retrieve a PaymentIntent client-side using a publishable key when the
|
|
12
|
+
// client_secret is provided in the query string" (GET /v1/payment_intents/{intent}), "Client-side retrieval using a
|
|
13
|
+
// publishable key is allowed when the client_secret is provided" (GET /v1/setup_intents/{intent}), "Required if a
|
|
14
|
+
// publishable key is used to retrieve the source" (GET /v1/sources/{source}), and the confirm and verify_microdeposits
|
|
15
|
+
// calls that take "The client secret of the PaymentIntent" (or SetupIntent). Anything else is refused with Stripe's
|
|
16
|
+
// code: "secret_key_required | The API key provided is a publishable key, but a secret key is required"
|
|
17
|
+
// (docs.stripe.com/error-codes), as 403, "The API key doesn't have permissions to perform the request"
|
|
18
|
+
// (docs.stripe.com/api/errors).
|
|
19
|
+
// Where the documentation stops and the twin decides: the list above is the whole of what a publishable key may call
|
|
20
|
+
// (Stripe publishes no complete list); a client-secret call without the secret answers parameter_missing, and one with a
|
|
21
|
+
// secret that is not the object's answers the object's 404, as though the key could not see it.
|
|
22
|
+
export const PUBLISHABLE_CREATES = new Set(['PostTokens', 'PostPaymentMethods']);
|
|
23
|
+
export const BY_CLIENT_SECRET: Record<string, { type: string; param: string }> = {
|
|
24
|
+
GetPaymentIntentsIntent: { type: 'payment_intent', param: 'intent' },
|
|
25
|
+
PostPaymentIntentsIntentConfirm: { type: 'payment_intent', param: 'intent' },
|
|
26
|
+
PostPaymentIntentsIntentVerifyMicrodeposits: { type: 'payment_intent', param: 'intent' },
|
|
27
|
+
GetSetupIntentsIntent: { type: 'setup_intent', param: 'intent' },
|
|
28
|
+
PostSetupIntentsIntentConfirm: { type: 'setup_intent', param: 'intent' },
|
|
29
|
+
PostSetupIntentsIntentVerifyMicrodeposits: { type: 'setup_intent', param: 'intent' },
|
|
30
|
+
GetSourcesSource: { type: 'source', param: 'source' },
|
|
31
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Invoice and InvoiceItem semantics. The machine in ../manifest.ts says which moves of an invoice's
|
|
2
|
+
// `status` are legal (finalize, send, pay, mark_uncollectible, void) and what Stripe answers
|
|
3
|
+
// otherwise; these handlers compute each move, keep a draft's lines and totals in step with its
|
|
4
|
+
// items, and mint the PaymentIntent a finalized invoice collects through. The invoice list and the
|
|
5
|
+
// item list are the derived core's.
|
|
6
|
+
import type { Row } from './common.ts';
|
|
7
|
+
|
|
8
|
+
export const INV = 'invoice';
|
|
9
|
+
export const linesOf = (inv: Row): Row[] => (inv.lines as { data?: Row[] } | undefined)?.data ?? [];
|
|
10
|
+
export const sumLines = (lines: Row[]): number => lines.reduce((s, l) => s + (Number(l.amount) || 0), 0);
|
|
11
|
+
|
|
12
|
+
/** A draft's line no invoice item made (a subscription's) stays as it is when an item changes. */
|
|
13
|
+
export function unlinkedLine(line: Row): Row {
|
|
14
|
+
return line;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A preview's proration for a change at proration_date. Where the documentation stops and the twin decides: half the
|
|
18
|
+
* period's charge credited as unused time and half charged as remaining time. */
|
|
19
|
+
export function prorationLines(out: Row[], subtotal: number, currency: string, now: number, period: number): void {
|
|
20
|
+
const half = Math.round(subtotal / 2);
|
|
21
|
+
out.unshift({ id: 'il_twin_proration_unused', object: 'line_item', type: 'invoiceitem', amount: -half, currency, proration: true, period: { start: now, end: period }, description: 'Unused time (proration credit)' });
|
|
22
|
+
out.push({ id: 'il_twin_proration_remaining', object: 'line_item', type: 'invoiceitem', amount: half, currency, proration: true, period: { start: now, end: period }, description: 'Remaining time (proration charge)' });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ── invoices ──
|
|
26
|
+
|
|
27
|
+
/** A draft invoice's fields before anything is on it: what a new invoice and a preview start from. */
|
|
28
|
+
export const draft = (now: number): Row => ({
|
|
29
|
+
status: 'draft', livemode: false, currency: 'usd', collection_method: 'charge_automatically',
|
|
30
|
+
auto_advance: false, attempt_count: 0, attempted: false,
|
|
31
|
+
amount_due: 0, amount_paid: 0, amount_remaining: 0, amount_overpaid: 0, amount_paid_off_stripe: 0, amount_shipping: 0,
|
|
32
|
+
subtotal: 0, total: 0, starting_balance: 0, post_payment_credit_notes_amount: 0, pre_payment_credit_notes_amount: 0,
|
|
33
|
+
period_start: now, period_end: now, default_tax_rates: [], discounts: [],
|
|
34
|
+
lines: { object: 'list', data: [], has_more: false, total_count: 0 },
|
|
35
|
+
automatic_tax: { enabled: false, liability: null, status: null },
|
|
36
|
+
status_transitions: { finalized_at: null, marked_uncollectible_at: null, paid_at: null, voided_at: null },
|
|
37
|
+
issuer: { type: 'self' },
|
|
38
|
+
payment_settings: { default_mandate: null, payment_method_options: null, payment_method_types: null },
|
|
39
|
+
// a draft has no PaymentIntent: finalize mints it
|
|
40
|
+
payment_intent: null,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/** Whether leaving draft mints a PaymentIntent: only a charge_automatically invoice with a balance. */
|
|
44
|
+
// "Finalizing an invoice does the following: ... It creates an incomplete payment intent for the invoice"
|
|
45
|
+
// (docs.stripe.com/invoicing/integration/workflow-transitions), whichever its collection method
|
|
46
|
+
export const collectsThroughIntent = (inv: Row): boolean => inv.status === 'draft' && (Number(inv.amount_due) || 0) > 0;
|
|
47
|
+
|
|
48
|
+
export const transitions = (inv: Row): object => (inv.status_transitions as object) ?? {};
|
|
49
|
+
|
|
50
|
+
/** What finalizing assigns: the invoice's `number`, the customer's invoice_prefix and a sequence
|
|
51
|
+
* (https://docs.stripe.com/api/invoices/object#invoice_object-number), and for send_invoice a
|
|
52
|
+
* `due_date` days_until_due after finalization (#invoice_object-due_date). */
|
|
53
|
+
/** A customer's invoice_prefix, "The prefix for the customer used to generate unique invoice numbers"
|
|
54
|
+
* (docs.stripe.com/api/customers/object): the twin's, from the customer's id. */
|
|
55
|
+
export const invoicePrefix = (customerId: string): string => customerId.replace(/[^a-z0-9]/gi, '').slice(-8).toUpperCase() || 'TWIN';
|
|
56
|
+
|
|
57
|
+
/** An invoice's payment as Stripe answers it (docs.stripe.com/api/invoice-payment/object): the PaymentIntent that
|
|
58
|
+
* collects the invoice, open until the invoice is paid. Where the documentation stops and the twin decides: an
|
|
59
|
+
* invoice has one payment, its default, and the payment's id is the invoice's with `inpay_`. */
|
|
60
|
+
export function invoicePayment(inv: Row): Row | undefined {
|
|
61
|
+
if (typeof inv.payment_intent !== 'string') return undefined;
|
|
62
|
+
const paid = inv.status === 'paid';
|
|
63
|
+
const transitions = (inv.status_transitions as Row | undefined) ?? {};
|
|
64
|
+
return {
|
|
65
|
+
id: `inpay_${String(inv.id).replace(/^in_/, '')}`, object: 'invoice_payment', invoice: inv.id, is_default: true, livemode: false,
|
|
66
|
+
amount_requested: Number(inv.amount_due ?? 0), amount_paid: paid ? Number(inv.amount_paid ?? inv.amount_due ?? 0) : null, currency: inv.currency,
|
|
67
|
+
created: inv.created, payment: { type: 'payment_intent', payment_intent: inv.payment_intent },
|
|
68
|
+
status: paid ? 'paid' : inv.status === 'void' ? 'canceled' : 'open',
|
|
69
|
+
status_transitions: { paid_at: paid ? (transitions.paid_at ?? null) : null, canceled_at: inv.status === 'void' ? (transitions.voided_at ?? null) : null },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Issuing semantics: cardholders, cards, authorizations, transactions, disputes, network tokens and
|
|
2
|
+
// personalization designs. An authorization's `card` is the full Card in every answer and webhook
|
|
3
|
+
// (the manifest's write hook embeds it on writes; reads embed it here). The machines in
|
|
4
|
+
// ../manifest.ts say how authorizations, disputes, cards, cardholders, tokens and designs move.
|
|
5
|
+
//
|
|
6
|
+
// The test-helper routes here (/v1/test_helpers/issuing/…) are Stripe's own doors for what no API
|
|
7
|
+
// verb makes: a card presented at a merchant, a capture, a force capture, a design's review. A
|
|
8
|
+
// presented authorization is decided in Stripe's order: card status, then spending controls
|
|
9
|
+
// (card, then cardholder), then the real-time issuing_authorization.request webhook delivered
|
|
10
|
+
// synchronously to the first enrolled endpoint; with none it is approved (card_active).
|
|
11
|
+
import type { Row } from './common.ts';
|
|
12
|
+
|
|
13
|
+
export const CH = 'issuing.cardholder';
|
|
14
|
+
/** The id of a card's cardholder: the card answers the cardholder in full (the served spec's issuing.card.cardholder is
|
|
15
|
+
* the Cardholder object, not an id); its id is read from it for filtering and for the objects that name it by id. */
|
|
16
|
+
export const holderId = (c: Row): string => (typeof c.cardholder === 'string' ? c.cardholder : String((c.cardholder as Row | undefined)?.id ?? ''));
|
|
17
|
+
export const CARD = 'issuing.card';
|
|
18
|
+
export const AUTH = 'issuing.authorization';
|
|
19
|
+
export const TXN = 'issuing.transaction';
|
|
20
|
+
export const DSP = 'issuing.dispute';
|
|
21
|
+
export const TOK = 'issuing.token';
|
|
22
|
+
export const PD = 'issuing.personalization_design';
|
|
23
|
+
|
|
24
|
+
/** One request_history entry; the authorization code is derived from the request time (Stripe's is not unique either). */
|
|
25
|
+
export function historyEntry(o: { amount: number; currency: string; approved: boolean; reason: string; reasonMessage: string | null; createdSec: number; merchantAmount: number; merchantCurrency: string }): Row {
|
|
26
|
+
return {
|
|
27
|
+
amount: o.amount, amount_details: null, approved: o.approved,
|
|
28
|
+
authorization_code: o.approved ? `S${String(100000 + (o.createdSec % 900000))}` : null,
|
|
29
|
+
created: o.createdSec, currency: o.currency,
|
|
30
|
+
merchant_amount: o.merchantAmount, merchant_currency: o.merchantCurrency,
|
|
31
|
+
network_risk_score: null, reason: o.reason, reason_message: o.reasonMessage,
|
|
32
|
+
requested_at: o.createdSec,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The test helper's default merchant, under any merchant_data the caller gives. */
|
|
37
|
+
export function merchantData(param: unknown): Row {
|
|
38
|
+
const base: Row = {
|
|
39
|
+
category: 'general', category_code: '5399', city: 'San Francisco', country: 'US',
|
|
40
|
+
name: 'Twin Test Merchant', network_id: '1234567890', postal_code: '94103', state: 'CA',
|
|
41
|
+
tax_id: null, terminal_id: null, url: null,
|
|
42
|
+
};
|
|
43
|
+
if (param && typeof param === 'object' && !Array.isArray(param)) Object.assign(base, param);
|
|
44
|
+
return base;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The card's stored id (an embedded card answers with its id). */
|
|
48
|
+
export const cardId = (auth: Row): unknown => (auth.card && typeof auth.card === 'object' ? (auth.card as Row).id : auth.card);
|
|
49
|
+
|
|
50
|
+
/** The real-time window: "If Stripe doesn't receive your approve or decline response within 2 seconds, the
|
|
51
|
+
* Authorization is automatically approved or declined based on your timeout settings"
|
|
52
|
+
* (docs.stripe.com/issuing/controls/real-time-authorizations). */
|
|
53
|
+
export const REALTIME_WINDOW_SECONDS = 2;
|
|
54
|
+
|
|
55
|
+
/** Stripe's physical bundles are its catalog, not the account's: the twin serves the one its reference publishes
|
|
56
|
+
* (docs.stripe.com/api/issuing/physical_bundles: "US Visa Credit White", standard, active, a card logo required and
|
|
57
|
+
* carrier text optional). Where the documentation stops and the twin decides: the catalog is that one bundle, and its
|
|
58
|
+
* second_line (required by the served spec, absent from the example) is optional. */
|
|
59
|
+
export const PHYSICAL_BUNDLES: Row[] = [
|
|
60
|
+
{ id: 'ics_NLuXJPDYSTjFON', object: 'issuing.physical_bundle', livemode: false, name: 'US Visa Credit White', features: { card_logo: 'required', carrier_text: 'optional', second_line: 'optional' }, status: 'active', type: 'standard' },
|
|
61
|
+
];
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Stripe's balance ledger (docs.stripe.com/api/balance_transactions): every movement of the account's money writes
|
|
2
|
+
// a balance transaction, and the balance is their sum. A captured charge adds its amount less Stripe's fee, pending
|
|
3
|
+
// until its available_on; a refund takes its amount back at once; a payout takes its amount out of what is available
|
|
4
|
+
// and is refused beyond it (balance_insufficient). A transaction's status is what the World clock says of its
|
|
5
|
+
// available_on (the machine's time move, pending → available), so a `wait` in a story settles funds as time does.
|
|
6
|
+
//
|
|
7
|
+
// Where the documentation stops and the twin decides: the fee is Stripe's standard US card pricing, 2.9% + 30¢
|
|
8
|
+
// (stripe.com/pricing), for every charge; funds become available two days after capture (Stripe's standard US
|
|
9
|
+
// payout schedule counts business days).
|
|
10
|
+
//
|
|
11
|
+
// Test mode (the twin serves livemode: false), as docs.stripe.com/testing documents it:
|
|
12
|
+
// - cards keep that delay but two: "Other test cards send funds from a successful payment to your pending balance",
|
|
13
|
+
// while 4000000000000077 and 4000003720000278 (and their test names pm_card_bypassPending,
|
|
14
|
+
// pm_card_bypassPendingInternational, tok_bypassPending, tok_bypassPendingInternational) "succeed. Funds are added
|
|
15
|
+
// directly to your available balance, bypassing your pending balance" (#available-balance; test-cards.ts). A
|
|
16
|
+
// PaymentMethod made from one keeps that (payment-methods.ts, after-payment.ts), so a saved card charged later by
|
|
17
|
+
// id bypasses too;
|
|
18
|
+
// - a US bank account debit: "Test transactions settle instantly and are added to your available test balance. This
|
|
19
|
+
// behavior differs from live mode" (ACH Direct Debit, "Test settlement behavior");
|
|
20
|
+
// - a source_transaction transfer "takes on the pending status of the associated charge"
|
|
21
|
+
// (docs.stripe.com/connect/separate-charges-and-transfers), so it is available at once when its charge is;
|
|
22
|
+
// - "Test payouts simulate a live payout but aren't processed with the bank" (docs.stripe.com/payouts#test-payouts):
|
|
23
|
+
// the payout clock (semantics/balance.ts) is live's.
|
|
24
|
+
// No other test-mode speed-up is documented, so 4242 4242 4242 4242 funds sit pending the two days.
|
|
25
|
+
//
|
|
26
|
+
// Where the documentation stops and the twin decides: every credit written available at once (a bypass or bank-debit
|
|
27
|
+
// charge, a transfer, a transfer from such a charge, an application fee, a top-up, an Issuing top-up included, a reversal) is marked not yet
|
|
28
|
+
// sent, and the drain sends its account balance.available for it ("Occurs whenever your Stripe balance has been
|
|
29
|
+
// updated (e.g., when a charge is available to be paid out). ... This event is not fired for negative
|
|
30
|
+
// transactions", docs.stripe.com/api/events/types), as it does for funds that came due after the delay. The Balance
|
|
31
|
+
// it carries is the account's at the drain: a debit or an automatic payout landing first shows in it.
|
|
32
|
+
//
|
|
33
|
+
// Each connected account keeps its own balance (docs.stripe.com/connect/account-balances): a transaction belongs to
|
|
34
|
+
// the account the request acts for (the Stripe-Account header), else to the platform. A transfer takes its amount out
|
|
35
|
+
// of the platform's available balance and adds it to the destination's; a destination charge transfers its amount
|
|
36
|
+
// less the application fee, available when the charge's funds are; a direct charge's application fee moves from the
|
|
37
|
+
// connected account to the platform.
|
|
38
|
+
import type { Row } from './common.ts';
|
|
39
|
+
import { BYPASS_PENDING_CARDS } from './test-cards.ts';
|
|
40
|
+
|
|
41
|
+
export const BT = 'balance_transaction';
|
|
42
|
+
export const DAY = 86_400;
|
|
43
|
+
/** A ledger entry's mark: available at once, its balance.available not yet sent (settleDueEntries). */
|
|
44
|
+
export const UNSENT = '_availableUnsent';
|
|
45
|
+
export const BYPASS_NUMBERS = new Set(Object.values(BYPASS_PENDING_CARDS).map((c) => c.number));
|
|
46
|
+
|
|
47
|
+
export const feeOf = (amount: number): number => (amount > 0 ? Math.round(amount * 0.029) + 30 : 0);
|
|
48
|
+
|
|
49
|
+
/** Whether a stored ledger entry is on this account's balance (undefined: the platform's). */
|
|
50
|
+
export const onAccount = (t: Row, account: string | undefined): boolean => (typeof t._account === 'string' ? t._account : undefined) === account;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// A connected account's OAuth connection to the platform (../screens/connect-oauth.tsx makes it), as the API reads it:
|
|
2
|
+
// whether it was revoked, which account an OAuth key acts as, and a key shown as Stripe's errors show one.
|
|
3
|
+
|
|
4
|
+
/** A key shown the way Stripe's errors show one: its prefix and its last four. */
|
|
5
|
+
export const redactKey = (key: string): string => {
|
|
6
|
+
const prefix = /^(sk|rk|pk)_(test|live)_/.exec(key)?.[0] ?? '';
|
|
7
|
+
const rest = key.slice(prefix.length);
|
|
8
|
+
return rest.length > 4 ? `${prefix}${'*'.repeat(rest.length - 4)}${rest.slice(-4)}` : `${prefix}${'*'.repeat(rest.length)}`;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/** The API key a request carries: Bearer, or Basic's user (`curl -u sk_…:`, docs.stripe.com/api/authentication). */
|
|
12
|
+
export function requestKey(request: Request): string {
|
|
13
|
+
const header = request.headers.get('authorization') ?? '';
|
|
14
|
+
const bearer = /^bearer\s+(\S+)/i.exec(header)?.[1];
|
|
15
|
+
if (bearer) return bearer;
|
|
16
|
+
const basic = /^basic\s+(\S+)/i.exec(header)?.[1];
|
|
17
|
+
if (basic) { try { return atob(basic).split(':')[0] ?? ''; } catch { return ''; } }
|
|
18
|
+
return '';
|
|
19
|
+
}
|