@volter/twin-stripe 3.0.0 → 3.0.2
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 +75 -21
- package/dist/journeys/coverage.json +596 -0
- package/dist/journeys/customer-life.json +6108 -0
- package/dist/journeys/customer-life.outline.md +371 -0
- package/dist/journeys/decisions.json +677 -0
- package/dist/journeys/demand.json +706 -0
- package/dist/journeys/score.json +853 -0
- package/dist/journeys/unreachable.json +102 -0
- package/dist/journeys/vendor-examples.json +8010 -0
- package/dist/spec/SOURCE.md +5 -1
- package/dist/spec/doc-examples.json +445 -0
- package/dist/spec/sources.json +135 -207
- package/dist/src/cli.d.ts +0 -1
- package/dist/src/cli.js +0 -2
- package/dist/src/engine/after-payment.d.ts +0 -1
- package/dist/src/engine/after-payment.js +0 -1
- package/dist/src/engine/common.d.ts +5 -5
- package/dist/src/engine/common.js +13 -11
- package/dist/src/engine/connect.d.ts +0 -21
- package/dist/src/engine/connect.js +1 -82
- package/dist/src/engine/customers.d.ts +0 -8
- package/dist/src/engine/customers.js +0 -22
- package/dist/src/engine/display.d.ts +0 -3
- package/dist/src/engine/display.js +0 -3
- package/dist/src/engine/files.d.ts +0 -7
- package/dist/src/engine/files.js +0 -10
- package/dist/src/engine/invoices.d.ts +6 -9
- package/dist/src/engine/invoices.js +56 -29
- package/dist/src/engine/issuing.d.ts +6 -8
- package/dist/src/engine/issuing.js +19 -10
- package/dist/src/engine/ledger.d.ts +0 -1
- package/dist/src/engine/ledger.js +0 -1
- package/dist/src/engine/oauth.js +1 -2
- package/dist/src/engine/refunds.js +2 -27
- package/dist/src/engine/stripe.d.ts +2 -7
- package/dist/src/engine/stripe.js +34 -83
- package/dist/src/engine/subscriptions.d.ts +0 -1
- package/dist/src/engine/subscriptions.js +0 -1
- package/dist/src/engine/version.d.ts +0 -2
- package/dist/src/engine/version.js +17 -6
- package/dist/src/fetch.js +2 -2
- package/dist/src/generated/surface.gen.json +1 -1
- package/dist/src/generated/ui.gen.json +1 -1
- package/dist/src/index.js +5 -3
- package/dist/src/manifest.js +348 -220
- package/dist/src/screens/billing-portal.js +5 -5
- package/dist/src/screens/{connect-oauth.d.ts → checkout-js.d.ts} +0 -1
- package/dist/src/screens/checkout-js.js +84 -0
- package/dist/src/screens/connect-onboarding.js +3 -3
- package/dist/src/screens/dashboard.d.ts +1 -1
- package/dist/src/screens/dashboard.js +56 -53
- package/dist/src/screens/stripe-js.js +53 -93
- package/dist/src/semantics/account.d.ts +2 -0
- package/dist/src/semantics/account.js +8 -0
- package/dist/src/semantics/accounts.js +3 -1
- package/dist/src/semantics/around.js +3 -6
- package/dist/src/semantics/billing_portal.d.ts +0 -1
- package/dist/src/semantics/billing_portal.js +1 -7
- package/dist/src/semantics/checkout.d.ts +1 -1
- package/dist/src/semantics/checkout.js +18 -15
- package/dist/src/semantics/clock.js +1 -7
- package/dist/src/semantics/customers.d.ts +1 -2
- package/dist/src/semantics/customers.js +32 -38
- package/dist/src/semantics/entitlements.d.ts +0 -2
- package/dist/src/semantics/entitlements.js +4 -50
- package/dist/src/semantics/events.d.ts +2 -3
- package/dist/src/semantics/events.js +6 -11
- package/dist/src/semantics/index.js +16 -38
- package/dist/src/semantics/invoices.d.ts +3 -8
- package/dist/src/semantics/invoices.js +134 -127
- package/dist/src/semantics/issuing.d.ts +6 -0
- package/dist/src/semantics/issuing.js +77 -0
- package/dist/src/semantics/payment_intents.d.ts +0 -1
- package/dist/src/semantics/payment_intents.js +5 -13
- package/dist/src/semantics/payment_methods.d.ts +0 -2
- package/dist/src/semantics/payment_methods.js +2 -26
- package/dist/src/semantics/payouts.d.ts +1 -0
- package/dist/src/semantics/payouts.js +8 -1
- package/dist/src/semantics/products.d.ts +1 -3
- package/dist/src/semantics/products.js +3 -14
- package/dist/src/semantics/refunds.d.ts +1 -2
- package/dist/src/semantics/refunds.js +1 -5
- package/dist/src/semantics/shared.d.ts +64 -336
- package/dist/src/semantics/shared.js +326 -1336
- package/dist/src/semantics/states.d.ts +20 -89
- package/dist/src/semantics/states.js +116 -334
- package/dist/src/semantics/subscriptions.d.ts +1 -2
- package/dist/src/semantics/subscriptions.js +23 -145
- package/dist/src/semantics/test_helpers.d.ts +2 -5
- package/dist/src/semantics/test_helpers.js +121 -43
- package/dist/src/semantics/webhook_endpoints.d.ts +2 -0
- package/dist/src/semantics/webhook_endpoints.js +26 -0
- package/generated/pack-facts.json +3 -3
- package/journeys/coverage.json +596 -0
- package/journeys/customer-life.json +6108 -0
- package/journeys/customer-life.outline.md +371 -0
- package/journeys/decisions.json +677 -0
- package/journeys/demand.json +706 -0
- package/journeys/score.json +853 -0
- package/journeys/unreachable.json +102 -0
- package/journeys/vendor-examples.json +8010 -0
- package/package.json +11 -10
- package/spec/SOURCE.md +5 -1
- package/spec/doc-examples.json +445 -0
- package/spec/sources.json +135 -207
- package/src/cli.ts +0 -3
- package/src/engine/after-payment.ts +0 -2
- package/src/engine/balance.ts +0 -1
- package/src/engine/checkout.ts +0 -1
- package/src/engine/common.ts +19 -17
- package/src/engine/connect.ts +1 -79
- package/src/engine/customers.ts +0 -25
- package/src/engine/display.ts +2 -3
- package/src/engine/files.ts +0 -13
- package/src/engine/invoices.ts +64 -33
- package/src/engine/issuing.ts +16 -11
- package/src/engine/ledger.ts +0 -2
- package/src/engine/oauth.ts +1 -2
- package/src/engine/onboarding.ts +0 -1
- package/src/engine/params.ts +0 -1
- package/src/engine/payment-intents.ts +0 -1
- package/src/engine/payment-methods.ts +0 -1
- package/src/engine/refunds.ts +2 -28
- package/src/engine/renewals.ts +0 -1
- package/src/engine/stripe.ts +35 -83
- package/src/engine/subscriptions.ts +0 -3
- package/src/engine/transfers.ts +0 -1
- package/src/engine/version.ts +15 -7
- package/src/engine/webhooks.ts +0 -2
- package/src/fetch.ts +2 -2
- package/src/generated/surface.gen.json +1 -1
- package/src/generated/ui.gen.json +1 -1
- package/src/index.ts +6 -4
- package/src/manifest.ts +348 -220
- package/src/screens/billing-portal.tsx +5 -7
- package/src/screens/checkout-js.ts +80 -0
- package/src/screens/checkout.tsx +0 -2
- package/src/screens/connect-onboarding.tsx +3 -5
- package/src/screens/dashboard.tsx +55 -73
- package/src/screens/stripe-js.ts +57 -125
- package/src/semantics/account.ts +10 -0
- package/src/semantics/account_links.ts +0 -1
- package/src/semantics/accounts.ts +8 -7
- package/src/semantics/apps.ts +1 -2
- package/src/semantics/around.ts +3 -6
- package/src/semantics/balance.ts +0 -1
- package/src/semantics/balance_transactions.ts +1 -3
- package/src/semantics/billing_portal.ts +1 -7
- package/src/semantics/charges.ts +1 -6
- package/src/semantics/checkout.ts +16 -12
- package/src/semantics/clock.ts +1 -8
- package/src/semantics/coupons.ts +0 -1
- package/src/semantics/customers.ts +32 -35
- package/src/semantics/doors.ts +2 -3
- package/src/semantics/entitlements.ts +1 -45
- package/src/semantics/events.ts +7 -14
- package/src/semantics/index.ts +16 -38
- package/src/semantics/invoice_payments.ts +1 -3
- package/src/semantics/invoices.ts +124 -119
- package/src/semantics/issuing.ts +74 -0
- package/src/semantics/payment_intents.ts +10 -17
- package/src/semantics/payment_methods.ts +2 -25
- package/src/semantics/payouts.ts +9 -3
- package/src/semantics/prices.ts +0 -1
- package/src/semantics/products.ts +4 -14
- package/src/semantics/promotion_codes.ts +0 -1
- package/src/semantics/radar.ts +1 -3
- package/src/semantics/refunds.ts +2 -11
- package/src/semantics/shared.ts +350 -1382
- package/src/semantics/states.ts +109 -330
- package/src/semantics/subscriptions.ts +25 -145
- package/src/semantics/test_helpers.ts +109 -43
- package/src/semantics/transfers.ts +1 -3
- package/src/semantics/webhook_endpoints.ts +27 -0
- package/src/server.ts +0 -1
- package/dist/generated/pack-facts.json +0 -94
- package/dist/src/engine/billing.d.ts +0 -5
- package/dist/src/engine/billing.js +0 -6
- package/dist/src/engine/charges.d.ts +0 -6
- package/dist/src/engine/charges.js +0 -13
- package/dist/src/engine/connect-settings.d.ts +0 -16
- package/dist/src/engine/connect-settings.js +0 -26
- package/dist/src/engine/coupons.d.ts +0 -0
- package/dist/src/engine/coupons.js +0 -5
- package/dist/src/engine/credit-notes.d.ts +0 -1
- package/dist/src/engine/credit-notes.js +0 -5
- package/dist/src/engine/disputes.d.ts +0 -0
- package/dist/src/engine/disputes.js +0 -9
- package/dist/src/engine/entitlements.d.ts +0 -1
- package/dist/src/engine/entitlements.js +0 -4
- package/dist/src/engine/ephemeral-keys.d.ts +0 -0
- package/dist/src/engine/ephemeral-keys.js +0 -4
- package/dist/src/engine/front.d.ts +0 -5
- package/dist/src/engine/front.js +0 -25
- package/dist/src/engine/payment-links.d.ts +0 -1
- package/dist/src/engine/payment-links.js +0 -5
- package/dist/src/engine/plans.d.ts +0 -6
- package/dist/src/engine/plans.js +0 -26
- package/dist/src/engine/platform.d.ts +0 -39
- package/dist/src/engine/platform.js +0 -34
- package/dist/src/engine/products.d.ts +0 -0
- package/dist/src/engine/products.js +0 -4
- package/dist/src/engine/setup-intents.d.ts +0 -1
- package/dist/src/engine/setup-intents.js +0 -4
- package/dist/src/engine/subscription-schedules.d.ts +0 -3
- package/dist/src/engine/subscription-schedules.js +0 -1
- package/dist/src/engine/tax.d.ts +0 -7
- package/dist/src/engine/tax.js +0 -28
- package/dist/src/engine/terminal.d.ts +0 -4
- package/dist/src/engine/terminal.js +0 -20
- package/dist/src/engine/test-clocks.d.ts +0 -5
- package/dist/src/engine/test-clocks.js +0 -9
- package/dist/src/engine/tokens.d.ts +0 -7
- package/dist/src/engine/tokens.js +0 -20
- package/dist/src/engine/treasury.d.ts +0 -23
- package/dist/src/engine/treasury.js +0 -45
- package/dist/src/screens/connect-oauth.js +0 -333
- package/dist/src/semantics/disputes.d.ts +0 -2
- package/dist/src/semantics/disputes.js +0 -5
- package/dist/src/semantics/invoiceitems.d.ts +0 -3
- package/dist/src/semantics/invoiceitems.js +0 -59
- package/dist/src/semantics/setup_intents.d.ts +0 -3
- package/dist/src/semantics/setup_intents.js +0 -41
- package/dist/src/semantics/subscription_items.d.ts +0 -5
- package/dist/src/semantics/subscription_items.js +0 -55
- package/dist/src/semantics/subscription_schedules.d.ts +0 -4
- package/dist/src/semantics/subscription_schedules.js +0 -60
- package/src/engine/billing.ts +0 -16
- package/src/engine/charges.ts +0 -19
- package/src/engine/connect-settings.ts +0 -23
- package/src/engine/coupons.ts +0 -5
- package/src/engine/credit-notes.ts +0 -6
- package/src/engine/disputes.ts +0 -9
- package/src/engine/entitlements.ts +0 -5
- package/src/engine/ephemeral-keys.ts +0 -3
- package/src/engine/front.ts +0 -31
- package/src/engine/payment-links.ts +0 -6
- package/src/engine/plans.ts +0 -42
- package/src/engine/platform.ts +0 -47
- package/src/engine/products.ts +0 -3
- package/src/engine/setup-intents.ts +0 -5
- package/src/engine/subscription-schedules.ts +0 -10
- package/src/engine/tax.ts +0 -39
- package/src/engine/terminal.ts +0 -23
- package/src/engine/test-clocks.ts +0 -11
- package/src/engine/tokens.ts +0 -22
- package/src/engine/treasury.ts +0 -47
- package/src/screens/connect-oauth.tsx +0 -376
- package/src/semantics/disputes.ts +0 -12
- package/src/semantics/invoiceitems.ts +0 -59
- package/src/semantics/setup_intents.ts +0 -45
- package/src/semantics/subscription_items.ts +0 -54
- package/src/semantics/subscription_schedules.ts +0 -66
package/src/engine/connect.ts
CHANGED
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
// sessions. A new connected account is not yet active: charges and payouts are off and its
|
|
4
4
|
// requirements are outstanding, as on Stripe before onboarding. Account retrieve and delete and
|
|
5
5
|
// the person list are the derived core's.
|
|
6
|
-
import { accountRequirements, accountSettings,
|
|
6
|
+
import { accountRequirements, accountSettings, PLATFORM_ACCOUNT_ID } from './stripe.ts';
|
|
7
7
|
import { currentlyDue, kindOf } from './onboarding.ts';
|
|
8
8
|
import { externalList, type Row } from './common.ts';
|
|
9
|
-
|
|
10
9
|
// the platform's own account: stored once it is written, otherwise Stripe's default. It "controls itself" (controller
|
|
11
10
|
// type `account`, docs.stripe.com/api/accounts/object), is fully onboarded with nothing due, and takes card payments
|
|
12
11
|
// and transfers. Where the documentation stops and the twin decides: it was created as the World began (2026-01-01),
|
|
@@ -64,8 +63,6 @@ export const at_path = (a: Row, path: string): unknown => path.split('.').reduce
|
|
|
64
63
|
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
64
|
export const CARD_COMPANY_LATER = ['company.phone', 'settings.payments.statement_descriptor', 'representative.dob.day', 'representative.dob.month', 'representative.dob.year', 'representative.phone'];
|
|
66
65
|
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
66
|
export const personHas = (p: Row, field: string): boolean => {
|
|
70
67
|
if (field === 'ssn_last_4') return p.ssn_last_4_provided === true;
|
|
71
68
|
const v = at_path(p, field);
|
|
@@ -107,78 +104,3 @@ export function keptKyc(params: Row): Row {
|
|
|
107
104
|
}
|
|
108
105
|
return out;
|
|
109
106
|
}
|
|
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
|
-
}
|
package/src/engine/customers.ts
CHANGED
|
@@ -3,33 +3,8 @@
|
|
|
3
3
|
// update and delete of a customer, retrieve of a Source and the flat tax-id list are the derived core's.
|
|
4
4
|
import { invoicePrefix } from './invoices.ts';
|
|
5
5
|
import type { Row } from './common.ts';
|
|
6
|
-
|
|
7
6
|
/** What a new customer answers before any invoice: its invoice_prefix and a next_invoice_sequence of 1, a zero balance,
|
|
8
7
|
* no metadata (docs.stripe.com/api/customers/object). */
|
|
9
8
|
export const newCustomer = (id: string): Row => ({ invoice_prefix: invoicePrefix(id), next_invoice_sequence: 1, balance: 0, metadata: {}, invoice_settings: {} });
|
|
10
9
|
|
|
11
10
|
// ── 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']);
|
package/src/engine/display.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// What Stripe's pages show of an account, and where its Connect OAuth connections are kept.
|
|
2
2
|
|
|
3
|
+
// What Stripe's pages show of an account, and where its Connect OAuth connections are kept.
|
|
4
|
+
|
|
3
5
|
type Row = Record<string, any>;
|
|
4
6
|
|
|
5
7
|
/** The name an account shows its customers (Checkout, the customer portal): its public business name,
|
|
@@ -10,9 +12,6 @@ type Row = Record<string, any>;
|
|
|
10
12
|
* Dashboard to differentiate between accounts", the same object page), and with neither to "Twin Inc.", the name the
|
|
11
13
|
* twin has always given the World's own account. */
|
|
12
14
|
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
15
|
export function publicBusinessName(account: Row | undefined): string {
|
|
17
16
|
const named = (v: unknown): string | undefined => (typeof v === 'string' && v.trim() ? v : undefined);
|
|
18
17
|
return named(account?.business_profile?.name) ?? named(account?.settings?.dashboard?.display_name) ?? PLATFORM_DEFAULT_NAME;
|
package/src/engine/files.ts
CHANGED
|
@@ -4,19 +4,6 @@
|
|
|
4
4
|
// are the derived core's to list and read, as are file list and retrieve, session retrieve and
|
|
5
5
|
// portal-configuration retrieve.
|
|
6
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
7
|
export const PBC = 'billing_portal.configuration';
|
|
21
8
|
|
|
22
9
|
/** A portal configuration's features, each filled where the request leaves it: every feature is answered (the served
|
package/src/engine/invoices.ts
CHANGED
|
@@ -4,41 +4,9 @@
|
|
|
4
4
|
// items, and mint the PaymentIntent a finalized invoice collects through. The invoice list and the
|
|
5
5
|
// item list are the derived core's.
|
|
6
6
|
import type { Row } from './common.ts';
|
|
7
|
+
import { applyCouponDiscount } from './stripe.ts';
|
|
7
8
|
|
|
8
9
|
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
10
|
|
|
43
11
|
/** Whether leaving draft mints a PaymentIntent: only a charge_automatically invoice with a balance. */
|
|
44
12
|
// "Finalizing an invoice does the following: ... It creates an incomplete payment intent for the invoice"
|
|
@@ -69,3 +37,66 @@ export function invoicePayment(inv: Row): Row | undefined {
|
|
|
69
37
|
status_transitions: { paid_at: paid ? (transitions.paid_at ?? null) : null, canceled_at: inv.status === 'void' ? (transitions.voided_at ?? null) : null },
|
|
70
38
|
};
|
|
71
39
|
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
export const linesOf = (inv: Row): Row[] => (inv.lines as { data?: Row[] } | undefined)?.data ?? [];
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
export const sumLines = (lines: Row[]): number => lines.reduce((s, l) => s + (Number(l.amount) || 0), 0);
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
/** Credit the previous price and debit the replacement for the same remaining service time. */
|
|
50
|
+
// source: https://docs.stripe.com/billing/subscriptions/prorations "Unused time on original 10 USD plan (credit)"
|
|
51
|
+
// source: https://docs.stripe.com/billing/subscriptions/prorations "Remaining time on new 20 USD plan (debit)"
|
|
52
|
+
// source: https://docs.stripe.com/billing/subscriptions/prorations "The current billing period’s start and end times are used to calculate the cost of the subscription before and after the change."
|
|
53
|
+
// source: https://docs.stripe.com/billing/subscriptions/prorations "By default, Stripe calculates prorations down to the second."
|
|
54
|
+
export function prorationLines(before: Row[], after: Row[], time: number, start: number, end: number, coupon: Row | undefined): Row[] {
|
|
55
|
+
const out: Row[] = [];
|
|
56
|
+
const discounted = (line: Row, lines: Row[]): number => {
|
|
57
|
+
const total = sumLines(lines);
|
|
58
|
+
// source: https://docs.stripe.com/billing/subscriptions/prorations "When calculating proration credits or debits, Stripe uses the subscription’s discounted price, not the original price."
|
|
59
|
+
return Number(line.amount) - (total > 0 ? applyCouponDiscount(total, coupon) * Number(line.amount) / total : 0);
|
|
60
|
+
};
|
|
61
|
+
const priceId = (line: Row): unknown => typeof line.price === 'object' ? (line.price as Row).id : line.price;
|
|
62
|
+
const append = (line: Row, lines: Row[], credit: boolean): void => {
|
|
63
|
+
const billingPeriod = line.period as Row;
|
|
64
|
+
const periodStart = Number(billingPeriod.start ?? start), periodEnd = Number(billingPeriod.end ?? end);
|
|
65
|
+
const fraction = Math.max(0, Math.min(1, (periodEnd - time) / Math.max(1, periodEnd - periodStart)));
|
|
66
|
+
const amount = Math.round(discounted(line, lines) * fraction) * (credit ? -1 : 1);
|
|
67
|
+
if (!amount) return;
|
|
68
|
+
out.push({ ...line, id: `${line.id}_${credit ? 'unused' : 'remaining'}`, amount, proration: true, discountable: false,
|
|
69
|
+
period: { start: time, end: periodEnd }, description: `${credit ? 'Unused' : 'Remaining'} time on ${String((line.price as Row)?.nickname ?? priceId(line))}` });
|
|
70
|
+
};
|
|
71
|
+
for (const old of before) {
|
|
72
|
+
const replacement = after.find(line => line.subscription_item === old.subscription_item);
|
|
73
|
+
if (replacement && priceId(replacement) === priceId(old) && replacement.quantity === old.quantity) continue;
|
|
74
|
+
append(old, before, true);
|
|
75
|
+
}
|
|
76
|
+
for (const next of after) {
|
|
77
|
+
const previous = before.find(line => line.subscription_item && line.subscription_item === next.subscription_item);
|
|
78
|
+
if (previous && priceId(previous) === priceId(next) && previous.quantity === next.quantity) continue;
|
|
79
|
+
append(next, after, false);
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
// ── invoices ──
|
|
87
|
+
|
|
88
|
+
/** A draft invoice's fields before anything is on it: what a new invoice and a preview start from. */
|
|
89
|
+
export const draft = (now: number): Row => ({
|
|
90
|
+
status: 'draft', livemode: false, currency: 'usd', collection_method: 'charge_automatically',
|
|
91
|
+
auto_advance: false, attempt_count: 0, attempted: false,
|
|
92
|
+
amount_due: 0, amount_paid: 0, amount_remaining: 0, amount_overpaid: 0, amount_paid_off_stripe: 0, amount_shipping: 0,
|
|
93
|
+
subtotal: 0, total: 0, starting_balance: 0, post_payment_credit_notes_amount: 0, pre_payment_credit_notes_amount: 0,
|
|
94
|
+
period_start: now, period_end: now, default_tax_rates: [], discounts: [],
|
|
95
|
+
lines: { object: 'list', data: [], has_more: false, total_count: 0 },
|
|
96
|
+
automatic_tax: { enabled: false, liability: null, status: null },
|
|
97
|
+
status_transitions: { finalized_at: null, marked_uncollectible_at: null, paid_at: null, voided_at: null },
|
|
98
|
+
issuer: { type: 'self' },
|
|
99
|
+
payment_settings: { default_mandate: null, payment_method_options: null, payment_method_types: null },
|
|
100
|
+
// a draft has no PaymentIntent: finalize mints it
|
|
101
|
+
payment_intent: null,
|
|
102
|
+
});
|
package/src/engine/issuing.ts
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
// (card, then cardholder), then the real-time issuing_authorization.request webhook delivered
|
|
10
10
|
// synchronously to the first enrolled endpoint; with none it is approved (card_active).
|
|
11
11
|
import type { Row } from './common.ts';
|
|
12
|
-
|
|
13
12
|
export const CH = 'issuing.cardholder';
|
|
14
13
|
/** The id of a card's cardholder: the card answers the cardholder in full (the served spec's issuing.card.cardholder is
|
|
15
14
|
* the Cardholder object, not an id); its id is read from it for filtering and for the objects that name it by id. */
|
|
@@ -17,9 +16,6 @@ export const holderId = (c: Row): string => (typeof c.cardholder === 'string' ?
|
|
|
17
16
|
export const CARD = 'issuing.card';
|
|
18
17
|
export const AUTH = 'issuing.authorization';
|
|
19
18
|
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
19
|
|
|
24
20
|
/** One request_history entry; the authorization code is derived from the request time (Stripe's is not unique either). */
|
|
25
21
|
export function historyEntry(o: { amount: number; currency: string; approved: boolean; reason: string; reasonMessage: string | null; createdSec: number; merchantAmount: number; merchantCurrency: string }): Row {
|
|
@@ -52,10 +48,19 @@ export const cardId = (auth: Row): unknown => (auth.card && typeof auth.card ===
|
|
|
52
48
|
* (docs.stripe.com/issuing/controls/real-time-authorizations). */
|
|
53
49
|
export const REALTIME_WINDOW_SECONDS = 2;
|
|
54
50
|
|
|
55
|
-
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
51
|
+
/** A virtual card's number, drawn from the World: a 16-digit Visa number ("The brand of the card": the twin issues
|
|
52
|
+
* Visa) whose last digit is the Luhn check digit every card number carries. Where the documentation stops and the twin
|
|
53
|
+
* decides: the digits are the World's draw, Stripe's are the issuer's. */
|
|
54
|
+
export function cardNumberOf(drawn: string): string {
|
|
55
|
+
const body = `4${[...drawn].map((ch) => String(ch.charCodeAt(0) % 10)).join('').padEnd(14, '0').slice(0, 14)}`;
|
|
56
|
+
let sum = 0;
|
|
57
|
+
for (let i = 0; i < body.length; i++) {
|
|
58
|
+
let d = Number(body[body.length - 1 - i]);
|
|
59
|
+
if (i % 2 === 0) { d *= 2; if (d > 9) d -= 9; }
|
|
60
|
+
sum += d;
|
|
61
|
+
}
|
|
62
|
+
return `${body}${(10 - (sum % 10)) % 10}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** A virtual card's CVC, drawn from the World: three digits. */
|
|
66
|
+
export const cvcOf = (drawn: string): string => [...drawn].map((ch) => String(ch.charCodeAt(0) % 10)).join('').padEnd(3, '0').slice(0, 3);
|
package/src/engine/ledger.ts
CHANGED
|
@@ -37,9 +37,7 @@
|
|
|
37
37
|
// connected account to the platform.
|
|
38
38
|
import type { Row } from './common.ts';
|
|
39
39
|
import { BYPASS_PENDING_CARDS } from './test-cards.ts';
|
|
40
|
-
|
|
41
40
|
export const BT = 'balance_transaction';
|
|
42
|
-
export const DAY = 86_400;
|
|
43
41
|
/** A ledger entry's mark: available at once, its balance.available not yet sent (settleDueEntries). */
|
|
44
42
|
export const UNSENT = '_availableUnsent';
|
|
45
43
|
export const BYPASS_NUMBERS = new Set(Object.values(BYPASS_PENDING_CARDS).map((c) => c.number));
|
package/src/engine/oauth.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
// whether it was revoked, which account an OAuth key acts as, and a key shown as Stripe's errors show one.
|
|
1
|
+
// Stripe API key parsing and error redaction, from the API authentication documentation.
|
|
3
2
|
|
|
4
3
|
/** A key shown the way Stripe's errors show one: its prefix and its last four. */
|
|
5
4
|
export const redactKey = (key: string): string => {
|
package/src/engine/onboarding.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// ../screens/connect-onboarding.tsx), as the Accounts API reports them (`requirements.currently_due`): the two US sets the twin
|
|
3
3
|
// models, a company's and an individual's.
|
|
4
4
|
import { INDUSTRIES as MCCS } from './industries.ts';
|
|
5
|
-
|
|
6
5
|
type Row = Record<string, unknown>;
|
|
7
6
|
|
|
8
7
|
export const OFFERED = new Set(MCCS.map(([mcc]) => mcc));
|
package/src/engine/params.ts
CHANGED
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
// the values of the payment_method_types it still takes (PAYMENT_METHOD_TYPES below); nested parameters are not checked.
|
|
12
12
|
|
|
13
13
|
import surface from '../generated/surface.gen.json' with { type: 'json' };
|
|
14
|
-
|
|
15
14
|
type Param = { name: string; required?: boolean };
|
|
16
15
|
type Op = { id: string; method: string; query?: Param[]; body?: Param[] };
|
|
17
16
|
export const OPS = new Map((surface.operations as Op[]).map((o) => [o.id, o]));
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// what Stripe answers otherwise; these handlers compute what each move does. List, retrieve and
|
|
3
3
|
// update are the derived core's; search is Stripe's query language over the intents.
|
|
4
4
|
import type { Row } from './common.ts';
|
|
5
|
-
|
|
6
5
|
export const PI = 'payment_intent';
|
|
7
6
|
/** Bookkeeping: the intents whose caller named the methods (payment_method_types, allowed_payment_method_types) on
|
|
8
7
|
* create. The twin's default list (['card']) stands for Stripe's choice and restricts nothing, so only a named list
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import { paymentMethodSubObject } from './stripe.ts';
|
|
4
4
|
import { AFTER_SUCCESS_CARDS } from './after-payment.ts';
|
|
5
5
|
import type { Row } from './common.ts';
|
|
6
|
-
|
|
7
6
|
export const PM = 'payment_method';
|
|
8
7
|
export const noBilling = { address: null, email: null, name: null, phone: null };
|
|
9
8
|
|
package/src/engine/refunds.ts
CHANGED
|
@@ -1,29 +1,3 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// refuses it. The machine in ../manifest.ts says only a refund waiting on the customer
|
|
4
|
-
// (requires_action) may be canceled. List and update are the derived core's.
|
|
5
|
-
//
|
|
6
|
-
// Refunding a destination charge can take back the connected account's share (reverse_transfer: a reversal of the
|
|
7
|
-
// charge's transfer, in proportion to the refund) and the platform's (refund_application_fee: a refund of the
|
|
8
|
-
// application fee, in proportion) (docs.stripe.com/api/refunds/create#create_refund-reverse_transfer,
|
|
9
|
-
// docs.stripe.com/connect/destination-charges#issue-refunds). Where the documentation stops and the twin decides: the
|
|
10
|
-
// twin's destination charge transfers the amount less the fee (the fee is kept back rather than collected from the
|
|
11
|
-
// connected account), so a fee refund moves no money of its own; the reversal and the refund settle both shares.
|
|
12
|
-
//
|
|
13
|
-
// A payment made by bank transfer (from the customer's cash balance) is refunded to the customer's bank account: Stripe
|
|
14
|
-
// emails the customer (instructions_email, else the customer's email) for their bank details and the refund waits in
|
|
15
|
-
// requires_action, moving no money, until they answer; only then can it still be canceled. With origin=customer_balance
|
|
16
|
-
// it goes back to the cash balance at once (docs.stripe.com/payments/customer-balance/refunding,
|
|
17
|
-
// docs.stripe.com/api/refunds/cancel). Where the documentation stops and the twin decides: the customer never answers
|
|
18
|
-
// the email in the World, and its link expires 30 days on.
|
|
19
|
-
//
|
|
20
|
-
// An uncaptured charge holds an authorization, not money. A PaymentIntent's is not refunded: "the charge attached to the
|
|
21
|
-
// PaymentIntent remains uncaptured and can't be refunded directly. You must cancel the PaymentIntent"
|
|
22
|
-
// (docs.stripe.com/refunds), which releases it (charges.ts cancelAuthorization). A charge made with capture=false is
|
|
23
|
-
// released by its refund, as its authorization would be "automatically refunded if uncaptured" (spec/openapi.json.gz,
|
|
24
|
-
// `capture_before`): refunded in full, no balance moved, and its capture refused afterwards. Where the documentation
|
|
25
|
-
// stops and the twin decides: the refusal's code (payment_intent_unexpected_state) and wording, and that a refund of a
|
|
26
|
-
// capture=false charge releases the whole authorization, so an `amount` short of it is refused.
|
|
27
|
-
|
|
28
|
-
// the reasons a caller may give; `expired_uncaptured_charge` is Stripe's own
|
|
1
|
+
// The published refund reasons; expired_uncaptured_charge is Stripe's own, not a request reason.
|
|
2
|
+
// source: spec:/paths/~1v1~1refunds/post/requestBody/content/application~1x-www-form-urlencoded/schema/properties/reason "String indicating the reason for the refund."
|
|
29
3
|
export const REFUND_REASONS = new Set(['duplicate', 'fraudulent', 'requested_by_customer']);
|
package/src/engine/renewals.ts
CHANGED
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
// (docs.stripe.com/billing/subscriptions/cancel#cancel-at-end-of-cycle).
|
|
26
26
|
import { priceAmount } from './stripe.ts';
|
|
27
27
|
import type { Row } from './common.ts';
|
|
28
|
-
|
|
29
28
|
export const SUB = 'subscription';
|
|
30
29
|
export const INV = 'invoice';
|
|
31
30
|
export const HOUR = 3600;
|