@stardeck-customer-apps/testing 0.26.0 → 0.27.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/dist/setup.js CHANGED
@@ -84,6 +84,11 @@ var state = globalSingleton("state", () => ({
84
84
  cardTokens: /* @__PURE__ */ new Map(),
85
85
  cardTokenCounter: 0,
86
86
  cardTokenizationEnabled: true,
87
+ stripePaymentIntents: /* @__PURE__ */ new Map(),
88
+ stripePaymentIntentCounter: 0,
89
+ stripeIdempotentIntents: /* @__PURE__ */ new Map(),
90
+ stripeAllowedMethods: ["card", "promptpay"],
91
+ stripeChargesEnabled: true,
87
92
  uploads: [],
88
93
  uploadCounter: 0,
89
94
  storageFiles: /* @__PURE__ */ new Map(),
@@ -570,8 +575,8 @@ async function handleNeonSql(db, request) {
570
575
  rowAsArray: arrayMode
571
576
  });
572
577
  } catch (error) {
573
- const err = error;
574
- return json({ message: err.message, code: err.code }, 400);
578
+ const err2 = error;
579
+ return json({ message: err2.message, code: err2.code }, 400);
575
580
  }
576
581
  }
577
582
 
@@ -1536,6 +1541,281 @@ var requestScopeStorage = globalSingleton(
1536
1541
  () => new import_node_async_hooks.AsyncLocalStorage()
1537
1542
  );
1538
1543
 
