@agent-cards/sdk 0.23.15 → 0.23.16
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 +2 -0
- package/LICENSE +1 -1
- package/dist/card-fields.generated.js +153 -16
- package/dist/client.d.ts +1 -0
- package/dist/client.js +15 -0
- package/dist/primer.generated.js +162 -25
- package/dist/stripe-checkout.generated.js +47 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
- Pause Redsys's hosted payment page (the TPV Virtual redirect most Spanish banks give their merchants) for the cardholder's device. The sync now also asks for `capabilities=redsys_hosted_payment_page`, and the API serves Redsys's recognizer only for a company Agentcard turned Redsys on for. The SDK pauses the merchant's signed entry form to `sis.redsys.es/sis/realizarPago` (or `/sis/realizarPago/utf-8`), which carries no card, as a `hosted_form` authorization, and answers the paused navigation with the `submitted_on_device` page once the cardholder's device has paid it. Every other post your browser makes to Redsys, its card page's own post, its older page's 3-D Secure card lookup, InSite's card post and any post to Redsys's test platform, carries the card your agent typed or opens a page that asks for one: the SDK fails it and reads `unsupported` with reason `processor_card_page`. Your events and error messages name Redsys's session id, the `;jsessionid=` part of its paths, as `{session}`. Matching API and Vault releases are required. Redsys stays off for every company Agentcard has not turned it on for.
|
|
16
16
|
|
|
17
|
+
- Keep a PayMongo checkout's session id out of your events and error messages. PayMongo's hosted checkout sends the card to its own page URL, `https://checkout.paymongo.com/<session>`, with no auth header and no cookie, so the session id is the only credential that request needs: anyone who holds it can open the checkout, its amount and its merchant, and pay it. `redactUrl`, every event and every error message now show it as `{session}` on that host, as in `https://checkout.paymongo.com/{session}`. The SDK does not pause a PayMongo payment yet. No API or Vault release is needed.
|
|
18
|
+
|
|
17
19
|
- Pay a Paddle checkout whose card method names TokenEx, from Agentcard's own account only for now (dark launch): Agentcard turns it on for another company once it has confirmed that a card encrypted for one Paddle checkout cannot pay another. On such a checkout, Paddle's frame RSA-encrypts the card number and the CVV in the page itself, each on its own, under the account's TokenEx key, and posts them to `https://checkout-service.paddle.com/<checkout>/pay` (`https://sandbox-checkout-service.paddle.com/<checkout>/pay` in test) with the placeholder's first six and last four digits, expiry and brand. The SDK now pauses that `/pay`, and once the cardholder approves, the cardholder's device encrypts the real card under the key the SDK pins for that checkout's environment and the SDK writes the two ciphertexts and those five facts over the placeholder's, changing nothing else in the request. The full card number never reaches the agent's page. The sync now also asks for `capabilities=paddle_tokenex_pay`. The ciphertext goes only into the `/pay` the approval was created from, byte for byte, because the account's TokenEx key would decrypt it in any later `/pay` of that seller. A `/pay` that names no TokenEx card (PayPal, a wallet, a saved card) carries none of the cardholder's cards and continues untouched, and the SDK refuses a `/pay` that is not the frame's shape with `paddle_tokenex_pay_unsupported` before anything is sent. No live payment has gone through this route yet. A payment also needs the matching API and Vault releases.
|
|
18
20
|
- Pay a Lyra payment form (PayZen, Systempay or Sogecommerce) for a company Agentcard turned Lyra on for (dark launch): the SDK declares `lyra_krypton_payment`, pauses the form's card payment and hands the form Lyra's answer with the payment id its 3-D Secure step needs.
|
|
19
21
|
- Keep a Lyra payment's session id out of your events and error messages. Lyra's Krypton payment form (PayZen, Systempay, Sogecommerce, Lyra Collect) sends its payment to `/api-payment/V4/Charge/Public/ProcessPayment/2;jsessionid=<id>` on the brand's API host, and that id, with the form token the page holds, continues the payment on Lyra's side. `redactUrl`, every event and every error message show it as `{session}`, as in `https://api.systempay.fr/api-payment/V4/Charge/Public/ProcessPayment/2;jsessionid={session}`. Lyra stays dark for every company Agentcard has not turned it on for, and no API or Vault release is needed.
|
package/LICENSE
CHANGED
|
@@ -35,4 +35,4 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
35
35
|
IMPLIED. IN NO EVENT SHALL AGENTCARD CORPORATION BE LIABLE FOR ANY CLAIM,
|
|
36
36
|
DAMAGES OR OTHER LIABILITY ARISING FROM THE SOFTWARE OR ITS USE.
|
|
37
37
|
|
|
38
|
-
Questions: legal@agentcard.
|
|
38
|
+
Questions: legal@agentcard.com
|
|
@@ -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: e4858832550c43f6e3cbef0c64facb820d7ae9b5118a2c8c4fcadb429c94b2b0
|
|
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
|
|
@@ -357,8 +357,8 @@ var CONTROL = /[\u0000-\u001f\u007f]/;
|
|
|
357
357
|
var cardLikeName = (name) => (name.match(/[0-9]/g) ?? []).length >= 12;
|
|
358
358
|
var record3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
359
359
|
var sameKeys = (value, keys) => {
|
|
360
|
-
const
|
|
361
|
-
return
|
|
360
|
+
const own2 = Object.keys(value);
|
|
361
|
+
return own2.length === keys.length && own2.every((key, at) => key === keys[at]);
|
|
362
362
|
};
|
|
363
363
|
var refuse4 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
364
364
|
var CARD4 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
@@ -425,6 +425,9 @@ var PAYFAST_BODY_KEYS = /* @__PURE__ */ Object.freeze([
|
|
|
425
425
|
"captcha_version",
|
|
426
426
|
"captcha_action"
|
|
427
427
|
]);
|
|
428
|
+
var CAPTCHA_ANSWER = "g-recaptcha-response";
|
|
429
|
+
var UNANSWERED_KEYS = /* @__PURE__ */ Object.freeze(PAYFAST_BODY_KEYS.filter((key) => key !== CAPTCHA_ANSWER));
|
|
430
|
+
var UNANSWERED_VERSIONS = /* @__PURE__ */ Object.freeze(["v0", "v1"]);
|
|
428
431
|
var BODY_LIMIT3 = 8192;
|
|
429
432
|
var URL_LIMIT = 256;
|
|
430
433
|
var PAY_PATH = /^\/eng\/method\/CreditCard\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/pay$/;
|
|
@@ -487,18 +490,20 @@ function cardNetwork(number) {
|
|
|
487
490
|
return "mastercard";
|
|
488
491
|
return AMEX.test(number) ? "amex" : null;
|
|
489
492
|
}
|
|
493
|
+
var namesKeys = (pairs, keys) => pairs.length === keys.length && pairs.every(([key], at) => key === keys[at]);
|
|
490
494
|
function judgeBody(body) {
|
|
491
495
|
const pairs = readForm(body);
|
|
492
496
|
if (pairs === null)
|
|
493
497
|
return { code: "payfast_body_unsupported", pairs: null };
|
|
494
498
|
if (pairs.some(([key]) => key === "cc_key"))
|
|
495
499
|
return { code: "payfast_saved_card", pairs: null };
|
|
496
|
-
|
|
500
|
+
const answered = namesKeys(pairs, PAYFAST_BODY_KEYS);
|
|
501
|
+
if (!answered && !namesKeys(pairs, UNANSWERED_KEYS))
|
|
497
502
|
return { code: "payfast_body_unsupported", pairs: null };
|
|
498
|
-
}
|
|
499
503
|
const value = Object.fromEntries(pairs);
|
|
500
|
-
const answer = value[
|
|
501
|
-
|
|
504
|
+
const answer = value[CAPTCHA_ANSWER];
|
|
505
|
+
const captcha = answered ? answer === "" || SOLVED_CAPTCHA2.test(answer) : UNANSWERED_VERSIONS.includes(value.captcha_version);
|
|
506
|
+
if (cardNetwork(value.card_number) === null || !SECURITY_CODE3.test(value.cvv) || !MONTH.test(value.expiry_month) || !YEAR.test(value.expiry_year) || value.cardholder_name.length > HOLDER_LIMIT || !HOLDER.test(value.cardholder_name) || !captcha || !CAPTCHA_VERSIONS.includes(value.captcha_version) || value.captcha_action !== "checkout") {
|
|
502
507
|
return { code: "payfast_body_unsupported", pairs: null };
|
|
503
508
|
}
|
|
504
509
|
if (value.consent_check === "1")
|
|
@@ -762,8 +767,8 @@ var PADDLE_TOKENEX_PSP = "paddle_tokenex";
|
|
|
762
767
|
var PADDLE_TOKENEX_FACT_KEYS = /* @__PURE__ */ Object.freeze(["card_first_six", "card_last_four", "expiry_month", "expiry_year", "card_brand"]);
|
|
763
768
|
var record4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
764
769
|
var sameKeys2 = (value, keys) => {
|
|
765
|
-
const
|
|
766
|
-
return
|
|
770
|
+
const own2 = Object.keys(value);
|
|
771
|
+
return own2.length === keys.length && own2.every((key, at) => key === keys[at]);
|
|
767
772
|
};
|
|
768
773
|
var refuse7 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
769
774
|
var CARD6 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
@@ -1252,8 +1257,8 @@ var TEXT_LIMIT = 256;
|
|
|
1252
1257
|
var CONTROL4 = /[\u0000-\u001f\u007f]/;
|
|
1253
1258
|
var record6 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1254
1259
|
var sameKeys3 = (value, keys) => {
|
|
1255
|
-
const
|
|
1256
|
-
return
|
|
1260
|
+
const own2 = Object.keys(value);
|
|
1261
|
+
return own2.length === keys.length && own2.every((key, at) => key === keys[at]);
|
|
1257
1262
|
};
|
|
1258
1263
|
var plainText = (value) => typeof value === "string" && value.length <= TEXT_LIMIT && !CONTROL4.test(value) && (value.match(/[0-9]/g) ?? []).length < 12;
|
|
1259
1264
|
var refuse9 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
@@ -1311,6 +1316,132 @@ var WIX_PAYMENTS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
|
|
|
1311
1316
|
pin3({ id: "psalms_and_potions", merchant: "Psalms & Potions", origins: ["https://www.psalmsandpotions.com"] }),
|
|
1312
1317
|
pin3({ id: "alexandra_hagel", merchant: "Alexandra Hagel, LLC", origins: ["https://www.alexandrahagel.com"] })
|
|
1313
1318
|
]);
|
|
1319
|
+
// src/paymongo.js
|
|
1320
|
+
var PAYMONGO_PSP = "paymongo";
|
|
1321
|
+
var PAYMONGO_CHECKOUT_HOST = "checkout.paymongo.com";
|
|
1322
|
+
var PAYMONGO_ATTRIBUTE_KEYS = /* @__PURE__ */ Object.freeze(["payment_method_type", "details", "billing", "return_url"]);
|
|
1323
|
+
var PAYMONGO_CARD_KEYS = /* @__PURE__ */ Object.freeze(["card_number", "exp_month", "exp_year", "cvc"]);
|
|
1324
|
+
var PAYMONGO_BILLING_KEYS = /* @__PURE__ */ Object.freeze(["name", "email", "phone", "address"]);
|
|
1325
|
+
var PAYMONGO_ADDRESS_KEYS = /* @__PURE__ */ Object.freeze(["line1", "line2", "city", "state", "country", "postal_code"]);
|
|
1326
|
+
var CLICK_TO_PAY_KEYS = /* @__PURE__ */ Object.freeze(["merchant_transaction_id", "correlation_id", "x_src_cx_flow_id"]);
|
|
1327
|
+
var URL_LIMIT2 = 128;
|
|
1328
|
+
var BODY_LIMIT6 = 16384;
|
|
1329
|
+
var ORIGIN = `https://${PAYMONGO_CHECKOUT_HOST}/`;
|
|
1330
|
+
var SESSION2 = /^[A-Za-z0-9]{24}$/;
|
|
1331
|
+
var CHECKOUT_PATH = /^\/(?:cs_)?([A-Za-z0-9]{24})$/;
|
|
1332
|
+
var NUMBER = /^[0-9]{12,19}$/;
|
|
1333
|
+
var SECURITY_CODE6 = /^[0-9]{3,4}$/;
|
|
1334
|
+
var TEXT = /^[^\x00-\x1f\x7f]{1,256}$/;
|
|
1335
|
+
var UNDEFINED = "$undefined";
|
|
1336
|
+
var CARD_MEMBER = /^(?:card_?number|pan|cvc|cvv)$/i;
|
|
1337
|
+
var record7 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1338
|
+
var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
1339
|
+
var keysAre = (value, keys) => {
|
|
1340
|
+
const names = Object.keys(value);
|
|
1341
|
+
return names.length === keys.length && names.every((name, at) => name === keys[at]);
|
|
1342
|
+
};
|
|
1343
|
+
var refuse10 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
1344
|
+
var CARD9 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
1345
|
+
var CONTINUE4 = /* @__PURE__ */ Object.freeze({ verdict: "continue" });
|
|
1346
|
+
function paymongoCheckoutSession(url) {
|
|
1347
|
+
if (typeof url !== "string" || url.length > URL_LIMIT2 || url.includes("?") || url.includes("#"))
|
|
1348
|
+
return null;
|
|
1349
|
+
let parsed;
|
|
1350
|
+
try {
|
|
1351
|
+
parsed = new URL(url);
|
|
1352
|
+
}
|
|
1353
|
+
catch {
|
|
1354
|
+
return null;
|
|
1355
|
+
}
|
|
1356
|
+
if (parsed.href !== url || parsed.protocol !== "https:" || parsed.port || parsed.username || parsed.password || parsed.hostname !== PAYMONGO_CHECKOUT_HOST)
|
|
1357
|
+
return null;
|
|
1358
|
+
const match = CHECKOUT_PATH.exec(parsed.pathname);
|
|
1359
|
+
return match ? match[1] : null;
|
|
1360
|
+
}
|
|
1361
|
+
function readJson2(body) {
|
|
1362
|
+
if (typeof body !== "string" || body === "")
|
|
1363
|
+
return { parsed: null, exact: false };
|
|
1364
|
+
let parsed;
|
|
1365
|
+
try {
|
|
1366
|
+
parsed = JSON.parse(body);
|
|
1367
|
+
}
|
|
1368
|
+
catch {
|
|
1369
|
+
return { parsed: null, exact: false };
|
|
1370
|
+
}
|
|
1371
|
+
let exact = false;
|
|
1372
|
+
try {
|
|
1373
|
+
exact = body.length <= BODY_LIMIT6 && JSON.stringify(parsed) === body;
|
|
1374
|
+
}
|
|
1375
|
+
catch {
|
|
1376
|
+
exact = false;
|
|
1377
|
+
}
|
|
1378
|
+
return { parsed, exact };
|
|
1379
|
+
}
|
|
1380
|
+
function billingValue(value) {
|
|
1381
|
+
if (value === UNDEFINED)
|
|
1382
|
+
return true;
|
|
1383
|
+
if (typeof value !== "string" || !TEXT.test(value))
|
|
1384
|
+
return false;
|
|
1385
|
+
return value[0] !== "$" || value.startsWith("$$");
|
|
1386
|
+
}
|
|
1387
|
+
function judgeCardAttach(args, session) {
|
|
1388
|
+
if (args.length !== 3 || typeof args[0] !== "string" || !args[0].startsWith("cs_") || !SESSION2.test(args[0].slice(3))) {
|
|
1389
|
+
return "paymongo_body_unsupported";
|
|
1390
|
+
}
|
|
1391
|
+
const id = args[0].slice(3);
|
|
1392
|
+
const attributes = args[1].data.attributes;
|
|
1393
|
+
const details = attributes.details;
|
|
1394
|
+
if (record7(details) && CLICK_TO_PAY_KEYS.some((key) => own(details, key)))
|
|
1395
|
+
return "paymongo_click_to_pay";
|
|
1396
|
+
if (!keysAre(args[1], ["data"]) || !keysAre(args[1].data, ["attributes"]) || !keysAre(attributes, PAYMONGO_ATTRIBUTE_KEYS) || !record7(details) || !keysAre(details, PAYMONGO_CARD_KEYS))
|
|
1397
|
+
return "paymongo_body_unsupported";
|
|
1398
|
+
if (typeof details.card_number !== "string" || !NUMBER.test(details.card_number) || !Number.isSafeInteger(details.exp_month) || details.exp_month < 1 || details.exp_month > 12 || !Number.isSafeInteger(details.exp_year) || details.exp_year < 0 || details.exp_year > 99 || typeof details.cvc !== "string" || !SECURITY_CODE6.test(details.cvc))
|
|
1399
|
+
return "paymongo_body_unsupported";
|
|
1400
|
+
const billing = attributes.billing;
|
|
1401
|
+
if (!record7(billing) || !keysAre(billing, PAYMONGO_BILLING_KEYS) || !record7(billing.address) || !keysAre(billing.address, PAYMONGO_ADDRESS_KEYS))
|
|
1402
|
+
return "paymongo_body_unsupported";
|
|
1403
|
+
if (billing.name === UNDEFINED || !billingValue(billing.name) || !billingValue(billing.email) || !billingValue(billing.phone) || !PAYMONGO_ADDRESS_KEYS.every((key) => billingValue(billing.address[key])))
|
|
1404
|
+
return "paymongo_body_unsupported";
|
|
1405
|
+
const third = args[2];
|
|
1406
|
+
if (!record7(third) || !keysAre(third, ["viewerAccept"]) || !billingValue(third.viewerAccept))
|
|
1407
|
+
return "paymongo_body_unsupported";
|
|
1408
|
+
if (attributes.return_url !== `${ORIGIN}${id}` || session !== void 0 && session !== id)
|
|
1409
|
+
return "paymongo_session_mismatch";
|
|
1410
|
+
return null;
|
|
1411
|
+
}
|
|
1412
|
+
function namesCardValue(parsed) {
|
|
1413
|
+
const pending = parsed !== null && typeof parsed === "object" ? [parsed] : [];
|
|
1414
|
+
while (pending.length > 0) {
|
|
1415
|
+
const node = pending.pop();
|
|
1416
|
+
for (const key of Object.keys(node)) {
|
|
1417
|
+
if (!Array.isArray(node) && CARD_MEMBER.test(key))
|
|
1418
|
+
return true;
|
|
1419
|
+
const value = node[key];
|
|
1420
|
+
if (value !== null && typeof value === "object")
|
|
1421
|
+
pending.push(value);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
return false;
|
|
1425
|
+
}
|
|
1426
|
+
function attachType(parsed) {
|
|
1427
|
+
if (!Array.isArray(parsed) || parsed.length < 2 || !record7(parsed[1]) || !record7(parsed[1].data) || !record7(parsed[1].data.attributes))
|
|
1428
|
+
return null;
|
|
1429
|
+
const type = parsed[1].data.attributes.payment_method_type;
|
|
1430
|
+
return typeof type === "string" ? type : null;
|
|
1431
|
+
}
|
|
1432
|
+
function paymongoBodyVerdict(body, session) {
|
|
1433
|
+
const { parsed, exact } = readJson2(body);
|
|
1434
|
+
const type = parsed === null ? null : attachType(parsed);
|
|
1435
|
+
if (type !== "card") {
|
|
1436
|
+
if (parsed === null)
|
|
1437
|
+
return body === void 0 || body === null || body === "" ? CONTINUE4 : refuse10("paymongo_body_unsupported");
|
|
1438
|
+
return namesCardValue(parsed) ? refuse10("paymongo_body_unsupported") : CONTINUE4;
|
|
1439
|
+
}
|
|
1440
|
+
if (!exact)
|
|
1441
|
+
return refuse10("paymongo_body_unsupported");
|
|
1442
|
+
const code = judgeCardAttach(parsed, session);
|
|
1443
|
+
return code === null ? CARD9 : refuse10(code);
|
|
1444
|
+
}
|
|
1314
1445
|
// src/card-fields.js
|
|
1315
1446
|
function processorGate(recognizer, url, body) {
|
|
1316
1447
|
if (recognizer && recognizer.psp === LYRA_PSP)
|
|
@@ -1336,6 +1467,12 @@ function processorGate(recognizer, url, body) {
|
|
|
1336
1467
|
return { verdict: "refuse", code: "paddle_tokenex_pay_unsupported" };
|
|
1337
1468
|
return paddleTokenexBodyVerdict(body, target ? target.line : void 0);
|
|
1338
1469
|
}
|
|
1470
|
+
if (recognizer && recognizer.psp === PAYMONGO_PSP) {
|
|
1471
|
+
const session = url !== void 0 ? paymongoCheckoutSession(url) : void 0;
|
|
1472
|
+
if (url !== void 0 && session === null)
|
|
1473
|
+
return { verdict: "refuse", code: "paymongo_url_refused" };
|
|
1474
|
+
return paymongoBodyVerdict(body, session);
|
|
1475
|
+
}
|
|
1339
1476
|
if (recognizer && recognizer.psp === GRAVY_PSP)
|
|
1340
1477
|
return gravyBodyVerdict(body);
|
|
1341
1478
|
if (recognizer && recognizer.psp === MONERIS_PSP) {
|
|
@@ -1453,7 +1590,7 @@ function splitCardLeg(recognizer, body) {
|
|
|
1453
1590
|
const absorb = list(split.absorb);
|
|
1454
1591
|
const pass = list(split.pass);
|
|
1455
1592
|
const sibling = list(split.siblingKeys);
|
|
1456
|
-
const
|
|
1593
|
+
const own2 = [];
|
|
1457
1594
|
const seen = /* @__PURE__ */ new Set();
|
|
1458
1595
|
for (const chunk of body.split("&")) {
|
|
1459
1596
|
const eq = chunk.indexOf("=");
|
|
@@ -1470,13 +1607,13 @@ function splitCardLeg(recognizer, body) {
|
|
|
1470
1607
|
return null;
|
|
1471
1608
|
seen.add(key);
|
|
1472
1609
|
if (!sibling.includes(key))
|
|
1473
|
-
|
|
1610
|
+
own2.push(key);
|
|
1474
1611
|
}
|
|
1475
|
-
if (
|
|
1612
|
+
if (own2.length === 0)
|
|
1476
1613
|
return null;
|
|
1477
|
-
if (
|
|
1614
|
+
if (own2.every((key) => absorb.includes(key)))
|
|
1478
1615
|
return "absorb";
|
|
1479
|
-
if (
|
|
1616
|
+
if (own2.every((key) => pass.includes(key)))
|
|
1480
1617
|
return "pass";
|
|
1481
1618
|
return null;
|
|
1482
1619
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1174,6 +1174,7 @@ export declare class CheckoutAuthError extends Error {
|
|
|
1174
1174
|
* On Lyra's API hosts the form token's session id reads `{session}`.
|
|
1175
1175
|
* On PayPal's hosts an order id reads `{order}`:
|
|
1176
1176
|
* https://www.paypal.com/v2/checkout/orders/{order}/confirm-payment-source.
|
|
1177
|
+
* On PayMongo's checkout host the session id reads `{session}`: https://checkout.paymongo.com/{session}.
|
|
1177
1178
|
* A string that is not a URL keeps the part before any `?` or `#`, with the
|
|
1178
1179
|
* same segments masked.
|
|
1179
1180
|
*/
|
package/dist/client.js
CHANGED
|
@@ -1102,6 +1102,19 @@ const LYRA_SESSION = /;jsessionid=[^/;?#]*/gi;
|
|
|
1102
1102
|
function maskLyraSession(path) {
|
|
1103
1103
|
return path.replace(LYRA_SESSION, ';jsessionid={session}');
|
|
1104
1104
|
}
|
|
1105
|
+
/**
|
|
1106
|
+
* PayMongo's hosted checkout sends its card form's attach to its own page URL,
|
|
1107
|
+
* https://checkout.paymongo.com/<session> (payment-core paymongo.js), and the session id is the only
|
|
1108
|
+
* credential there, no cookies: with it anyone can open that checkout, its amount and its merchant, and
|
|
1109
|
+
* pay it. Every route of the host is a session page, so on it any run of 24 or more letters and digits,
|
|
1110
|
+
* with or without its cs_ prefix, in any case, after an escaped slash or with any character
|
|
1111
|
+
* percent-encoded (a path keeps %36 where it means 6), reads {session}.
|
|
1112
|
+
*/
|
|
1113
|
+
const PAYMONGO_CHECKOUT_HOST = /^checkout\.paymongo\.com\.?$/i;
|
|
1114
|
+
const PAYMONGO_SESSION = /(?:cs_)?(?:[A-Za-z0-9]|%[0-9A-Fa-f]{2}){24,}/gi;
|
|
1115
|
+
function maskPaymongoSession(path) {
|
|
1116
|
+
return path.replace(PAYMONGO_SESSION, '{session}');
|
|
1117
|
+
}
|
|
1105
1118
|
/**
|
|
1106
1119
|
* The processors whose card request carries its own credential in the URL path, with the hosts
|
|
1107
1120
|
* that send it, the text that names those hosts in a string that is not a URL, and the mask. The
|
|
@@ -1141,6 +1154,7 @@ const PATH_CREDENTIALS = [
|
|
|
1141
1154
|
{ host: REDSYS_HOST, named: /redsys\.es/i, mask: maskRedsysSession },
|
|
1142
1155
|
{ host: LYRA_HOST, named: /(?:payzen\.eu|systempay\.fr|societegenerale\.eu|lyra\.com)/i, mask: maskLyraSession },
|
|
1143
1156
|
{ host: PAYPAL_HOST, named: /paypal\.com/i, mask: maskPaypalOrder },
|
|
1157
|
+
{ host: PAYMONGO_CHECKOUT_HOST, named: /checkout\.paymongo\.com/i, mask: maskPaymongoSession },
|
|
1144
1158
|
];
|
|
1145
1159
|
/**
|
|
1146
1160
|
* A URL as it may appear in an error message: origin + path only. A paused
|
|
@@ -1163,6 +1177,7 @@ const PATH_CREDENTIALS = [
|
|
|
1163
1177
|
* On Lyra's API hosts the form token's session id reads `{session}`.
|
|
1164
1178
|
* On PayPal's hosts an order id reads `{order}`:
|
|
1165
1179
|
* https://www.paypal.com/v2/checkout/orders/{order}/confirm-payment-source.
|
|
1180
|
+
* On PayMongo's checkout host the session id reads `{session}`: https://checkout.paymongo.com/{session}.
|
|
1166
1181
|
* A string that is not a URL keeps the part before any `?` or `#`, with the
|
|
1167
1182
|
* same segments masked.
|
|
1168
1183
|
*/
|
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: 833d97716c57df725c942d14494012032a87b561ac7734676fe9dcb1d6be4819
|
|
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
|
|
@@ -365,8 +365,8 @@ var CONTROL = /[\u0000-\u001f\u007f]/;
|
|
|
365
365
|
var cardLikeName = (name) => (name.match(/[0-9]/g) ?? []).length >= 12;
|
|
366
366
|
var record3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
367
367
|
var sameKeys = (value, keys) => {
|
|
368
|
-
const
|
|
369
|
-
return
|
|
368
|
+
const own2 = Object.keys(value);
|
|
369
|
+
return own2.length === keys.length && own2.every((key, at) => key === keys[at]);
|
|
370
370
|
};
|
|
371
371
|
var refuse4 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
372
372
|
var CARD4 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
@@ -433,6 +433,9 @@ var PAYFAST_BODY_KEYS = /* @__PURE__ */ Object.freeze([
|
|
|
433
433
|
"captcha_version",
|
|
434
434
|
"captcha_action"
|
|
435
435
|
]);
|
|
436
|
+
var CAPTCHA_ANSWER = "g-recaptcha-response";
|
|
437
|
+
var UNANSWERED_KEYS = /* @__PURE__ */ Object.freeze(PAYFAST_BODY_KEYS.filter((key) => key !== CAPTCHA_ANSWER));
|
|
438
|
+
var UNANSWERED_VERSIONS = /* @__PURE__ */ Object.freeze(["v0", "v1"]);
|
|
436
439
|
var BODY_LIMIT3 = 8192;
|
|
437
440
|
var URL_LIMIT = 256;
|
|
438
441
|
var PAY_PATH = /^\/eng\/method\/CreditCard\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/pay$/;
|
|
@@ -495,18 +498,20 @@ function cardNetwork(number) {
|
|
|
495
498
|
return "mastercard";
|
|
496
499
|
return AMEX.test(number) ? "amex" : null;
|
|
497
500
|
}
|
|
501
|
+
var namesKeys = (pairs, keys) => pairs.length === keys.length && pairs.every(([key], at) => key === keys[at]);
|
|
498
502
|
function judgeBody(body) {
|
|
499
503
|
const pairs = readForm(body);
|
|
500
504
|
if (pairs === null)
|
|
501
505
|
return { code: "payfast_body_unsupported", pairs: null };
|
|
502
506
|
if (pairs.some(([key]) => key === "cc_key"))
|
|
503
507
|
return { code: "payfast_saved_card", pairs: null };
|
|
504
|
-
|
|
508
|
+
const answered = namesKeys(pairs, PAYFAST_BODY_KEYS);
|
|
509
|
+
if (!answered && !namesKeys(pairs, UNANSWERED_KEYS))
|
|
505
510
|
return { code: "payfast_body_unsupported", pairs: null };
|
|
506
|
-
}
|
|
507
511
|
const value = Object.fromEntries(pairs);
|
|
508
|
-
const answer = value[
|
|
509
|
-
|
|
512
|
+
const answer = value[CAPTCHA_ANSWER];
|
|
513
|
+
const captcha = answered ? answer === "" || SOLVED_CAPTCHA2.test(answer) : UNANSWERED_VERSIONS.includes(value.captcha_version);
|
|
514
|
+
if (cardNetwork(value.card_number) === null || !SECURITY_CODE3.test(value.cvv) || !MONTH.test(value.expiry_month) || !YEAR.test(value.expiry_year) || value.cardholder_name.length > HOLDER_LIMIT || !HOLDER.test(value.cardholder_name) || !captcha || !CAPTCHA_VERSIONS.includes(value.captcha_version) || value.captcha_action !== "checkout") {
|
|
510
515
|
return { code: "payfast_body_unsupported", pairs: null };
|
|
511
516
|
}
|
|
512
517
|
if (value.consent_check === "1")
|
|
@@ -770,8 +775,8 @@ var PADDLE_TOKENEX_PSP = "paddle_tokenex";
|
|
|
770
775
|
var PADDLE_TOKENEX_FACT_KEYS = /* @__PURE__ */ Object.freeze(["card_first_six", "card_last_four", "expiry_month", "expiry_year", "card_brand"]);
|
|
771
776
|
var record4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
772
777
|
var sameKeys2 = (value, keys) => {
|
|
773
|
-
const
|
|
774
|
-
return
|
|
778
|
+
const own2 = Object.keys(value);
|
|
779
|
+
return own2.length === keys.length && own2.every((key, at) => key === keys[at]);
|
|
775
780
|
};
|
|
776
781
|
var refuse7 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
777
782
|
var CARD6 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
@@ -1260,8 +1265,8 @@ var TEXT_LIMIT = 256;
|
|
|
1260
1265
|
var CONTROL4 = /[\u0000-\u001f\u007f]/;
|
|
1261
1266
|
var record6 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1262
1267
|
var sameKeys3 = (value, keys) => {
|
|
1263
|
-
const
|
|
1264
|
-
return
|
|
1268
|
+
const own2 = Object.keys(value);
|
|
1269
|
+
return own2.length === keys.length && own2.every((key, at) => key === keys[at]);
|
|
1265
1270
|
};
|
|
1266
1271
|
var plainText = (value) => typeof value === "string" && value.length <= TEXT_LIMIT && !CONTROL4.test(value) && (value.match(/[0-9]/g) ?? []).length < 12;
|
|
1267
1272
|
var refuse9 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
@@ -1319,6 +1324,132 @@ var WIX_PAYMENTS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
|
|
|
1319
1324
|
pin3({ id: "psalms_and_potions", merchant: "Psalms & Potions", origins: ["https://www.psalmsandpotions.com"] }),
|
|
1320
1325
|
pin3({ id: "alexandra_hagel", merchant: "Alexandra Hagel, LLC", origins: ["https://www.alexandrahagel.com"] })
|
|
1321
1326
|
]);
|
|
1327
|
+
// src/paymongo.js
|
|
1328
|
+
var PAYMONGO_PSP = "paymongo";
|
|
1329
|
+
var PAYMONGO_CHECKOUT_HOST = "checkout.paymongo.com";
|
|
1330
|
+
var PAYMONGO_ATTRIBUTE_KEYS = /* @__PURE__ */ Object.freeze(["payment_method_type", "details", "billing", "return_url"]);
|
|
1331
|
+
var PAYMONGO_CARD_KEYS = /* @__PURE__ */ Object.freeze(["card_number", "exp_month", "exp_year", "cvc"]);
|
|
1332
|
+
var PAYMONGO_BILLING_KEYS = /* @__PURE__ */ Object.freeze(["name", "email", "phone", "address"]);
|
|
1333
|
+
var PAYMONGO_ADDRESS_KEYS = /* @__PURE__ */ Object.freeze(["line1", "line2", "city", "state", "country", "postal_code"]);
|
|
1334
|
+
var CLICK_TO_PAY_KEYS = /* @__PURE__ */ Object.freeze(["merchant_transaction_id", "correlation_id", "x_src_cx_flow_id"]);
|
|
1335
|
+
var URL_LIMIT2 = 128;
|
|
1336
|
+
var BODY_LIMIT6 = 16384;
|
|
1337
|
+
var ORIGIN = `https://${PAYMONGO_CHECKOUT_HOST}/`;
|
|
1338
|
+
var SESSION2 = /^[A-Za-z0-9]{24}$/;
|
|
1339
|
+
var CHECKOUT_PATH = /^\/(?:cs_)?([A-Za-z0-9]{24})$/;
|
|
1340
|
+
var NUMBER = /^[0-9]{12,19}$/;
|
|
1341
|
+
var SECURITY_CODE6 = /^[0-9]{3,4}$/;
|
|
1342
|
+
var TEXT = /^[^\x00-\x1f\x7f]{1,256}$/;
|
|
1343
|
+
var UNDEFINED = "$undefined";
|
|
1344
|
+
var CARD_MEMBER = /^(?:card_?number|pan|cvc|cvv)$/i;
|
|
1345
|
+
var record7 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1346
|
+
var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
1347
|
+
var keysAre = (value, keys) => {
|
|
1348
|
+
const names = Object.keys(value);
|
|
1349
|
+
return names.length === keys.length && names.every((name, at) => name === keys[at]);
|
|
1350
|
+
};
|
|
1351
|
+
var refuse10 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
1352
|
+
var CARD9 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
1353
|
+
var CONTINUE4 = /* @__PURE__ */ Object.freeze({ verdict: "continue" });
|
|
1354
|
+
function paymongoCheckoutSession(url) {
|
|
1355
|
+
if (typeof url !== "string" || url.length > URL_LIMIT2 || url.includes("?") || url.includes("#"))
|
|
1356
|
+
return null;
|
|
1357
|
+
let parsed;
|
|
1358
|
+
try {
|
|
1359
|
+
parsed = new URL(url);
|
|
1360
|
+
}
|
|
1361
|
+
catch {
|
|
1362
|
+
return null;
|
|
1363
|
+
}
|
|
1364
|
+
if (parsed.href !== url || parsed.protocol !== "https:" || parsed.port || parsed.username || parsed.password || parsed.hostname !== PAYMONGO_CHECKOUT_HOST)
|
|
1365
|
+
return null;
|
|
1366
|
+
const match = CHECKOUT_PATH.exec(parsed.pathname);
|
|
1367
|
+
return match ? match[1] : null;
|
|
1368
|
+
}
|
|
1369
|
+
function readJson2(body) {
|
|
1370
|
+
if (typeof body !== "string" || body === "")
|
|
1371
|
+
return { parsed: null, exact: false };
|
|
1372
|
+
let parsed;
|
|
1373
|
+
try {
|
|
1374
|
+
parsed = JSON.parse(body);
|
|
1375
|
+
}
|
|
1376
|
+
catch {
|
|
1377
|
+
return { parsed: null, exact: false };
|
|
1378
|
+
}
|
|
1379
|
+
let exact2 = false;
|
|
1380
|
+
try {
|
|
1381
|
+
exact2 = body.length <= BODY_LIMIT6 && JSON.stringify(parsed) === body;
|
|
1382
|
+
}
|
|
1383
|
+
catch {
|
|
1384
|
+
exact2 = false;
|
|
1385
|
+
}
|
|
1386
|
+
return { parsed, exact: exact2 };
|
|
1387
|
+
}
|
|
1388
|
+
function billingValue(value) {
|
|
1389
|
+
if (value === UNDEFINED)
|
|
1390
|
+
return true;
|
|
1391
|
+
if (typeof value !== "string" || !TEXT.test(value))
|
|
1392
|
+
return false;
|
|
1393
|
+
return value[0] !== "$" || value.startsWith("$$");
|
|
1394
|
+
}
|
|
1395
|
+
function judgeCardAttach(args, session) {
|
|
1396
|
+
if (args.length !== 3 || typeof args[0] !== "string" || !args[0].startsWith("cs_") || !SESSION2.test(args[0].slice(3))) {
|
|
1397
|
+
return "paymongo_body_unsupported";
|
|
1398
|
+
}
|
|
1399
|
+
const id = args[0].slice(3);
|
|
1400
|
+
const attributes = args[1].data.attributes;
|
|
1401
|
+
const details = attributes.details;
|
|
1402
|
+
if (record7(details) && CLICK_TO_PAY_KEYS.some((key) => own(details, key)))
|
|
1403
|
+
return "paymongo_click_to_pay";
|
|
1404
|
+
if (!keysAre(args[1], ["data"]) || !keysAre(args[1].data, ["attributes"]) || !keysAre(attributes, PAYMONGO_ATTRIBUTE_KEYS) || !record7(details) || !keysAre(details, PAYMONGO_CARD_KEYS))
|
|
1405
|
+
return "paymongo_body_unsupported";
|
|
1406
|
+
if (typeof details.card_number !== "string" || !NUMBER.test(details.card_number) || !Number.isSafeInteger(details.exp_month) || details.exp_month < 1 || details.exp_month > 12 || !Number.isSafeInteger(details.exp_year) || details.exp_year < 0 || details.exp_year > 99 || typeof details.cvc !== "string" || !SECURITY_CODE6.test(details.cvc))
|
|
1407
|
+
return "paymongo_body_unsupported";
|
|
1408
|
+
const billing = attributes.billing;
|
|
1409
|
+
if (!record7(billing) || !keysAre(billing, PAYMONGO_BILLING_KEYS) || !record7(billing.address) || !keysAre(billing.address, PAYMONGO_ADDRESS_KEYS))
|
|
1410
|
+
return "paymongo_body_unsupported";
|
|
1411
|
+
if (billing.name === UNDEFINED || !billingValue(billing.name) || !billingValue(billing.email) || !billingValue(billing.phone) || !PAYMONGO_ADDRESS_KEYS.every((key) => billingValue(billing.address[key])))
|
|
1412
|
+
return "paymongo_body_unsupported";
|
|
1413
|
+
const third = args[2];
|
|
1414
|
+
if (!record7(third) || !keysAre(third, ["viewerAccept"]) || !billingValue(third.viewerAccept))
|
|
1415
|
+
return "paymongo_body_unsupported";
|
|
1416
|
+
if (attributes.return_url !== `${ORIGIN}${id}` || session !== void 0 && session !== id)
|
|
1417
|
+
return "paymongo_session_mismatch";
|
|
1418
|
+
return null;
|
|
1419
|
+
}
|
|
1420
|
+
function namesCardValue(parsed) {
|
|
1421
|
+
const pending = parsed !== null && typeof parsed === "object" ? [parsed] : [];
|
|
1422
|
+
while (pending.length > 0) {
|
|
1423
|
+
const node = pending.pop();
|
|
1424
|
+
for (const key of Object.keys(node)) {
|
|
1425
|
+
if (!Array.isArray(node) && CARD_MEMBER.test(key))
|
|
1426
|
+
return true;
|
|
1427
|
+
const value = node[key];
|
|
1428
|
+
if (value !== null && typeof value === "object")
|
|
1429
|
+
pending.push(value);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
return false;
|
|
1433
|
+
}
|
|
1434
|
+
function attachType(parsed) {
|
|
1435
|
+
if (!Array.isArray(parsed) || parsed.length < 2 || !record7(parsed[1]) || !record7(parsed[1].data) || !record7(parsed[1].data.attributes))
|
|
1436
|
+
return null;
|
|
1437
|
+
const type = parsed[1].data.attributes.payment_method_type;
|
|
1438
|
+
return typeof type === "string" ? type : null;
|
|
1439
|
+
}
|
|
1440
|
+
function paymongoBodyVerdict(body, session) {
|
|
1441
|
+
const { parsed, exact: exact2 } = readJson2(body);
|
|
1442
|
+
const type = parsed === null ? null : attachType(parsed);
|
|
1443
|
+
if (type !== "card") {
|
|
1444
|
+
if (parsed === null)
|
|
1445
|
+
return body === void 0 || body === null || body === "" ? CONTINUE4 : refuse10("paymongo_body_unsupported");
|
|
1446
|
+
return namesCardValue(parsed) ? refuse10("paymongo_body_unsupported") : CONTINUE4;
|
|
1447
|
+
}
|
|
1448
|
+
if (!exact2)
|
|
1449
|
+
return refuse10("paymongo_body_unsupported");
|
|
1450
|
+
const code = judgeCardAttach(parsed, session);
|
|
1451
|
+
return code === null ? CARD9 : refuse10(code);
|
|
1452
|
+
}
|
|
1322
1453
|
// src/card-fields.js
|
|
1323
1454
|
function processorGate(recognizer, url, body) {
|
|
1324
1455
|
if (recognizer && recognizer.psp === LYRA_PSP)
|
|
@@ -1344,6 +1475,12 @@ function processorGate(recognizer, url, body) {
|
|
|
1344
1475
|
return { verdict: "refuse", code: "paddle_tokenex_pay_unsupported" };
|
|
1345
1476
|
return paddleTokenexBodyVerdict(body, target ? target.line : void 0);
|
|
1346
1477
|
}
|
|
1478
|
+
if (recognizer && recognizer.psp === PAYMONGO_PSP) {
|
|
1479
|
+
const session = url !== void 0 ? paymongoCheckoutSession(url) : void 0;
|
|
1480
|
+
if (url !== void 0 && session === null)
|
|
1481
|
+
return { verdict: "refuse", code: "paymongo_url_refused" };
|
|
1482
|
+
return paymongoBodyVerdict(body, session);
|
|
1483
|
+
}
|
|
1347
1484
|
if (recognizer && recognizer.psp === GRAVY_PSP)
|
|
1348
1485
|
return gravyBodyVerdict(body);
|
|
1349
1486
|
if (recognizer && recognizer.psp === MONERIS_PSP) {
|
|
@@ -1464,12 +1601,12 @@ var KEEP_ON_FILE_REFUSAL = "Refusing to pay: this Primer payment would keep the
|
|
|
1464
1601
|
var UNSUPPORTED_SDK_REFUSAL = "Refusing to pay: this checkout uses a Primer SDK Agentcard does not support yet. Nothing was sent.";
|
|
1465
1602
|
var NETWORK_MISMATCH_REFUSAL = "Refusing to pay: this Primer payment names a card network that is not your card's. Nothing was sent.";
|
|
1466
1603
|
var FILL_REFUSAL = "Could not fill the card fields for Primer. Nothing was sent.";
|
|
1467
|
-
var
|
|
1604
|
+
var BODY_LIMIT7 = 16384;
|
|
1468
1605
|
var RESPONSE_LIMIT = 32768;
|
|
1469
1606
|
var ROOT_KEYS4 = ["paymentInstrument", "userDescription", "tokenType", "paymentFlow"];
|
|
1470
1607
|
var INSTRUMENT_KEYS = ["number", "cvv", "expirationMonth", "expirationYear", "cardholderName", "preferredNetwork"];
|
|
1471
1608
|
var REQUIRED_INSTRUMENT_KEYS = ["number", "cvv", "expirationMonth", "expirationYear"];
|
|
1472
|
-
var
|
|
1609
|
+
var record8 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1473
1610
|
var owns5 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
1474
1611
|
var exact = (pattern, value) => typeof value === "string" && pattern.exec(value)?.[0] === value;
|
|
1475
1612
|
var text2 = (value, max) => typeof value === "string" && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value);
|
|
@@ -1492,7 +1629,7 @@ function isPrimerTokenizeUrl(url, method = "POST") {
|
|
|
1492
1629
|
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;
|
|
1493
1630
|
}
|
|
1494
1631
|
function judgeBody2(body) {
|
|
1495
|
-
if (typeof body !== "string" || body === "" || body.length >
|
|
1632
|
+
if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT7)
|
|
1496
1633
|
return null;
|
|
1497
1634
|
let value;
|
|
1498
1635
|
try {
|
|
@@ -1501,12 +1638,12 @@ function judgeBody2(body) {
|
|
|
1501
1638
|
catch {
|
|
1502
1639
|
return null;
|
|
1503
1640
|
}
|
|
1504
|
-
if (!
|
|
1641
|
+
if (!record8(value) || !roundTrips(value, body))
|
|
1505
1642
|
return null;
|
|
1506
1643
|
if (!Object.keys(value).every((key) => ROOT_KEYS4.includes(key)) || !owns5(value, "paymentInstrument"))
|
|
1507
1644
|
return null;
|
|
1508
1645
|
const instrument = value.paymentInstrument;
|
|
1509
|
-
if (!
|
|
1646
|
+
if (!record8(instrument) || !Object.keys(instrument).every((key) => INSTRUMENT_KEYS.includes(key)) || !REQUIRED_INSTRUMENT_KEYS.every((key) => owns5(instrument, key)))
|
|
1510
1647
|
return null;
|
|
1511
1648
|
if (!digits(instrument.number, 12, 19) || !digits(instrument.cvv, 3, 4) || !exact(MONTH3, instrument.expirationMonth) || !exact(YEAR3, instrument.expirationYear))
|
|
1512
1649
|
return null;
|
|
@@ -1534,7 +1671,7 @@ var CLIENT_TOKEN = /^[A-Za-z0-9._~+/=-]{16,4096}$/;
|
|
|
1534
1671
|
var REQUEST_ID = /^[A-Za-z0-9-]{1,64}$/;
|
|
1535
1672
|
var API_VERSION = /^2\.[0-9]{1,2}$/;
|
|
1536
1673
|
function readHeaders(headers) {
|
|
1537
|
-
if (!
|
|
1674
|
+
if (!record8(headers))
|
|
1538
1675
|
return null;
|
|
1539
1676
|
const folded = /* @__PURE__ */ new Map();
|
|
1540
1677
|
for (const [name, value] of Object.entries(headers)) {
|
|
@@ -1581,7 +1718,7 @@ function onTokenizePath(url) {
|
|
|
1581
1718
|
}
|
|
1582
1719
|
}
|
|
1583
1720
|
function primerTokenizeRefusal(request) {
|
|
1584
|
-
if (!
|
|
1721
|
+
if (!record8(request) || request.method !== "POST" || !isPrimerTokenizeUrl(request.url))
|
|
1585
1722
|
return "unsupported";
|
|
1586
1723
|
return headersRefusal(request.headers) ?? primerBodyRefusal(request.body);
|
|
1587
1724
|
}
|
|
@@ -1596,7 +1733,7 @@ function primerCardOnFileRefusal(url, options, policy = PRIMER_SESSION_CARD_ON_F
|
|
|
1596
1733
|
return "unsupported";
|
|
1597
1734
|
if (new URL(url).hostname !== LIVE_PCI_HOST)
|
|
1598
1735
|
return null;
|
|
1599
|
-
return primerCardOnFileDisclosure(url, policy) &&
|
|
1736
|
+
return primerCardOnFileDisclosure(url, policy) && record8(options) && options.keepOnFile === true ? null : "keep_on_file";
|
|
1600
1737
|
}
|
|
1601
1738
|
function primerRefusalMessage(reason) {
|
|
1602
1739
|
if (reason === "keep_on_file")
|
|
@@ -1680,7 +1817,7 @@ function readAnswer(body) {
|
|
|
1680
1817
|
return null;
|
|
1681
1818
|
try {
|
|
1682
1819
|
const value = JSON.parse(body);
|
|
1683
|
-
return
|
|
1820
|
+
return record8(value) ? value : null;
|
|
1684
1821
|
}
|
|
1685
1822
|
catch {
|
|
1686
1823
|
return null;
|
|
@@ -1689,16 +1826,16 @@ function readAnswer(body) {
|
|
|
1689
1826
|
var tokenValue = (value) => exact(TOKEN2, value) && !panLike(value);
|
|
1690
1827
|
var optional = (value, key, check) => !owns5(value, key) || value[key] === null || check(value[key]);
|
|
1691
1828
|
function cardDetails(data) {
|
|
1692
|
-
if (!
|
|
1829
|
+
if (!record8(data) || !exact(/^[0-9]{4}$/, data.last4Digits))
|
|
1693
1830
|
return null;
|
|
1694
|
-
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",
|
|
1831
|
+
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", record8))
|
|
1695
1832
|
return null;
|
|
1696
1833
|
const safe = { last4Digits: data.last4Digits };
|
|
1697
1834
|
for (const key of ["first6Digits", "expirationMonth", "expirationYear", "cardholderName", "network", "isNetworkTokenized"]) {
|
|
1698
1835
|
if (owns5(data, key) && data[key] !== null)
|
|
1699
1836
|
safe[key] = data[key];
|
|
1700
1837
|
}
|
|
1701
|
-
if (
|
|
1838
|
+
if (record8(data.binData)) {
|
|
1702
1839
|
const bin = {};
|
|
1703
1840
|
for (const key of BIN_KEYS) {
|
|
1704
1841
|
if (!owns5(data.binData, key) || data.binData[key] === null)
|
|
@@ -1714,7 +1851,7 @@ function cardDetails(data) {
|
|
|
1714
1851
|
function threeDsSummary(value) {
|
|
1715
1852
|
if (value === void 0 || value === null)
|
|
1716
1853
|
return null;
|
|
1717
|
-
if (!
|
|
1854
|
+
if (!record8(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"))
|
|
1718
1855
|
return void 0;
|
|
1719
1856
|
const safe = { responseCode: value.responseCode };
|
|
1720
1857
|
for (const key of ["reasonCode", "reasonText", "protocolVersion", "challengeIssued"]) {
|
|
@@ -1742,7 +1879,7 @@ function rebuildToken(value) {
|
|
|
1742
1879
|
}
|
|
1743
1880
|
function primerErrorId(body) {
|
|
1744
1881
|
const value = readAnswer(body);
|
|
1745
|
-
const id = value &&
|
|
1882
|
+
const id = value && record8(value.error) ? value.error.errorId : null;
|
|
1746
1883
|
return exact(ERROR_ID, id) && !panLike(id) ? id : null;
|
|
1747
1884
|
}
|
|
1748
1885
|
function matchesCard(details, card) {
|
|
@@ -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: 3e73afac7d2a16c36e81a7ddca5ea143be4880e056cb31558d31d3d4d72d84b1
|
|
4
4
|
// src/deep-freeze.js
|
|
5
5
|
function deepFreeze(value) {
|
|
6
6
|
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
@@ -1685,6 +1685,36 @@ var PROCESSOR_DEFINITIONS = deepFreeze([
|
|
|
1685
1685
|
preparationRequired: true,
|
|
1686
1686
|
encoding: "json",
|
|
1687
1687
|
passthroughHeaders: ["^authorization$", "^x-wix-client-artifact-id$", "^commonconfig$", "^x-wix-brand$", "^consent-policy$", "^x-wix-linguist$"]
|
|
1688
|
+
},
|
|
1689
|
+
{
|
|
1690
|
+
psp: "paymongo",
|
|
1691
|
+
// PayMongo's hosted checkout (Philippines) at checkout.paymongo.com/<session>, which every PayMongo
|
|
1692
|
+
// payment link, Storefront page, Shopify hosted app, WooCommerce hosted plugin and Checkout Session
|
|
1693
|
+
// API opens. The card form's Pay click runs the page's Next.js Server Action attachCheckout: a POST
|
|
1694
|
+
// to the page's own URL under its Next-Action id, Accept text/x-component, the body React's
|
|
1695
|
+
// encodeReply writes as text/plain: [session, {data: {attributes: {payment_method_type: "card",
|
|
1696
|
+
// details: {card_number, exp_month, exp_year, cvc}, billing, return_url}}}, {viewerAccept}].
|
|
1697
|
+
// Unencrypted. The request names no amount: the session bound its line items (always PHP) when the
|
|
1698
|
+
// merchant created it. paymongo.js holds the rule the pause decision reads: the exact URL, the Server
|
|
1699
|
+
// Action's headers, and the card form's attach for that URL's session, key by key, with a Click to Pay
|
|
1700
|
+
// attach and any other card shape refused. Every other action of the page (polling, fees, another
|
|
1701
|
+
// method's attach) carries no card on the same URL and continues untouched (passWithoutCard). Next
|
|
1702
|
+
// refuses an action whose Origin names another host, and the page answers no other origin's preflight,
|
|
1703
|
+
// so the card leaves only through the checkout relay. 3-D Secure follows the answer on
|
|
1704
|
+
// secure-authentication.paymongo.com with no card. Test mode and live mode share the host (a session's
|
|
1705
|
+
// livemode, not its host, separates them), so no sandbox host is marked.
|
|
1706
|
+
// Ships dark: served only to a client that declares `paymongo_checkout_pay`, gated off for every org
|
|
1707
|
+
// until an operator lists one (`paymongo:<org>`), and autopilot never sends this request
|
|
1708
|
+
// (autopilot.js DEVICE_REPLAY_REFUSED_OPERATIONS).
|
|
1709
|
+
capability: "paymongo_checkout_pay",
|
|
1710
|
+
match: "checkout\\.paymongo\\.com/(?:cs_)?[A-Za-z0-9]{24}",
|
|
1711
|
+
hosts: ["^checkout\\.paymongo\\.com$"],
|
|
1712
|
+
encoding: "json",
|
|
1713
|
+
// Next answers a Server Action only under its id, renders with the page's router state, and the page
|
|
1714
|
+
// reads the answer only as an RSC stream. Origin, Referer and cookies never ride (none is needed).
|
|
1715
|
+
passthroughHeaders: ["^next-action$", "^next-router-state-tree$", "^accept$"],
|
|
1716
|
+
jsonCardFields: [[1, "data", "attributes", "details", "card_number"]],
|
|
1717
|
+
passWithoutCard: "checkout\\.paymongo\\.com/(?:cs_)?[A-Za-z0-9]{24}"
|
|
1688
1718
|
}
|
|
1689
1719
|
]);
|
|
1690
1720
|
var PSP_RECOGNIZERS = deepFreeze(PROCESSOR_DEFINITIONS.map(({ compose, encryptedCardFields, deviceSteps, deviceEncrypt, ...definition }) => definition));
|
|
@@ -1807,6 +1837,19 @@ var PAYFAST_PAY_HOSTS = /* @__PURE__ */ Object.freeze({
|
|
|
1807
1837
|
production: Object.freeze(["payment.payfast.io", "www.payfast.co.za"]),
|
|
1808
1838
|
sandbox: Object.freeze(["sandbox.payfast.co.za"])
|
|
1809
1839
|
});
|
|
1840
|
+
var PAYFAST_BODY_KEYS = /* @__PURE__ */ Object.freeze([
|
|
1841
|
+
"card_number",
|
|
1842
|
+
"cardholder_name",
|
|
1843
|
+
"consent_check",
|
|
1844
|
+
"cvv",
|
|
1845
|
+
"expiry_month",
|
|
1846
|
+
"expiry_year",
|
|
1847
|
+
"g-recaptcha-response",
|
|
1848
|
+
"captcha_version",
|
|
1849
|
+
"captcha_action"
|
|
1850
|
+
]);
|
|
1851
|
+
var CAPTCHA_ANSWER = "g-recaptcha-response";
|
|
1852
|
+
var UNANSWERED_KEYS = /* @__PURE__ */ Object.freeze(PAYFAST_BODY_KEYS.filter((key) => key !== CAPTCHA_ANSWER));
|
|
1810
1853
|
// src/rsa-pkcs1v15.js
|
|
1811
1854
|
var TYPED_ARRAY = Object.getPrototypeOf(Uint8Array.prototype);
|
|
1812
1855
|
var typedArrayName = Object.getOwnPropertyDescriptor(TYPED_ARRAY, Symbol.toStringTag).get;
|
|
@@ -1849,6 +1892,9 @@ var WIX_PAYMENTS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
|
|
|
1849
1892
|
pin3({ id: "psalms_and_potions", merchant: "Psalms & Potions", origins: ["https://www.psalmsandpotions.com"] }),
|
|
1850
1893
|
pin3({ id: "alexandra_hagel", merchant: "Alexandra Hagel, LLC", origins: ["https://www.alexandrahagel.com"] })
|
|
1851
1894
|
]);
|
|
1895
|
+
// src/paymongo.js
|
|
1896
|
+
var PAYMONGO_CHECKOUT_HOST = "checkout.paymongo.com";
|
|
1897
|
+
var ORIGIN = `https://${PAYMONGO_CHECKOUT_HOST}/`;
|
|
1852
1898
|
// src/cybersource-secure-acceptance.js
|
|
1853
1899
|
var SA_BODY_LIMIT = 32 * 1024;
|
|
1854
1900
|
var SA_REPLY_LIMIT = 64 * 1024;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-cards/sdk",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.16",
|
|
4
4
|
"description": "Agentcard's SDK: one client, Agentcard, with two modules. checkout pauses a browser purchase and pays it with the person's own card; voice connects a person to your product once and approves a purchase by voice inside it. Card data never enters your process.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|