@agent-cards/sdk 0.23.22 → 0.23.23

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.
@@ -228,9 +228,10 @@ export declare function recognizerRequestMethod(rec: Pick<Recognizer, 'request'>
228
228
  * pauses like a POST, and neither pauses in the other's method. A GET is a card
229
229
  * request only for a processor whose exact URL this build checks: Worldpay
230
230
  * eProtect's registration, whose query carries the card (payment-core's
231
- * grammar), and a hosted page entered with a plain GET, whose link carries no
232
- * card and which the cardholder's device takes over (hosted-entry.ts). Any other
233
- * GET recognizer is nothing this build can complete and is never paused.
231
+ * grammar), SB Payment Service's token request, checked through payment-core's
232
+ * grammar the same way, and a hosted page entered with a plain GET, whose link
233
+ * carries no card and which the cardholder's device takes over (hosted-entry.ts).
234
+ * Any other GET recognizer is nothing this build can complete and is never paused.
234
235
  */
235
236
  export declare function isRecognizerRequest(rec: Recognizer, url: string, method: string): boolean;
236
237
  /**
package/dist/registry.js CHANGED
@@ -11,6 +11,8 @@ import { BUILTIN_REGISTRY } from './builtin-registry.generated.js';
11
11
  import { isEprotectTokenRequest } from './eprotect.generated.js';
12
12
  import { EPROTECT_PSP } from './eprotect.js';
13
13
  import { isSecureAcceptanceTokenRequest } from './cybersource-secure-acceptance.generated.js';
14
+ import { isSbpsTokenRequest } from './sbps.generated.js';
15
+ import { SBPS_PSP } from './sbps.js';
14
16
  import { SECURE_ACCEPTANCE_PSP } from './cybersource-secure-acceptance.js';
15
17
  import { hasHostedGetEntry, isHostedGetEntry } from './hosted-entry.js';
16
18
  import { TOKENEX_IFRAME_PSP, isTokenexIframeUrl } from './tokenex-iframe.generated.js';
@@ -120,6 +122,10 @@ export function findRecognizer(url, registry = BUILTIN_REGISTRY, opts = {}) {
120
122
  // /eProtect/paypage call.
121
123
  if (r.psp === EPROTECT_PSP)
122
124
  return isEprotectTokenRequest(url, 'GET');
125
+ // SB Payment Service's card rides in the query of a GET too: only the exact URL the browser writes
126
+ // for its token script's <script> is recognized, as on the server (payment-core sbps.js).
127
+ if (r.psp === SBPS_PSP)
128
+ return isSbpsTokenRequest(url, 'GET');
123
129
  // CyberSource Secure Acceptance's token create is recognized as the server
124
130
  // recognizes it: its exact URL, with no query, fragment, trailing dot or
125
131
  // doubled slash. Any other spelling stays the stop's (pre-claim.ts).
@@ -154,16 +160,18 @@ export function recognizerRequestMethod(rec) {
154
160
  * pauses like a POST, and neither pauses in the other's method. A GET is a card
155
161
  * request only for a processor whose exact URL this build checks: Worldpay
156
162
  * eProtect's registration, whose query carries the card (payment-core's
157
- * grammar), and a hosted page entered with a plain GET, whose link carries no
158
- * card and which the cardholder's device takes over (hosted-entry.ts). Any other
159
- * GET recognizer is nothing this build can complete and is never paused.
163
+ * grammar), SB Payment Service's token request, checked through payment-core's
164
+ * grammar the same way, and a hosted page entered with a plain GET, whose link
165
+ * carries no card and which the cardholder's device takes over (hosted-entry.ts).
166
+ * Any other GET recognizer is nothing this build can complete and is never paused.
160
167
  */
161
168
  export function isRecognizerRequest(rec, url, method) {
162
169
  const expected = recognizerRequestMethod(rec);
163
170
  if (typeof method !== 'string' || method.toUpperCase() !== expected)
164
171
  return false;
165
172
  return expected !== 'GET' || (rec.psp === EPROTECT_PSP && isEprotectTokenRequest(url, 'GET'))
166
- || (rec.mode === 'hosted_form' && isHostedGetEntry(rec.psp, url));
173
+ || (rec.mode === 'hosted_form' && isHostedGetEntry(rec.psp, url))
174
+ || (rec.psp === SBPS_PSP && isSbpsTokenRequest(url, 'GET'));
167
175
  }