1544
+ // src/simulator/stripe-payment-intents.ts
1545
+ var SIM_STRIPE_PUBLISHABLE_KEY = "pk_test_sim_stripe";
1546
+ var SIM_STRIPE_DEFAULT_ACCOUNT = "acct_sim_default";
1547
+ var METHODS = ["card", "promptpay"];
1548
+ var RESERVED_METADATA_KEYS = ["projectId", "stardeckReferenceId", "stardeckStagingDeploymentId"];
1549
+ var PROMPTPAY_QR_TTL_SECONDS = 15 * 60;
1550
+ var PATH_RE = /^\/api\/store\/(?!beam\/)([^/]+)\/(payment-intents(?:\/([^/]+)(?:\/(cancel))?)?|elements-config|readiness)$/;
1551
+ function err(error, status, code) {
1552
+ return json({ error, code }, status);
1553
+ }
1554
+ function decode(segment) {
1555
+ try {
1556
+ return decodeURIComponent(segment);
1557
+ } catch {
1558
+ return segment;
1559
+ }
1560
+ }
1561
+ function isPlayground(account) {
1562
+ return account === "playground";
1563
+ }
1564
+ function resolveAccount(account) {
1565
+ if (isPlayground(account)) return null;
1566
+ if (account === "default") {
1567
+ return process.env.STRIPE_CONNECT_ACCOUNT_ID || SIM_STRIPE_DEFAULT_ACCOUNT;
1568
+ }
1569
+ return account;
1570
+ }
1571
+ var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1572
+ var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1573
+ var APP_METADATA_MAX_KEYS = 50 - RESERVED_METADATA_KEYS.length;
1574
+ function isStringRecord(value) {
1575
+ return !!value && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((v) => typeof v === "string");
1576
+ }
1577
+ function isValidMetadata(value) {
1578
+ if (!isStringRecord(value)) return false;
1579
+ const entries = Object.entries(value);
1580
+ return entries.length <= APP_METADATA_MAX_KEYS && entries.every(([k, v]) => k.length >= 1 && k.length <= 40 && v.length <= 500);
1581
+ }
1582
+ function isOptional(value, test) {
1583
+ return value === void 0 || test(value);
1584
+ }
1585
+ var isEmail = (v) => typeof v === "string" && EMAIL_RE.test(v);
1586
+ function validateCreate(body) {
1587
+ const { amount, currency, method, referenceId, idempotencyKey } = body;
1588
+ const checks = [
1589
+ [
1590
+ typeof amount === "number" && Number.isInteger(amount) && amount > 0,
1591
+ "amount must be a positive integer in minor units"
1592
+ ],
1593
+ [typeof currency === "string" && currency.length === 3, "currency must be a 3-letter ISO code"],
1594
+ [
1595
+ typeof method === "string" && METHODS.includes(method),
1596
+ `method must be one of: ${METHODS.join(", ")}`
1597
+ ],
1598
+ [
1599
+ typeof referenceId === "string" && referenceId.length >= 1 && referenceId.length <= 200,
1600
+ "referenceId must be 1 to 200 characters"
1601
+ ],
1602
+ [
1603
+ typeof idempotencyKey === "string" && idempotencyKey.length >= 1 && idempotencyKey.length <= 200,
1604
+ "idempotencyKey is required (1 to 200 characters)"
1605
+ ],
1606
+ [
1607
+ isOptional(body.description, (v) => typeof v === "string" && v.length <= 1e3),
1608
+ "description must be at most 1000 characters"
1609
+ ],
1610
+ [isOptional(body.receiptEmail, isEmail), "receiptEmail must be an email address"],
1611
+ [isOptional(body.billingEmail, isEmail), "billingEmail must be an email address"],
1612
+ [
1613
+ isOptional(body.returnUrl, (v) => typeof v === "string" && URL.canParse(v)),
1614
+ "returnUrl must be an absolute URL"
1615
+ ],
1616
+ [
1617
+ isOptional(body.metadata, isValidMetadata),
1618
+ `metadata must map keys (1-40 chars) to strings (max 500), at most ${APP_METADATA_MAX_KEYS} keys`
1619
+ ],
1620
+ [
1621
+ typeof body.deploymentId === "string" && UUID_RE2.test(body.deploymentId),
1622
+ "Invalid deployment ID"
1623
+ ]
1624
+ ];
1625
+ const failed = checks.find(([ok]) => !ok);
1626
+ return failed ? failed[1] : null;
1627
+ }
1628
+ function appMetadata(raw) {
1629
+ if (!isStringRecord(raw)) return {};
1630
+ const out = {};
1631
+ for (const [key, value] of Object.entries(raw)) {
1632
+ if (!RESERVED_METADATA_KEYS.includes(key)) out[key] = value;
1633
+ }
1634
+ return out;
1635
+ }
1636
+ function stableStringify(value) {
1637
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
1638
+ if (value && typeof value === "object") {
1639
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
1640
+ return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(",")}}`;
1641
+ }
1642
+ return JSON.stringify(value);
1643
+ }
1644
+ function toStripePaymentIntentSummary(intent) {
1645
+ const open = intent.status !== "succeeded" && intent.status !== "canceled";
1646
+ let actionRequired = "NONE";
1647
+ if (open && intent.method === "card") actionRequired = "CLIENT_CONFIRM";
1648
+ if (open && intent.method === "promptpay" && intent.status === "requires_action") {
1649
+ actionRequired = "ENCODED_IMAGE";
1650
+ }
1651
+ return {
1652
+ id: intent.id,
1653
+ status: intent.status,
1654
+ amount: intent.amount,
1655
+ currency: intent.currency,
1656
+ method: intent.method,
1657
+ referenceId: intent.referenceId,
1658
+ metadata: { ...intent.appMetadata },
1659
+ actionRequired,
1660
+ clientSecret: intent.method === "card" ? intent.clientSecret : null,
1661
+ qr: actionRequired === "ENCODED_IMAGE" ? { ...intent.qr } : null,
1662
+ latestChargeId: intent.latestChargeId,
1663
+ amountRefunded: intent.amountRefunded,
1664
+ lastPaymentError: intent.lastPaymentError ? { ...intent.lastPaymentError } : null,
1665
+ created: intent.created,
1666
+ livemode: false,
1667
+ playground: intent.playground
1668
+ };
1669
+ }
1670
+ function promptPayQr(id, created) {
1671
+ return {
1672
+ imageUrlPng: `https://qr.stripe.test/${id}.png`,
1673
+ imageUrlSvg: `https://qr.stripe.test/${id}.svg`,
1674
+ data: `00020101021230SIM${id}`,
1675
+ hostedInstructionsUrl: `https://payments.stripe.test/promptpay/${id}`,
1676
+ expiresAt: created + PROMPTPAY_QR_TTL_SECONDS
1677
+ };
1678
+ }
1679
+ function createIntent(body, account, projectId) {
1680
+ const problem = validateCreate(body);
1681
+ if (problem) return err(problem, 400, "invalid_request");
1682
+ const method = body.method;
1683
+ const currency = body.currency.toLowerCase();
1684
+ if (!state.stripeAllowedMethods.includes(method)) {
1685
+ return err(
1686
+ `The ${method} payment method is turned off for this account`,
1687
+ 422,
1688
+ "method_not_allowed"
1689
+ );
1690
+ }
1691
+ if (method === "promptpay" && currency !== "thb") {
1692
+ return err("PromptPay only takes THB", 422, "currency_not_supported");
1693
+ }
1694
+ if (!state.stripeChargesEnabled) {
1695
+ return err("This Stripe account can't take payments yet", 503, "account_not_ready");
1696
+ }
1697
+ const scopedKey = JSON.stringify(["pi", projectId, account, body.idempotencyKey]);
1698
+ const comparable = { ...body };
1699
+ delete comparable.deploymentId;
1700
+ delete comparable.idempotencyKey;
1701
+ const fingerprint = stableStringify(comparable);
1702
+ const saved = state.stripeIdempotentIntents.get(scopedKey);
1703
+ if (saved) {
1704
+ if (saved.fingerprint !== fingerprint) {
1705
+ return err(
1706
+ "This idempotency key was already used with different parameters. Use a new key for a different payment.",
1707
+ 409,
1708
+ "idempotency_conflict"
1709
+ );
1710
+ }
1711
+ const existing = state.stripePaymentIntents.get(saved.id);
1712
+ return json({ paymentIntent: toStripePaymentIntentSummary(existing) }, 201);
1713
+ }
1714
+ state.stripePaymentIntentCounter += 1;
1715
+ const id = `pi_sim_${state.stripePaymentIntentCounter}`;
1716
+ const created = Math.floor(Date.now() / 1e3);
1717
+ const referenceId = body.referenceId;
1718
+ const metadata = appMetadata(body.metadata);
1719
+ const intent = {
1720
+ id,
1721
+ projectId,
1722
+ account,
1723
+ connectedAccountId: resolveAccount(account),
1724
+ playground: isPlayground(account),
1725
+ status: method === "card" ? "requires_payment_method" : "requires_action",
1726
+ amount: body.amount,
1727
+ currency,
1728
+ method,
1729
+ referenceId,
1730
+ appMetadata: metadata,
1731
+ metadata: { ...metadata, projectId, stardeckReferenceId: referenceId },
1732
+ description: typeof body.description === "string" ? body.description : null,
1733
+ receiptEmail: typeof body.receiptEmail === "string" ? body.receiptEmail : null,
1734
+ returnUrl: typeof body.returnUrl === "string" ? body.returnUrl : null,
1735
+ clientSecret: `${id}_secret_sim`,
1736
+ qr: method === "promptpay" ? promptPayQr(id, created) : null,
1737
+ latestChargeId: null,
1738
+ amountRefunded: 0,
1739
+ lastPaymentError: null,
1740
+ created
1741
+ };
1742
+ state.stripePaymentIntents.set(id, intent);
1743
+ state.stripeIdempotentIntents.set(scopedKey, { fingerprint, id });
1744
+ console.log(
1745
+ `[stardeck-testing] stripe payment intent ${id} created method=${method} amount=${intent.amount} ${currency}`
1746
+ );
1747
+ return json({ paymentIntent: toStripePaymentIntentSummary(intent) }, 201);
1748
+ }
1749
+ function ownedIntent(id, projectId) {
1750
+ const intent = state.stripePaymentIntents.get(id);
1751
+ return intent && intent.projectId === projectId ? intent : void 0;
1752
+ }
1753
+ function cancelIntent(intent) {
1754
+ if (intent.status === "succeeded") {
1755
+ return err("This payment already succeeded and can't be canceled", 409, "already_succeeded");
1756
+ }
1757
+ intent.status = "canceled";
1758
+ return json({ paymentIntent: toStripePaymentIntentSummary(intent) });
1759
+ }
1760
+ function handleIntents(request, url, account, intentId, action, projectId) {
1761
+ if (!intentId && request.method === "POST") {
1762
+ return readJsonBody(request).then((body) => createIntent(body, account, projectId));
1763
+ }
1764
+ if (!intentId && request.method === "GET") {
1765
+ const referenceId = url.searchParams.get("referenceId");
1766
+ if (!referenceId) return err("referenceId is required", 400, "invalid_request");
1767
+ const paymentIntents = [...state.stripePaymentIntents.values()].filter((i) => i.projectId === projectId && i.referenceId === referenceId).map(toStripePaymentIntentSummary);
1768
+ return json({ paymentIntents });
1769
+ }
1770
+ if (!intentId) return null;
1771
+ const intent = ownedIntent(decode(intentId), projectId);
1772
+ if (!action && request.method === "GET") {
1773
+ if (!intent) return err("Payment intent not found", 404, "not_found");
1774
+ return json({ paymentIntent: toStripePaymentIntentSummary(intent) });
1775
+ }
1776
+ if (action === "cancel" && request.method === "POST") {
1777
+ return readJsonBody(request).then((body) => {
1778
+ if (typeof body.deploymentId !== "string" || !UUID_RE2.test(body.deploymentId)) {
1779
+ return err("deploymentId is required", 400, "invalid_request");
1780
+ }
1781
+ if (!intent) return err("Payment intent not found", 404, "not_found");
1782
+ return cancelIntent(intent);
1783
+ });
1784
+ }
1785
+ return null;
1786
+ }
1787
+ async function handleStripePaymentIntentRequest(request, url, projectId) {
1788
+ const match = url.pathname.match(PATH_RE);
1789
+ if (!match) return null;
1790
+ if (!projectId) return err("Missing authentication", 401, "unauthorized");
1791
+ const account = decode(match[1]);
1792
+ const resource = match[2];
1793
+ if (request.method === "GET" && !url.searchParams.get("deploymentId")) {
1794
+ return err("deploymentId is required", 400, "invalid_request");
1795
+ }
1796
+ if (resource === "elements-config" && request.method === "GET") {
1797
+ return json({
1798
+ publishableKey: SIM_STRIPE_PUBLISHABLE_KEY,
1799
+ stripeAccount: resolveAccount(account),
1800
+ playground: isPlayground(account)
1801
+ });
1802
+ }
1803
+ if (resource === "readiness" && request.method === "GET") {
1804
+ const methods = [...state.stripeAllowedMethods];
1805
+ return json({
1806
+ provider: "stripe",
1807
+ ready: state.stripeChargesEnabled && methods.length > 0,
1808
+ chargesEnabled: state.stripeChargesEnabled,
1809
+ methods,
1810
+ playground: isPlayground(account)
1811
+ });
1812
+ }
1813
+ if (resource.startsWith("payment-intents")) {
1814
+ return handleIntents(request, url, account, match[3], match[4], projectId);
1815
+ }
1816
+ return null;
1817
+ }
1818
+
1539
1819
  // src/simulator/webhook-signing.ts
