@stardeck-customer-apps/testing 0.12.0 → 0.13.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/SKILL.md CHANGED
@@ -193,6 +193,21 @@ await app.payments.deliverBeamEvent(beamPost, {
193
193
  // charge-failure or expiry event for bolt payments.
194
194
  // EXPIRED is derived from expiresAt on read, never stored.
195
195
 
196
+ // Fail exactly one real SDK call, then let the next poll recover normally.
197
+ app.payments.failNext("beam.getPaymentLinkStatus", {
198
+ status: 503,
199
+ message: "Beam is temporarily unavailable",
200
+ });
201
+ await expect(payments.getPaymentLinkStatus("plink_test_1")).rejects.toMatchObject({
202
+ statusCode: 503,
203
+ });
204
+
205
+ // Network failures reject fetch instead of returning a PaymentError response.
206
+ app.payments.failNext("stripe.getCheckoutSessionStatus", {
207
+ kind: "network",
208
+ message: "connection reset",
209
+ });
210
+
196
211
  // File upload
197
212
  const storage = new StorageClient();
198
213
  await storage.upload(new File(["hi"], "note.txt", { type: "text/plain" }));
@@ -302,6 +317,8 @@ externalId })` helper models a trusted platform/channel link, and
302
317
  identities through the integrations SDK.
303
318
  - `app.payments` — checkouts created through payments-sdk:
304
319
  `.checkouts`, `.latest()`, `.setProducts()`, `.markPaid(id)`,
320
+ `.setSessionStatus(id, status)`, `.setPaymentLinkStatus(id, status)`,
321
+ `.failNext(operation, failure)`,
305
322
  `.deliverStripeEvent(handler, event)`, `.deliverBeamEvent(handler, event)`,
306
323
  Bolt+ controls: `.approveBoltIntent(id)`, `.declineBoltIntent(id)`,
307
324
  `.expireBoltIntent(id)`, `.listBoltIntents()`, `.getBoltIntent(id)`,
@@ -385,7 +402,21 @@ cursor, limit })`; `list()` intentionally keeps its previous array shape.
385
402
  connections/intents/charges, product list) — captured in `app.payments`;
386
403
  fulfill checkout via `markPaid` (poll) or `deliverStripeEvent` /
387
404
  `deliverBeamEvent` (webhook push). Drive a terminal without hardware via
388
- `approveBoltIntent` / `declineBoltIntent` / `expireBoltIntent`.
405
+ `approveBoltIntent` / `declineBoltIntent` / `expireBoltIntent`. Beam payment-link
406
+ polling returns the app's original `order.referenceId`; playground project
407
+ scoping is an internal control-plane detail and must not be handled in app code.
408
+ - Payment outcomes are controllable without bypassing the SDK: Stripe sessions
409
+ support `open`, `complete`, and `expired` with `paid`, `unpaid`, or
410
+ `no_payment_required`; Beam links support `ACTIVE`, `DISABLED`, `EXPIRED`,
411
+ `PAID`, `VOIDED`, and `REFUNDED`; Bolt+ supports approve, decline, expiry, and
412
+ cancellation through the real SDK call. Use `app.payments.failNext()` for
413
+ one-shot HTTP or network failures during Stripe checkout creation/status polling
414
+ and Beam payment-link creation/status polling. The operation names are
415
+ `stripe.createCheckout`, `stripe.getCheckoutSessionStatus`,
416
+ `beam.createPaymentLink`, and `beam.getPaymentLinkStatus`. HTTP failures support
417
+ statuses 400, 402, 409, 429, 500, and 503 plus optional `code`, `details`, and
418
+ `retryAfterSeconds`; `{ kind: "network", message }` rejects fetch instead.
419
+ `app.reset()` clears pending failures.
389
420
  - `StorageClient` (upload/list/get/patch/delete, presigned upload) — captured
390
421
  in `app.storage` against the simulated `STORAGE_URL` host.
391
422
  - `client.slack` / `client.line` / `client.facebook` send endpoints — captured
package/dist/index.d.mts CHANGED
@@ -138,6 +138,18 @@ interface CapturedCheckout {
138
138
  metadata?: Record<string, string>;
139
139
  createdAt: Date;
140
140
  }
