@agent-cards/sdk 0.23.8 → 0.23.10
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/CHANGELOG.md +42 -0
- package/PREFLIGHT.md +7 -5
- package/README.md +181 -92
- package/dist/aci.d.ts +7 -5
- package/dist/aci.generated.d.ts +2 -1
- package/dist/aci.generated.js +14 -4
- package/dist/aci.js +28 -7
- package/dist/amadeus-checkout.generated.js +2 -2
- package/dist/builtin-registry.generated.js +1 -1
- package/dist/card-fields.generated.js +163 -9
- package/dist/cdp.js +79 -34
- package/dist/client.d.ts +61 -31
- package/dist/client.js +99 -42
- package/dist/creditguard.generated.d.ts +1 -1
- package/dist/creditguard.generated.js +1 -3
- package/dist/cse-body.d.ts +7 -6
- package/dist/cse-body.js +8 -7
- package/dist/cybersource-secure-acceptance.generated.js +4 -3
- package/dist/datatrans.generated.js +8 -5
- package/dist/elavon-converge.d.ts +1 -1
- package/dist/elavon-converge.generated.js +7 -4
- package/dist/elavon-converge.js +4 -3
- package/dist/fiserv.d.ts +9 -5
- package/dist/fiserv.generated.d.ts +3 -3
- package/dist/fiserv.generated.js +50 -20
- package/dist/fiserv.js +10 -6
- package/dist/payrails.d.ts +78 -18
- package/dist/payrails.generated.d.ts +12 -2
- package/dist/payrails.generated.js +213 -18
- package/dist/payrails.js +165 -44
- package/dist/pre-claim.js +51 -29
- package/dist/preflight-capabilities.generated.d.ts +0 -106
- package/dist/preflight-capabilities.generated.js +0 -153
- package/dist/preflight-catalog.json +61 -181
- package/dist/preflight-schemas.json +6 -6
- package/dist/preflight.generated.d.ts +0 -106
- package/dist/preflight.generated.js +27 -15
- package/dist/preparation.d.ts +2 -0
- package/dist/preparation.js +4 -2
- package/dist/prepared-processor.d.ts +19 -5
- package/dist/prepared-processor.js +21 -5
- package/dist/primer.generated.d.ts +5 -1
- package/dist/primer.generated.js +200 -33
- package/dist/registry.d.ts +18 -12
- package/dist/registry.js +11 -8
- package/dist/shiji-sts.d.ts +24 -22
- package/dist/shiji-sts.generated.d.ts +1 -1
- package/dist/shiji-sts.generated.js +12 -8
- package/dist/shiji-sts.js +49 -46
- package/dist/stripe-checkout.generated.js +146 -49
- package/dist/verifone.generated.js +192 -17
- package/dist/verifone.js +1 -1
- package/dist/wspay.d.ts +17 -16
- package/dist/wspay.generated.d.ts +6 -1
- package/dist/wspay.generated.js +13 -3
- package/dist/wspay.js +9 -11
- package/examples/preflight/kernel-native/README.md +1 -1
- package/examples/preflight/kernel-native/inventory.json +1 -19
- package/package.json +3 -3
|
@@ -20,6 +20,22 @@ export function preparationMode(psp) {
|
|
|
20
20
|
export function preparationRequestMethod(psp) {
|
|
21
21
|
return psp === 'worldpay_eprotect' ? 'GET' : 'POST';
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Whether a prepared checkout's approval outlives the page's own request, as an unprepared token
|
|
25
|
+
* approval does (cdp.ts survivesAbandonment): the page's next request for the same purchase, from
|
|
26
|
+
* the card frame that sent the first, is answered from it, with no second prompt. The approval stays
|
|
27
|
+
* that frame's (cdp.ts sameFrame): another frame's request is refused, and the frame's removal ends it.
|
|
28
|
+
*
|
|
29
|
+
* Bambora only. Custom Checkout's card frame fails its tokenization request 5 seconds after
|
|
30
|
+
* sending it, and a Pay button that runs the tokenization again sends the same placeholder and
|
|
31
|
+
* fields (WooCommerce's Bambora plugin shows the timeout, clears its token fields and lets Pay be
|
|
32
|
+
* clicked again). The cardholder's device answers with a token that pays nothing until the
|
|
33
|
+
* merchant's server charges it. Every other prepared checkout still answers only the request it
|
|
34
|
+
* was bound to.
|
|
35
|
+
*/
|
|
36
|
+
export function preparedApprovalAnswersRetry(psp) {
|
|
37
|
+
return psp === 'bambora';
|
|
38
|
+
}
|
|
23
39
|
/**
|
|
24
40
|
* Shared endpoint processors cannot attest test/live mode from their URL or key prefix.
|
|
25
41
|
* Adyen can: test and live are separate hosts and key prefixes. Fiserv's are separate
|
|
@@ -356,11 +372,11 @@ function secureAcceptanceClaimRefusal(requestUrl, body, headers, merchantOrigin,
|
|
|
356
372
|
/**
|
|
357
373
|
* The reason a claim reports for a request its preparation does not match. An Amadeus preparation
|
|
358
374
|
* names what payment-core's Amadeus check refused on its own environment's card URL, as
|
|
359
|
-
* `amadeus_<reason>`:
|
|
360
|
-
*
|
|
361
|
-
* `
|
|
362
|
-
*
|
|
363
|
-
*
|
|
375
|
+
* `amadeus_<reason>`: every Data Collection Page store is `amadeus_dcp_field_config_unreviewed`
|
|
376
|
+
* until a merchant's field config is reviewed, an add that keeps the card on file is
|
|
377
|
+
* `amadeus_card_on_file`, and a Host header that does not name the approved page is
|
|
378
|
+
* `amadeus_header_binding`. Everything else, another processor's request or the other
|
|
379
|
+
* environment's host included, is `checkout_changed`.
|
|
364
380
|
*/
|
|
365
381
|
export function preparedRequestRefusal(psp, environment, requestUrl, body, headers, merchantOrigin, currency) {
|
|
366
382
|
// A Secure Acceptance token create on the prepared environment's host names what payment-core's request
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
declare var PRIMER_PCI_HOSTS: readonly string[];
|
|
2
2
|
declare var PRIMER_TOKENIZE_PATH: string;
|
|
3
|
+
declare var PRIMER_SESSION_CARD_ON_FILE_POLICIES: readonly string[];
|
|
4
|
+
declare var PRIMER_SESSION_CARD_ON_FILE: string;
|
|
3
5
|
declare var PRIMER_REFUSAL_REASONS: readonly string[];
|
|
4
6
|
declare var PRIMER_PROVABLE_NETWORKS: readonly string[];
|
|
5
7
|
declare function isPrimerTokenizeUrl(url: any, method?: string): boolean;
|
|
@@ -9,6 +11,8 @@ declare function primerKeepOnFileRequested(body: any): boolean | null;
|
|
|
9
11
|
declare function classifyPrimerRequest(url: any, method: any, body: any, headers: any): "card" | "unsupported" | "keep_on_file" | "unsupported_sdk" | null;
|
|
10
12
|
declare function primerTokenizeRefusal(request: any): "unsupported" | "keep_on_file" | "unsupported_sdk" | null;
|
|
11
13
|
declare function isPrimerTokenizeRequest(request: any): boolean;
|
|
14
|
+
declare function primerCardOnFileDisclosure(url: any, policy?: string): boolean;
|
|
15
|
+
declare function primerCardOnFileRefusal(url: any, options: any, policy?: string): "unsupported" | "keep_on_file" | null;
|
|
12
16
|
declare function primerRefusalMessage(reason: any): string;
|
|
13
17
|
declare function primerCardNetwork(number: any): "VISA" | "AMEX" | "MASTERCARD" | null;
|
|
14
18
|
declare function primerNetworkRefusal(body: any, card: any): "unsupported" | "network_mismatch" | null;
|
|
@@ -35,4 +39,4 @@ declare function primerFacts(status: any, body: any): {
|
|
|
35
39
|
outcome: string;
|
|
36
40
|
refusalCode: any;
|
|
37
41
|
};
|
|
38
|
-
export { PRIMER_PCI_HOSTS, PRIMER_PROVABLE_NETWORKS, PRIMER_REFUSAL_REASONS, PRIMER_TOKENIZE_PATH, classifyPrimerRequest, injectPrimerCard, isPrimerNewCardBody, isPrimerTokenizeRequest, isPrimerTokenizeUrl, primerBodyRefusal, primerCardNetwork, primerErrorId, primerFacts, primerKeepOnFileRequested, primerNetworkRefusal, primerRefusalMessage, primerResponseOutcome, primerTokenizeRefusal, sanitizePrimerResponse };
|
|
42
|
+
export { PRIMER_PCI_HOSTS, PRIMER_PROVABLE_NETWORKS, PRIMER_REFUSAL_REASONS, PRIMER_SESSION_CARD_ON_FILE, PRIMER_SESSION_CARD_ON_FILE_POLICIES, PRIMER_TOKENIZE_PATH, classifyPrimerRequest, injectPrimerCard, isPrimerNewCardBody, isPrimerTokenizeRequest, isPrimerTokenizeUrl, primerBodyRefusal, primerCardNetwork, primerCardOnFileDisclosure, primerCardOnFileRefusal, primerErrorId, primerFacts, primerKeepOnFileRequested, primerNetworkRefusal, primerRefusalMessage, primerResponseOutcome, primerTokenizeRefusal, sanitizePrimerResponse };
|
package/dist/primer.generated.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
-
// artifact-sha256:
|
|
3
|
+
// artifact-sha256: bbfd8946fd3963bf32fe66d5c17ba50dd69e141187c1d5896ee60c33b3aa0e41
|
|
4
4
|
// src/card-number-key.js
|
|
5
5
|
var CARD_NUMBER_KEY = /^(number|card_number|cardnumber|cardNum|card_num|pan)$/i;
|
|
6
6
|
// src/json-path.js
|
|
@@ -37,7 +37,8 @@ function roundTrips(parsed, body) {
|
|
|
37
37
|
// src/elavon-converge.js
|
|
38
38
|
var CONVERGE_PROCESS_URLS = /* @__PURE__ */ Object.freeze([
|
|
39
39
|
"https://api.convergepay.com/hosted-payments/service/payment/hpp/process",
|
|
40
|
-
"https://api.demo.convergepay.com/hosted-payments/service/payment/hpp/process"
|
|
40
|
+
"https://api.demo.convergepay.com/hosted-payments/service/payment/hpp/process",
|
|
41
|
+
"https://www.convergepay.com/hosted-payments/service/payment/hpp/process"
|
|
41
42
|
]);
|
|
42
43
|
var TOP_LEVEL_KEYS = /* @__PURE__ */ Object.freeze(["paymentType", "paymentData", "context", "captchaToken", "shippingSameAsBilling"]);
|
|
43
44
|
var WALLET_FIELDS = /* @__PURE__ */ Object.freeze([
|
|
@@ -78,6 +79,7 @@ var RECURRING_FIELDS = /* @__PURE__ */ Object.freeze([
|
|
|
78
79
|
"ssl_recurring_batch_count",
|
|
79
80
|
"ssl_installment_id"
|
|
80
81
|
]);
|
|
82
|
+
var ONE_TIME_RECURRING_FLAG = "false";
|
|
81
83
|
var CARD_SAVING_FLAGS = /* @__PURE__ */ Object.freeze(["ssl_get_token", "ssl_add_token"]);
|
|
82
84
|
var CREDENTIAL_FIELDS = /* @__PURE__ */ Object.freeze(["ssl_account_id", "ssl_merchant_id", "ssl_user_id", "ssl_pin", "ssl_vendor_id"]);
|
|
83
85
|
var AMOUNT_ADD_ON_FIELDS = /* @__PURE__ */ Object.freeze(["ssl_tip_amount", "ssl_cashback_amount"]);
|
|
@@ -87,6 +89,7 @@ var CVC = /^\d{3,4}$/;
|
|
|
87
89
|
var AMOUNT = /^(\d{1,13})(?:\.(\d{1,2}))?$/;
|
|
88
90
|
var isRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
89
91
|
var owns = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
92
|
+
var recurringRequested = (data) => RECURRING_FIELDS.some((key) => owns(data, key) && !(key === "recurring" && data[key] === ONE_TIME_RECURRING_FLAG));
|
|
90
93
|
var refuse = (code) => Object.freeze({ verdict: "refuse", code });
|
|
91
94
|
var CARD = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
92
95
|
var CONTINUE = /* @__PURE__ */ Object.freeze({ verdict: "continue" });
|
|
@@ -148,7 +151,7 @@ function convergeBodyVerdict(body) {
|
|
|
148
151
|
if (owns(data, "ssl_token") || CARD_SAVING_FLAGS.some((key) => owns(data, key) && !(typeof data[key] === "string" && /^n$/i.test(data[key])))) {
|
|
149
152
|
return refuse("converge_card_saving_refused");
|
|
150
153
|
}
|
|
151
|
-
if (
|
|
154
|
+
if (recurringRequested(data))
|
|
152
155
|
return refuse("converge_recurring_refused");
|
|
153
156
|
if (owns(data, "ssl_transaction_currency") && !(typeof data.ssl_transaction_currency === "string" && /^usd$/i.test(data.ssl_transaction_currency))) {
|
|
154
157
|
return refuse("converge_currency_refused");
|
|
@@ -177,11 +180,155 @@ function isConvergeProcessUrl(url) {
|
|
|
177
180
|
}
|
|
178
181
|
return parsed.href === url && CONVERGE_PROCESS_URLS.includes(parsed.href);
|
|
179
182
|
}
|
|
180
|
-
// src/
|
|
181
|
-
var
|
|
183
|
+
// src/gravy.js
|
|
184
|
+
var GRAVY_PSP = "gravy";
|
|
185
|
+
var BODY_LIMIT = 4096;
|
|
186
|
+
var CARD_KEYS = /* @__PURE__ */ Object.freeze(["method", "number", "expiration_date", "security_code"]);
|
|
187
|
+
var ROOT_KEYS = /* @__PURE__ */ Object.freeze(["payment_method", "postal_code"]);
|
|
188
|
+
var CARD_NUMBER2 = /^[0-9]{12,19}$/;
|
|
189
|
+
var EXPIRY = /^(?:0[1-9]|1[0-2])\/[0-9]{2}$/;
|
|
190
|
+
var SECURITY_CODE = /^[0-9]{3,4}$/;
|
|
191
|
+
var POSTAL_CODE = /^[A-Za-z0-9 -]{0,16}$/;
|
|
192
|
+
var PAN_LIKE = /(?:[0-9][ _.:-]*){12,}/;
|
|
193
|
+
var EXPIRY_OR_CODE = /^(?:expiration_?date|expiry(?:_?date)?|exp_?date|card_?expiry|security_?code|cvc|cvc2|cvv|cvv2|cvd|card_?cvv|card_?cvc|verification_?value)$/i;
|
|
182
194
|
var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
183
195
|
var owns2 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
184
|
-
|
|
196
|
+
function cardNamed(key) {
|
|
197
|
+
const name = key.slice(key.lastIndexOf(".") + 1);
|
|
198
|
+
return CARD_NUMBER_KEY.test(name) || /cardnumber$/i.test(name) || EXPIRY_OR_CODE.test(name);
|
|
199
|
+
}
|
|
200
|
+
function cardOutside(root) {
|
|
201
|
+
const pending = [];
|
|
202
|
+
for (const key of Object.keys(root)) {
|
|
203
|
+
const value = root[key];
|
|
204
|
+
if (key === "payment_method" && record(value)) {
|
|
205
|
+
for (const inner of Object.keys(value))
|
|
206
|
+
if (!CARD_KEYS.includes(inner))
|
|
207
|
+
pending.push([inner, value[inner]]);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
pending.push([key, value]);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
while (pending.length > 0) {
|
|
214
|
+
const [key, value] = pending.pop();
|
|
215
|
+
if (key !== null && cardNamed(key))
|
|
216
|
+
return true;
|
|
217
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
218
|
+
if (PAN_LIKE.test(String(value)))
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
else if (Array.isArray(value)) {
|
|
222
|
+
for (const item of value)
|
|
223
|
+
pending.push([null, item]);
|
|
224
|
+
}
|
|
225
|
+
else if (record(value)) {
|
|
226
|
+
for (const inner of Object.keys(value))
|
|
227
|
+
pending.push([inner, value[inner]]);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
function readBody(body) {
|
|
233
|
+
if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT)
|
|
234
|
+
return null;
|
|
235
|
+
let value;
|
|
236
|
+
try {
|
|
237
|
+
value = JSON.parse(body);
|
|
238
|
+
if (!record(value) || JSON.stringify(value) !== body)
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
return value;
|
|
245
|
+
}
|
|
246
|
+
var refuse2 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
247
|
+
var CARD2 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
248
|
+
function gravyBodyVerdict(body) {
|
|
249
|
+
const value = readBody(body);
|
|
250
|
+
if (value === null)
|
|
251
|
+
return refuse2("gravy_body_unsupported");
|
|
252
|
+
if (cardOutside(value))
|
|
253
|
+
return refuse2("gravy_card_outside_payment_method");
|
|
254
|
+
const card = value.payment_method;
|
|
255
|
+
if (!record(card) || card.method !== "card" || !CARD_KEYS.every((key) => owns2(card, key)))
|
|
256
|
+
return refuse2("gravy_new_card_missing");
|
|
257
|
+
if (!Object.keys(value).every((key) => ROOT_KEYS.includes(key)) || !Object.keys(card).every((key) => CARD_KEYS.includes(key)) || typeof card.number !== "string" || !CARD_NUMBER2.test(card.number) || typeof card.expiration_date !== "string" || !EXPIRY.test(card.expiration_date) || typeof card.security_code !== "string" || !SECURITY_CODE.test(card.security_code) || owns2(value, "postal_code") && (typeof value.postal_code !== "string" || !POSTAL_CODE.test(value.postal_code))) {
|
|
258
|
+
return refuse2("gravy_body_unsupported");
|
|
259
|
+
}
|
|
260
|
+
return CARD2;
|
|
261
|
+
}
|
|
262
|
+
// src/moneris.js
|
|
263
|
+
var MONERIS_PSP = "moneris";
|
|
264
|
+
var CARD_KEYS2 = /* @__PURE__ */ Object.freeze(["input_data", "input_exp", "input_cvd", "hpt_id", "hpt_ticket", "doTokenize"]);
|
|
265
|
+
var PARTIAL_KEYS = /* @__PURE__ */ Object.freeze([
|
|
266
|
+
["input_data", "hpt_id", "hpt_ticket", "doTokenize"],
|
|
267
|
+
["input_data", "input_exp", "hpt_id", "hpt_ticket", "doTokenize"],
|
|
268
|
+
["input_data", "input_cvd", "hpt_id", "hpt_ticket", "doTokenize"]
|
|
269
|
+
]);
|
|
270
|
+
var CARD_NUMBER3 = /^\d{12,19}$/;
|
|
271
|
+
var GROUPED_DIGITS = /^\d+(?: \d+)*$/;
|
|
272
|
+
var EXPIRY_MMYY2 = /^(0[1-9]|1[0-2])\d{2}$/;
|
|
273
|
+
var CVD = /^\d{3,4}$/;
|
|
274
|
+
var PROFILE_ID = /^ht[A-Za-z0-9]{1,64}$/;
|
|
275
|
+
var TICKET = /^tt[A-Za-z0-9]{1,128}$/;
|
|
276
|
+
var refuse3 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
277
|
+
var CARD3 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
278
|
+
function readPairs(body) {
|
|
279
|
+
if (typeof body !== "string" || body === "" || body.length > 4096)
|
|
280
|
+
return null;
|
|
281
|
+
const pairs = [];
|
|
282
|
+
for (const chunk of body.split("&")) {
|
|
283
|
+
const eq = chunk.indexOf("=");
|
|
284
|
+
if (eq <= 0)
|
|
285
|
+
return null;
|
|
286
|
+
pairs.push([chunk.slice(0, eq), chunk.slice(eq + 1)]);
|
|
287
|
+
}
|
|
288
|
+
return pairs;
|
|
289
|
+
}
|
|
290
|
+
var hasKeys = (pairs, keys) => pairs.length === keys.length && pairs.every(([key], at) => key === keys[at]);
|
|
291
|
+
function monerisBodyVerdict(body) {
|
|
292
|
+
const pairs = readPairs(body);
|
|
293
|
+
if (pairs === null)
|
|
294
|
+
return refuse3("moneris_body_malformed");
|
|
295
|
+
if (PARTIAL_KEYS.some((keys) => hasKeys(pairs, keys)))
|
|
296
|
+
return refuse3("moneris_partial_card");
|
|
297
|
+
if (!hasKeys(pairs, CARD_KEYS2))
|
|
298
|
+
return refuse3("moneris_body_malformed");
|
|
299
|
+
const [[, number], [, expiry], [, cvd], [, profile], [, ticket], [, tokenize]] = pairs;
|
|
300
|
+
if (!GROUPED_DIGITS.test(number) || !CARD_NUMBER3.test(number.replace(/ /g, "")) || !EXPIRY_MMYY2.test(expiry) || !CVD.test(cvd) || !PROFILE_ID.test(profile) || !TICKET.test(ticket) || tokenize !== "true")
|
|
301
|
+
return refuse3("moneris_body_malformed");
|
|
302
|
+
return CARD3;
|
|
303
|
+
}
|
|
304
|
+
var MONERIS_TOKENIZE_HOSTS = /* @__PURE__ */ Object.freeze({
|
|
305
|
+
production: Object.freeze(["www3.moneris.com", "gateway.moneris.com"]),
|
|
306
|
+
sandbox: Object.freeze(["esqa.moneris.com"])
|
|
307
|
+
});
|
|
308
|
+
var MONERIS_TOKENIZE_PATH = "/HPPtoken/request.php";
|
|
309
|
+
function monerisTokenizeEnvironment(url) {
|
|
310
|
+
if (typeof url !== "string")
|
|
311
|
+
return null;
|
|
312
|
+
for (const environment of
|
|
313
|
+
/** @type {const} */
|
|
314
|
+
["production", "sandbox"]) {
|
|
315
|
+
if (MONERIS_TOKENIZE_HOSTS[environment].some((host) => url === `https://${host}${MONERIS_TOKENIZE_PATH}`))
|
|
316
|
+
return environment;
|
|
317
|
+
}
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
function isMonerisTokenizeUrl(url) {
|
|
321
|
+
return monerisTokenizeEnvironment(url) !== null;
|
|
322
|
+
}
|
|
323
|
+
var pin = (fields) => Object.freeze({ ...fields, origins: Object.freeze([...fields.origins]), profileIds: Object.freeze([...fields.profileIds]) });
|
|
324
|
+
var MONERIS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
|
|
325
|
+
pin({ id: "jack_org_raisin", merchant: "Jack.org", origins: ["https://jack.akaraisin.com"], profileIds: ["htL7TR9H7ICYN2B"] })
|
|
326
|
+
]);
|
|
327
|
+
// src/amadeus-body.js
|
|
328
|
+
var AMADEUS_CARD_MOPID = /^creditcard[0-9]*$/;
|
|
329
|
+
var record2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
330
|
+
var owns3 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
331
|
+
var holdsPan = (mopdata) => record2(mopdata) && owns3(mopdata, "pan") && mopdata.pan !== null && mopdata.pan !== "";
|
|
185
332
|
function amadeusBodyKind(body) {
|
|
186
333
|
let value;
|
|
187
334
|
try {
|
|
@@ -190,18 +337,25 @@ function amadeusBodyKind(body) {
|
|
|
190
337
|
catch {
|
|
191
338
|
return "card";
|
|
192
339
|
}
|
|
193
|
-
if (!
|
|
340
|
+
if (!record2(value))
|
|
194
341
|
return "card";
|
|
195
342
|
if (value.action === "add") {
|
|
196
343
|
const entries = Array.isArray(value.data) ? value.data : [value.data];
|
|
197
|
-
return entries.some((entry) =>
|
|
344
|
+
return entries.some((entry) => record2(entry) && typeof entry.mopid === "string" && AMADEUS_CARD_MOPID.test(entry.mopid) && holdsPan(entry.mopdata)) ? "card" : "other";
|
|
198
345
|
}
|
|
199
346
|
if (value.action === "store")
|
|
200
|
-
return
|
|
347
|
+
return record2(value.data) && holdsPan(value.data.mopdata) ? "card" : "other";
|
|
201
348
|
return "other";
|
|
202
349
|
}
|
|
203
350
|
// src/card-fields.js
|
|
204
351
|
function processorGate(recognizer, url, body) {
|
|
352
|
+
if (recognizer && recognizer.psp === GRAVY_PSP)
|
|
353
|
+
return gravyBodyVerdict(body);
|
|
354
|
+
if (recognizer && recognizer.psp === MONERIS_PSP) {
|
|
355
|
+
if (url !== void 0 && !isMonerisTokenizeUrl(url))
|
|
356
|
+
return { verdict: "refuse", code: "moneris_url_refused" };
|
|
357
|
+
return monerisBodyVerdict(body);
|
|
358
|
+
}
|
|
205
359
|
if (!recognizer || recognizer.psp !== "elavon_converge")
|
|
206
360
|
return null;
|
|
207
361
|
if (url !== void 0 && !isConvergeProcessUrl(url))
|
|
@@ -305,6 +459,9 @@ function namesCardNumber(root, declared) {
|
|
|
305
459
|
// src/primer.js
|
|
306
460
|
var PRIMER_PCI_HOSTS = /* @__PURE__ */ Object.freeze(["sdk.api.primer.io", "sdk.api.sandbox.primer.io"]);
|
|
307
461
|
var PRIMER_TOKENIZE_PATH = "/payment-instruments";
|
|
462
|
+
var PRIMER_SESSION_CARD_ON_FILE_POLICIES = /* @__PURE__ */ Object.freeze(["refuse", "disclose"]);
|
|
463
|
+
var PRIMER_SESSION_CARD_ON_FILE = "disclose";
|
|
464
|
+
var LIVE_PCI_HOST = "sdk.api.primer.io";
|
|
308
465
|
var PRIMER_REFUSAL_REASONS = /* @__PURE__ */ Object.freeze(["unsupported", "keep_on_file", "unsupported_sdk", "network_mismatch"]);
|
|
309
466
|
var PRIMER_PROVABLE_NETWORKS = /* @__PURE__ */ Object.freeze(["VISA", "MASTERCARD", "AMEX"]);
|
|
310
467
|
var UNSUPPORTED_REFUSAL = "Refusing to pay: unsupported Primer card request.";
|
|
@@ -312,13 +469,13 @@ var KEEP_ON_FILE_REFUSAL = "Refusing to pay: this Primer payment would keep the
|
|
|
312
469
|
var UNSUPPORTED_SDK_REFUSAL = "Refusing to pay: this checkout uses a Primer SDK Agentcard does not support yet. Nothing was sent.";
|
|
313
470
|
var NETWORK_MISMATCH_REFUSAL = "Refusing to pay: this Primer payment names a card network that is not your card's. Nothing was sent.";
|
|
314
471
|
var FILL_REFUSAL = "Could not fill the card fields for Primer. Nothing was sent.";
|
|
315
|
-
var
|
|
472
|
+
var BODY_LIMIT2 = 16384;
|
|
316
473
|
var RESPONSE_LIMIT = 32768;
|
|
317
|
-
var
|
|
474
|
+
var ROOT_KEYS2 = ["paymentInstrument", "userDescription", "tokenType", "paymentFlow"];
|
|
318
475
|
var INSTRUMENT_KEYS = ["number", "cvv", "expirationMonth", "expirationYear", "cardholderName", "preferredNetwork"];
|
|
319
476
|
var REQUIRED_INSTRUMENT_KEYS = ["number", "cvv", "expirationMonth", "expirationYear"];
|
|
320
|
-
var
|
|
321
|
-
var
|
|
477
|
+
var record3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
478
|
+
var owns4 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
322
479
|
var exact = (pattern, value) => typeof value === "string" && pattern.exec(value)?.[0] === value;
|
|
323
480
|
var text = (value, max) => typeof value === "string" && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value);
|
|
324
481
|
var digits = (value, min, max) => typeof value === "string" && value.length >= min && value.length <= max && !/[^0-9]/.test(value);
|
|
@@ -340,7 +497,7 @@ function isPrimerTokenizeUrl(url, method = "POST") {
|
|
|
340
497
|
return parsed.href === url && parsed.protocol === "https:" && !parsed.port && !parsed.username && !parsed.password && !parsed.search && !parsed.hash && PRIMER_PCI_HOSTS.includes(parsed.hostname) && parsed.pathname === PRIMER_TOKENIZE_PATH;
|
|
341
498
|
}
|
|
342
499
|
function judgeBody(body) {
|
|
343
|
-
if (typeof body !== "string" || body === "" || body.length >
|
|
500
|
+
if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT2)
|
|
344
501
|
return null;
|
|
345
502
|
let value;
|
|
346
503
|
try {
|
|
@@ -349,18 +506,18 @@ function judgeBody(body) {
|
|
|
349
506
|
catch {
|
|
350
507
|
return null;
|
|
351
508
|
}
|
|
352
|
-
if (!
|
|
509
|
+
if (!record3(value) || !roundTrips(value, body))
|
|
353
510
|
return null;
|
|
354
|
-
if (!Object.keys(value).every((key) =>
|
|
511
|
+
if (!Object.keys(value).every((key) => ROOT_KEYS2.includes(key)) || !owns4(value, "paymentInstrument"))
|
|
355
512
|
return null;
|
|
356
513
|
const instrument = value.paymentInstrument;
|
|
357
|
-
if (!
|
|
514
|
+
if (!record3(instrument) || !Object.keys(instrument).every((key) => INSTRUMENT_KEYS.includes(key)) || !REQUIRED_INSTRUMENT_KEYS.every((key) => owns4(instrument, key)))
|
|
358
515
|
return null;
|
|
359
516
|
if (!digits(instrument.number, 12, 19) || !digits(instrument.cvv, 3, 4) || !exact(MONTH, instrument.expirationMonth) || !exact(YEAR, instrument.expirationYear))
|
|
360
517
|
return null;
|
|
361
|
-
if (
|
|
518
|
+
if (owns4(instrument, "cardholderName") && instrument.cardholderName !== null && !plain(instrument.cardholderName, 256) || owns4(instrument, "preferredNetwork") && instrument.preferredNetwork !== null && !exact(NETWORK, instrument.preferredNetwork) || owns4(value, "userDescription") && value.userDescription !== null && !plain(value.userDescription, 256) || owns4(value, "tokenType") && value.tokenType !== "MULTI_USE" || owns4(value, "paymentFlow") && value.paymentFlow !== "VAULT")
|
|
362
519
|
return null;
|
|
363
|
-
return { value, keepOnFile:
|
|
520
|
+
return { value, keepOnFile: owns4(value, "tokenType") || owns4(value, "paymentFlow") };
|
|
364
521
|
}
|
|
365
522
|
function primerBodyRefusal(body) {
|
|
366
523
|
const judged = judgeBody(body);
|
|
@@ -382,7 +539,7 @@ var CLIENT_TOKEN = /^[A-Za-z0-9._~+/=-]{16,4096}$/;
|
|
|
382
539
|
var REQUEST_ID = /^[A-Za-z0-9-]{1,64}$/;
|
|
383
540
|
var API_VERSION = /^2\.[0-9]{1,2}$/;
|
|
384
541
|
function readHeaders(headers) {
|
|
385
|
-
if (!
|
|
542
|
+
if (!record3(headers))
|
|
386
543
|
return null;
|
|
387
544
|
const folded = /* @__PURE__ */ new Map();
|
|
388
545
|
for (const [name, value] of Object.entries(headers)) {
|
|
@@ -429,13 +586,23 @@ function onTokenizePath(url) {
|
|
|
429
586
|
}
|
|
430
587
|
}
|
|
431
588
|
function primerTokenizeRefusal(request) {
|
|
432
|
-
if (!
|
|
589
|
+
if (!record3(request) || request.method !== "POST" || !isPrimerTokenizeUrl(request.url))
|
|
433
590
|
return "unsupported";
|
|
434
591
|
return headersRefusal(request.headers) ?? primerBodyRefusal(request.body);
|
|
435
592
|
}
|
|
436
593
|
function isPrimerTokenizeRequest(request) {
|
|
437
594
|
return primerTokenizeRefusal(request) === null;
|
|
438
595
|
}
|
|
596
|
+
function primerCardOnFileDisclosure(url, policy = PRIMER_SESSION_CARD_ON_FILE) {
|
|
597
|
+
return policy === "disclose" && isPrimerTokenizeUrl(url) && new URL(url).hostname === LIVE_PCI_HOST;
|
|
598
|
+
}
|
|
599
|
+
function primerCardOnFileRefusal(url, options, policy = PRIMER_SESSION_CARD_ON_FILE) {
|
|
600
|
+
if (!isPrimerTokenizeUrl(url))
|
|
601
|
+
return "unsupported";
|
|
602
|
+
if (new URL(url).hostname !== LIVE_PCI_HOST)
|
|
603
|
+
return null;
|
|
604
|
+
return primerCardOnFileDisclosure(url, policy) && record3(options) && options.keepOnFile === true ? null : "keep_on_file";
|
|
605
|
+
}
|
|
439
606
|
function primerRefusalMessage(reason) {
|
|
440
607
|
if (reason === "keep_on_file")
|
|
441
608
|
return KEEP_ON_FILE_REFUSAL;
|
|
@@ -518,28 +685,28 @@ function readAnswer(body) {
|
|
|
518
685
|
return null;
|
|
519
686
|
try {
|
|
520
687
|
const value = JSON.parse(body);
|
|
521
|
-
return
|
|
688
|
+
return record3(value) ? value : null;
|
|
522
689
|
}
|
|
523
690
|
catch {
|
|
524
691
|
return null;
|
|
525
692
|
}
|
|
526
693
|
}
|
|
527
694
|
var tokenValue = (value) => exact(TOKEN, value) && !panLike(value);
|
|
528
|
-
var optional = (value, key, check) => !
|
|
695
|
+
var optional = (value, key, check) => !owns4(value, key) || value[key] === null || check(value[key]);
|
|
529
696
|
function cardDetails(data) {
|
|
530
|
-
if (!
|
|
697
|
+
if (!record3(data) || !exact(/^[0-9]{4}$/, data.last4Digits))
|
|
531
698
|
return null;
|
|
532
|
-
if (!optional(data, "first6Digits", (v) => exact(/^[0-9]{6}$/, v)) || !optional(data, "expirationMonth", (v) => exact(RESPONSE_MONTH, v)) || !optional(data, "expirationYear", (v) => exact(RESPONSE_YEAR, v)) || !optional(data, "cardholderName", (v) => plain(v, 256)) || !optional(data, "network", (v) => exact(NETWORK_NAME, v)) || !optional(data, "isNetworkTokenized", (v) => typeof v === "boolean") || !optional(data, "binData",
|
|
699
|
+
if (!optional(data, "first6Digits", (v) => exact(/^[0-9]{6}$/, v)) || !optional(data, "expirationMonth", (v) => exact(RESPONSE_MONTH, v)) || !optional(data, "expirationYear", (v) => exact(RESPONSE_YEAR, v)) || !optional(data, "cardholderName", (v) => plain(v, 256)) || !optional(data, "network", (v) => exact(NETWORK_NAME, v)) || !optional(data, "isNetworkTokenized", (v) => typeof v === "boolean") || !optional(data, "binData", record3))
|
|
533
700
|
return null;
|
|
534
701
|
const safe = { last4Digits: data.last4Digits };
|
|
535
702
|
for (const key of ["first6Digits", "expirationMonth", "expirationYear", "cardholderName", "network", "isNetworkTokenized"]) {
|
|
536
|
-
if (
|
|
703
|
+
if (owns4(data, key) && data[key] !== null)
|
|
537
704
|
safe[key] = data[key];
|
|
538
705
|
}
|
|
539
|
-
if (
|
|
706
|
+
if (record3(data.binData)) {
|
|
540
707
|
const bin = {};
|
|
541
708
|
for (const key of BIN_KEYS) {
|
|
542
|
-
if (!
|
|
709
|
+
if (!owns4(data.binData, key) || data.binData[key] === null)
|
|
543
710
|
continue;
|
|
544
711
|
if (!plain(data.binData[key], 128))
|
|
545
712
|
return null;
|
|
@@ -552,11 +719,11 @@ function cardDetails(data) {
|
|
|
552
719
|
function threeDsSummary(value) {
|
|
553
720
|
if (value === void 0 || value === null)
|
|
554
721
|
return null;
|
|
555
|
-
if (!
|
|
722
|
+
if (!record3(value) || !exact(CODE, value.responseCode) || !optional(value, "reasonCode", (v) => exact(CODE, v)) || !optional(value, "reasonText", (v) => plain(v, 256)) || !optional(value, "protocolVersion", (v) => v === "" || exact(PROTOCOL, v)) || !optional(value, "challengeIssued", (v) => typeof v === "boolean"))
|
|
556
723
|
return void 0;
|
|
557
724
|
const safe = { responseCode: value.responseCode };
|
|
558
725
|
for (const key of ["reasonCode", "reasonText", "protocolVersion", "challengeIssued"]) {
|
|
559
|
-
if (
|
|
726
|
+
if (owns4(value, key) && value[key] !== null)
|
|
560
727
|
safe[key] = value[key];
|
|
561
728
|
}
|
|
562
729
|
return safe;
|
|
@@ -569,7 +736,7 @@ function rebuildToken(value) {
|
|
|
569
736
|
if (!details || threeDs === void 0)
|
|
570
737
|
return null;
|
|
571
738
|
const safe = { token: value.token };
|
|
572
|
-
if (
|
|
739
|
+
if (owns4(value, "analyticsId") && value.analyticsId !== null)
|
|
573
740
|
safe.analyticsId = value.analyticsId;
|
|
574
741
|
safe.tokenType = "SINGLE_USE";
|
|
575
742
|
safe.paymentInstrumentType = "PAYMENT_CARD";
|
|
@@ -580,7 +747,7 @@ function rebuildToken(value) {
|
|
|
580
747
|
}
|
|
581
748
|
function primerErrorId(body) {
|
|
582
749
|
const value = readAnswer(body);
|
|
583
|
-
const id = value &&
|
|
750
|
+
const id = value && record3(value.error) ? value.error.errorId : null;
|
|
584
751
|
return exact(ERROR_ID, id) && !panLike(id) ? id : null;
|
|
585
752
|
}
|
|
586
753
|
function matchesCard(details, card) {
|
|
@@ -621,4 +788,4 @@ function primerFacts(status, body) {
|
|
|
621
788
|
const verdict = primerResponseOutcome(status, body);
|
|
622
789
|
return { outcome: verdict.outcome, refusalCode: verdict.outcome === "refused" ? verdict.code : null };
|
|
623
790
|
}
|
|
624
|
-
export { PRIMER_PCI_HOSTS, PRIMER_PROVABLE_NETWORKS, PRIMER_REFUSAL_REASONS, PRIMER_TOKENIZE_PATH, classifyPrimerRequest, injectPrimerCard, isPrimerNewCardBody, isPrimerTokenizeRequest, isPrimerTokenizeUrl, primerBodyRefusal, primerCardNetwork, primerErrorId, primerFacts, primerKeepOnFileRequested, primerNetworkRefusal, primerRefusalMessage, primerResponseOutcome, primerTokenizeRefusal, sanitizePrimerResponse };
|
|
791
|
+
export { PRIMER_PCI_HOSTS, PRIMER_PROVABLE_NETWORKS, PRIMER_REFUSAL_REASONS, PRIMER_SESSION_CARD_ON_FILE, PRIMER_SESSION_CARD_ON_FILE_POLICIES, PRIMER_TOKENIZE_PATH, classifyPrimerRequest, injectPrimerCard, isPrimerNewCardBody, isPrimerTokenizeRequest, isPrimerTokenizeUrl, primerBodyRefusal, primerCardNetwork, primerCardOnFileDisclosure, primerCardOnFileRefusal, primerErrorId, primerFacts, primerKeepOnFileRequested, primerNetworkRefusal, primerRefusalMessage, primerResponseOutcome, primerTokenizeRefusal, sanitizePrimerResponse };
|
package/dist/registry.d.ts
CHANGED
|
@@ -118,7 +118,9 @@ export interface Recognizer {
|
|
|
118
118
|
* names `authorize_net_dpm_form`, Pelecard names `pelecard_transaction`,
|
|
119
119
|
* Primer names `primer_payment_instruments`, Payrails names
|
|
120
120
|
* `payrails_device_encrypt`, Elavon Converge names `converge_hpp_process`,
|
|
121
|
-
* WSPay names `wspay_confirm
|
|
121
|
+
* WSPay names `wspay_confirm`, Shiji STS names `shiji_sts_pinned_key`, Gravy
|
|
122
|
+
* names `gravy_session_fields_put` and Moneris Hosted Tokenization names
|
|
123
|
+
* `moneris_hosted_tokenization`.
|
|
122
124
|
*/
|
|
123
125
|
capability?: string;
|
|
124
126
|
/**
|
|
@@ -165,13 +167,15 @@ export interface Recognizer {
|
|
|
165
167
|
/**
|
|
166
168
|
* The card request's shape when it is not a POST body. Worldpay eProtect
|
|
167
169
|
* declares `{ method: 'GET', cardIn: 'query', response: 'jsonp' }`: its card
|
|
168
|
-
* rides in the query of one JSONP <script> GET.
|
|
170
|
+
* rides in the query of one JSONP <script> GET. Gravy declares
|
|
171
|
+
* `{ method: 'PUT', cardIn: 'body', response: 'no_content' }`: its card rides in
|
|
172
|
+
* the JSON body of a PUT, as a POST's does. Absent means a POST. Carried
|
|
169
173
|
* verbatim by syncRegistry; isCardRequest reads `method` through
|
|
170
|
-
* recognizerRequestMethod,
|
|
171
|
-
* query this build can check.
|
|
174
|
+
* recognizerRequestMethod, pauses a PUT as it pauses a POST, and pauses a GET
|
|
175
|
+
* only for a processor whose exact query this build can check.
|
|
172
176
|
*/
|
|
173
177
|
request?: {
|
|
174
|
-
method: 'GET' | 'POST';
|
|
178
|
+
method: 'GET' | 'POST' | 'PUT';
|
|
175
179
|
cardIn?: string;
|
|
176
180
|
response?: string;
|
|
177
181
|
};
|
|
@@ -212,15 +216,17 @@ export declare function urlsOnProcessorHosts(url: string, rec: Recognizer, opts?
|
|
|
212
216
|
export declare function findRecognizer(url: string, registry?: Recognizer[], opts?: RecognizerOptions): Recognizer | null;
|
|
213
217
|
/**
|
|
214
218
|
* The HTTP method of a recognizer's card request, the rule payment-core's
|
|
215
|
-
* recognizerRequestMethod applies: GET only when the entry declares
|
|
216
|
-
* `request.method
|
|
217
|
-
* value that rode in over the wire as anything else never widens a
|
|
218
|
-
* recognizer.
|
|
219
|
+
* recognizerRequestMethod applies: GET or PUT only when the entry declares that
|
|
220
|
+
* `request.method` (Worldpay eProtect's GET, Gravy's PUT), POST for everything
|
|
221
|
+
* else, so a value that rode in over the wire as anything else never widens a
|
|
222
|
+
* POST recognizer.
|
|
219
223
|
*/
|
|
220
|
-
export declare function recognizerRequestMethod(rec: Pick<Recognizer, 'request'>): 'GET' | 'POST';
|
|
224
|
+
export declare function recognizerRequestMethod(rec: Pick<Recognizer, 'request'>): 'GET' | 'POST' | 'PUT';
|
|
221
225
|
/**
|
|
222
|
-
* Whether `method` is this recognizer's card request method for `url`. A
|
|
223
|
-
* carries the card in its
|
|
226
|
+
* Whether `method` is this recognizer's card request method for `url`. A PUT
|
|
227
|
+
* recognizer (Gravy) carries the card in its body as a POST one does, so its PUT
|
|
228
|
+
* pauses like a POST, and neither pauses in the other's method. A GET carries
|
|
229
|
+
* the card in its URL, so a GET recognizer is a card request only for a
|
|
224
230
|
* processor whose exact query this build checks (Worldpay eProtect, through
|
|
225
231
|
* payment-core's grammar); any other GET recognizer is nothing this build can
|
|
226
232
|
* complete and is never paused.
|
package/dist/registry.js
CHANGED
|
@@ -128,17 +128,20 @@ export function findRecognizer(url, registry = BUILTIN_REGISTRY, opts = {}) {
|
|
|
128
128
|
}
|
|
129
129
|
/**
|
|
130
130
|
* The HTTP method of a recognizer's card request, the rule payment-core's
|
|
131
|
-
* recognizerRequestMethod applies: GET only when the entry declares
|
|
132
|
-
* `request.method
|
|
133
|
-
* value that rode in over the wire as anything else never widens a
|
|
134
|
-
* recognizer.
|
|
131
|
+
* recognizerRequestMethod applies: GET or PUT only when the entry declares that
|
|
132
|
+
* `request.method` (Worldpay eProtect's GET, Gravy's PUT), POST for everything
|
|
133
|
+
* else, so a value that rode in over the wire as anything else never widens a
|
|
134
|
+
* POST recognizer.
|
|
135
135
|
*/
|
|
136
136
|
export function recognizerRequestMethod(rec) {
|
|
137
|
-
|
|
137
|
+
const method = rec?.request?.method;
|
|
138
|
+
return method === 'GET' || method === 'PUT' ? method : 'POST';
|
|
138
139
|
}
|
|
139
140
|
/**
|
|
140
|
-
* Whether `method` is this recognizer's card request method for `url`. A
|
|
141
|
-
* carries the card in its
|
|
141
|
+
* Whether `method` is this recognizer's card request method for `url`. A PUT
|
|
142
|
+
* recognizer (Gravy) carries the card in its body as a POST one does, so its PUT
|
|
143
|
+
* pauses like a POST, and neither pauses in the other's method. A GET carries
|
|
144
|
+
* the card in its URL, so a GET recognizer is a card request only for a
|
|
142
145
|
* processor whose exact query this build checks (Worldpay eProtect, through
|
|
143
146
|
* payment-core's grammar); any other GET recognizer is nothing this build can
|
|
144
147
|
* complete and is never paused.
|
|
@@ -147,7 +150,7 @@ export function isRecognizerRequest(rec, url, method) {
|
|
|
147
150
|
const expected = recognizerRequestMethod(rec);
|
|
148
151
|
if (typeof method !== 'string' || method.toUpperCase() !== expected)
|
|
149
152
|
return false;
|
|
150
|
-
return expected
|
|
153
|
+
return expected !== 'GET' || (rec.psp === EPROTECT_PSP && isEprotectTokenRequest(url, 'GET'));
|
|
151
154
|
}
|
|
152
155
|
/**
|
|
153
156
|
* True when this processor cannot be taken over on an ordinary authorization:
|