@volter/twin-stripe 3.0.0 → 3.0.1
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 +51 -21
- package/dist/generated/pack-facts.json +3 -3
- 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 +52 -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 +4 -3
- 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 +53 -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/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
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
// and its declines, 3-D Secure and micro-deposits, the list page and the search grammar, a subscription's items, billing
|
|
3
|
-
// intervals and discounts, Issuing's spending controls, a payment method's sub-object, a Connect account's settings and a
|
|
4
|
-
// Checkout Session's line items. None of it reads or writes state: a caller hands it what it looked up (FindStored).
|
|
1
|
+
import { digest } from '@volter/world-core';
|
|
5
2
|
import { ownFields } from '@volter/world-core';
|
|
6
3
|
import { SERVED_VERSION } from "./version.js";
|
|
7
4
|
/** Stripe's error body: an invalid_request_error with the message, and the code when there is one. */
|
|
@@ -138,8 +135,6 @@ export function threeDsNextAction() {
|
|
|
138
135
|
// a `descriptor_code` (the test value `SM11AA`). We model that exact pair so the verify
|
|
139
136
|
// can FAIL with the wrong amounts. The well-known token that triggers this flow:
|
|
140
137
|
const MICRODEPOSIT_TOKENS = new Set(['pm_usBankAccount_requiresVerification', 'pm_us_bank_account']);
|
|
141
|
-
const MICRODEPOSIT_AMOUNTS = [32, 45];
|
|
142
|
-
const MICRODEPOSIT_DESCRIPTOR = 'SM11AA';
|
|
143
138
|
export function requiresMicrodeposits(...paramSets) {
|
|
144
139
|
for (const params of paramSets) {
|
|
145
140
|
const ref = resolveCardRef(params);
|
|
@@ -157,31 +152,6 @@ export function microdepositsNextAction() {
|
|
|
157
152
|
},
|
|
158
153
|
};
|
|
159
154
|
}
|
|
160
|
-
/** The two deposits' amounts as sent: the test values (docs.stripe.com/payments/ach-direct-debit/accept-a-payment). */
|
|
161
|
-
function amountsMatch(params) {
|
|
162
|
-
const got = params.amounts.map((a) => Math.trunc(Number(a) || 0));
|
|
163
|
-
return got.length === 2 && got[0] === MICRODEPOSIT_AMOUNTS[0] && got[1] === MICRODEPOSIT_AMOUNTS[1];
|
|
164
|
-
}
|
|
165
|
-
/** The deposit's descriptor code as sent: the test value. */
|
|
166
|
-
function descriptorMatches(params) {
|
|
167
|
-
return String(params.descriptor_code) === MICRODEPOSIT_DESCRIPTOR;
|
|
168
|
-
}
|
|
169
|
-
/** Stripe's refusal of deposits that do not match what was sent. */
|
|
170
|
-
function depositMismatch(byAmounts) {
|
|
171
|
-
return byAmounts
|
|
172
|
-
? err('The amounts provided do not match the amounts that were sent to the bank account.', 400, 'payment_intent_microdeposit_verification_amounts_mismatch')
|
|
173
|
-
: err('The descriptor code provided does not match the one that was sent to the bank account.', 400, 'payment_intent_microdeposit_verification_descriptor_code_mismatch');
|
|
174
|
-
}
|
|
175
|
-
// Validate the submitted micro-deposit verification params against the modeled test values.
|
|
176
|
-
// Returns a vendor 400 on missing/both/wrong inputs, or null when the verification matches.
|
|
177
|
-
export function verifyMicrodeposits(params, _existing) {
|
|
178
|
-
const hasAmounts = Array.isArray(params.amounts);
|
|
179
|
-
const hasDescriptor = typeof params.descriptor_code === 'string' && params.descriptor_code !== '';
|
|
180
|
-
if (hasAmounts === hasDescriptor)
|
|
181
|
-
return err('You must pass exactly one of `amounts` and `descriptor_code`.', 400, 'parameter_missing');
|
|
182
|
-
const matches = hasAmounts ? amountsMatch(params) : descriptorMatches(params);
|
|
183
|
-
return matches ? null : depositMismatch(hasAmounts);
|
|
184
|
-
}
|
|
185
155
|
// Build the real Stripe card-error envelope (HTTP 402). `param` is 'card' for card
|
|
186
156
|
// errors; charge/payment_intent ids are attached when known so SDK error objects carry
|
|
187
157
|
// them (Stripe does this for confirm/charge failures).
|
|
@@ -332,7 +302,7 @@ function parseSearchQuery(query) {
|
|
|
332
302
|
const disjunction = / OR /i.test(trimmed) && !/ AND /i.test(trimmed);
|
|
333
303
|
const parts = trimmed.split(disjunction ? / OR /i : / AND /i);
|
|
334
304
|
const clauses = [];
|
|
335
|
-
const re = /^\s*([A-Za-z0-9_]+(?:\[[^\]]+\])?)\s*(
|
|
305
|
+
const re = /^\s*([A-Za-z0-9_]+(?:\[[^\]]+\])?)\s*(>=|<=|>|<|:|~)\s*(.+?)\s*$/;
|
|
336
306
|
for (const part of parts) {
|
|
337
307
|
const m = re.exec(part);
|
|
338
308
|
if (!m)
|
|
@@ -340,6 +310,8 @@ function parseSearchQuery(query) {
|
|
|
340
310
|
let value = m[3].trim();
|
|
341
311
|
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")))
|
|
342
312
|
value = value.slice(1, -1);
|
|
313
|
+
if (m[2] === '~' && (!['email', 'name'].includes(m[1]) || value.length < 3))
|
|
314
|
+
return null;
|
|
343
315
|
clauses.push({ field: m[1], op: m[2], value });
|
|
344
316
|
}
|
|
345
317
|
return { clauses, disjunction };
|
|
@@ -357,15 +329,13 @@ function searchFieldValue(item, field) {
|
|
|
357
329
|
}
|
|
358
330
|
/** A search's numeric comparison other than `>=` (docs.stripe.com/search#search-query-language). */
|
|
359
331
|
function otherComparison(op, a, n) {
|
|
360
|
-
|
|
361
|
-
case '>': return a > n;
|
|
362
|
-
case '<': return a < n;
|
|
363
|
-
case '<=': return a <= n;
|
|
364
|
-
}
|
|
365
|
-
return false;
|
|
332
|
+
return op === '>' ? a > n : op === '<' ? a < n : a <= n;
|
|
366
333
|
}
|
|
367
334
|
function evalSearchClause(item, c) {
|
|
368
335
|
const actual = searchFieldValue(item, c.field);
|
|
336
|
+
// source: https://docs.stripe.com/search "Substring match operator (substrings must be a minimum of 3 characters)"
|
|
337
|
+
if (c.op === '~')
|
|
338
|
+
return actual != null && String(actual).toLowerCase().includes(c.value.toLowerCase());
|
|
369
339
|
if (c.op === ':') {
|
|
370
340
|
if (c.value === 'null')
|
|
371
341
|
return actual === null || actual === undefined;
|
|
@@ -373,8 +343,8 @@ function evalSearchClause(item, c) {
|
|
|
373
343
|
if (typeof actual === 'number' && /^-?\d+(\.\d+)?$/.test(c.value))
|
|
374
344
|
return actual === Number(c.value);
|
|
375
345
|
if (typeof actual === 'boolean')
|
|
376
|
-
return String(actual) === c.value;
|
|
377
|
-
return String(actual) === c.value;
|
|
346
|
+
return String(actual).toLowerCase() === c.value.toLowerCase();
|
|
347
|
+
return String(actual).toLowerCase() === c.value.toLowerCase();
|
|
378
348
|
}
|
|
379
349
|
const n = Number(c.value);
|
|
380
350
|
const a = Number(actual);
|
|
@@ -432,24 +402,6 @@ export function buildDiscount(coupon, customer, at, on = {}) {
|
|
|
432
402
|
invoice: null, invoice_item: null, promotion_code: null, checkout_session: null,
|
|
433
403
|
};
|
|
434
404
|
}
|
|
435
|
-
// The (price id, quantity) pairs a subscription bills, normalized across the shapes the
|
|
436
|
-
// twin stores items in (the create form array items:[{price,quantity}], or the canonical
|
|
437
|
-
// items:{data:[{price}]} list). Used to compute upcoming-invoice lines.
|
|
438
|
-
export function subscriptionItemPairs(sub) {
|
|
439
|
-
const items = sub.items;
|
|
440
|
-
const data = Array.isArray(items?.data) ? items.data : Array.isArray(sub.items) ? sub.items : [];
|
|
441
|
-
const out = [];
|
|
442
|
-
for (const it of data) {
|
|
443
|
-
const p = it?.price;
|
|
444
|
-
const id = typeof p === 'string' ? p : (p && typeof p === 'object' ? String(p.id ?? '') : '');
|
|
445
|
-
if (id)
|
|
446
|
-
out.push({ price: id, quantity: Math.max(1, Math.trunc(Number(it.quantity) || 1)) });
|
|
447
|
-
}
|
|
448
|
-
// a bare `price` field (the connector's flattened shape) counts as one item.
|
|
449
|
-
if (out.length === 0 && typeof sub.price === 'string' && sub.price)
|
|
450
|
-
out.push({ price: sub.price, quantity: 1 });
|
|
451
|
-
return out;
|
|
452
|
-
}
|
|
453
405
|
// The raw item entries a subscription/checkout-derived-subscription create sent,
|
|
454
406
|
// normalized to an ordered array. The form reader already turns the bracket form
|
|
455
407
|
// items[0][price]=… into an array of objects (see lineItemEntries() above for the
|
|
@@ -507,18 +459,12 @@ export function addBillingInterval(atUnix, interval, count) {
|
|
|
507
459
|
// response includes the actual items, mirroring what was requested.
|
|
508
460
|
/** What a subscription item carries of its price: the Price itself and its plan view (a subscription item answers both,
|
|
509
461
|
* docs.stripe.com/api/subscription_items/object), by the price's id or an already-resolved Price. */
|
|
462
|
+
// source: https://docs.stripe.com/api/plans/create "It replaces the Plans API and is backwards compatible to simplify your migration."
|
|
463
|
+
// source: spec:/components/schemas/plan "amount_decimal"
|
|
510
464
|
export function subscriptionItemPrice(p, find) {
|
|
511
465
|
const priceId = typeof p === 'string' ? p : (p && typeof p === 'object' ? String(p.id ?? '') : '');
|
|
512
466
|
const price = priceId ? find('price', priceId) : undefined;
|
|
513
|
-
const
|
|
514
|
-
const plan = price ? {
|
|
515
|
-
id: price.id, object: 'plan', active: price.active ?? true, amount: price.unit_amount ?? null,
|
|
516
|
-
amount_decimal: price.unit_amount_decimal ?? null, billing_scheme: price.billing_scheme ?? 'per_unit',
|
|
517
|
-
currency: price.currency ?? 'usd', interval: recurring?.interval ?? null,
|
|
518
|
-
interval_count: recurring?.interval_count ?? 1, livemode: false,
|
|
519
|
-
metadata: price.metadata ?? {}, nickname: price.nickname ?? null,
|
|
520
|
-
product: price.product ?? null, tiers_mode: price.tiers_mode ?? null, usage_type: 'licensed',
|
|
521
|
-
} : null;
|
|
467
|
+
const plan = price ? planView(price) : null;
|
|
522
468
|
return { price: price ?? (priceId || null), plan };
|
|
523
469
|
}
|
|
524
470
|
export function buildSubscriptionItemsList(entries, subId, at, find) {
|
|
@@ -569,20 +515,6 @@ export function resolveTrial(params, at) {
|
|
|
569
515
|
}
|
|
570
516
|
return undefined;
|
|
571
517
|
}
|
|
572
|
-
// ── ISSUING helpers: spending controls, real-time-auth shapes ─────────────────────────
|
|
573
|
-
// Authorization.card is the FULL Card object in every vendor egress (API responses and
|
|
574
|
-
// webhook payloads) — it is not expandable (stripe@22.3.0 Issuing.Authorization.card:
|
|
575
|
-
// Stripe.Issuing.Card). The twin stores the id internally and embeds at the boundary;
|
|
576
|
-
// cardholder stays an id string (expandable, unexpanded default). Caught live: the
|
|
577
|
-
// issuing-bridge reads event.data.object.card.id and refused every twin presentment.
|
|
578
|
-
export function embedIssuingAuthorizationCard(body, find) {
|
|
579
|
-
if (typeof body.card === 'string') {
|
|
580
|
-
const cardRow = find('issuing_card', body.card);
|
|
581
|
-
if (cardRow !== undefined)
|
|
582
|
-
body.card = cardRow;
|
|
583
|
-
}
|
|
584
|
-
return body;
|
|
585
|
-
}
|
|
586
518
|
// The closed interval set for spending_controls[spending_limits][][interval]
|
|
587
519
|
// (stripe@22.3.0 Issuing/Cards.d.ts SpendingLimit.Interval — a documented closed enum).
|
|
588
520
|
const SPENDING_LIMIT_INTERVALS = new Set(['all_time', 'daily', 'monthly', 'per_authorization', 'weekly', 'yearly']);
|
|
@@ -610,12 +542,12 @@ function controlsRefused(what, i = 0) {
|
|
|
610
542
|
// Vendor rules enforced: interval must be in the closed enum; a spending limit amount is a
|
|
611
543
|
// positive integer; allowed_categories "Cannot be set with blocked_categories" (and the
|
|
612
544
|
// merchant-country pair likewise) — both restrictions verbatim from the SDK's field docs.
|
|
613
|
-
export function normalizeSpendingControls(raw, currency) {
|
|
545
|
+
export function normalizeSpendingControls(raw, currency, previous) {
|
|
614
546
|
if (raw === undefined)
|
|
615
547
|
return {};
|
|
616
548
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
617
549
|
return controlsRefused('dictionary');
|
|
618
|
-
const sc = raw;
|
|
550
|
+
const sc = { ...previous, ...raw };
|
|
619
551
|
const strArr = (v) => (Array.isArray(v) ? v.map(String) : undefined);
|
|
620
552
|
const allowedCats = strArr(sc.allowed_categories);
|
|
621
553
|
const blockedCats = strArr(sc.blocked_categories);
|
|
@@ -642,6 +574,9 @@ export function normalizeSpendingControls(raw, currency) {
|
|
|
642
574
|
const arrOrNull = (v) => (v === undefined ? undefined : v.length ? v : null);
|
|
643
575
|
const controls = {
|
|
644
576
|
...emptySpendingControls(),
|
|
577
|
+
// source: spec:/paths/~1v1~1issuing~1cards~1{card}/post/requestBody/content/application~1x-www-form-urlencoded/schema/properties/spending_controls "Rules that control spending for this card."
|
|
578
|
+
// Keep every documented control, including allowed_card_presences and blocked_card_presences.
|
|
579
|
+
...sc,
|
|
645
580
|
spending_limits: limits,
|
|
646
581
|
...(limits.length ? { spending_limits_currency: typeof sc.spending_limits_currency === 'string' ? sc.spending_limits_currency : currency } : {}),
|
|
647
582
|
};
|
|
@@ -749,6 +684,9 @@ function cardSubObject(params) {
|
|
|
749
684
|
brand: 'visa', last4: number ? number.slice(-4) : '4242',
|
|
750
685
|
exp_month: Number(c.exp_month) || 12, exp_year: Number(c.exp_year) || 2034,
|
|
751
686
|
funding: 'credit', country: 'US',
|
|
687
|
+
// source: spec:/components/schemas/payment_method_card/properties/fingerprint/description "Uniquely identifies this particular card number."
|
|
688
|
+
// The synthetic fingerprint is stable for a number; Stripe's opaque fingerprint algorithm is not published.
|
|
689
|
+
fingerprint: digest('sha256', number || '4242424242424242').slice(0, 16),
|
|
752
690
|
checks: { address_line1_check: null, address_postal_code_check: null, cvc_check: 'pass' },
|
|
753
691
|
networks: { available: ['visa'], preferred: null },
|
|
754
692
|
three_d_secure_usage: { supported: true }, wallet: null,
|
|
@@ -945,3 +883,16 @@ export function chargeDefaults(chargeId, amount, captured, occurredAt) {
|
|
|
945
883
|
}
|
|
946
884
|
// collection path segment → resource type, for the generic list of a collection Stripe does not
|
|
947
885
|
// list at that path (the twin's test clocks, which Stripe lists under /v1/test_helpers).
|
|
886
|
+
export function planView(price) {
|
|
887
|
+
const recurring = price.recurring;
|
|
888
|
+
return {
|
|
889
|
+
id: price.id, object: 'plan', active: price.active ?? true, amount: price.unit_amount ?? null,
|
|
890
|
+
amount_decimal: price.unit_amount_decimal ?? (price.unit_amount == null ? null : String(price.unit_amount)),
|
|
891
|
+
billing_scheme: price.billing_scheme ?? 'per_unit', created: price.created, currency: price.currency,
|
|
892
|
+
interval: recurring?.interval ?? null, interval_count: recurring?.interval_count ?? 1,
|
|
893
|
+
livemode: price.livemode ?? false, metadata: price.metadata ?? {}, nickname: price.nickname ?? null,
|
|
894
|
+
product: price.product ?? null, tiers_mode: price.tiers_mode ?? null,
|
|
895
|
+
meter: recurring?.meter ?? null, trial_period_days: recurring?.trial_period_days ?? null,
|
|
896
|
+
transform_usage: price.transform_quantity ?? null, usage_type: recurring?.usage_type ?? 'licensed',
|
|
897
|
+
};
|
|
898
|
+
}
|
|
@@ -9,4 +9,3 @@ export declare function pauseCollection(pc: unknown, fields: Row): string | unde
|
|
|
9
9
|
export declare const MAX_TRIAL: number;
|
|
10
10
|
/** A trial moved to end at `end`, or a new one to `end` on a subscription out of its trial (applyTrialEnd). */
|
|
11
11
|
export declare function movedTrial(sub: Row, fields: Row, end: number, now: number): undefined;
|
|
12
|
-
export declare const ITEM_ATTRIBUTES: Set<string>;
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
export declare const SERVED_VERSION: string;
|
|
3
3
|
/** Whether a request is answered in the served version's shape. */
|
|
4
4
|
export declare const servesCurrent: (pinned?: string | null) => boolean;
|
|
5
|
-
/** Every includable field name, as `expand` paths on a top-level object: what a check of an object's FULL answer expands. */
|
|
6
|
-
export declare const INCLUDABLE_FIELDS: string[];
|
|
7
5
|
/** The `expand[]` paths a request names, in its query or its body (JSON or form). */
|
|
8
6
|
export declare function expandOf(request: Request): Promise<string[]>;
|
|
9
7
|
/** An answer with every webhook endpoint's `secret` left out: Stripe answers it only when the endpoint is created. */
|
|
@@ -12,12 +12,16 @@ import surface from '../generated/surface.gen.json' with { type: 'json' };
|
|
|
12
12
|
/** The version the pack serves: its vendored spec's. */
|
|
13
13
|
export const SERVED_VERSION = String(surface.version);
|
|
14
14
|
const BASIL = '2025-03-31';
|
|
15
|
+
const CLOVER = '2025-09-30';
|
|
15
16
|
/** Whether a request is answered in the served version's shape. */
|
|
16
17
|
export const servesCurrent = (pinned) => !pinned || pinned >= BASIL;
|
|
17
18
|
// What the twin keeps on an object for its rules that Stripe never answers: request parameters (a capture
|
|
18
19
|
// flag, the payment behavior a subscription was created with) and links it follows internally.
|
|
19
20
|
const KEPT = {
|
|
20
21
|
account: ['livemode'],
|
|
22
|
+
// source: spec:PostWebhookEndpoints "Whether this endpoint should receive events from connected accounts"
|
|
23
|
+
// connect configures event delivery; it is not a WebhookEndpoint response field.
|
|
24
|
+
webhook_endpoint: ['connect'],
|
|
21
25
|
// a meter event is identified by its identifier; the twin's row id is its own
|
|
22
26
|
'billing.meter_event': ['id'],
|
|
23
27
|
// a card source's creation time is the twin's, for ordering
|
|
@@ -32,7 +36,7 @@ const KEPT = {
|
|
|
32
36
|
payment_intent: ['mandate', 'off_session'],
|
|
33
37
|
subscription: ['payment_behavior'],
|
|
34
38
|
subscription_item: ['livemode'],
|
|
35
|
-
subscription_schedule: ['from_subscription', 'renewal_interval'],
|
|
39
|
+
subscription_schedule: ['from_subscription', 'renewal_interval', 'proration_behavior'],
|
|
36
40
|
'tax.transaction': ['calculation'],
|
|
37
41
|
'terminal.reader': ['registration_code'],
|
|
38
42
|
topup: ['destination_balance'],
|
|
@@ -59,8 +63,7 @@ const BASIL_CHANGES = {
|
|
|
59
63
|
// "confirmation_secret … Currently, this contains the client_secret of the PaymentIntent that Stripe creates during
|
|
60
64
|
// invoice finalization" (docs.stripe.com/api/invoices/object; includable, answered only when expanded: INCLUDABLE);
|
|
61
65
|
// the intent's client_secret is the one ./stripe.ts mintClientSecret gives it
|
|
62
|
-
const
|
|
63
|
-
const confirmation_secret = intent ? { type: 'payment_intent', client_secret: `${intent}_secret_twin` } : null;
|
|
66
|
+
const confirmation_secret = o.confirmation_secret ?? null;
|
|
64
67
|
return { ...omit(o, ['payment_intent', 'charge', 'paid', 'paid_out_of_band', 'subscription']), parent, confirmation_secret };
|
|
65
68
|
},
|
|
66
69
|
charge: (o) => omit(o, ['invoice', 'source']),
|
|
@@ -221,8 +224,6 @@ const INCLUDABLE = {
|
|
|
221
224
|
// request it with the `expand` request parameter))"
|
|
222
225
|
invoice: ['confirmation_secret'],
|
|
223
226
|
};
|
|
224
|
-
/** Every includable field name, as `expand` paths on a top-level object: what a check of an object's FULL answer expands. */
|
|
225
|
-
export const INCLUDABLE_FIELDS = [...new Set(Object.values(INCLUDABLE).flat())];
|
|
226
227
|
/** The `expand[]` paths a request names, in its query or its body (JSON or form). */
|
|
227
228
|
export async function expandOf(request) {
|
|
228
229
|
const url = new URL(request.url);
|
|
@@ -280,9 +281,19 @@ export function render(value, pinned, expand = []) {
|
|
|
280
281
|
// reads the same, and a key posted "" to remove it is not there (../semantics/shared.ts withMetadata)
|
|
281
282
|
if (o.metadata && typeof o.metadata === 'object' && !Array.isArray(o.metadata))
|
|
282
283
|
o.metadata = Object.fromEntries(Object.entries(o.metadata).filter(([, v]) => v !== '').map(([k, v]) => [k, typeof v === 'number' || typeof v === 'boolean' ? String(v) : v]));
|
|
284
|
+
// source: spec:/components/schemas/checkout.session/properties/ui_mode "Defaults to `hosted_page`."
|
|
285
|
+
// Endive names hosted/custom Checkout hosted_page/elements; older SDK versions keep their own names.
|
|
286
|
+
if (kind === 'checkout.session' && (!pinned || pinned >= '2026-09-30')) {
|
|
287
|
+
if (o.ui_mode === 'hosted')
|
|
288
|
+
o.ui_mode = 'hosted_page';
|
|
289
|
+
if (o.ui_mode === 'custom')
|
|
290
|
+
o.ui_mode = 'elements';
|
|
291
|
+
}
|
|
283
292
|
const kept = KEPT[kind] ? omit(o, KEPT[kind]) : o;
|
|
284
293
|
// an includable field a version's change adds (an invoice's confirmation_secret) is hidden as its stored ones are
|
|
285
|
-
|
|
294
|
+
// source: https://docs.stripe.com/changelog/clover/2025-09-30/polymorphic-coupon "Moves the top-level coupon field on Promotion Codes to promotion.coupon"
|
|
295
|
+
const changed = kind === 'promotion_code' && pinned && pinned < CLOVER ? omit(kept, ['promotion']) : BASIL_CHANGES[kind] ? BASIL_CHANGES[kind](kept) : kept;
|
|
296
|
+
return omit(withNulls(changed, kind), hidden);
|
|
286
297
|
};
|
|
287
298
|
return walk(value);
|
|
288
299
|
}
|
package/dist/src/fetch.js
CHANGED
|
@@ -8,8 +8,8 @@ import { clock } from "./semantics/clock.js";
|
|
|
8
8
|
import * as doors from "./semantics/doors.js";
|
|
9
9
|
import { stripeSemantics } from "./semantics/index.js";
|
|
10
10
|
import { screen as screen_billing_portal } from "./screens/billing-portal.js";
|
|
11
|
+
import { screen as screen_checkout_js } from "./screens/checkout-js.js";
|
|
11
12
|
import { screen as screen_checkout } from "./screens/checkout.js";
|
|
12
|
-
import { screen as screen_connect_oauth } from "./screens/connect-oauth.js";
|
|
13
13
|
import { screen as screen_connect_onboarding } from "./screens/connect-onboarding.js";
|
|
14
14
|
import { screen as screen_dashboard } from "./screens/dashboard.js";
|
|
15
15
|
import { screen as screen_stripe_js } from "./screens/stripe-js.js";
|
|
@@ -21,6 +21,6 @@ export function createStripeFetch(options = {}) {
|
|
|
21
21
|
doors,
|
|
22
22
|
around,
|
|
23
23
|
clock,
|
|
24
|
-
screens: { "billing-portal": screen_billing_portal, "checkout":
|
|
24
|
+
screens: { "billing-portal": screen_billing_portal, "checkout-js": screen_checkout_js, "checkout": screen_checkout, "connect-onboarding": screen_connect_onboarding, "dashboard": screen_dashboard, "stripe-js": screen_stripe_js },
|
|
25
25
|
}, options);
|
|
26
26
|
}
|