@stardeck-customer-apps/testing 0.21.0 → 0.23.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/index.mjs CHANGED
@@ -271,6 +271,7 @@ var TEST_ENV_DEFAULTS = {
271
271
  DEPLOYMENT_ID: "00000000-0000-4000-8000-00000000000c",
272
272
  STORAGE_URL: STORAGE_TEST_URL
273
273
  };
274
+ var LIFF_ID_HEADER = "x-stardeck-liff-id";
274
275
  var DEFAULT_TEST_USER = {
275
276
  id: "test-user-1",
276
277
  email: "test-user@example.com",
@@ -285,6 +286,7 @@ var state = globalSingleton("state", () => ({
285
286
  db: null,
286
287
  lastHarnessDataStoreManifest: null,
287
288
  currentUser: null,
289
+ liffId: null,
288
290
  sessions: /* @__PURE__ */ new Map(),
289
291
  refreshSessions: /* @__PURE__ */ new Map(),
290
292
  emails: [],
@@ -309,6 +311,11 @@ var state = globalSingleton("state", () => ({
309
311
  boltIntentCounter: 0,
310
312
  boltCharges: [],
311
313
  boltChargeCounter: 0,
314
+ directCharges: /* @__PURE__ */ new Map(),
315
+ directChargeCounter: 0,
316
+ cardTokens: /* @__PURE__ */ new Map(),
317
+ cardTokenCounter: 0,
318
+ cardTokenizationEnabled: true,
312
319
  uploads: [],
313
320
  uploadCounter: 0,
314
321
  storageFiles: /* @__PURE__ */ new Map(),
@@ -1676,6 +1683,311 @@ function createDirectory() {
1676
1683
  };
1677
1684
  }
1678
1685
 
1686
+ // src/simulator/beam-direct-charges.ts
1687
+ var SIM_BEAM_HOST = "beam.stardeck.test";
1688
+ var SIM_BEAM_MERCHANT_ID = "sim_merchant";
1689
+ var SIM_BEAM_PUBLISHABLE_KEY = "pk_test_sim";
1690
+ var DIRECT_CHARGE_METHODS = /* @__PURE__ */ new Set([
1691
+ "CARD",
1692
+ "CARD_INSTALLMENTS",
1693
+ "CARD_TOKEN",
1694
+ "CARD_TOKEN_INSTALLMENTS",
1695
+ "QR_PROMPT_PAY",
1696
+ "ALIPAY",
1697
+ "ALIPAY_PLUS",
1698
+ "WECHAT_PAY",
1699
+ "TRUE_MONEY",
1700
+ "LINE_PAY",
1701
+ "SHOPEE_PAY",
1702
+ "KPLUS",
1703
+ "MAKE",
1704
+ "SCB_EASY",
1705
+ "KRUNGSRI_APP",
1706
+ "BANGKOK_BANK_APP",
1707
+ "SPAY_LATER"
1708
+ ]);
1709
+ var TEST_CARDS = {
1710
+ "4111111111111111": { outcome: "SUCCEEDED" },
1711
+ "5372074248113841": { outcome: "SUCCEEDED" },
1712
+ "378282246310005": { outcome: "SUCCEEDED" },
1713
+ "4953261730509988": { outcome: "REDIRECT" },
1714
+ "4389783280144506": { outcome: "REDIRECT" },
1715
+ "4943129900084541": { outcome: "FAILED", failureCode: "CH_INSUFFICIENT_FUNDS" },
1716
+ "4111111111000025": { outcome: "FAILED", failureCode: "CH_CARD_DECLINED" },
1717
+ "4111111111000033": { outcome: "FAILED", failureCode: "CH_CARD_RESTRICTED" }
1718
+ };
1719
+ var SIM_QR_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
1720
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1721
+ function invalid(issues) {
1722
+ return json({ error: "Invalid request body", details: issues }, 400);
1723
+ }
1724
+ var DEVICE_TYPES = /* @__PURE__ */ new Set(["ANDROID", "IOS", "WEB"]);
1725
+ function issue(path, message, code = "custom") {
1726
+ return { path, message, code };
1727
+ }
1728
+ function validateChargeFields(body) {
1729
+ const issues = [];
1730
+ const isInt = (v) => typeof v === "number" && Number.isInteger(v);
1731
+ if (!isInt(body.amount) || body.amount < 1) {
1732
+ issues.push(issue(["amount"], "Number must be greater than 0", "too_small"));
1733
+ }
1734
+ if (typeof body.deploymentId !== "string" || !UUID_RE.test(body.deploymentId)) {
1735
+ issues.push(issue(["deploymentId"], "Invalid deployment ID", "invalid_string"));
1736
+ }
1737
+ if (body.referenceId !== void 0 && (typeof body.referenceId !== "string" || body.referenceId.length > 100)) {
1738
+ issues.push(issue(["referenceId"], "Too long", "too_big"));
1739
+ }
1740
+ const expiry = body.expiryDurationInSec;
1741
+ if (expiry !== void 0 && (!isInt(expiry) || expiry < 60 || expiry > 3600)) {
1742
+ issues.push(issue(["expiryDurationInSec"], "Must be an integer from 60 to 3600", "too_big"));
1743
+ }
1744
+ if (body.returnUrl !== void 0 && (typeof body.returnUrl !== "string" || !URL.canParse(body.returnUrl))) {
1745
+ issues.push(issue(["returnUrl"], "Invalid URL", "invalid_format"));
1746
+ }
1747
+ if (body.deviceType !== void 0 && !DEVICE_TYPES.has(String(body.deviceType))) {
1748
+ issues.push(issue(["deviceType"], "Invalid enum value", "invalid_value"));
1749
+ }
1750
+ const key = body.idempotencyKey;
1751
+ if (key !== void 0 && (typeof key !== "string" || key.length < 1 || key.length > 255)) {
1752
+ issues.push(issue(["idempotencyKey"], "Must be 1 to 255 characters", "too_small"));
1753
+ }
1754
+ return issues;
1755
+ }
1756
+ function validateChargeMethod(body) {
1757
+ const method = body.paymentMethod;
1758
+ const type = method?.paymentMethodType;
1759
+ if (typeof type !== "string" || !DIRECT_CHARGE_METHODS.has(type)) {
1760
+ return [issue(["paymentMethod", "paymentMethodType"], "Invalid enum value", "invalid_value")];
1761
+ }
1762
+ const issues = [];
1763
+ const isToken = type === "CARD_TOKEN" || type === "CARD_TOKEN_INSTALLMENTS";
1764
+ const token = method?.cardToken;
1765
+ if (isToken && (typeof token?.cardTokenId !== "string" || token.cardTokenId === "")) {
1766
+ issues.push(
1767
+ issue(["paymentMethod", "cardToken"], `${type} requires paymentMethod.cardToken.cardTokenId`)
1768
+ );
1769
+ }
1770
+ if (token?.securityCode !== void 0 && !/^\d{3,4}$/.test(String(token.securityCode))) {
1771
+ issues.push(
1772
+ issue(
1773
+ ["paymentMethod", "cardToken", "securityCode"],
1774
+ "Invalid security code",
1775
+ "invalid_format"
1776
+ )
1777
+ );
1778
+ }
1779
+ if (type === "CARD_TOKEN_INSTALLMENTS" && ![3, 4, 6, 10].includes(method?.installmentPeriod)) {
1780
+ issues.push(
1781
+ issue(
1782
+ ["paymentMethod", "installmentPeriod"],
1783
+ "CARD_TOKEN_INSTALLMENTS requires paymentMethod.installmentPeriod (3, 4, 6 or 10)"
1784
+ )
1785
+ );
1786
+ }
1787
+ if (type !== "QR_PROMPT_PAY" && !body.returnUrl) {
1788
+ issues.push(
1789
+ issue(
1790
+ ["returnUrl"],
1791
+ `${type} sends the shopper away to pay; returnUrl is required to bring them back`
1792
+ )
1793
+ );
1794
+ }
1795
+ if (type === "CARD" || type === "CARD_INSTALLMENTS") {
1796
+ issues.push(
1797
+ issue(
1798
+ ["paymentMethod", "paymentMethodType"],
1799
+ `${type} takes raw card data; tokenize the card in the browser and charge CARD_TOKEN instead`
1800
+ )
1801
+ );
1802
+ }
1803
+ return issues;
1804
+ }
1805
+ function validateDirectCharge(body) {
1806
+ return [...validateChargeFields(body), ...validateChargeMethod(body)];
1807
+ }
1808
+ function chargeResponse(charge) {
1809
+ const response = {
1810
+ chargeId: charge.id,
1811
+ actionRequired: charge.actionRequired,
1812
+ paymentMethodType: charge.paymentMethodType
1813
+ };
1814
+ if (charge.actionRequired === "ENCODED_IMAGE") {
1815
+ response.encodedImage = {
1816
+ imageBase64Encoded: SIM_QR_PNG,
1817
+ rawData: `00020101021230SIM${charge.id}`,
1818
+ expiry: charge.expiresAt
1819
+ };
1820
+ }
1821
+ if (charge.actionRequired === "REDIRECT") {
1822
+ response.redirect = { redirectUrl: charge.redirectUrl };
1823
+ response.redirectUrl = charge.redirectUrl;
1824
+ }
1825
+ return response;
1826
+ }
1827
+ function nextChargeId() {
1828
+ state.directChargeCounter += 1;
1829
+ return `ch_sim_${state.directChargeCounter}`;
1830
+ }
1831
+ function createCharge(body) {
1832
+ const method = body.paymentMethod;
1833
+ const type = method.paymentMethodType;
1834
+ const id = nextChargeId();
1835
+ const now3 = /* @__PURE__ */ new Date();
1836
+ const expirySec = typeof body.expiryDurationInSec === "number" ? body.expiryDurationInSec : 30 * 60;
1837
+ const returnUrl = typeof body.returnUrl === "string" ? body.returnUrl : null;
1838
+ let actionRequired = "REDIRECT";
1839
+ let status = "PENDING";
1840
+ let failureCode = null;
1841
+ let cardLast4 = null;
1842
+ if (type === "QR_PROMPT_PAY") {
1843
+ actionRequired = "ENCODED_IMAGE";
1844
+ } else if (type === "CARD_TOKEN" || type === "CARD_TOKEN_INSTALLMENTS") {
1845
+ const token = state.cardTokens.get(method.cardToken.cardTokenId);
1846
+ if (!token) {
1847
+ return json({ error: "Card token not found", code: "CARD_TOKEN_NOT_FOUND" }, 404);
1848
+ }
1849
+ cardLast4 = token.last4;
1850
+ const card = TEST_CARDS[token.pan] ?? {
1851
+ outcome: "FAILED",
1852
+ failureCode: "CH_PROCESSING_FAILED"
1853
+ };
1854
+ if (card.outcome === "REDIRECT") {
1855
+ actionRequired = "REDIRECT";
1856
+ } else {
1857
+ actionRequired = "NONE";
1858
+ status = card.outcome;
1859
+ failureCode = card.failureCode ?? null;
1860
+ }
1861
+ }
1862
+ const charge = {
1863
+ id,
1864
+ status,
1865
+ amount: body.amount,
1866
+ currency: String(body.currency ?? "THB"),
1867
+ paymentMethodType: type,
1868
+ referenceId: typeof body.referenceId === "string" ? body.referenceId : null,
1869
+ deviceType: typeof body.deviceType === "string" ? body.deviceType : null,
1870
+ returnUrl,
1871
+ actionRequired,
1872
+ redirectUrl: actionRequired === "REDIRECT" ? `https://${SIM_BEAM_HOST}/pay/${id}${returnUrl ? `?returnUrl=${encodeURIComponent(returnUrl)}` : ""}` : null,
1873
+ expiresAt: new Date(now3.getTime() + expirySec * 1e3).toISOString(),
1874
+ failureCode,
1875
+ cardLast4,
1876
+ idempotencyKey: typeof body.idempotencyKey === "string" ? body.idempotencyKey : null,
1877
+ createdAt: now3.toISOString()
1878
+ };
1879
+ state.directCharges.set(id, charge);
1880
+ return charge;
1881
+ }
1882
+ async function handleDirectChargeRequest(request, pathname) {
1883
+ if (request.method === "POST" && /^\/api\/store\/beam\/[^/]+\/charges$/.test(pathname)) {
1884
+ const body = await readJsonBody(request);
1885
+ const issues = validateDirectCharge(body);
1886
+ if (issues.length > 0) return invalid(issues);
1887
+ const key = typeof body.idempotencyKey === "string" ? body.idempotencyKey : null;
1888
+ if (key) {
1889
+ const existing = [...state.directCharges.values()].find((c) => c.idempotencyKey === key);
1890
+ if (existing) return json(chargeResponse(existing));
1891
+ }
1892
+ const created = createCharge(body);
1893
+ if (created instanceof Response) return created;
1894
+ return json(chargeResponse(created));
1895
+ }
1896
+ const getMatch = pathname.match(/^\/api\/store\/beam\/[^/]+\/charges\/([^/]+)$/);
1897
+ if (request.method === "GET" && getMatch) {
1898
+ const charge = state.directCharges.get(decodeURIComponent(getMatch[1]));
1899
+ if (!charge) return json({ error: "Charge not found", code: "NOT_FOUND" }, 404);
1900
+ return json({ charge: toChargeDetail(charge) });
1901
+ }
1902
+ if (request.method === "GET" && /^\/api\/store\/beam\/[^/]+\/card-tokenization$/.test(pathname)) {
1903
+ if (!state.cardTokenizationEnabled) {
1904
+ return json(
1905
+ {
1906
+ error: "Add the Beam publishable key to this merchant account to take cards in your own checkout",
1907
+ code: "CARD_TOKENIZATION_NOT_CONFIGURED"
1908
+ },
1909
+ 409
1910
+ );
1911
+ }
1912
+ return json({
1913
+ merchantId: SIM_BEAM_MERCHANT_ID,
1914
+ publishableKey: SIM_BEAM_PUBLISHABLE_KEY,
1915
+ tokenizeUrl: `https://${SIM_BEAM_HOST}/client/v1/card-tokens`,
1916
+ isSandbox: true
1917
+ });
1918
+ }
1919
+ return null;
1920
+ }
1921
+ function toChargeDetail(charge) {
1922
+ return {
1923
+ id: charge.id,
1924
+ chargeId: charge.id,
1925
+ status: charge.status,
1926
+ amount: charge.amount,
1927
+ currency: charge.currency,
1928
+ paymentMethodType: charge.paymentMethodType,
1929
+ ...charge.referenceId !== null ? { referenceId: charge.referenceId } : {},
1930
+ ...charge.failureCode ? { failureCode: charge.failureCode } : {},
1931
+ source: "API",
1932
+ createdAt: charge.createdAt
1933
+ };
1934
+ }
1935
+ async function handleCardTokenRequest(request, url) {
1936
+ if (request.method !== "POST" || url.pathname !== "/client/v1/card-tokens") {
1937
+ return json({ error: `No Beam simulator for ${request.method} ${url.pathname}` }, 404);
1938
+ }
1939
+ const expected = `Basic ${btoa(`${SIM_BEAM_MERCHANT_ID}:${SIM_BEAM_PUBLISHABLE_KEY}`)}`;
1940
+ if (request.headers.get("Authorization") !== expected) {
1941
+ return json(
1942
+ {
1943
+ message: "invalid credentials",
1944
+ error: { errorCode: "INVALID_CREDENTIALS_ERROR", errorMessage: "invalid credentials" }
1945
+ },
1946
+ 401
1947
+ );
1948
+ }
1949
+ const body = await readJsonBody(request);
1950
+ const pan = typeof body.pan === "string" ? body.pan : "";
1951
+ const month = body.expiryMonth;
1952
+ const year = body.expiryYear;
1953
+ if (!/^\d{12,19}$/.test(pan) || typeof month !== "number" || month < 1 || month > 12 || typeof year !== "number" || year < 0 || year > 99) {
1954
+ return json(
1955
+ {
1956
+ message: "inputs are failing validation",
1957
+ error: {
1958
+ errorCode: "API_VALIDATION_ERROR",
1959
+ errorMessage: "inputs are failing validation"
1960
+ }
1961
+ },
1962
+ 400
1963
+ );
1964
+ }
1965
+ state.cardTokenCounter += 1;
1966
+ const token = {
1967
+ id: `ctok_sim_${state.cardTokenCounter}`,
1968
+ pan,
1969
+ last4: pan.slice(-4),
1970
+ expiryMonth: month,
1971
+ expiryYear: year
1972
+ };
1973
+ state.cardTokens.set(token.id, token);
1974
+ return json({ id: token.id });
1975
+ }
1976
+ function chargeWebhookPayload(charge) {
1977
+ return {
1978
+ chargeId: charge.id,
1979
+ merchantId: SIM_BEAM_MERCHANT_ID,
1980
+ ...charge.referenceId !== null ? { referenceId: charge.referenceId } : {},
1981
+ status: charge.status,
1982
+ currency: charge.currency,
1983
+ amount: charge.amount,
1984
+ source: "API",
1985
+ sourceId: charge.id,
1986
+ ...charge.failureCode ? { failureCode: charge.failureCode } : {},
1987
+ paymentMethod: { paymentMethodType: charge.paymentMethodType }
1988
+ };
1989
+ }
1990
+
1679
1991
  // src/simulator/payments.ts
1680
1992
  import crypto5 from "crypto";
1681
1993
 
@@ -1717,6 +2029,9 @@ async function callRoute(handler, options = {}) {
1717
2029
  if (user && !headers.has("x-stardeck-user")) {
1718
2030
  headers.set("x-stardeck-user", JSON.stringify(user));
1719
2031
  }
2032
+ if (state.liffId && !headers.has(LIFF_ID_HEADER)) {
2033
+ headers.set(LIFF_ID_HEADER, state.liffId);
2034
+ }
1720
2035
  if (options.body !== void 0 && !headers.has("Content-Type")) {
1721
2036
  headers.set("Content-Type", "application/json");
1722
2037
  }
@@ -1790,7 +2105,7 @@ var BEAM_BOLT_PAYMENT_METHODS = [
1790
2105
  "SPAY_LATER"
1791
2106
  ];
1792
2107
  var BEAM_BOLT_INTENT_STATUSES = ["PENDING", "PAID", "FAILED", "CANCELED", "EXPIRED"];
1793
- var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
2108
+ 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;
1794
2109
  function payErr(error, status = 400, code, details) {
1795
2110
  const body = { error };
1796
2111
  if (code) body.code = code;
@@ -1804,6 +2119,12 @@ function paymentOperationForRequest(request, pathname) {
1804
2119
  if (request.method === "GET" && /^\/api\/store\/beam\/[^/]+\/payment-links\/[^/]+$/.test(pathname)) {
1805
2120
  return "beam.getPaymentLinkStatus";
1806
2121
  }
2122
+ if (request.method === "POST" && /^\/api\/store\/beam\/[^/]+\/charges$/.test(pathname)) {
2123
+ return "beam.createDirectCharge";
2124
+ }
2125
+ if (request.method === "GET" && /^\/api\/store\/beam\/[^/]+\/charges\/[^/]+$/.test(pathname)) {
2126
+ return "beam.getCharge";
2127
+ }
1807
2128
  if (request.method === "POST" && /^\/api\/store\/(?!beam\/)[^/]+\/checkout$/.test(pathname)) {
1808
2129
  return "stripe.createCheckout";
1809
2130
  }
@@ -1979,6 +2300,8 @@ async function handlePaymentsRequest(request, url) {
1979
2300
  const pathname = url.pathname;
1980
2301
  const injectedFailure = consumePaymentFailure(request, pathname);
1981
2302
  if (injectedFailure) return injectedFailure;
2303
+ const directChargeResponse = await handleDirectChargeRequest(request, pathname);
2304
+ if (directChargeResponse) return directChargeResponse;
1982
2305
  if (/\/billing-portal$/.test(pathname)) {
1983
2306
  return payErr("Not found", 404, "NOT_FOUND");
1984
2307
  }
@@ -2075,7 +2398,7 @@ async function handlePaymentsRequest(request, url) {
2075
2398
  code: "too_small"
2076
2399
  });
2077
2400
  }
2078
- if (typeof body.deploymentId !== "string" || !UUID_RE.test(body.deploymentId)) {
2401
+ if (typeof body.deploymentId !== "string" || !UUID_RE2.test(body.deploymentId)) {
2079
2402
  issues.push({
2080
2403
  path: ["deploymentId"],
2081
2404
  message: "Invalid uuid",
@@ -2262,6 +2585,18 @@ async function handlePaymentsRequest(request, url) {
2262
2585
  }
2263
2586
  return payErr(`No payments simulator for ${request.method} ${pathname}`, 404);
2264
2587
  }
2588
+ function requirePendingDirectCharge(chargeId) {
2589
+ const charge = state.directCharges.get(chargeId);
2590
+ if (!charge) {
2591
+ throw new Error(`[stardeck-testing] Unknown direct charge id: ${chargeId}`);
2592
+ }
2593
+ if (charge.status !== "PENDING") {
2594
+ throw new Error(
2595
+ `[stardeck-testing] Direct charge ${chargeId} is ${charge.status}, not PENDING`
2596
+ );
2597
+ }
2598
+ return charge;
2599
+ }
2265
2600
  function createPayments() {
2266
2601
  return {
2267
2602
  get checkouts() {
@@ -2380,6 +2715,26 @@ function createPayments() {
2380
2715
  if (!intent) return void 0;
2381
2716
  return toBoltIntentRecord(intent);
2382
2717
  },
2718
+ get directCharges() {
2719
+ return [...state.directCharges.values()];
2720
+ },
2721
+ getDirectCharge(chargeId) {
2722
+ return state.directCharges.get(chargeId);
2723
+ },
2724
+ completeDirectCharge(chargeId) {
2725
+ const charge = requirePendingDirectCharge(chargeId);
2726
+ charge.status = "SUCCEEDED";
2727
+ return chargeWebhookPayload(charge);
2728
+ },
2729
+ failDirectCharge(chargeId, failureCode = "CH_PROCESSING_FAILED") {
2730
+ const charge = requirePendingDirectCharge(chargeId);
2731
+ charge.status = "FAILED";
2732
+ charge.failureCode = failureCode;
2733
+ return chargeWebhookPayload(charge);
2734
+ },
2735
+ setCardTokenization(enabled) {
2736
+ state.cardTokenizationEnabled = enabled;
2737
+ },
2383
2738
  clear() {
2384
2739
  state.checkouts = [];
2385
2740
  state.checkoutCounter = 0;
@@ -2394,6 +2749,11 @@ function createPayments() {
2394
2749
  state.boltIntentCounter = 0;
2395
2750
  state.boltCharges = [];
2396
2751
  state.boltChargeCounter = 0;
2752
+ state.directCharges.clear();
2753
+ state.directChargeCounter = 0;
2754
+ state.cardTokens.clear();
2755
+ state.cardTokenCounter = 0;
2756
+ state.cardTokenizationEnabled = true;
2397
2757
  },
2398
2758
  get count() {
2399
2759
  return state.checkouts.length;
@@ -3273,6 +3633,9 @@ async function handleSimulatedRequest(request, url) {
3273
3633
  if (isStorageHost) {
3274
3634
  return handleStorageRequest(request, url);
3275
3635
  }
3636
+ if (url.hostname === SIM_BEAM_HOST) {
3637
+ return handleCardTokenRequest(request, url);
3638
+ }
3276
3639
  if (isEmail && request.method === "POST") {
3277
3640
  return handleEmailSend(request);
3278
3641
  }
@@ -3459,6 +3822,8 @@ async function createTestApp(options = {}) {
3459
3822
  }
3460
3823
  applyTestEnv(options);
3461
3824
  state.allowNetwork = options.allowNetwork ?? false;
3825
+ const initialLiffId = options.liffId ?? null;
3826
+ state.liffId = initialLiffId;
3462
3827
  installFetchRouter();
3463
3828
  const schemaSql = loadSchemaSql(options) ?? "";
3464
3829
  const db = await bootWithSchema(schemaSql);
@@ -3511,6 +3876,9 @@ async function createTestApp(options = {}) {
3511
3876
  signOut() {
3512
3877
  state.currentUser = null;
3513
3878
  },
3879
+ setLiffId(liffId) {
3880
+ state.liffId = liffId;
3881
+ },
3514
3882
  issueSession(user) {
3515
3883
  const fullUser = buildUser(user);
3516
3884
  const tokens = {
@@ -3533,6 +3901,7 @@ async function createTestApp(options = {}) {
3533
3901
  }
3534
3902
  }
3535
3903
  state.currentUser = null;
3904
+ state.liffId = initialLiffId;
3536
3905
  state.sessions.clear();
3537
3906
  state.refreshSessions.clear();
3538
3907
  state.emails = [];
@@ -3547,6 +3916,7 @@ async function createTestApp(options = {}) {
3547
3916
  async close() {
3548
3917
  state.db = null;
3549
3918
  state.currentUser = null;
3919
+ state.liffId = null;
3550
3920
  state.sessions.clear();
3551
3921
  state.refreshSessions.clear();
3552
3922
  state.emails = [];
@@ -28,6 +28,12 @@ __export(headers_shim_exports, {
28
28
  module.exports = __toCommonJS(headers_shim_exports);
29
29
  var import_node_async_hooks = require("async_hooks");
30
30
 
31
+ // src/constants.ts
32
+ var DATA_STORE_TEST_HOST = "db.stardeck.test";
33
+ var DATA_STORE_TEST_URL = `postgresql://test:test@${DATA_STORE_TEST_HOST}/main`;
34
+ var APP_TEST_ORIGIN = "http://localhost:3333";
35
+ var LIFF_ID_HEADER = "x-stardeck-liff-id";
36
+
31
37
  // src/global-singleton.ts
32
38
  function globalSingleton(key, create) {
33
39
  const symbol = /* @__PURE__ */ Symbol.for(`stardeck-testing.${key}`);
@@ -36,16 +42,12 @@ function globalSingleton(key, create) {
36
42
  return holder[symbol];
37
43
  }
38
44
 
39
- // src/constants.ts
40
- var DATA_STORE_TEST_HOST = "db.stardeck.test";
41
- var DATA_STORE_TEST_URL = `postgresql://test:test@${DATA_STORE_TEST_HOST}/main`;
42
- var APP_TEST_ORIGIN = "http://localhost:3333";
43
-
44
45
  // src/state.ts
45
46
  var state = globalSingleton("state", () => ({
46
47
  db: null,
47
48
  lastHarnessDataStoreManifest: null,
48
49
  currentUser: null,
50
+ liffId: null,
49
51
  sessions: /* @__PURE__ */ new Map(),
50
52
  refreshSessions: /* @__PURE__ */ new Map(),
51
53
  emails: [],
@@ -70,6 +72,11 @@ var state = globalSingleton("state", () => ({
70
72
  boltIntentCounter: 0,
71
73
  boltCharges: [],
72
74
  boltChargeCounter: 0,
75
+ directCharges: /* @__PURE__ */ new Map(),
76
+ directChargeCounter: 0,
77
+ cardTokens: /* @__PURE__ */ new Map(),
78
+ cardTokenCounter: 0,
79
+ cardTokenizationEnabled: true,
73
80
  uploads: [],
74
81
  uploadCounter: 0,
75
82
  storageFiles: /* @__PURE__ */ new Map(),
@@ -100,6 +107,7 @@ function ambientScope() {
100
107
  if (state.currentUser) {
101
108
  headers2.set("x-stardeck-user", JSON.stringify(state.currentUser));
102
109
  }
110
+ if (state.liffId) headers2.set(LIFF_ID_HEADER, state.liffId);
103
111
  return { headers: headers2, cookies: /* @__PURE__ */ new Map() };
104
112
  }
105
113
  function currentScope() {
@@ -1,6 +1,12 @@
1
1
  // src/next/headers-shim.ts
2
2
  import { AsyncLocalStorage } from "async_hooks";
3
3
 
4
+ // src/constants.ts
5
+ var DATA_STORE_TEST_HOST = "db.stardeck.test";
6
+ var DATA_STORE_TEST_URL = `postgresql://test:test@${DATA_STORE_TEST_HOST}/main`;
7
+ var APP_TEST_ORIGIN = "http://localhost:3333";
8
+ var LIFF_ID_HEADER = "x-stardeck-liff-id";
9
+
4
10
  // src/global-singleton.ts
5
11
  function globalSingleton(key, create) {
6
12
  const symbol = /* @__PURE__ */ Symbol.for(`stardeck-testing.${key}`);
@@ -9,16 +15,12 @@ function globalSingleton(key, create) {
9
15
  return holder[symbol];
10
16
  }
11
17
 
12
- // src/constants.ts
13
- var DATA_STORE_TEST_HOST = "db.stardeck.test";
14
- var DATA_STORE_TEST_URL = `postgresql://test:test@${DATA_STORE_TEST_HOST}/main`;
15
- var APP_TEST_ORIGIN = "http://localhost:3333";
16
-
17
18
  // src/state.ts
18
19
  var state = globalSingleton("state", () => ({
19
20
  db: null,
20
21
  lastHarnessDataStoreManifest: null,
21
22
  currentUser: null,
23
+ liffId: null,
22
24
  sessions: /* @__PURE__ */ new Map(),
23
25
  refreshSessions: /* @__PURE__ */ new Map(),
24
26
  emails: [],
@@ -43,6 +45,11 @@ var state = globalSingleton("state", () => ({
43
45
  boltIntentCounter: 0,
44
46
  boltCharges: [],
45
47
  boltChargeCounter: 0,
48
+ directCharges: /* @__PURE__ */ new Map(),
49
+ directChargeCounter: 0,
50
+ cardTokens: /* @__PURE__ */ new Map(),
51
+ cardTokenCounter: 0,
52
+ cardTokenizationEnabled: true,
46
53
  uploads: [],
47
54
  uploadCounter: 0,
48
55
  storageFiles: /* @__PURE__ */ new Map(),
@@ -73,6 +80,7 @@ function ambientScope() {
73
80
  if (state.currentUser) {
74
81
  headers2.set("x-stardeck-user", JSON.stringify(state.currentUser));
75
82
  }
83
+ if (state.liffId) headers2.set(LIFF_ID_HEADER, state.liffId);
76
84
  return { headers: headers2, cookies: /* @__PURE__ */ new Map() };
77
85
  }
78
86
  function currentScope() {