168
176
  /**
169
177
  * True when this processor cannot be taken over on an ordinary authorization:
package/dist/sbps.d.ts ADDED
@@ -0,0 +1,57 @@
1
+ import { SBPS_ANSWER_CONTENT_TYPE } from './sbps.generated.js';
2
+ /** The processor id, as the registry and the API name it. */
3
+ export declare const SBPS_PSP = "sb_payment_service";
4
+ /** The dark-launch capability the API serves the SBPS recognizer behind (payment-core definitions.js). */
5
+ export declare const SBPS_CAPABILITY = "sbps_token_jsonp";
6
+ /** What the paused <script> is answered with. */
7
+ export { SBPS_ANSWER_CONTENT_TYPE };
8
+ /**
9
+ * What a request on SBPS's token hosts is, for an attachment:
10
+ * token_request the token script's GET, exactly as the browser writes it: the card request;
11
+ * unsupported any other request on /token/generateToken: another method, query or spelling;
12
+ * sibling everything else on those hosts: the scripts and the 3-D Secure info request.
13
+ */
14
+ export type SbpsRequest = {
15
+ kind: 'token_request';
16
+ } | {
17
+ kind: 'unsupported';
18
+ } | {
19
+ kind: 'sibling';
20
+ };
21
+ /** Which SBPS request this is, or null for a URL that is not on one of SBPS's token hosts. */
22
+ export declare function classifySbpsRequest(url: string, method: string): SbpsRequest | null;
23
+ /** The token request GET exactly as payment-core admits it: its URL alone, with no body. */
24
+ export declare function isSbpsTokenGet(request: {
25
+ url: string;
26
+ method?: string;
27
+ body?: string | null;
28
+ }): boolean;
29
+ /**
30
+ * The answer a paused token request gets, or null when the stored answer is not one to hand the page:
31
+ * SBPS's own shape, `<callback>(<tokenRes>)` under a script type, with the callback the paused URL
32
+ * named. The answer must be one payment-core's judge accepts for that exact URL (result OK and the
33
+ * five token fields), byte for byte what the API stored after running the same judge.
34
+ */
35
+ export declare function sbpsScriptAnswer(templateUrl: string, response: {
36
+ status: number;
37
+ body: string;
38
+ }): {
39
+ status: 200;
40
+ headers: Record<string, string>;
41
+ body: string;
42
+ } | null;
43
+ /**
44
+ * One attachment's view of SBPS's token hosts. Inert (classify returns null) until the registry serves
45
+ * SBPS, which a client reaches only by syncing with the capability for an org the API enabled, so a
46
+ * build, an org or an API that never served it leaves every SBPS request exactly as before.
47
+ */
48
+ export declare class SbpsGate {
49
+ private readonly vault;
50
+ constructor(vault: {
51
+ recognizesProcessor?: (psp: string) => boolean;
52
+ } | undefined);
53
+ /** What this request is, when the registry serves SBPS and the URL is on its token hosts; null leaves it to the other gates. */
54
+ classify(url: string, method: string): SbpsRequest | null;
55
+ /** Playwright's route predicate sees only the URL: route the token request family, never the scripts or the 3-D Secure info request. */
56
+ matches(url: string): boolean;
57
+ }
@@ -0,0 +1,96 @@
1
+ declare var SBPS_PSP: string;
2
+ declare var SBPS_CAPABILITY: string;
3
+ declare var SBPS_OPERATION: string;
4
+ declare var SBPS_TOKEN_PATH: string;
5
+ declare var SBPS_TOKEN_HOSTS: Readonly<{
6
+ production: "token.sps-system.com";
7
+ sandbox: "stbtoken.sps-system.com";
8
+ }>;
9
+ declare function sbpsTokenEnvironment(hostname: any): "production" | "sandbox" | null;
10
+ declare function sbpsTokenTemplate(url: any): {
11
+ environment: string;
12
+ origin: string;
13
+ pairs: {
14
+ name: string;
15
+ raw: string;
16
+ }[];
17
+ raws: any;
18
+ callback: string;
19
+ } | null;
20
+ declare function isSbpsTokenRequest(url: any, method: any): boolean;
21
+ declare function isSbpsRequest(request: any): boolean;
22
+ declare function sbpsRequestFacts(url: any): {
23
+ environment: string;
24
+ merchantId: any;
25
+ serviceId: any;
26
+ callback: string;
27
+ requestTime: any;
28
+ } | null;
29
+ declare var SBPS_MERCHANT_PINS: readonly any[];
30
+ declare function sbpsMerchantPin(merchantId: any, serviceId: any, merchantOrigin: any, pins?: readonly any[]): any;
31
+ declare function sbpsMerchantRefusal(environment: any, merchantId: any, serviceId: any, merchantOrigin: any, pins?: readonly any[]): "unsupported_environment" | "invalid_sbps_merchant" | "sbps_merchant_unreviewed" | null;
32
+ declare var SBPS_PUBLIC_KEY: Readonly<{
33
+ spki: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0BMW0T80ZB48dIIEkhcucncJTIthJtKnZN1vh3vfTCbuu7e1OXERSolFa8Me9bTnHqR3y8fXGXXYIxU8BBHBYWbNZWk9wMAn+Hcej8091zYu5BqAlzRxs1S9k6bX/aH24l7qXxzfW6rX71qYuP3v9RRwgnd3tOcWN9wrsIhryTryoC6KSVeh1z/27k1W8uVKCXJniihZ99nUtRSSoST9nZdXDLXyehTBZyZKAWEH5I5wRc3VnjkMUBo5Ksi7G8x0pqLrCgk46Z0YHMpU4M8TORYA1ZyFug7gCOOumfzWiJRBJmctvmwrnfobGC7z/6zSiZH/3YbigZgTsw2073cp0wIDAQAB";
34
+ sha256: "47d39e0b61172848ddf6bfc9417c049c6ed9e6b8e38380d31a0bf24fe873bb8d";
35
+ }>;
36
+ declare var SBPS_TOKEN_SCRIPT_PATH: string;
37
+ declare var SBPS_TOKEN_SCRIPTS: Readonly<{
38
+ production: Readonly<{
39
+ host: "token.sps-system.com";
40
+ sha256: "f86d4d93946ef558ccb980e5d80aaf378ab13b0b78b553389d233be5a119901a";
41
+ }>;
42
+ sandbox: Readonly<{
43
+ host: "stbtoken.sps-system.com";
44
+ sha256: "068dfc8a5ba33789e439401f03e6e2ab9fa195469d5ed2e6e4da91e40f97adaf";
45
+ }>;
46
+ }>;
47
+ declare var SBPS_TOKEN_SCRIPT_MAX_BYTES: number;
48
+ declare function sbpsTokenScriptUrl(environment: any): string;
49
+ declare function sbpsTokenScriptVerdict(environment: any, bytes: any, scripts?: Readonly<{
50
+ production: Readonly<{
51
+ host: "token.sps-system.com";
52
+ sha256: "f86d4d93946ef558ccb980e5d80aaf378ab13b0b78b553389d233be5a119901a";
53
+ }>;
54
+ sandbox: Readonly<{
55
+ host: "stbtoken.sps-system.com";
56
+ sha256: "068dfc8a5ba33789e439401f03e6e2ab9fa195469d5ed2e6e4da91e40f97adaf";
57
+ }>;
58
+ }>): {
59
+ ok: boolean;
60
+ reason: string;
61
+ } | {
62
+ ok: boolean;
63
+ reason?: undefined;
64
+ };
65
+ declare var SBPS_REFUSAL_REASONS: readonly string[];
66
+ declare var SbpsError: {
67
+ new (reason: any, message: any): {
68
+ name: string;
69
+ message: string;
70
+ stack?: string;
71
+ cause?: unknown;
72
+ };
73
+ };
74
+ declare function isSbpsDeviceRequest(templateUrl: any, deviceUrl: any): boolean;
75
+ declare var SBPS_ANSWER_CONTENT_TYPE: string;
76
+ declare var SBPS_REFUSAL_CODES: readonly string[];
77
+ declare function sbpsOutcome(status: any, bodyText: any, templateUrl: any, card: any): {
78
+ outcome: string;
79
+ reason: string;
80
+ code?: undefined;
81
+ sanitizedJson?: undefined;
82
+ } | {
83
+ outcome: string;
84
+ code: any;
85
+ reason?: undefined;
86
+ sanitizedJson?: undefined;
87
+ } | {
88
+ outcome: string;
89
+ sanitizedJson: string;
90
+ reason?: undefined;
91
+ code?: undefined;
92
+ };
93
+ declare function sanitizeSbpsAnswer(bodyText: any, templateUrl: any): string | undefined;
94
+ declare function sbpsJsonp(callback: any, json: any): string;
95
+ declare function sbpsRefusalCode(bodyText: any): any;
96
+ export { SBPS_ANSWER_CONTENT_TYPE, SBPS_CAPABILITY, SBPS_MERCHANT_PINS, SBPS_OPERATION, SBPS_PSP, SBPS_PUBLIC_KEY, SBPS_REFUSAL_CODES, SBPS_REFUSAL_REASONS, SBPS_TOKEN_HOSTS, SBPS_TOKEN_PATH, SBPS_TOKEN_SCRIPTS, SBPS_TOKEN_SCRIPT_MAX_BYTES, SBPS_TOKEN_SCRIPT_PATH, SbpsError, isSbpsDeviceRequest, isSbpsRequest, isSbpsTokenRequest, sanitizeSbpsAnswer, sbpsJsonp, sbpsMerchantPin, sbpsMerchantRefusal, sbpsOutcome, sbpsRefusalCode, sbpsRequestFacts, sbpsTokenEnvironment, sbpsTokenScriptUrl, sbpsTokenScriptVerdict, sbpsTokenTemplate };
@@ -0,0 +1,427 @@
1
+ // @ts-nocheck
2
+ // Generated from @agent-cards/payment-core. Do not edit.
3
+ // artifact-sha256: 95b75e926c63b686635d8dddea32bc4ccf569b3598d283784e3bcba9d262bace
4
+ // src/sha256.js
5
+ var K = /* @__PURE__ */ new Uint32Array([
6
+ 1116352408,
7
+ 1899447441,
8
+ 3049323471,
9
+ 3921009573,
10
+ 961987163,
11
+ 1508970993,
12
+ 2453635748,
13
+ 2870763221,
14
+ 3624381080,
15
+ 310598401,
16
+ 607225278,
17
+ 1426881987,
18
+ 1925078388,
19
+ 2162078206,
20
+ 2614888103,
21
+ 3248222580,
22
+ 3835390401,
23
+ 4022224774,
24
+ 264347078,
25
+ 604807628,
26
+ 770255983,
27
+ 1249150122,
28
+ 1555081692,
29
+ 1996064986,
30
+ 2554220882,
31
+ 2821834349,
32
+ 2952996808,
33
+ 3210313671,
34
+ 3336571891,
35
+ 3584528711,
36
+ 113926993,
37
+ 338241895,
38
+ 666307205,
39
+ 773529912,
40
+ 1294757372,
41
+ 1396182291,
42
+ 1695183700,
43
+ 1986661051,
44
+ 2177026350,
45
+ 2456956037,
46
+ 2730485921,
47
+ 2820302411,
48
+ 3259730800,
49
+ 3345764771,
50
+ 3516065817,
51
+ 3600352804,
52
+ 4094571909,
53
+ 275423344,
54
+ 430227734,
55
+ 506948616,
56
+ 659060556,
57
+ 883997877,
58
+ 958139571,
59
+ 1322822218,
60
+ 1537002063,
61
+ 1747873779,
62
+ 1955562222,
63
+ 2024104815,
64
+ 2227730452,
65
+ 2361852424,
66
+ 2428436474,
67
+ 2756734187,
68
+ 3204031479,
69
+ 3329325298
70
+ ]);
71
+ var rotr = (x, n) => x >>> n | x << 32 - n;
72
+ function utf8(text) {
73
+ const out = [];
74
+ for (const char of text) {
75
+ let cp = char.codePointAt(0);
76
+ if (cp >= 55296 && cp <= 57343)
77
+ cp = 65533;
78
+ if (cp < 128)
79
+ out.push(cp);
80
+ else if (cp < 2048)
81
+ out.push(192 | cp >> 6, 128 | cp & 63);
82
+ else if (cp < 65536)
83
+ out.push(224 | cp >> 12, 128 | cp >> 6 & 63, 128 | cp & 63);
84
+ else
85
+ out.push(240 | cp >> 18, 128 | cp >> 12 & 63, 128 | cp >> 6 & 63, 128 | cp & 63);
86
+ }
87
+ return Uint8Array.from(out);
88
+ }
89
+ function sha256Hex(input) {
90
+ const isBytes = Object.prototype.toString.call(input) === "[object Uint8Array]";
91
+ if (typeof input !== "string" && !isBytes)
92
+ throw new TypeError("sha256Hex takes a string or a Uint8Array.");
93
+ const bytes = typeof input === "string" ? utf8(input) : input;
94
+ const length = bytes.length;
95
+ const padded = new Uint8Array((length + 8 >> 6) + 1 << 6);
96
+ padded.set(bytes);
97
+ padded[length] = 128;
98
+ const view = new DataView(padded.buffer);
99
+ view.setUint32(padded.length - 8, Math.floor(length / 536870912));
100
+ view.setUint32(padded.length - 4, length << 3 >>> 0);
101
+ const H = new Uint32Array([1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]);
102
+ const W = new Uint32Array(64);
103
+ for (let offset = 0; offset < padded.length; offset += 64) {
104
+ for (let t = 0; t < 16; t++)
105
+ W[t] = view.getUint32(offset + t * 4);
106
+ for (let t = 16; t < 64; t++) {
107
+ const s0 = rotr(W[t - 15], 7) ^ rotr(W[t - 15], 18) ^ W[t - 15] >>> 3;
108
+ const s1 = rotr(W[t - 2], 17) ^ rotr(W[t - 2], 19) ^ W[t - 2] >>> 10;
109
+ W[t] = W[t - 16] + s0 + W[t - 7] + s1 >>> 0;
110
+ }
111
+ let a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h = H[7];
112
+ for (let t = 0; t < 64; t++) {
113
+ const t1 = h + (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) + (e & f ^ ~e & g) + K[t] + W[t] >>> 0;
114
+ const t2 = (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
115
+ h = g;
116
+ g = f;
117
+ f = e;
118
+ e = d + t1 >>> 0;
119
+ d = c;
120
+ c = b;
121
+ b = a;
122
+ a = t1 + t2 >>> 0;
123
+ }
124
+ H[0] += a;
125
+ H[1] += b;
126
+ H[2] += c;
127
+ H[3] += d;
128
+ H[4] += e;
129
+ H[5] += f;
130
+ H[6] += g;
131
+ H[7] += h;
132
+ }
133
+ return Array.from(H, (word) => word.toString(16).padStart(8, "0")).join("");
134
+ }
135
+ // src/sbps.js
136
+ var SBPS_PSP = "sb_payment_service";
137
+ var SBPS_CAPABILITY = "sbps_token_jsonp";
138
+ var SBPS_OPERATION = "sb_payment_service.card_request";
139
+ var SBPS_TOKEN_PATH = "/token/generateToken";
140
+ var SBPS_TOKEN_HOSTS = /* @__PURE__ */ Object.freeze({ production: "token.sps-system.com", sandbox: "stbtoken.sps-system.com" });
141
+ var QUERY_ORDER = /* @__PURE__ */ Object.freeze(["callback", "mId", "sId", "key", "val", "requestTime"]);
142
+ var URL_LIMIT = 2048;
143
+ var CALLBACK_RAW = /^com_sbps_system\.rm\[%27([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})%27\]\.cb$/;
144
+ var MERCHANT_ID = /^[0-9]{5}$/;
145
+ var SERVICE_ID = /^[0-9]{3}$/;
146
+ var REQUEST_TIME = /^[0-9]{10,15}$/;
147
+ var BASE64 = /^[A-Za-z0-9+/]+={0,2}$/;
148
+ var KEY_MIN_BYTES = 248;
149
+ var KEY_MAX_BYTES = 256;
150
+ var VAL_BYTES = 32;
151
+ var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
152
+ function sbpsTokenEnvironment(hostname) {
153
+ if (hostname === SBPS_TOKEN_HOSTS.production)
154
+ return "production";
155
+ if (hostname === SBPS_TOKEN_HOSTS.sandbox)
156
+ return "sandbox";
157
+ return null;
158
+ }
159
+ function base64Length(text) {
160
+ if (typeof text !== "string" || text.length === 0 || text.length % 4 !== 0 || !BASE64.test(text))
161
+ return -1;
162
+ let binary;
163
+ try {
164
+ binary = atob(text);
165
+ }
166
+ catch {
167
+ return -1;
168
+ }
169
+ return btoa(binary) === text ? binary.length : -1;
170
+ }
171
+ function encodedBase64(raw) {
172
+ let value;
173
+ try {
174
+ value = decodeURIComponent(raw);
175
+ }
176
+ catch {
177
+ return null;
178
+ }
179
+ return encodeURIComponent(value) === raw ? value : null;
180
+ }
181
+ var RAW_RULES = {
182
+ callback: (raw) => CALLBACK_RAW.test(raw),
183
+ mId: (raw) => MERCHANT_ID.test(raw),
184
+ sId: (raw) => SERVICE_ID.test(raw),
185
+ key: (raw) => {
186
+ const length = base64Length(encodedBase64(raw));
187
+ return length >= KEY_MIN_BYTES && length <= KEY_MAX_BYTES;
188
+ },
189
+ val: (raw) => base64Length(encodedBase64(raw)) === VAL_BYTES,
190
+ requestTime: (raw) => REQUEST_TIME.test(raw)
191
+ };
192
+ function parseSbpsTokenUrl(url) {
193
+ if (typeof url !== "string" || url.length > URL_LIMIT || url.includes("#"))
194
+ return null;
195
+ let parsed;
196
+ try {
197
+ parsed = new URL(url);
198
+ }
199
+ catch {
200
+ return null;
201
+ }
202
+ if (parsed.href !== url || parsed.protocol !== "https:" || parsed.port || parsed.username || parsed.password)
203
+ return null;
204
+ const environment = sbpsTokenEnvironment(parsed.hostname);
205
+ if (!environment || parsed.pathname !== SBPS_TOKEN_PATH || !parsed.search.startsWith("?"))
206
+ return null;
207
+ const chunks = parsed.search.slice(1).split("&");
208
+ if (chunks.length !== QUERY_ORDER.length)
209
+ return null;
210
+ const pairs = [];
211
+ const raws = /* @__PURE__ */ Object.create(null);
212
+ for (let i = 0; i < chunks.length; i++) {
213
+ const name = QUERY_ORDER[i];
214
+ if (!chunks[i].startsWith(`${name}=`))
215
+ return null;
216
+ const raw = chunks[i].slice(name.length + 1);
217
+ if (!RAW_RULES[name](raw))
218
+ return null;
219
+ pairs.push({ name, raw });
220
+ raws[name] = raw;
221
+ }
222
+ return {
223
+ environment,
224
+ origin: parsed.origin,
225
+ pairs,
226
+ raws,
227
+ callback: `com_sbps_system.rm['${CALLBACK_RAW.exec(raws.callback)[1]}'].cb`
228
+ };
229
+ }
230
+ function sbpsTokenTemplate(url) {
231
+ return parseSbpsTokenUrl(url);
232
+ }
233
+ function isSbpsTokenRequest(url, method) {
234
+ return method === "GET" && parseSbpsTokenUrl(url) !== null;
235
+ }
236
+ function isSbpsRequest(request) {
237
+ return !!request && typeof request === "object" && !Array.isArray(request) && request.method === "GET" && (!own(request, "body") || request.body === void 0 || request.body === null || request.body === "") && isSbpsTokenRequest(request.url, "GET");
238
+ }
239
+ function sbpsRequestFacts(url) {
240
+ const parsed = parseSbpsTokenUrl(url);
241
+ if (!parsed)
242
+ return null;
243
+ return {
244
+ environment: parsed.environment,
245
+ merchantId: parsed.raws.mId,
246
+ serviceId: parsed.raws.sId,
247
+ callback: parsed.callback,
248
+ requestTime: parsed.raws.requestTime
249
+ };
250
+ }
251
+ var pin = (fields) => /* @__PURE__ */ Object.freeze({
252
+ ...fields,
253
+ origins: Object.freeze([...fields.origins]),
254
+ accounts: Object.freeze(fields.accounts.map((account) => Object.freeze({ ...account })))
255
+ });
256
+ var SBPS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
257
+ /* @__PURE__ */ pin({
258
+ id: "boss_ezo_fukuoka",
259
+ merchant: `BOSS E${ /* @__PURE__ */String.fromCharCode(12539)}ZO FUKUOKA`,
260
+ origins: ["https://ticket.e-zofukuoka.com"],
261
+ accounts: [{ merchantId: "75082", serviceId: "001" }]
262
+ })
263
+ ]);
264
+ function sbpsMerchantPin(merchantId, serviceId, merchantOrigin, pins = SBPS_MERCHANT_PINS) {
265
+ if (typeof merchantId !== "string" || !MERCHANT_ID.test(merchantId) || typeof serviceId !== "string" || !SERVICE_ID.test(serviceId) || typeof merchantOrigin !== "string" || !Array.isArray(pins))
266
+ return null;
267
+ return pins.find((entry) => !!entry && Array.isArray(entry.origins) && Array.isArray(entry.accounts) && entry.origins.includes(merchantOrigin) && entry.accounts.some((account) => !!account && account.merchantId === merchantId && account.serviceId === serviceId)) ?? null;
268
+ }
269
+ function sbpsMerchantRefusal(environment, merchantId, serviceId, merchantOrigin, pins = SBPS_MERCHANT_PINS) {
270
+ if (typeof merchantId !== "string" || !MERCHANT_ID.test(merchantId) || typeof serviceId !== "string" || !SERVICE_ID.test(serviceId)) {
271
+ return "invalid_sbps_merchant";
272
+ }
273
+ if (environment === "sandbox")
274
+ return null;
275
+ if (environment !== "production")
276
+ return "unsupported_environment";
277
+ return sbpsMerchantPin(merchantId, serviceId, merchantOrigin, pins) ? null : "sbps_merchant_unreviewed";
278
+ }
279
+ var SBPS_PUBLIC_KEY = /* @__PURE__ */ Object.freeze({
280
+ spki: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0BMW0T80ZB48dIIEkhcucncJTIthJtKnZN1vh3vfTCbuu7e1OXERSolFa8Me9bTnHqR3y8fXGXXYIxU8BBHBYWbNZWk9wMAn+Hcej8091zYu5BqAlzRxs1S9k6bX/aH24l7qXxzfW6rX71qYuP3v9RRwgnd3tOcWN9wrsIhryTryoC6KSVeh1z/27k1W8uVKCXJniihZ99nUtRSSoST9nZdXDLXyehTBZyZKAWEH5I5wRc3VnjkMUBo5Ksi7G8x0pqLrCgk46Z0YHMpU4M8TORYA1ZyFug7gCOOumfzWiJRBJmctvmwrnfobGC7z/6zSiZH/3YbigZgTsw2073cp0wIDAQAB",
281
+ sha256: "47d39e0b61172848ddf6bfc9417c049c6ed9e6b8e38380d31a0bf24fe873bb8d"
282
+ });
283
+ var SBPS_TOKEN_SCRIPT_PATH = "/sbpstoken/com_sbps_system_token.js";
284
+ var SBPS_TOKEN_SCRIPTS = /* @__PURE__ */ Object.freeze({
285
+ production: /* @__PURE__ */ Object.freeze({ host: "token.sps-system.com", sha256: "f86d4d93946ef558ccb980e5d80aaf378ab13b0b78b553389d233be5a119901a" }),
286
+ sandbox: /* @__PURE__ */ Object.freeze({ host: "stbtoken.sps-system.com", sha256: "068dfc8a5ba33789e439401f03e6e2ab9fa195469d5ed2e6e4da91e40f97adaf" })
287
+ });
288
+ var SBPS_TOKEN_SCRIPT_MAX_BYTES = 131072;
289
+ function sbpsTokenScriptUrl(environment) {
290
+ if (environment !== "production" && environment !== "sandbox")
291
+ throw new Error("unsupported_sbps_environment");
292
+ return `https://${SBPS_TOKEN_SCRIPTS[environment].host}${SBPS_TOKEN_SCRIPT_PATH}`;
293
+ }
294
+ function sbpsTokenScriptVerdict(environment, bytes, scripts = SBPS_TOKEN_SCRIPTS) {
295
+ const pin2 = (environment === "production" || environment === "sandbox") && scripts && typeof scripts === "object" && scripts[environment] && typeof scripts[environment] === "object" ? scripts[environment].sha256 : null;
296
+ if (typeof pin2 !== "string" || !/^[0-9a-f]{64}$/.test(pin2))
297
+ return { ok: false, reason: "unsupported_sbps_environment" };
298
+ if (!ArrayBuffer.isView(bytes) || Object.prototype.toString.call(bytes) !== "[object Uint8Array]" || bytes.byteLength === 0 || bytes.byteLength > SBPS_TOKEN_SCRIPT_MAX_BYTES)
299
+ return { ok: false, reason: "sbps_script_unreadable" };
300
+ return sha256Hex(bytes) === pin2 ? { ok: true } : { ok: false, reason: "sbps_script_changed" };
301
+ }
302
+ var SBPS_REFUSAL_REASONS = /* @__PURE__ */ Object.freeze([
303
+ "request",
304
+ "psp",
305
+ "url",
306
+ "environment_mismatch",
307
+ "template",
308
+ "card",
309
+ "test_card_required",
310
+ "crypto"
311
+ ]);
312
+ var SbpsError = class extends Error {
313
+ /** @param {string} reason one of SBPS_REFUSAL_REASONS @param {string} message */
314
+ constructor(reason, message) {
315
+ super(`Refusing to pay: ${message}`);
316
+ this.name = "SbpsError";
317
+ this.reason = reason;
318
+ }
319
+ };
320
+ function isSbpsDeviceRequest(templateUrl, deviceUrl) {
321
+ const template = parseSbpsTokenUrl(templateUrl);
322
+ const device = parseSbpsTokenUrl(deviceUrl);
323
+ if (!template || !device || device.origin !== template.origin)
324
+ return false;
325
+ return device.pairs.every((pair, i) => pair.name === template.pairs[i].name && (pair.name === "key" || pair.name === "val" ? pair.raw !== template.pairs[i].raw : pair.raw === template.pairs[i].raw));
326
+ }
327
+ var SBPS_ANSWER_CONTENT_TYPE = "application/javascript;charset=UTF-8";
328
+ var ERROR_TYPES = /* @__PURE__ */ Object.freeze(["03", "04", "05", "07", "99"]);
329
+ var ERROR_FIELDS = /* @__PURE__ */ Object.freeze(["001", "002", "003", "004", "005", "999"]);
330
+ var SBPS_REFUSAL_CODES = /* @__PURE__ */ Object.freeze(ERROR_TYPES.flatMap((type) => ERROR_FIELDS.map((field) => `${type}${field}`)));
331
+ var ANSWER_LIMIT = 8192;
332
+ var TOKEN = /^[A-Za-z0-9+/=_.:-]{1,256}$/;
333
+ var MASKED = /^([0-9]{0,6})[*Xx]{4,15}([0-9]{4})$/;
334
+ var EXPIRY = /^20[0-9]{2}(?:0[1-9]|1[0-2])$/;
335
+ var BRANDS = /* @__PURE__ */ Object.freeze(["J", "V", "M", "A", "D", "X"]);
336
+ var TOKEN_FIELDS = /* @__PURE__ */ Object.freeze(["token", "tokenKey", "maskedCcNumber", "ccExpiration", "cardBrandCode"]);
337
+ var plainObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
338
+ function readAnswer(text, callback) {
339
+ if (typeof text !== "string" || text.length > ANSWER_LIMIT || typeof callback !== "string")
340
+ return null;
341
+ const body = text.replace(/^\s+/, "");
342
+ if (!body.startsWith(`${callback}(`))
343
+ return null;
344
+ const close = /\)\s*;?\s*$/.exec(body);
345
+ if (!close || close.index < callback.length + 1)
346
+ return null;
347
+ let parsed;
348
+ try {
349
+ parsed = JSON.parse(body.slice(callback.length + 1, close.index));
350
+ }
351
+ catch {
352
+ return null;
353
+ }
354
+ if (!plainObject(parsed) || Object.keys(parsed).length !== 1 || !plainObject(parsed.tokenRes))
355
+ return null;
356
+ return parsed.tokenRes;
357
+ }
358
+ function tokenFields(tokenRes) {
359
+ if (!Object.keys(tokenRes).every((key) => ["result", "tokenResponse", "errorCode"].includes(key)))
360
+ return null;
361
+ if (own(tokenRes, "errorCode") && tokenRes.errorCode !== null && tokenRes.errorCode !== "")
362
+ return null;
363
+ const response = tokenRes.tokenResponse;
364
+ if (!plainObject(response) || !TOKEN_FIELDS.every((key) => typeof response[key] === "string"))
365
+ return null;
366
+ const { token, tokenKey, maskedCcNumber, ccExpiration, cardBrandCode } = response;
367
+ const masked = MASKED.exec(maskedCcNumber);
368
+ if (!TOKEN.test(token) || !TOKEN.test(tokenKey) || !masked || maskedCcNumber.length < 12 || maskedCcNumber.length > 19 || !EXPIRY.test(ccExpiration) || !BRANDS.includes(cardBrandCode))
369
+ return null;
370
+ return { token, tokenKey, maskedCcNumber, ccExpiration, cardBrandCode, head: masked[1], tail: masked[2] };
371
+ }
372
+ function sbpsOutcome(status, bodyText, templateUrl, card) {
373
+ const facts = sbpsRequestFacts(templateUrl);
374
+ if (!facts)
375
+ return { outcome: "unknown", reason: "sbps_template_invalid" };
376
+ if (status !== 200)
377
+ return { outcome: "unknown", reason: "sbps_status" };
378
+ const tokenRes = readAnswer(bodyText, facts.callback);
379
+ if (!tokenRes)
380
+ return { outcome: "unknown", reason: "sbps_unreadable" };
381
+ if (tokenRes.result === "NG") {
382
+ const extra = Object.keys(tokenRes).some((key) => !["result", "errorCode", "tokenResponse"].includes(key));
383
+ const token2 = own(tokenRes, "tokenResponse") && tokenRes.tokenResponse !== null;
384
+ return !extra && !token2 && SBPS_REFUSAL_CODES.includes(tokenRes.errorCode) ? { outcome: "refused", code: tokenRes.errorCode } : { outcome: "unknown", reason: "sbps_unconfirmed" };
385
+ }
386
+ if (tokenRes.result !== "OK")
387
+ return { outcome: "unknown", reason: "sbps_unconfirmed" };
388
+ const fields = tokenFields(tokenRes);
389
+ if (!fields)
390
+ return { outcome: "unknown", reason: "sbps_shape" };
391
+ if (card) {
392
+ const number = String(card.number ?? "").replace(/[ -]/g, "");
393
+ const month = String(card.month ?? "").padStart(2, "0");
394
+ const year = String(card.year ?? "").length === 2 ? `20${card.year}` : String(card.year ?? "");
395
+ if (!number.startsWith(fields.head) || !number.endsWith(fields.tail) || fields.ccExpiration !== `${year}${month}` || number.length >= 12 && (fields.token.includes(number) || fields.tokenKey.includes(number))) {
396
+ return { outcome: "unknown", reason: "sbps_card_mismatch" };
397
+ }
398
+ }
399
+ const { token, tokenKey, maskedCcNumber, ccExpiration, cardBrandCode } = fields;
400
+ return { outcome: "token", sanitizedJson: JSON.stringify({ tokenRes: {
401
+ result: "OK",
402
+ tokenResponse: { token, tokenKey, maskedCcNumber, ccExpiration, cardBrandCode }
403
+ } }) };
404
+ }
405
+ function sanitizeSbpsAnswer(bodyText, templateUrl) {
406
+ const verdict = sbpsOutcome(200, bodyText, templateUrl, void 0);
407
+ if (verdict.outcome !== "token")
408
+ throw new Error("Invalid SB Payment Service token answer.");
409
+ return verdict.sanitizedJson;
410
+ }
411
+ function sbpsJsonp(callback, json) {
412
+ let parsed = null;
413
+ try {
414
+ parsed = JSON.parse(json);
415
+ }
416
+ catch {
417
+ }
418
+ if (typeof callback !== "string" || !/^com_sbps_system\.rm\['[0-9a-f-]{36}'\]\.cb$/.test(callback) || !plainObject(parsed) || JSON.stringify(parsed) !== json)
419
+ throw new Error("Refusing to answer: malformed SB Payment Service callback.");
420
+ return `${callback}(${json})`;
421
+ }
422
+ function sbpsRefusalCode(bodyText) {
423
+ const callback = typeof bodyText === "string" ? /^\s*(com_sbps_system\.rm\['[0-9a-f-]{36}'\]\.cb)\(/.exec(bodyText)?.[1] : void 0;
424
+ const tokenRes = callback ? readAnswer(bodyText, callback) : null;
425
+ return tokenRes && tokenRes.result === "NG" && SBPS_REFUSAL_CODES.includes(tokenRes.errorCode) ? tokenRes.errorCode : null;
426
+ }
427
+ export { SBPS_ANSWER_CONTENT_TYPE, SBPS_CAPABILITY, SBPS_MERCHANT_PINS, SBPS_OPERATION, SBPS_PSP, SBPS_PUBLIC_KEY, SBPS_REFUSAL_CODES, SBPS_REFUSAL_REASONS, SBPS_TOKEN_HOSTS, SBPS_TOKEN_PATH, SBPS_TOKEN_SCRIPTS, SBPS_TOKEN_SCRIPT_MAX_BYTES, SBPS_TOKEN_SCRIPT_PATH, SbpsError, isSbpsDeviceRequest, isSbpsRequest, isSbpsTokenRequest, sanitizeSbpsAnswer, sbpsJsonp, sbpsMerchantPin, sbpsMerchantRefusal, sbpsOutcome, sbpsRefusalCode, sbpsRequestFacts, sbpsTokenEnvironment, sbpsTokenScriptUrl, sbpsTokenScriptVerdict, sbpsTokenTemplate };