@crvouga/mockingbird-service-flex 4.0.0 → 4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog — @crvouga/mockingbird-service-flex
2
2
 
3
+ ## 4.0.1 (2026-10-29)
4
+
5
+ ### Fixes and improvements
6
+
7
+ - send checkout.session.refunded on a refund, as Flex does ([607eb28](https://github.com/crvouga/mockingbird/commit/607eb289587b2096fa5d9de447ccbeb03ea72563))
8
+
3
9
  ## 4.0.0 (2026-10-29)
4
10
 
5
11
  ### ⚠️ Breaking changes
package/README.md CHANGED
@@ -156,10 +156,15 @@ finds the payment through the payment intent.
156
156
  | `checkout.session.async_payment_succeeded` | settling a session whose intent was `processing` |
157
157
  | `checkout.session.async_payment_failed` | a decline (page, admin, off-session) |
158
158
  | `checkout.session.expired` | `…/expire`, or `expires_at` passing on the emulator clock (default 24 h) |
159
- | `refund.created` (twice), `charge.refunded`, `refund.updated`, `charge.refund.updated` | each refund, through the API or the dashboard (`POST /__admin/sessions/:id/refund`), in the order production delivers a dashboard refund. `checkout.session.refunded` is in Flex's event list but has never been seen on a refund, so it is only sent through `POST /__admin/events` |
159
+ | `checkout.session.refunded` (the session, with every refund in `refunds`), then `refund.created` (twice), `charge.refunded`, `refund.updated`, `charge.refund.updated` | each refund, through the API or the dashboard (`POST /__admin/sessions/:id/refund`). The refund and charge events follow the order production delivers a dashboard refund; each refund's `items` name its payment intent |
160
160
  | `product.updated` | `PATCH /v1/products/{id}` and `PUT /__admin/products/:id` |
161
161
  | `checkout_session.completed`, `checkout_session.expired` | the aliases, with `PUT /__admin/settings {"eventNaming": "underscored"}` |
162
162
 
163
+ An endpoint receives only the event types it subscribes to, as in Flex's dashboard: pass
164
+ `webhooks: {events: [...]}` (default: every type) or `PUT /__admin/webhook-endpoints`. Subscribe
165
+ to what your production endpoint subscribes to, so a test cannot pass on an event production
166
+ never receives.
167
+
163
168
  `POST /__admin/events {type, session | product}` emits any type on demand. Non-2xx answers are
164
169
  retried (immediately, 5 s, 5 min, 30 min, 2 h); `GET /__admin/webhooks`, `…/events`,
165
170
  `POST /__admin/webhooks/flush`, `…/:id/replay`, `PUT /__admin/webhook-endpoints` as usual.
@@ -227,7 +232,7 @@ so product names are synthesised.
227
232
  | Export | Kind | Description |
228
233
  | --- | --- | --- |
229
234
  | `FlexAPI` | class | The in-process emulator: `fetch(request)`, `reset()`, `settle(id)`, `decline(id)`, `expire(id)`, `requireAction(id, type)`, `setPaymentIntent(id, patch)`, `applyRefund(id, amount)`, `dashboardRefund(id, amount?)`, `putProduct(product)`, `emitFor(type, target)`, `tick()`, `sessions()`, `subscriptions()`, `present(session, view)`. Options: `sqlite`, `now`, `namespace`, `publicNamespace`, `products`, `settings`, `onEvent`. |
230
- | `createRuntime` | function | The emulator with the full service contract. Options: `webhooks: {url, secret, retryDelaysMs?, fetch?}`, `products`, `settings`, `tickMs`, `clock`, `seed`, `adminKey`, `onLog`. |
235
+ | `createRuntime` | function | The emulator with the full service contract. Options: `webhooks: {url, secret, events?, retryDelaysMs?, fetch?}`, `products`, `settings`, `tickMs`, `clock`, `seed`, `adminKey`, `onLog`. |
231
236
  | `FLEX_PRESETS` | object | Every named fault preset. |
232
237
  | `FLEX_NAMESPACE` | string | The service name, `"flex"`. |
233
238
  | `FLEX_EVENT_TYPES` | array | Every webhook event type, aliases included. |
@@ -4097,7 +4097,7 @@ var stateAdminRoutes = (routes) => {
4097
4097
  };
4098
4098
 
4099
4099
  // ../core/dist/version.js
4100
- var PACKAGE_VERSION = true ? "4.0.0" : UNRELEASED_VERSION;
4100
+ var PACKAGE_VERSION = true ? "4.0.1" : UNRELEASED_VERSION;
4101
4101
 
4102
4102
  // ../core/dist/signing.js
4103
4103
  var encoder = new TextEncoder();
@@ -7859,10 +7859,12 @@ var FlexAPI = class {
7859
7859
  const { expiresAtMs: _expires, url, ...rest } = session;
7860
7860
  const customer = session.customer ? view.expandCustomer ? this.state.customers.get(session.customer) ?? session.customer : session.customer : null;
7861
7861
  const intent = session.payment_intent ? view.expandPaymentIntent ? this.state.paymentIntents.get(session.payment_intent) ?? session.payment_intent : session.payment_intent : null;
7862
+ const refunds = this.state.refunds.list().map((row) => row.value).filter((refund) => refund.checkout_session === session.checkout_session_id).map((refund) => this.presentRefund(refund, session.test_mode));
7862
7863
  const out = {
7863
7864
  ...rest,
7864
7865
  customer,
7865
7866
  payment_intent: intent,
7867
+ refunds,
7866
7868
  redirect_url: url,
7867
7869
  url
7868
7870
  };
@@ -7933,9 +7935,11 @@ var FlexAPI = class {
7933
7935
  return this.sessionResponse(context, id);
7934
7936
  }
7935
7937
  /**
7936
- * Refund part or all of a paid session, emitting the refund events in the order Flex sends
7937
- * them for a dashboard refund: `refund.created` twice, `charge.refunded`, then
7938
- * `refund.updated` and `charge.refund.updated`.
7938
+ * Refund part or all of a paid session, emitting the refund events Flex sends:
7939
+ * `checkout.session.refunded` (seen in test mode, carrying the session and its refunds),
7940
+ * then, in the order production delivers a dashboard refund, `refund.created` twice,
7941
+ * `charge.refunded`, `refund.updated` and `charge.refund.updated`. An endpoint receives only
7942
+ * the types it subscribes to.
7939
7943
  */
7940
7944
  applyRefund(id, amount) {
7941
7945
  const session = this.state.sessions.get(id);
@@ -7953,6 +7957,7 @@ var FlexAPI = class {
7953
7957
  this.state.refunds.insert(refund.refund_id, refund);
7954
7958
  const refundObject = this.presentRefund(refund, session.test_mode);
7955
7959
  const chargeObject = this.presentCharge(next, refund.refund_id);
7960
+ this.emitSession("checkout.session.refunded", id);
7956
7961
  this.emit("refund.created", refundObject, session.test_mode);
7957
7962
  this.emit("refund.created", refundObject, session.test_mode);
7958
7963
  this.emit("charge.refunded", chargeObject, session.test_mode);
@@ -7976,12 +7981,22 @@ var FlexAPI = class {
7976
7981
  return { session: this.applyRefund(id, value) };
7977
7982
  }
7978
7983
  presentRefund(refund, testMode) {
7984
+ const intent = refund.payment_intent ? this.state.paymentIntents.get(refund.payment_intent) : void 0;
7979
7985
  return {
7980
7986
  refund_id: refund.refund_id,
7981
7987
  amount: refund.amount,
7982
7988
  status: refund.status,
7983
7989
  payment_intent_id: refund.payment_intent,
7984
- items: [],
7990
+ items: [
7991
+ {
7992
+ payment_intent: refund.payment_intent,
7993
+ amount_refunded: refund.amount,
7994
+ payment_method_id: intent?.payment_method ?? null,
7995
+ reference_id: null,
7996
+ reference_type: null,
7997
+ reference_status: null
7998
+ }
7999
+ ],
7985
8000
  metadata: null,
7986
8001
  reason: null,
7987
8002
  reference_id: null,
@@ -8441,4 +8456,4 @@ export {
8441
8456
  FlexAPI,
8442
8457
  isNextActionType
8443
8458
  };
8444
- //# sourceMappingURL=chunk-3FSLZROR.js.map
8459
+ //# sourceMappingURL=chunk-AGWRBLUO.js.map