141
+ type PaymentSimulatorOperation = "stripe.createCheckout" | "stripe.getCheckoutSessionStatus" | "beam.createPaymentLink" | "beam.getPaymentLinkStatus";
142
+ type PaymentSimulatorFailure = {
143
+ kind?: "http";
144
+ status: 400 | 402 | 409 | 429 | 500 | 503;
145
+ message: string;
146
+ code?: string;
147
+ details?: unknown;
148
+ retryAfterSeconds?: number;
149
+ } | {
150
+ kind: "network";
151
+ message: string;
152
+ };
141
153
  /** Simulated Bolt+ connection as returned by the control-plane store API. */
142
154
  interface SimBoltConnection {
143
155
  id: string;
@@ -189,6 +201,8 @@ interface TestPayments {
189
201
  get checkouts(): CapturedCheckout[];
190
202
  latest(): CapturedCheckout | undefined;
191
203
  setProducts(products: _stardeck_customer_apps_payments_sdk.Product[]): void;
204
+ /** Fail the next matching SDK request once, then resume normal simulator behavior. */
205
+ failNext(operation: PaymentSimulatorOperation, failure: PaymentSimulatorFailure): void;
192
206
  markPaid(id: string): void;
193
207
  setSessionStatus(id: string, status: Partial<_stardeck_customer_apps_payments_sdk.CheckoutSessionStatus>): void;
194
208
  setPaymentLinkStatus(id: string, status: _stardeck_customer_apps_payments_sdk.BeamPaymentLinkStatus): void;
@@ -550,4 +564,4 @@ declare const TEST_ENV_DEFAULTS: {
550
564
  /** Default location of the DDL snapshot written by `generate-types`. */
551
565
  declare const DEFAULT_SCHEMA_PATH = "./src/generated/data-store-schema.sql";
552
566
 
553
- export { type BindingInfo, CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedCheckout, type CapturedDisplay, type CapturedEmail, type CapturedIdentity, type CapturedIdentityLink, type CapturedMessage, type CapturedPrint, type CapturedTestPrint, type CapturedUpload, DATA_STORE_TEST_HOST, DATA_STORE_TEST_URL, DEFAULT_SCHEMA_PATH, type DeviceInfo, type PeripheralInfo, STORAGE_TEST_HOST, STORAGE_TEST_URL, type SessionTokens, type SimBoltCharge, type SimBoltConnection, type SimBoltIntentRecord, type SimBoltIntentStatus, TEST_ENV_DEFAULTS, type TestApp, type TestAppOptions, TestDataStoreInput, type TestDirectory, type TestEdge, type TestInbox, type TestMessages, type TestPayments, type TestStorage, type TestUser, WORKFLOW_NAME_PREFIX, callRoute, createTestApp, describeWorkflow, parseWorkflowName };
567
+ export { type BindingInfo, CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedCheckout, type CapturedDisplay, type CapturedEmail, type CapturedIdentity, type CapturedIdentityLink, type CapturedMessage, type CapturedPrint, type CapturedTestPrint, type CapturedUpload, DATA_STORE_TEST_HOST, DATA_STORE_TEST_URL, DEFAULT_SCHEMA_PATH, type DeviceInfo, type PaymentSimulatorFailure, type PaymentSimulatorOperation, type PeripheralInfo, STORAGE_TEST_HOST, STORAGE_TEST_URL, type SessionTokens, type SimBoltCharge, type SimBoltConnection, type SimBoltIntentRecord, type SimBoltIntentStatus, TEST_ENV_DEFAULTS, type TestApp, type TestAppOptions, TestDataStoreInput, type TestDirectory, type TestEdge, type TestInbox, type TestMessages, type TestPayments, type TestStorage, type TestUser, WORKFLOW_NAME_PREFIX, callRoute, createTestApp, describeWorkflow, parseWorkflowName };
package/dist/index.d.ts CHANGED
@@ -138,6 +138,18 @@ interface CapturedCheckout {
138
138
  metadata?: Record<string, string>;
139
139
  createdAt: Date;
140
140
  }
141
+ type PaymentSimulatorOperation = "stripe.createCheckout" | "stripe.getCheckoutSessionStatus" | "beam.createPaymentLink" | "beam.getPaymentLinkStatus";
142
+ type PaymentSimulatorFailure = {
143
+ kind?: "http";
144
+ status: 400 | 402 | 409 | 429 | 500 | 503;
145
+ message: string;
146
+ code?: string;
147
+ details?: unknown;
148
+ retryAfterSeconds?: number;
149
+ } | {
150
+ kind: "network";
151
+ message: string;
152
+ };
141
153
  /** Simulated Bolt+ connection as returned by the control-plane store API. */
142
154
  interface SimBoltConnection {
143
155
  id: string;
@@ -189,6 +201,8 @@ interface TestPayments {
189
201
  get checkouts(): CapturedCheckout[];
190
202
  latest(): CapturedCheckout | undefined;
191
203
  setProducts(products: _stardeck_customer_apps_payments_sdk.Product[]): void;
204
+ /** Fail the next matching SDK request once, then resume normal simulator behavior. */
205
+ failNext(operation: PaymentSimulatorOperation, failure: PaymentSimulatorFailure): void;
192
206
  markPaid(id: string): void;
193
207
  setSessionStatus(id: string, status: Partial<_stardeck_customer_apps_payments_sdk.CheckoutSessionStatus>): void;
194
208
  setPaymentLinkStatus(id: string, status: _stardeck_customer_apps_payments_sdk.BeamPaymentLinkStatus): void;
@@ -550,4 +564,4 @@ declare const TEST_ENV_DEFAULTS: {
550
564
  /** Default location of the DDL snapshot written by `generate-types`. */
551
565
  declare const DEFAULT_SCHEMA_PATH = "./src/generated/data-store-schema.sql";
552
566
 
553
- export { type BindingInfo, CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedCheckout, type CapturedDisplay, type CapturedEmail, type CapturedIdentity, type CapturedIdentityLink, type CapturedMessage, type CapturedPrint, type CapturedTestPrint, type CapturedUpload, DATA_STORE_TEST_HOST, DATA_STORE_TEST_URL, DEFAULT_SCHEMA_PATH, type DeviceInfo, type PeripheralInfo, STORAGE_TEST_HOST, STORAGE_TEST_URL, type SessionTokens, type SimBoltCharge, type SimBoltConnection, type SimBoltIntentRecord, type SimBoltIntentStatus, TEST_ENV_DEFAULTS, type TestApp, type TestAppOptions, TestDataStoreInput, type TestDirectory, type TestEdge, type TestInbox, type TestMessages, type TestPayments, type TestStorage, type TestUser, WORKFLOW_NAME_PREFIX, callRoute, createTestApp, describeWorkflow, parseWorkflowName };
567
+ export { type BindingInfo, CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedCheckout, type CapturedDisplay, type CapturedEmail, type CapturedIdentity, type CapturedIdentityLink, type CapturedMessage, type CapturedPrint, type CapturedTestPrint, type CapturedUpload, DATA_STORE_TEST_HOST, DATA_STORE_TEST_URL, DEFAULT_SCHEMA_PATH, type DeviceInfo, type PaymentSimulatorFailure, type PaymentSimulatorOperation, type PeripheralInfo, STORAGE_TEST_HOST, STORAGE_TEST_URL, type SessionTokens, type SimBoltCharge, type SimBoltConnection, type SimBoltIntentRecord, type SimBoltIntentStatus, TEST_ENV_DEFAULTS, type TestApp, type TestAppOptions, TestDataStoreInput, type TestDirectory, type TestEdge, type TestInbox, type TestMessages, type TestPayments, type TestStorage, type TestUser, WORKFLOW_NAME_PREFIX, callRoute, createTestApp, describeWorkflow, parseWorkflowName };
package/dist/index.js CHANGED
@@ -108,6 +108,7 @@ var state = globalSingleton("state", () => ({
108
108
  checkoutCounter: 0,
109
109
  sessionStatuses: /* @__PURE__ */ new Map(),
110
110
  paymentLinks: /* @__PURE__ */ new Map(),
111
+ paymentFailures: [],
111
112
  products: [],
112
113
  boltConnections: /* @__PURE__ */ new Map(),
113
114
  boltUsedPairingCodes: /* @__PURE__ */ new Set(),
@@ -1274,6 +1275,43 @@ function payErr(error, status = 400, code, details) {
1274
1275
  if (details !== void 0) body.details = details;
1275
1276
  return json(body, status);
1276
1277
  }
1278
+ function paymentOperationForRequest(request, pathname) {
1279
+ if (request.method === "POST" && /^\/api\/store\/beam\/[^/]+\/payment-links$/.test(pathname)) {
1280
+ return "beam.createPaymentLink";
1281
+ }
1282
+ if (request.method === "GET" && /^\/api\/store\/beam\/[^/]+\/payment-links\/[^/]+$/.test(pathname)) {
1283
+ return "beam.getPaymentLinkStatus";
1284
+ }
1285
+ if (request.method === "POST" && /^\/api\/store\/(?!beam\/)[^/]+\/checkout$/.test(pathname)) {
1286
+ return "stripe.createCheckout";
1287
+ }
1288
+ if (request.method === "GET" && /^\/api\/store\/(?!beam\/)[^/]+\/checkout-sessions\/[^/]+$/.test(pathname)) {
1289
+ return "stripe.getCheckoutSessionStatus";
1290
+ }
1291
+ return null;
1292
+ }
1293
+ function consumePaymentFailure(request, pathname) {
1294
+ const operation = paymentOperationForRequest(request, pathname);
1295
+ if (!operation) return null;
1296
+ const index = state.paymentFailures.findIndex((entry) => entry.operation === operation);
1297
+ if (index === -1) return null;
1298
+ const [{ failure: failure2 }] = state.paymentFailures.splice(index, 1);
1299
+ if (failure2.kind === "network") {
1300
+ throw new TypeError(failure2.message);
1301
+ }
1302
+ const headers = new Headers({ "Content-Type": "application/json" });
1303
+ if (failure2.retryAfterSeconds !== void 0) {
1304
+ headers.set("Retry-After", String(failure2.retryAfterSeconds));
1305
+ }
1306
+ return new Response(
1307
+ JSON.stringify({
1308
+ error: failure2.message,
1309
+ code: failure2.code ?? "REQUEST_ERROR",
1310
+ ...failure2.details !== void 0 ? { details: failure2.details } : {}
1311
+ }),
1312
+ { status: failure2.status, headers }
1313
+ );
1314
+ }
1277
1315
  function isBoltPaymentMethod(value) {
1278
1316
  return typeof value === "string" && BEAM_BOLT_PAYMENT_METHODS.includes(value);
1279
1317
  }
@@ -1417,6 +1455,8 @@ async function deliverEvent(envelope, handler, options) {
1417
1455
  }
1418
1456
  async function handlePaymentsRequest(request, url) {
1419
1457
  const pathname = url.pathname;
1458
+ const injectedFailure = consumePaymentFailure(request, pathname);
1459
+ if (injectedFailure) return injectedFailure;
1420
1460
  if (/\/billing-portal$/.test(pathname)) {
1421
1461
  return payErr("Not found", 404, "NOT_FOUND");
1422
1462
  }
@@ -1711,6 +1751,9 @@ function createPayments() {
1711
1751
  setProducts(products) {
1712
1752
  state.products = products;
1713
1753
  },
1754
+ failNext(operation, failure2) {
1755
+ state.paymentFailures.push({ operation, failure: failure2 });
1756
+ },
1714
1757
  markPaid(id) {
1715
1758
  const session = state.sessionStatuses.get(id);
1716
1759
  if (session) {
@@ -1820,6 +1863,7 @@ function createPayments() {
1820
1863
  state.checkoutCounter = 0;
1821
1864
  state.sessionStatuses.clear();
1822
1865
  state.paymentLinks.clear();
1866
+ state.paymentFailures = [];
1823
1867
  state.products = [];
1824
1868
  state.boltConnections.clear();
1825
1869
  state.boltUsedPairingCodes.clear();
package/dist/index.mjs CHANGED
@@ -59,6 +59,7 @@ var state = globalSingleton("state", () => ({
59
59
  checkoutCounter: 0,
60
60
  sessionStatuses: /* @__PURE__ */ new Map(),
61
61
  paymentLinks: /* @__PURE__ */ new Map(),
62
+ paymentFailures: [],
62
63
  products: [],
63
64
  boltConnections: /* @__PURE__ */ new Map(),
64
65
  boltUsedPairingCodes: /* @__PURE__ */ new Set(),
@@ -1225,6 +1226,43 @@ function payErr(error, status = 400, code, details) {
1225
1226
  if (details !== void 0) body.details = details;
1226
1227
  return json(body, status);
1227
1228
  }
1229
+ function paymentOperationForRequest(request, pathname) {
1230
+ if (request.method === "POST" && /^\/api\/store\/beam\/[^/]+\/payment-links$/.test(pathname)) {
1231
+ return "beam.createPaymentLink";
1232
+ }
1233
+ if (request.method === "GET" && /^\/api\/store\/beam\/[^/]+\/payment-links\/[^/]+$/.test(pathname)) {
1234
+ return "beam.getPaymentLinkStatus";
1235
+ }
1236
+ if (request.method === "POST" && /^\/api\/store\/(?!beam\/)[^/]+\/checkout$/.test(pathname)) {
1237
+ return "stripe.createCheckout";
1238
+ }
1239
+ if (request.method === "GET" && /^\/api\/store\/(?!beam\/)[^/]+\/checkout-sessions\/[^/]+$/.test(pathname)) {
1240
+ return "stripe.getCheckoutSessionStatus";
1241
+ }
1242
+ return null;
1243
+ }
1244
+ function consumePaymentFailure(request, pathname) {
1245
+ const operation = paymentOperationForRequest(request, pathname);
1246
+ if (!operation) return null;
1247
+ const index = state.paymentFailures.findIndex((entry) => entry.operation === operation);
1248
+ if (index === -1) return null;
1249
+ const [{ failure: failure2 }] = state.paymentFailures.splice(index, 1);
1250
+ if (failure2.kind === "network") {
1251
+ throw new TypeError(failure2.message);
1252
+ }
1253
+ const headers = new Headers({ "Content-Type": "application/json" });
1254
+ if (failure2.retryAfterSeconds !== void 0) {
1255
+ headers.set("Retry-After", String(failure2.retryAfterSeconds));
1256
+ }
1257
+ return new Response(
1258
+ JSON.stringify({
1259
+ error: failure2.message,
1260
+ code: failure2.code ?? "REQUEST_ERROR",
1261
+ ...failure2.details !== void 0 ? { details: failure2.details } : {}
1262
+ }),
1263
+ { status: failure2.status, headers }
1264
+ );
1265
+ }
1228
1266
  function isBoltPaymentMethod(value) {
1229
1267
  return typeof value === "string" && BEAM_BOLT_PAYMENT_METHODS.includes(value);
1230
1268
  }
@@ -1368,6 +1406,8 @@ async function deliverEvent(envelope, handler, options) {
1368
1406
  }
1369
1407
  async function handlePaymentsRequest(request, url) {
1370
1408
  const pathname = url.pathname;
1409
+ const injectedFailure = consumePaymentFailure(request, pathname);
1410
+ if (injectedFailure) return injectedFailure;
1371
1411
  if (/\/billing-portal$/.test(pathname)) {
1372
1412
  return payErr("Not found", 404, "NOT_FOUND");
1373
1413
  }
@@ -1662,6 +1702,9 @@ function createPayments() {
1662
1702
  setProducts(products) {
1663
1703
  state.products = products;
1664
1704
  },
1705
+ failNext(operation, failure2) {
1706
+ state.paymentFailures.push({ operation, failure: failure2 });
1707
+ },
1665
1708
  markPaid(id) {
1666
1709
  const session = state.sessionStatuses.get(id);
1667
1710
  if (session) {
@@ -1771,6 +1814,7 @@ function createPayments() {
1771
1814
  state.checkoutCounter = 0;
1772
1815
  state.sessionStatuses.clear();
1773
1816
  state.paymentLinks.clear();
1817
+ state.paymentFailures = [];
1774
1818
  state.products = [];
1775
1819
  state.boltConnections.clear();
1776
1820
  state.boltUsedPairingCodes.clear();
@@ -51,6 +51,7 @@ var state = globalSingleton("state", () => ({
51
51
  checkoutCounter: 0,
52
52
  sessionStatuses: /* @__PURE__ */ new Map(),
53
53
  paymentLinks: /* @__PURE__ */ new Map(),
54
+ paymentFailures: [],
54
55
  products: [],
55
56
  boltConnections: /* @__PURE__ */ new Map(),
56
57
  boltUsedPairingCodes: /* @__PURE__ */ new Set(),
@@ -24,6 +24,7 @@ var state = globalSingleton("state", () => ({
24
24
  checkoutCounter: 0,
25
25
  sessionStatuses: /* @__PURE__ */ new Map(),
26
26
  paymentLinks: /* @__PURE__ */ new Map(),
27
+ paymentFailures: [],
27
28
  products: [],
28
29
  boltConnections: /* @__PURE__ */ new Map(),
29
30
  boltUsedPairingCodes: /* @__PURE__ */ new Set(),
package/dist/setup.js CHANGED
@@ -45,6 +45,7 @@ var state = globalSingleton("state", () => ({
45
45
  checkoutCounter: 0,
46
46
  sessionStatuses: /* @__PURE__ */ new Map(),
47
47
  paymentLinks: /* @__PURE__ */ new Map(),
48
+ paymentFailures: [],
48
49
  products: [],
49
50
  boltConnections: /* @__PURE__ */ new Map(),
50
51
  boltUsedPairingCodes: /* @__PURE__ */ new Set(),
@@ -1012,6 +1013,43 @@ function payErr(error, status = 400, code, details) {
1012
1013
  if (details !== void 0) body.details = details;
1013
1014
  return json(body, status);
1014
1015
  }
1016
+ function paymentOperationForRequest(request, pathname) {
1017
+ if (request.method === "POST" && /^\/api\/store\/beam\/[^/]+\/payment-links$/.test(pathname)) {
1018
+ return "beam.createPaymentLink";
1019
+ }
1020
+ if (request.method === "GET" && /^\/api\/store\/beam\/[^/]+\/payment-links\/[^/]+$/.test(pathname)) {
1021
+ return "beam.getPaymentLinkStatus";
1022
+ }
1023
+ if (request.method === "POST" && /^\/api\/store\/(?!beam\/)[^/]+\/checkout$/.test(pathname)) {
1024
+ return "stripe.createCheckout";
1025
+ }
1026
+ if (request.method === "GET" && /^\/api\/store\/(?!beam\/)[^/]+\/checkout-sessions\/[^/]+$/.test(pathname)) {
1027
+ return "stripe.getCheckoutSessionStatus";
1028
+ }
1029
+ return null;
1030
+ }
1031
+ function consumePaymentFailure(request, pathname) {
1032
+ const operation = paymentOperationForRequest(request, pathname);
1033
+ if (!operation) return null;
1034
+ const index = state.paymentFailures.findIndex((entry) => entry.operation === operation);
1035
+ if (index === -1) return null;
1036
+ const [{ failure: failure2 }] = state.paymentFailures.splice(index, 1);
1037
+ if (failure2.kind === "network") {
1038
+ throw new TypeError(failure2.message);
1039
+ }
1040
+ const headers = new Headers({ "Content-Type": "application/json" });
1041
+ if (failure2.retryAfterSeconds !== void 0) {
1042
+ headers.set("Retry-After", String(failure2.retryAfterSeconds));
1043
+ }
1044
+ return new Response(
1045
+ JSON.stringify({
1046
+ error: failure2.message,
1047
+ code: failure2.code ?? "REQUEST_ERROR",
1048
+ ...failure2.details !== void 0 ? { details: failure2.details } : {}
1049
+ }),
1050
+ { status: failure2.status, headers }
1051
+ );
1052
+ }
1015
1053
  function isBoltPaymentMethod(value) {
1016
1054
  return typeof value === "string" && BEAM_BOLT_PAYMENT_METHODS.includes(value);
1017
1055
  }
@@ -1120,6 +1158,8 @@ function storedStatusesForFilter(statuses) {
1120
1158
  }
1121
1159
  async function handlePaymentsRequest(request, url) {
1122
1160
  const pathname = url.pathname;
1161
+ const injectedFailure = consumePaymentFailure(request, pathname);
1162
+ if (injectedFailure) return injectedFailure;
1123
1163
  if (/\/billing-portal$/.test(pathname)) {
1124
1164
  return payErr("Not found", 404, "NOT_FOUND");
1125
1165
  }
package/dist/setup.mjs CHANGED
@@ -21,6 +21,7 @@ var state = globalSingleton("state", () => ({
21
21
  checkoutCounter: 0,
22
22
  sessionStatuses: /* @__PURE__ */ new Map(),
23
23
  paymentLinks: /* @__PURE__ */ new Map(),
24
+ paymentFailures: [],
24
25
  products: [],
25
26
  boltConnections: /* @__PURE__ */ new Map(),
26
27
  boltUsedPairingCodes: /* @__PURE__ */ new Set(),
@@ -988,6 +989,43 @@ function payErr(error, status = 400, code, details) {
988
989
  if (details !== void 0) body.details = details;
989
990
  return json(body, status);
990
991
  }
992
+ function paymentOperationForRequest(request, pathname) {
993
+ if (request.method === "POST" && /^\/api\/store\/beam\/[^/]+\/payment-links$/.test(pathname)) {
994
+ return "beam.createPaymentLink";
995
+ }
996
+ if (request.method === "GET" && /^\/api\/store\/beam\/[^/]+\/payment-links\/[^/]+$/.test(pathname)) {
997
+ return "beam.getPaymentLinkStatus";
998
+ }
999
+ if (request.method === "POST" && /^\/api\/store\/(?!beam\/)[^/]+\/checkout$/.test(pathname)) {
1000
+ return "stripe.createCheckout";
1001
+ }
1002
+ if (request.method === "GET" && /^\/api\/store\/(?!beam\/)[^/]+\/checkout-sessions\/[^/]+$/.test(pathname)) {
1003
+ return "stripe.getCheckoutSessionStatus";
1004
+ }
1005
+ return null;
1006
+ }
1007
+ function consumePaymentFailure(request, pathname) {
1008
+ const operation = paymentOperationForRequest(request, pathname);
1009
+ if (!operation) return null;
1010
+ const index = state.paymentFailures.findIndex((entry) => entry.operation === operation);
1011
+ if (index === -1) return null;
1012
+ const [{ failure: failure2 }] = state.paymentFailures.splice(index, 1);
1013
+ if (failure2.kind === "network") {
1014
+ throw new TypeError(failure2.message);
1015
+ }
1016
+ const headers = new Headers({ "Content-Type": "application/json" });
1017
+ if (failure2.retryAfterSeconds !== void 0) {
1018
+ headers.set("Retry-After", String(failure2.retryAfterSeconds));
1019
+ }
1020
+ return new Response(
1021
+ JSON.stringify({
1022
+ error: failure2.message,
1023
+ code: failure2.code ?? "REQUEST_ERROR",
1024
+ ...failure2.details !== void 0 ? { details: failure2.details } : {}
1025
+ }),
1026
+ { status: failure2.status, headers }
1027
+ );
1028
+ }
991
1029
  function isBoltPaymentMethod(value) {
992
1030
  return typeof value === "string" && BEAM_BOLT_PAYMENT_METHODS.includes(value);
993
1031
  }
@@ -1096,6 +1134,8 @@ function storedStatusesForFilter(statuses) {
1096
1134
  }
1097
1135
  async function handlePaymentsRequest(request, url) {
1098
1136
  const pathname = url.pathname;
1137
+ const injectedFailure = consumePaymentFailure(request, pathname);
1138
+ if (injectedFailure) return injectedFailure;
1099
1139
  if (/\/billing-portal$/.test(pathname)) {
1100
1140
  return payErr("Not found", 404, "NOT_FOUND");
1101
1141
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stardeck-customer-apps/testing",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Vitest test harness for Stardeck customer apps — in-process Postgres (PGlite) plus a control-plane simulator so the real Stardeck SDKs run unmodified in tests",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",