@crvouga/mockingbird-service-stripe 1.3.0 → 1.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog — @crvouga/mockingbird-service-stripe
2
2
 
3
+ ## 1.3.1 (2026-09-29)
4
+
5
+ ### Fixes and improvements
6
+
7
+ - say 'No such configuration' for a missing portal configuration ([6fad72a](https://github.com/crvouga/mockingbird/commit/6fad72a676b359672653508d9dd654e656ac6aa1))
8
+ - upper-case only two-letter country codes like the live API ([4ee5121](https://github.com/crvouga/mockingbird/commit/4ee5121cf2254fd75eef34bbeefee496cd16964a))
9
+ - leave portal configuration listing out of live parity walks ([20d715a](https://github.com/crvouga/mockingbird/commit/20d715a548e60dd933fab52e7ba661870607049e))
10
+ - keep the shipping address country verbatim like the live API ([052e285](https://github.com/crvouga/mockingbird/commit/052e285a0ae9bcc26050f1c50ed635085438c3d1))
11
+ - use --all-snapshot so the Stripe CLI webhook oracle starts ([09809bf](https://github.com/crvouga/mockingbird/commit/09809bfcd106cc4570d74e781ab0619efb191e05))
12
+ - listen for all events so newer Stripe CLIs start the webhook oracle ([278e364](https://github.com/crvouga/mockingbird/commit/278e364c77263d8cff65fb6d03fde9e89a1010ca))
13
+ - surface redacted stripe listen output when the webhook oracle fails to start ([dd179bb](https://github.com/crvouga/mockingbird/commit/dd179bbdf7a0d38ba097f79041ec818eaf69dfc5))
14
+ - sign and retry webhooks on the injected clock ([1091bde](https://github.com/crvouga/mockingbird/commit/1091bde0df69d9cc3eba2062d8523755e6caca14))
15
+
3
16
  ## 1.3.0 (2026-09-29)
4
17
 
5
18
  ### Features
package/README.md CHANGED
@@ -118,7 +118,7 @@ holds, ancestors included); Stripe's own rules are enforced: a non-expandable fi
118
118
 
119
119
  Every state change records an event in the account's log (`GET /v1/events`, filterable by
120
120
  `types[]` and `created`) and publishes it through the shared webhook hub, signed
121
- `Stripe-Signature: t=<wall-clock unix>,v1=<hex HMAC-SHA256(secret, "t.body")>` over the exact bytes —
121
+ `Stripe-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, "t.body")>` over the exact bytes —
122
122
  `stripe.webhooks.constructEvent` verifies them.
123
123
 
124
124
  - Endpoints: `PUT /__admin/webhook-endpoints [{account, url, secret, enabledEvents: ["*"|…]}]`
@@ -126,6 +126,11 @@ Every state change records an event in the account's log (`GET /v1/events`, filt
126
126
  `serve --webhook-url/--webhook-secret`, accounts' `webhookSecrets`, and endpoints created through
127
127
  `POST /v1/webhook_endpoints` (signed with the `whsec_` returned at creation). One event fans out to
128
128
  every matching endpoint, as on Stripe. Retries follow the hub's schedule.
129
+ - `t` is the wall clock, unless the runtime's `clock` is injected (`createRuntime({ clock })`):
130
+ then `t`, retries and the attempt timeout run on that clock, so a virtual-time suite verifies with
131
+ `constructEventAsync(body, sig, secret, tolerance, undefined, clock.now())` and sees a retry when it
132
+ advances the clock (`runtime.clock.advance`, or `runtime.tick()` after moving its own clock).
133
+ `webhooks: { now, schedule, cancel, id }` override the hub's sources directly.
129
134
  - `GET /__admin/webhooks`, `/webhooks/events`, `POST /__admin/webhooks/:id/replay`, `/webhooks/flush`.
130
135
  - Delivery faults: presets `webhook_duplicate` (same event id twice — our receiver's in-flight
131
136
  dedupe answers 500), `webhook_reorder` (the next two swapped), `webhook_drop` (never delivered,
@@ -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.3.0" : UNRELEASED_VERSION;
1562
+ var PACKAGE_VERSION = true ? "1.3.1" : UNRELEASED_VERSION;
1563
1563
 
1564
1564
  // ../core/dist/signing.js
1565
1565
  var encoder = new TextEncoder();
@@ -7184,7 +7184,7 @@ var loginPageOf = (scope, raw, base2, id) => {
7184
7184
  var requireConfiguration = (scope, id, param = "configuration") => {
7185
7185
  if (!scope.account.portalConfigurations.get(id)) ensureDefaultConfiguration(scope);
7186
7186
  const config = scope.account.portalConfigurations.get(id);
7187
- if (!config) throw resourceMissing("billing portal configuration", id, param);
7187
+ if (!config) throw resourceMissing("configuration", id, param);
7188
7188
  return config;
7189
7189
  };
7190
7190
  var flowSubscription = (scope, customer, id, param) => {
@@ -7314,14 +7314,13 @@ var billingPortalHandlers = (services) => ({
7314
7314
  GetBillingPortalConfigurations: async (context) => {
7315
7315
  const scope = requestScope(services, context);
7316
7316
  const params = queryParams(context);
7317
- ensureDefaultConfiguration(scope);
7318
7317
  const active = booleanOf(params.active);
7319
7318
  const isDefault = booleanOf(params.is_default);
7320
7319
  return jsonRes(
7321
7320
  200,
7322
7321
  await paginate(scope.account.portalConfigurations, params, {
7323
7322
  url: "/v1/billing_portal/configurations",
7324
- kind: "billing portal configuration",
7323
+ kind: "configuration",
7325
7324
  where: (config) => (active === void 0 || config.active === active) && (isDefault === void 0 || config.is_default === isDefault),
7326
7325
  render: (config) => renderConfiguration(scope, config)
7327
7326
  })
@@ -10891,7 +10890,7 @@ var toAddress = (raw) => {
10891
10890
  const out = {};
10892
10891
  for (const key of ADDRESS_KEYS) {
10893
10892
  const value = input[key];
10894
- out[key] = value === void 0 ? null : key === "country" ? value.replace(/[a-z]/g, (letter) => letter.toUpperCase()) : value;
10893
+ out[key] = value === void 0 ? null : key === "country" && /^[A-Za-z]{2}$/.test(value) ? value.toUpperCase() : value;
10895
10894
  }
10896
10895
  return out;
10897
10896
  };
@@ -16428,6 +16427,29 @@ var reshapeQaCommand = (command, _state, rng) => {
16428
16427
  return withBody(command, next);
16429
16428
  };
16430
16429
 
16430
+ // src/webhook-timers.ts
16431
+ var createClockTimers = (clock) => {
16432
+ const timers = /* @__PURE__ */ new Set();
16433
+ return {
16434
+ schedule(callback, delayMs) {
16435
+ const timer = { due: clock.now() + delayMs, callback };
16436
+ timers.add(timer);
16437
+ return timer;
16438
+ },
16439
+ cancel(handle) {
16440
+ timers.delete(handle);
16441
+ },
16442
+ run() {
16443
+ const now = clock.now();
16444
+ const due = [...timers].filter((timer) => timer.due <= now).sort((a, b) => a.due - b.due);
16445
+ for (const timer of due) {
16446
+ if (!timers.delete(timer)) continue;
16447
+ timer.callback();
16448
+ }
16449
+ }
16450
+ };
16451
+ };
16452
+
16431
16453
  // src/runtime.ts
16432
16454
  var RATE_LIMITED = {
16433
16455
  error: {
@@ -16542,10 +16564,18 @@ var guard = (run) => {
16542
16564
  };
16543
16565
  var createRuntime2 = (options = {}) => {
16544
16566
  const accounts = new AccountDirectory(options.accounts ?? []);
16567
+ const baseClock = options.clock ?? createClock();
16568
+ const timers = options.clock ? createClockTimers(options.clock) : void 0;
16545
16569
  const hub = createWebhookHub({
16546
16570
  signer: signers.timestamped("Stripe-Signature"),
16547
16571
  ...options.webhooks?.retryDelaysMs ? { retryDelaysMs: options.webhooks.retryDelaysMs } : {},
16548
16572
  ...options.webhooks?.fetch ? { fetch: options.webhooks.fetch } : {},
16573
+ ...options.clock ? { now: () => baseClock.now() } : {},
16574
+ ...options.webhooks?.now ? { now: options.webhooks.now } : {},
16575
+ ...timers ? { schedule: timers.schedule, cancel: timers.cancel } : {},
16576
+ ...options.webhooks?.schedule ? { schedule: options.webhooks.schedule } : {},
16577
+ ...options.webhooks?.cancel ? { cancel: options.webhooks.cancel } : {},
16578
+ ...options.webhooks?.id ? { id: options.webhooks.id } : {},
16549
16579
  endpoints: options.webhooks?.endpoints ?? []
16550
16580
  });
16551
16581
  let runtimeRef;
@@ -16607,8 +16637,8 @@ var createRuntime2 = (options = {}) => {
16607
16637
  else syncEndpoints(namespace);
16608
16638
  }
16609
16639
  };
16610
- const baseClock = options.clock ?? createClock();
16611
16640
  const tickAll = () => {
16641
+ timers?.run();
16612
16642
  if (!runtimeRef) return;
16613
16643
  for (const name of runtimeRef.namespaces()) runtimeRef.instance(name).tick(true);
16614
16644
  };
@@ -17214,4 +17244,4 @@ export {
17214
17244
  createRuntime2 as createRuntime,
17215
17245
  StripeAPI
17216
17246
  };
17217
- //# sourceMappingURL=chunk-2WP3EV2W.js.map
17247
+ //# sourceMappingURL=chunk-E54EAWQ7.js.map