1540
1820
  var import_node_crypto4 = __toESM(require("crypto"));
1541
1821
 
@@ -1555,7 +1835,7 @@ var BEAM_BOLT_PAYMENT_METHODS = [
1555
1835
  "SPAY_LATER"
1556
1836
  ];
1557
1837
  var BEAM_BOLT_INTENT_STATUSES = ["PENDING", "PAID", "FAILED", "CANCELED", "EXPIRED"];
1558
- var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1838
+ var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1559
1839
  function payErr(error, status = 400, code, details) {
1560
1840
  const body = { error };
1561
1841
  if (code) body.code = code;
@@ -1581,7 +1861,23 @@ function paymentOperationForRequest(request, pathname) {
1581
1861
  if (request.method === "GET" && /^\/api\/store\/(?!beam\/)[^/]+\/checkout-sessions\/[^/]+$/.test(pathname)) {
1582
1862
  return "stripe.getCheckoutSessionStatus";
1583
1863
  }
1584
- return null;
1864
+ return stripeInlineOperation(request.method, pathname);
1865
+ }
1866
+ var STRIPE_INLINE_OPERATIONS = [
1867
+ ["POST", /^\/api\/store\/(?!beam\/)[^/]+\/payment-intents$/, "stripe.createPaymentIntent"],
1868
+ ["GET", /^\/api\/store\/(?!beam\/)[^/]+\/payment-intents$/, "stripe.findPaymentIntents"],
1869
+ ["GET", /^\/api\/store\/(?!beam\/)[^/]+\/payment-intents\/[^/]+$/, "stripe.getPaymentIntent"],
1870
+ [
1871
+ "POST",
1872
+ /^\/api\/store\/(?!beam\/)[^/]+\/payment-intents\/[^/]+\/cancel$/,
1873
+ "stripe.cancelPaymentIntent"
1874
+ ],
1875
+ ["GET", /^\/api\/store\/(?!beam\/)[^/]+\/elements-config$/, "stripe.getElementsConfig"],
1876
+ ["GET", /^\/api\/store\/(?!beam\/)[^/]+\/readiness$/, "stripe.getReadiness"]
1877
+ ];
1878
+ function stripeInlineOperation(method, pathname) {
1879
+ const hit = STRIPE_INLINE_OPERATIONS.find(([m, re]) => m === method && re.test(pathname));
1880
+ return hit ? hit[2] : null;
1585
1881
  }
1586
1882
  function consumePaymentFailure(request, pathname) {
1587
1883
  const operation = paymentOperationForRequest(request, pathname);
@@ -1611,11 +1907,11 @@ function isBoltPaymentMethod(value) {
1611
1907
  function isBoltIntentStatus(value) {
1612
1908
  return BEAM_BOLT_INTENT_STATUSES.includes(value);
1613
1909
  }
1614
- function stableStringify(value) {
1615
- if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
1910
+ function stableStringify2(value) {
1911
+ if (Array.isArray(value)) return `[${value.map(stableStringify2).join(",")}]`;
1616
1912
  if (value && typeof value === "object") {
1617
1913
  const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
1618
- return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(",")}}`;
1914
+ return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify2(v)}`).join(",")}}`;
1619
1915
  }
1620
1916
  return JSON.stringify(value);
1621
1917
  }
@@ -1721,10 +2017,16 @@ function storedStatusesForFilter(statuses) {
1721
2017
  }
1722
2018
  return [...stored];
1723
2019
  }
1724
- async function handlePaymentsRequest(request, url) {
2020
+ async function handlePaymentsRequest(request, url, signedProjectId = null) {
1725
2021
  const pathname = url.pathname;
1726
2022
  const injectedFailure = consumePaymentFailure(request, pathname);
1727
2023
  if (injectedFailure) return injectedFailure;
2024
+ const inlineStripeResponse = await handleStripePaymentIntentRequest(
2025
+ request,
2026
+ url,
2027
+ signedProjectId
2028
+ );
2029
+ if (inlineStripeResponse) return inlineStripeResponse;
1728
2030
  const directChargeResponse = await handleDirectChargeRequest(request, pathname);
1729
2031
  if (directChargeResponse) return directChargeResponse;
1730
2032
  if (/\/billing-portal$/.test(pathname)) {
@@ -1823,7 +2125,7 @@ async function handlePaymentsRequest(request, url) {
1823
2125
  code: "too_small"
1824
2126
  });
1825
2127
  }
1826
- if (typeof body.deploymentId !== "string" || !UUID_RE2.test(body.deploymentId)) {
2128
+ if (typeof body.deploymentId !== "string" || !UUID_RE3.test(body.deploymentId)) {
1827
2129
  issues.push({
1828
2130
  path: ["deploymentId"],
1829
2131
  message: "Invalid uuid",
@@ -2007,7 +2309,7 @@ async function handlePaymentsRequest(request, url) {
2007
2309
  const comparable = { ...body };
2008
2310
  delete comparable.deploymentId;
2009
2311
  delete comparable.idempotencyKey;
2010
- const fingerprint = stableStringify(comparable);
2312
+ const fingerprint = stableStringify2(comparable);
2011
2313
  if (scopedKey) {
2012
2314
  const saved = state.idempotentCheckouts.get(scopedKey);
2013
2315
  if (saved) {
@@ -2847,7 +3149,7 @@ function requiresDeploymentHmac(request, url) {
2847
3149
  return !isPresignedPut;
2848
3150
  }
2849
3151
  const dataStoreMatch = url.pathname.match(/^\/api\/data-stores\/[^/]+(\/.*)?$/);
2850
- const isEmail = url.pathname === "/api/email/send";
3152
+ const isEmail2 = url.pathname === "/api/email/send";
2851
3153
  const identitiesMatch = url.pathname.match(/^\/api\/deployments\/[^/]+\/identities(\/.*)?$/);
2852
3154
  const storeMatch = url.pathname.match(/^\/api\/store\//);
2853
3155
  const messagingMatch = url.pathname.match(
@@ -2856,7 +3158,7 @@ function requiresDeploymentHmac(request, url) {
2856
3158
  const edgeMatch = url.pathname.match(/^\/api\/deployments\/[^/]+\/edge(\/.*)?$/);
2857
3159
  const isStarlens = url.pathname === "/api/starlens/extract";
2858
3160
  const scheduleWakeMatch = url.pathname.match(SCHEDULE_WAKE_PATH);
2859
- return !!(isStarlens || scheduleWakeMatch || dataStoreMatch || isEmail || identitiesMatch || storeMatch || messagingMatch || edgeMatch);
3161
+ return !!(isStarlens || scheduleWakeMatch || dataStoreMatch || isEmail2 || identitiesMatch || storeMatch || messagingMatch || edgeMatch);
2860
3162
  }
2861
3163
  var SCHEDULE_WAKE_PATH = /^\/api\/deployments\/[^/]+\/schedules\/([^/]+)\/wake$/;
2862
3164
  function deploymentIdFromPath(url) {
@@ -2879,7 +3181,7 @@ async function handleSimulatedRequest(request, url) {
2879
3181
  return authMatch[1].endsWith("verify") ? handleAuthVerify(request) : handleAuthRefresh(request);
2880
3182
  }
2881
3183
  const dataStoreMatch = url.pathname.match(/^\/api\/data-stores\/[^/]+(\/.*)?$/);
2882
- const isEmail = url.pathname === "/api/email/send";
3184
+ const isEmail2 = url.pathname === "/api/email/send";
2883
3185
  const identitiesMatch = url.pathname.match(/^\/api\/deployments\/[^/]+\/identities(\/.*)?$/);
2884
3186
  const storeMatch = url.pathname.match(/^\/api\/store\//);
2885
3187
  const messagingMatch = url.pathname.match(
@@ -2888,6 +3190,7 @@ async function handleSimulatedRequest(request, url) {
2888
3190
  const edgeMatch = url.pathname.match(/^\/api\/deployments\/[^/]+\/edge(\/.*)?$/);
2889
3191
  const isStorageHost = url.hostname === STORAGE_TEST_HOST;
2890
3192
  let signedDeploymentId = null;
3193
+ let signedProjectId = null;
2891
3194
  if (requiresDeploymentHmac(request, url)) {
2892
3195
  const authHeader = request.headers.get("X-Stardeck-Auth");
2893
3196
  if (!authHeader) {
@@ -2900,6 +3203,7 @@ async function handleSimulatedRequest(request, url) {
2900
3203
  return failure("Invalid authentication", 401);
2901
3204
  }
2902
3205
  signedDeploymentId = authPayload.deploymentId;
3206
+ signedProjectId = authPayload.projectId;
2903
3207
  }
2904
3208
  if (isStorageHost) {
2905
3209
  return handleStorageRequest(request, url);
@@ -2907,14 +3211,14 @@ async function handleSimulatedRequest(request, url) {
2907
3211
  if (url.hostname === SIM_BEAM_HOST) {
2908
3212
  return handleCardTokenRequest(request, url);
2909
3213
  }
2910
- if (isEmail && request.method === "POST") {
3214
+ if (isEmail2 && request.method === "POST") {
2911
3215
  return handleEmailSend(request);
2912
3216
  }
2913
3217
  if (identitiesMatch) {
2914
3218
  return handleIdentitiesRequest(request, identitiesMatch[1] ?? "");
2915
3219
  }
2916
3220
  if (storeMatch) {
2917
- return handlePaymentsRequest(request, url);
3221
+ return handlePaymentsRequest(request, url, signedProjectId);
2918
3222
  }
2919
3223
  if (messagingMatch) {
2920
3224
  const channel = messagingMatch[1];