@crvouga/mockingbird-service-stripe 1.3.0 → 1.4.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/CHANGELOG.md +28 -0
- package/README.md +21 -4
- package/dist/{chunk-2WP3EV2W.js → chunk-P77GCNSW.js} +120 -13
- package/dist/chunk-P77GCNSW.js.map +7 -0
- package/dist/{chunk-UCGHUTFD.js → chunk-ZSCTOAWQ.js} +2 -2
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +31 -0
- package/dist/index.js +1 -1
- package/dist/server.d.ts +31 -0
- package/dist/server.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-2WP3EV2W.js.map +0 -7
- /package/dist/{chunk-UCGHUTFD.js.map → chunk-ZSCTOAWQ.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog — @crvouga/mockingbird-service-stripe
|
|
2
2
|
|
|
3
|
+
## 1.4.0 (2026-09-29)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- retry failed subscription renewals and apply the configured outcome ([c737a68](https://github.com/crvouga/mockingbird/commit/c737a6873a5e9b984b382b9f00c0b01f1712116c))
|
|
8
|
+
|
|
9
|
+
### Fixes and improvements
|
|
10
|
+
|
|
11
|
+
- validate list elements ahead of a schema issue in order ([5a09c88](https://github.com/crvouga/mockingbird/commit/5a09c880a432b4e13e2ef5b519a8a76962d372da))
|
|
12
|
+
- answer a missing SetupIntent with Stripe's 404 resource_missing ([9e8368f](https://github.com/crvouga/mockingbird/commit/9e8368ff2d01303cf5ff5410afc3387c75163ca6))
|
|
13
|
+
- leave the portal configuration list out of live parity walks ([9adfc89](https://github.com/crvouga/mockingbird/commit/9adfc89ed7dbe88d798724deaf9d9ccddea60800))
|
|
14
|
+
- use --all-snapshot with Stripe CLI 1.52 in the webhook oracle ([d95804c](https://github.com/crvouga/mockingbird/commit/d95804cbdf98bcbd2199865dcdd13157d44b1d12))
|
|
15
|
+
- pass --events to stripe listen so Stripe CLI 1.52 starts ([b31d3da](https://github.com/crvouga/mockingbird/commit/b31d3da44396f79630f21f732fd4fb2cc1d3fcc9))
|
|
16
|
+
- report why stripe listen exited in the webhook oracle ([19461e4](https://github.com/crvouga/mockingbird/commit/19461e478a3d0489aac646820ecaad12b2d6ce2a))
|
|
17
|
+
|
|
18
|
+
## 1.3.1 (2026-09-29)
|
|
19
|
+
|
|
20
|
+
### Fixes and improvements
|
|
21
|
+
|
|
22
|
+
- say 'No such configuration' for a missing portal configuration ([6fad72a](https://github.com/crvouga/mockingbird/commit/6fad72a676b359672653508d9dd654e656ac6aa1))
|
|
23
|
+
- upper-case only two-letter country codes like the live API ([4ee5121](https://github.com/crvouga/mockingbird/commit/4ee5121cf2254fd75eef34bbeefee496cd16964a))
|
|
24
|
+
- leave portal configuration listing out of live parity walks ([20d715a](https://github.com/crvouga/mockingbird/commit/20d715a548e60dd933fab52e7ba661870607049e))
|
|
25
|
+
- keep the shipping address country verbatim like the live API ([052e285](https://github.com/crvouga/mockingbird/commit/052e285a0ae9bcc26050f1c50ed635085438c3d1))
|
|
26
|
+
- use --all-snapshot so the Stripe CLI webhook oracle starts ([09809bf](https://github.com/crvouga/mockingbird/commit/09809bfcd106cc4570d74e781ab0619efb191e05))
|
|
27
|
+
- listen for all events so newer Stripe CLIs start the webhook oracle ([278e364](https://github.com/crvouga/mockingbird/commit/278e364c77263d8cff65fb6d03fde9e89a1010ca))
|
|
28
|
+
- surface redacted stripe listen output when the webhook oracle fails to start ([dd179bb](https://github.com/crvouga/mockingbird/commit/dd179bbdf7a0d38ba097f79041ec818eaf69dfc5))
|
|
29
|
+
- sign and retry webhooks on the injected clock ([1091bde](https://github.com/crvouga/mockingbird/commit/1091bde0df69d9cc3eba2062d8523755e6caca14))
|
|
30
|
+
|
|
3
31
|
## 1.3.0 (2026-09-29)
|
|
4
32
|
|
|
5
33
|
### Features
|
package/README.md
CHANGED
|
@@ -86,7 +86,7 @@ the mock's hosted page.
|
|
|
86
86
|
|
|
87
87
|
State is partitioned by **account**, and the account is chosen by API key:
|
|
88
88
|
|
|
89
|
-
- `PUT /__admin/accounts {"accounts": [{id, keys, apiVersion?, webhookSecrets?, corpus?, displayName?}]}`
|
|
89
|
+
- `PUT /__admin/accounts {"accounts": [{id, keys, apiVersion?, webhookSecrets?, corpus?, displayName?, billing?}]}`
|
|
90
90
|
(also `createRuntime({accounts})` / `serve --accounts <json|file>`). Every key listed on an
|
|
91
91
|
account acts as it. Any other test key is an account of its own (`accountOfKey(key)`), so an MSO
|
|
92
92
|
key reading a PC object gets Stripe's exact `404 resource_missing` (`No such payment_intent: 'pi_…'`).
|
|
@@ -94,6 +94,8 @@ State is partitioned by **account**, and the account is chosen by API key:
|
|
|
94
94
|
its webhook payloads render at (default `2024-06-20`, what every backend receiver of ours pins).
|
|
95
95
|
- `webhookSecrets: {"<receiver url>": "whsec_…"}` delivers every event of the account there.
|
|
96
96
|
- `corpus: true` seeds the recorded catalog (below).
|
|
97
|
+
- `billing: {retries: {scheduleDays?, afterAllFail?}}` sets the account's failed-renewal retries
|
|
98
|
+
(see Lifecycles and the clock); unset fields use `createRuntime({lifecycle: {paymentRetries}})`.
|
|
97
99
|
|
|
98
100
|
**Namespaces** isolate parallel workers; each namespace has its own copy of every account. Carriers:
|
|
99
101
|
the `x-mockingbird-namespace` header, the `/ns/<namespace>/…` path prefix, or **by API key**:
|
|
@@ -118,7 +120,7 @@ holds, ancestors included); Stripe's own rules are enforced: a non-expandable fi
|
|
|
118
120
|
|
|
119
121
|
Every state change records an event in the account's log (`GET /v1/events`, filterable by
|
|
120
122
|
`types[]` and `created`) and publishes it through the shared webhook hub, signed
|
|
121
|
-
`Stripe-Signature: t=<
|
|
123
|
+
`Stripe-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, "t.body")>` over the exact bytes —
|
|
122
124
|
`stripe.webhooks.constructEvent` verifies them.
|
|
123
125
|
|
|
124
126
|
- Endpoints: `PUT /__admin/webhook-endpoints [{account, url, secret, enabledEvents: ["*"|…]}]`
|
|
@@ -126,6 +128,11 @@ Every state change records an event in the account's log (`GET /v1/events`, filt
|
|
|
126
128
|
`serve --webhook-url/--webhook-secret`, accounts' `webhookSecrets`, and endpoints created through
|
|
127
129
|
`POST /v1/webhook_endpoints` (signed with the `whsec_` returned at creation). One event fans out to
|
|
128
130
|
every matching endpoint, as on Stripe. Retries follow the hub's schedule.
|
|
131
|
+
- `t` is the wall clock, unless the runtime's `clock` is injected (`createRuntime({ clock })`):
|
|
132
|
+
then `t`, retries and the attempt timeout run on that clock, so a virtual-time suite verifies with
|
|
133
|
+
`constructEventAsync(body, sig, secret, tolerance, undefined, clock.now())` and sees a retry when it
|
|
134
|
+
advances the clock (`runtime.clock.advance`, or `runtime.tick()` after moving its own clock).
|
|
135
|
+
`webhooks: { now, schedule, cancel, id }` override the hub's sources directly.
|
|
129
136
|
- `GET /__admin/webhooks`, `/webhooks/events`, `POST /__admin/webhooks/:id/replay`, `/webhooks/flush`.
|
|
130
137
|
- Delivery faults: presets `webhook_duplicate` (same event id twice — our receiver's in-flight
|
|
131
138
|
dedupe answers 500), `webhook_reorder` (the next two swapped), `webhook_drop` (never delivered,
|
|
@@ -166,6 +173,18 @@ clock-driven lifecycle, so their webhooks fire at once; the served mock also tic
|
|
|
166
173
|
voided (`void`), marked `uncollectible` (`mark_uncollectible`, `invoice.marked_uncollectible`) or
|
|
167
174
|
left as drafts (`keep_as_draft`), never charged; the status holds. `pause_collection[resumes_at]`
|
|
168
175
|
lifts the pause on its own before the renewal it precedes; `pause_collection=""` lifts it now.
|
|
176
|
+
- **Payment retries**: a declined renewal (`charge_automatically`) leaves the invoice `open` with
|
|
177
|
+
`attempt_count: 1` and `next_payment_attempt`, and the subscription `past_due`. Each retry
|
|
178
|
+
`scheduleDays` after the first failure (default `[3, 5, 7]`, Stripe's classic "3 retries over
|
|
179
|
+
1 week") charges again when the clock reaches it: a decline bumps `attempt_count`, emits
|
|
180
|
+
`invoice.payment_failed` and moves `next_payment_attempt`; a working default payment method pays
|
|
181
|
+
the invoice (`invoice.paid`) and the subscription is `active` again
|
|
182
|
+
(`customer.subscription.updated`). After the last retry `next_payment_attempt` is `null` and
|
|
183
|
+
`afterAllFail` applies: `cancel` (the default: `canceled`, `cancellation_details.reason:
|
|
184
|
+
payment_failed`, `customer.subscription.deleted`), `unpaid` (`customer.subscription.updated`) or
|
|
185
|
+
`past_due` (nothing changes). An empty schedule applies it at the first failure. The schedule is
|
|
186
|
+
fixed, not Stripe's ML-timed Smart Retries; set it per account (`billing.retries`, also
|
|
187
|
+
`PUT /__admin/accounts`) or for the runtime (`lifecycle.paymentRetries`). Dunning emails are not sent.
|
|
169
188
|
- `incomplete` subscriptions become `incomplete_expired` after 23 h (their invoice is voided).
|
|
170
189
|
- Checkout Sessions expire at `expires_at` (`checkout.session.expired`).
|
|
171
190
|
- Schedule phases advance; the last one releases or cancels per `end_behavior`.
|
|
@@ -362,8 +381,6 @@ plus `port`, `host`; resolves `{url, port, runtime, close}`), `serveTarget` (the
|
|
|
362
381
|
PaymentSheet cannot be redirected (member-app native keeps its fake provider).
|
|
363
382
|
- **Connect** (`Stripe-Account`, application fees, transfers), tax, shipping, Radar, mandates,
|
|
364
383
|
meters, quotes, credit notes, payouts, and non-card payment methods (bank debits, wallets).
|
|
365
|
-
- **Smart retries / dunning**: a failed renewal goes `past_due` once; later automatic retries,
|
|
366
|
-
`unpaid` and dunning emails are not run.
|
|
367
384
|
- **Proration arithmetic** is day-fraction approximate (Stripe prorates to the second);
|
|
368
385
|
`auto_advance` drafts are not finalized an hour later.
|
|
369
386
|
- **Customer portal extras**: the login page (`login_page.url` is not served), `schedule_at_period_end`
|
|
@@ -1559,7 +1559,7 @@ var restoreNamespace = (sqlite, namespace, snapshot) => {
|
|
|
1559
1559
|
};
|
|
1560
1560
|
|
|
1561
1561
|
// ../core/dist/version.js
|
|
1562
|
-
var PACKAGE_VERSION = true ? "1.
|
|
1562
|
+
var PACKAGE_VERSION = true ? "1.4.0" : UNRELEASED_VERSION;
|
|
1563
1563
|
|
|
1564
1564
|
// ../core/dist/signing.js
|
|
1565
1565
|
var encoder = new TextEncoder();
|
|
@@ -2583,6 +2583,27 @@ var eraOf = (version) => {
|
|
|
2583
2583
|
};
|
|
2584
2584
|
|
|
2585
2585
|
// src/accounts.ts
|
|
2586
|
+
var DEFAULT_PAYMENT_RETRIES = {
|
|
2587
|
+
scheduleDays: [3, 5, 7],
|
|
2588
|
+
afterAllFail: "cancel"
|
|
2589
|
+
};
|
|
2590
|
+
var RETRY_OUTCOMES = ["cancel", "unpaid", "past_due"];
|
|
2591
|
+
var validateRetries = (id, value) => {
|
|
2592
|
+
const raw = value?.retries;
|
|
2593
|
+
if (typeof value !== "object" || value === null || typeof raw !== "object" || raw === null)
|
|
2594
|
+
return `${id}: billing must be {"retries": {"scheduleDays": [3, 5, 7], "afterAllFail": "cancel"}}`;
|
|
2595
|
+
const { scheduleDays, afterAllFail } = raw;
|
|
2596
|
+
if (scheduleDays !== void 0 && (!Array.isArray(scheduleDays) || scheduleDays.some(
|
|
2597
|
+
(day, index) => typeof day !== "number" || !Number.isFinite(day) || day <= 0 || index > 0 && day <= scheduleDays[index - 1]
|
|
2598
|
+
)))
|
|
2599
|
+
return `${id}: billing.retries.scheduleDays must be increasing positive numbers of days`;
|
|
2600
|
+
if (afterAllFail !== void 0 && !RETRY_OUTCOMES.includes(afterAllFail))
|
|
2601
|
+
return `${id}: billing.retries.afterAllFail must be one of ${RETRY_OUTCOMES.join(", ")}`;
|
|
2602
|
+
return {
|
|
2603
|
+
...scheduleDays !== void 0 ? { scheduleDays } : {},
|
|
2604
|
+
...afterAllFail !== void 0 ? { afterAllFail } : {}
|
|
2605
|
+
};
|
|
2606
|
+
};
|
|
2586
2607
|
var DEFAULT_WEBHOOK_API_VERSION = "2024-06-20";
|
|
2587
2608
|
var KEY = /^(?:sk|rk|pk)_(?:test|live)_[A-Za-z0-9_]+$/;
|
|
2588
2609
|
var validateAccounts = (value) => {
|
|
@@ -2607,8 +2628,11 @@ var validateAccounts = (value) => {
|
|
|
2607
2628
|
const secrets = raw.webhookSecrets;
|
|
2608
2629
|
if (secrets !== void 0 && (typeof secrets !== "object" || secrets === null || Object.values(secrets).some((secret) => typeof secret !== "string")))
|
|
2609
2630
|
return `${raw.id}: webhookSecrets must map receiver URLs to whsec_ secrets`;
|
|
2631
|
+
const retries = raw.billing === void 0 ? void 0 : validateRetries(raw.id, raw.billing);
|
|
2632
|
+
if (typeof retries === "string") return retries;
|
|
2610
2633
|
accounts.push({
|
|
2611
2634
|
id: raw.id,
|
|
2635
|
+
...retries ? { billing: { retries } } : {},
|
|
2612
2636
|
keys: raw.keys,
|
|
2613
2637
|
...typeof raw.apiVersion === "string" ? { apiVersion: raw.apiVersion } : {},
|
|
2614
2638
|
...secrets ? { webhookSecrets: secrets } : {},
|
|
@@ -4875,11 +4899,19 @@ var activateAfterPayment = (scope, subscriptionId, intent) => {
|
|
|
4875
4899
|
};
|
|
4876
4900
|
var markPaymentFailed = (scope, invoice) => {
|
|
4877
4901
|
const now = customerNow(scope, invoice.customer);
|
|
4902
|
+
const attempts = invoice.attempt_count + 1;
|
|
4903
|
+
let next = now + 3 * DAY;
|
|
4904
|
+
if (invoice.subscription !== null) {
|
|
4905
|
+
const days = scope.services.paymentRetries(scope.account.account).scheduleDays;
|
|
4906
|
+
const first = attempts === 1 || invoice.next_payment_attempt === null ? now : invoice.next_payment_attempt - (days[attempts - 2] ?? 0) * DAY;
|
|
4907
|
+
const day = days[attempts - 1];
|
|
4908
|
+
next = day === void 0 ? null : first + day * DAY;
|
|
4909
|
+
}
|
|
4878
4910
|
const failed = {
|
|
4879
4911
|
...invoice,
|
|
4880
|
-
attempt_count:
|
|
4912
|
+
attempt_count: attempts,
|
|
4881
4913
|
attempted: true,
|
|
4882
|
-
next_payment_attempt:
|
|
4914
|
+
next_payment_attempt: next
|
|
4883
4915
|
};
|
|
4884
4916
|
scope.account.invoices.update(failed.id, failed);
|
|
4885
4917
|
scope.emit("invoice.payment_failed", renderInvoice(failed, scope.account));
|
|
@@ -5315,12 +5347,15 @@ var cycleSubscription = (scope, current) => {
|
|
|
5315
5347
|
});
|
|
5316
5348
|
claimInvoiceItems(scope, invoice);
|
|
5317
5349
|
let status = moved.status;
|
|
5350
|
+
let exhausted = false;
|
|
5318
5351
|
if (!keepDraft) {
|
|
5319
5352
|
const finalized = finalizeInvoice(scope, invoice);
|
|
5320
5353
|
const collected = collectCycleInvoice(scope, current, finalized);
|
|
5321
5354
|
invoice = collected.invoice;
|
|
5322
|
-
if (finalized.status === "open" && current.pause_collection === null && finalized.collection_method === "charge_automatically")
|
|
5355
|
+
if (finalized.status === "open" && current.pause_collection === null && finalized.collection_method === "charge_automatically") {
|
|
5323
5356
|
status = collected.failed ? "past_due" : "active";
|
|
5357
|
+
exhausted = collected.failed && invoice.attempt_count > 0 && invoice.next_payment_attempt === null;
|
|
5358
|
+
}
|
|
5324
5359
|
}
|
|
5325
5360
|
const latest = scope.account.subscriptions.get(current.id) ?? moved;
|
|
5326
5361
|
const next = {
|
|
@@ -5334,6 +5369,33 @@ var cycleSubscription = (scope, current) => {
|
|
|
5334
5369
|
const after = renderSubscription(next, scope.account);
|
|
5335
5370
|
const changed = changedFields(before, after);
|
|
5336
5371
|
scope.emit("customer.subscription.updated", after, changed);
|
|
5372
|
+
if (exhausted) applyFailedPaymentOutcome(scope, next);
|
|
5373
|
+
};
|
|
5374
|
+
var applyFailedPaymentOutcome = (scope, current) => {
|
|
5375
|
+
const outcome = scope.services.paymentRetries(scope.account.account).afterAllFail;
|
|
5376
|
+
if (outcome === "cancel") cancelSubscription(scope, current, "payment_failed");
|
|
5377
|
+
else if (outcome === "unpaid") saveSubscription(scope, current, { ...current, status: "unpaid" });
|
|
5378
|
+
};
|
|
5379
|
+
var retryDueInvoices = (scope, subscription, now) => {
|
|
5380
|
+
for (let guard2 = 0; guard2 < 50; guard2 += 1) {
|
|
5381
|
+
const due = scope.account.invoices.list({
|
|
5382
|
+
order: "oldest",
|
|
5383
|
+
where: (invoice) => invoice.subscription === subscription.id && invoice.status === "open" && invoice.collection_method === "charge_automatically" && invoice.next_payment_attempt !== null && invoice.next_payment_attempt <= now
|
|
5384
|
+
})[0]?.value;
|
|
5385
|
+
if (due === void 0) return;
|
|
5386
|
+
let settled;
|
|
5387
|
+
try {
|
|
5388
|
+
settled = payInvoice(scope, due, { offSession: true });
|
|
5389
|
+
} catch (error) {
|
|
5390
|
+
if (!(error instanceof StripeError)) throw error;
|
|
5391
|
+
settled = scope.account.invoices.get(due.id) ?? due;
|
|
5392
|
+
}
|
|
5393
|
+
const current = scope.account.subscriptions.get(subscription.id);
|
|
5394
|
+
if (settled.status !== "open" || current === void 0) return;
|
|
5395
|
+
if (settled.next_payment_attempt === due.next_payment_attempt) return;
|
|
5396
|
+
if (settled.next_payment_attempt === null) applyFailedPaymentOutcome(scope, current);
|
|
5397
|
+
if (settled.next_payment_attempt === null || current.status !== "past_due") return;
|
|
5398
|
+
}
|
|
5337
5399
|
};
|
|
5338
5400
|
var periodAmount = (scope, subscription, at) => {
|
|
5339
5401
|
const lines = subscriptionItems(scope, subscription).map((item) => {
|
|
@@ -5744,7 +5806,8 @@ var releaseSchedule = (scope, schedule, now) => {
|
|
|
5744
5806
|
var DEFAULT_LIFECYCLE = {
|
|
5745
5807
|
upcomingInvoiceDays: 3,
|
|
5746
5808
|
trialWillEndDays: 3,
|
|
5747
|
-
incompleteExpiryHours: 23
|
|
5809
|
+
incompleteExpiryHours: 23,
|
|
5810
|
+
paymentRetries: DEFAULT_PAYMENT_RETRIES
|
|
5748
5811
|
};
|
|
5749
5812
|
var runLifecycle = (scope, settings, onlyCustomers) => {
|
|
5750
5813
|
const inScope = (customer) => onlyCustomers === void 0 ? customer === null || !findCustomer(scope, customer)?.test_clock : customer !== null && onlyCustomers.has(customer);
|
|
@@ -5778,6 +5841,10 @@ var runLifecycle = (scope, settings, onlyCustomers) => {
|
|
|
5778
5841
|
);
|
|
5779
5842
|
}
|
|
5780
5843
|
for (let guard2 = 0; guard2 < 120; guard2 += 1) {
|
|
5844
|
+
if (subscription.status === "past_due") {
|
|
5845
|
+
retryDueInvoices(scope, subscription, now2);
|
|
5846
|
+
subscription = scope.account.subscriptions.get(subscription.id) ?? subscription;
|
|
5847
|
+
}
|
|
5781
5848
|
if (!["active", "trialing", "past_due"].includes(subscription.status)) break;
|
|
5782
5849
|
const cancelAt = subscription.cancel_at;
|
|
5783
5850
|
if (cancelAt !== null && cancelAt <= now2) {
|
|
@@ -6461,7 +6528,13 @@ var parseParams = (schema, raw, options = {}) => {
|
|
|
6461
6528
|
if (error) throw error;
|
|
6462
6529
|
}
|
|
6463
6530
|
const issue = issues[0];
|
|
6464
|
-
if (issue)
|
|
6531
|
+
if (issue) {
|
|
6532
|
+
const failing = /^[^[]+\[(\d+)\]/.exec(issue.path)?.[1];
|
|
6533
|
+
const list = params[key];
|
|
6534
|
+
if (failing !== void 0 && Number(failing) > 0 && Array.isArray(list))
|
|
6535
|
+
options.validate?.[key]?.({ ...params, [key]: list.slice(0, Number(failing)) });
|
|
6536
|
+
throw issueToError(issue);
|
|
6537
|
+
}
|
|
6465
6538
|
options.validate?.[key]?.(params);
|
|
6466
6539
|
}
|
|
6467
6540
|
const stray = parsed.issues[0];
|
|
@@ -7184,7 +7257,7 @@ var loginPageOf = (scope, raw, base2, id) => {
|
|
|
7184
7257
|
var requireConfiguration = (scope, id, param = "configuration") => {
|
|
7185
7258
|
if (!scope.account.portalConfigurations.get(id)) ensureDefaultConfiguration(scope);
|
|
7186
7259
|
const config = scope.account.portalConfigurations.get(id);
|
|
7187
|
-
if (!config) throw resourceMissing("
|
|
7260
|
+
if (!config) throw resourceMissing("configuration", id, param);
|
|
7188
7261
|
return config;
|
|
7189
7262
|
};
|
|
7190
7263
|
var flowSubscription = (scope, customer, id, param) => {
|
|
@@ -7314,14 +7387,13 @@ var billingPortalHandlers = (services) => ({
|
|
|
7314
7387
|
GetBillingPortalConfigurations: async (context) => {
|
|
7315
7388
|
const scope = requestScope(services, context);
|
|
7316
7389
|
const params = queryParams(context);
|
|
7317
|
-
ensureDefaultConfiguration(scope);
|
|
7318
7390
|
const active = booleanOf(params.active);
|
|
7319
7391
|
const isDefault = booleanOf(params.is_default);
|
|
7320
7392
|
return jsonRes(
|
|
7321
7393
|
200,
|
|
7322
7394
|
await paginate(scope.account.portalConfigurations, params, {
|
|
7323
7395
|
url: "/v1/billing_portal/configurations",
|
|
7324
|
-
kind: "
|
|
7396
|
+
kind: "configuration",
|
|
7325
7397
|
where: (config) => (active === void 0 || config.active === active) && (isDefault === void 0 || config.is_default === isDefault),
|
|
7326
7398
|
render: (config) => renderConfiguration(scope, config)
|
|
7327
7399
|
})
|
|
@@ -9852,7 +9924,7 @@ var paymentIntentHandlers = (services) => {
|
|
|
9852
9924
|
// src/setup-intents.ts
|
|
9853
9925
|
var requireSetupIntent = (scope, id) => {
|
|
9854
9926
|
const intent = scope.account.setupIntents.get(id);
|
|
9855
|
-
if (!intent) throw
|
|
9927
|
+
if (!intent) throw resourceMissing("setupintent", id, "intent");
|
|
9856
9928
|
return intent;
|
|
9857
9929
|
};
|
|
9858
9930
|
var confirmSetup = (scope, intent, method, autoAuthenticate = false) => {
|
|
@@ -10891,7 +10963,7 @@ var toAddress = (raw) => {
|
|
|
10891
10963
|
const out = {};
|
|
10892
10964
|
for (const key of ADDRESS_KEYS) {
|
|
10893
10965
|
const value = input[key];
|
|
10894
|
-
out[key] = value === void 0 ? null : key === "country"
|
|
10966
|
+
out[key] = value === void 0 ? null : key === "country" && /^[A-Za-z]{2}$/.test(value) ? value.toUpperCase() : value;
|
|
10895
10967
|
}
|
|
10896
10968
|
return out;
|
|
10897
10969
|
};
|
|
@@ -16428,6 +16500,29 @@ var reshapeQaCommand = (command, _state, rng) => {
|
|
|
16428
16500
|
return withBody(command, next);
|
|
16429
16501
|
};
|
|
16430
16502
|
|
|
16503
|
+
// src/webhook-timers.ts
|
|
16504
|
+
var createClockTimers = (clock) => {
|
|
16505
|
+
const timers = /* @__PURE__ */ new Set();
|
|
16506
|
+
return {
|
|
16507
|
+
schedule(callback, delayMs) {
|
|
16508
|
+
const timer = { due: clock.now() + delayMs, callback };
|
|
16509
|
+
timers.add(timer);
|
|
16510
|
+
return timer;
|
|
16511
|
+
},
|
|
16512
|
+
cancel(handle) {
|
|
16513
|
+
timers.delete(handle);
|
|
16514
|
+
},
|
|
16515
|
+
run() {
|
|
16516
|
+
const now = clock.now();
|
|
16517
|
+
const due = [...timers].filter((timer) => timer.due <= now).sort((a, b) => a.due - b.due);
|
|
16518
|
+
for (const timer of due) {
|
|
16519
|
+
if (!timers.delete(timer)) continue;
|
|
16520
|
+
timer.callback();
|
|
16521
|
+
}
|
|
16522
|
+
}
|
|
16523
|
+
};
|
|
16524
|
+
};
|
|
16525
|
+
|
|
16431
16526
|
// src/runtime.ts
|
|
16432
16527
|
var RATE_LIMITED = {
|
|
16433
16528
|
error: {
|
|
@@ -16542,10 +16637,18 @@ var guard = (run) => {
|
|
|
16542
16637
|
};
|
|
16543
16638
|
var createRuntime2 = (options = {}) => {
|
|
16544
16639
|
const accounts = new AccountDirectory(options.accounts ?? []);
|
|
16640
|
+
const baseClock = options.clock ?? createClock();
|
|
16641
|
+
const timers = options.clock ? createClockTimers(options.clock) : void 0;
|
|
16545
16642
|
const hub = createWebhookHub({
|
|
16546
16643
|
signer: signers.timestamped("Stripe-Signature"),
|
|
16547
16644
|
...options.webhooks?.retryDelaysMs ? { retryDelaysMs: options.webhooks.retryDelaysMs } : {},
|
|
16548
16645
|
...options.webhooks?.fetch ? { fetch: options.webhooks.fetch } : {},
|
|
16646
|
+
...options.clock ? { now: () => baseClock.now() } : {},
|
|
16647
|
+
...options.webhooks?.now ? { now: options.webhooks.now } : {},
|
|
16648
|
+
...timers ? { schedule: timers.schedule, cancel: timers.cancel } : {},
|
|
16649
|
+
...options.webhooks?.schedule ? { schedule: options.webhooks.schedule } : {},
|
|
16650
|
+
...options.webhooks?.cancel ? { cancel: options.webhooks.cancel } : {},
|
|
16651
|
+
...options.webhooks?.id ? { id: options.webhooks.id } : {},
|
|
16549
16652
|
endpoints: options.webhooks?.endpoints ?? []
|
|
16550
16653
|
});
|
|
16551
16654
|
let runtimeRef;
|
|
@@ -16607,8 +16710,8 @@ var createRuntime2 = (options = {}) => {
|
|
|
16607
16710
|
else syncEndpoints(namespace);
|
|
16608
16711
|
}
|
|
16609
16712
|
};
|
|
16610
|
-
const baseClock = options.clock ?? createClock();
|
|
16611
16713
|
const tickAll = () => {
|
|
16714
|
+
timers?.run();
|
|
16612
16715
|
if (!runtimeRef) return;
|
|
16613
16716
|
for (const name of runtimeRef.namespaces()) runtimeRef.instance(name).tick(true);
|
|
16614
16717
|
};
|
|
@@ -16912,6 +17015,10 @@ var StripeAPI = class {
|
|
|
16912
17015
|
publish: options.onWebhook,
|
|
16913
17016
|
accounts,
|
|
16914
17017
|
deliveryVersion: (account) => accounts.config(account)?.apiVersion ?? webhookVersion,
|
|
17018
|
+
paymentRetries: (account) => ({
|
|
17019
|
+
...this.settings.paymentRetries,
|
|
17020
|
+
...accounts.config(account)?.billing?.retries
|
|
17021
|
+
}),
|
|
16915
17022
|
pendingWebhooks: options.pendingWebhooks ?? (() => 0),
|
|
16916
17023
|
namespacePrefix: options.publicNamespace === void 0 || options.publicNamespace === "default" ? "" : `/ns/${encodeURIComponent(options.publicNamespace)}`,
|
|
16917
17024
|
publicUrl: options.publicUrl,
|
|
@@ -17214,4 +17321,4 @@ export {
|
|
|
17214
17321
|
createRuntime2 as createRuntime,
|
|
17215
17322
|
StripeAPI
|
|
17216
17323
|
};
|
|
17217
|
-
//# sourceMappingURL=chunk-
|
|
17324
|
+
//# sourceMappingURL=chunk-P77GCNSW.js.map
|