@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/SKILL.md +55 -4
- package/dist/index.d.mts +51 -1
- package/dist/index.d.ts +51 -1
- package/dist/index.js +372 -2
- package/dist/index.mjs +372 -2
- package/dist/next/headers-shim.js +13 -5
- package/dist/next/headers-shim.mjs +13 -5
- package/dist/setup.js +310 -2
- package/dist/setup.mjs +310 -2
- package/package.json +2 -2
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
|
|
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" || !
|
|
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() {
|