@agent-cards/sdk 0.23.15 → 0.23.17
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 +6 -0
- package/LICENSE +1 -1
- package/dist/card-fields.generated.js +253 -16
- package/dist/cdp.js +19 -2
- package/dist/client.d.ts +2 -0
- package/dist/client.js +29 -0
- package/dist/primer.generated.js +267 -30
- package/dist/stripe-checkout.generated.js +75 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Finish a Worldpay eProtect checkout that your code resumes itself. With `attachToCdp(…, { pauseOnly: true })`, you create the authorization, read it, and call `controller.resume(read)`. The read carries the card registration as Agentcard stored it, bare JSON, and earlier releases handed exactly that to the merchant's paused registration script. Worldpay's script reads its answer only through the callback the request named, so it never received the registration: the page waited until the merchant's timeout, and the checkout read `outcome_unknown` with reason `merchant_request_aborted` after the cardholder had approved. The SDK now answers the resumed registration as it answers one it created itself, with the registration inside the request's own callback under Worldpay's script type. The SDK never hands the page a read that is not this request's registration, such as one already inside a callback or another request's answer: `resume` rejects with `PaymentOutcomeUnknownError`, reason `eprotect_registration_unconfirmed`, and the paused request fails. A checkout whose authorization the SDK creates was already answered this way and does not change. No API or Vault release is needed.
|
|
6
|
+
|
|
5
7
|
- Refuse a sandbox declaration whose endpoint is on agentcard.com, Agentcard's own domain, as one on agentcard.sh is already refused. A `sandboxMerchants` entry on `VaultClient` whose endpoint is agentcard.com or any host under it makes the constructor throw:
|
|
6
8
|
|
|
7
9
|
```text
|
|
@@ -14,6 +16,8 @@
|
|
|
14
16
|
|
|
15
17
|
- 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
18
|
|
|
19
|
+
- 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.
|
|
20
|
+
|
|
17
21
|
- 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
22
|
- 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
23
|
- 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.
|
|
@@ -139,6 +143,8 @@
|
|
|
139
143
|
|
|
140
144
|
- Pay a Wix checkout whose card goes to Wix's own tokenizer, from Agentcard's own account only for now (dark launch): Agentcard turns it on for another company once it checks that the site instance Wix's card frame sends belongs to the reviewed store. Every Wix checkout mounts Wix Cashier's card frame, and for Wix Payments and classic Stripe on Wix that frame posts the card to `https://cashier-pci.wixapps.net/_api/payment-gateway-web/tokenize/v2/tokens`. Wix's payment widget fails the payment 30 seconds after it asks the frame for a token, too soon for an approval after Pay, so the SDK pays a Wix checkout only through `prepare({ psp: 'wix_payments', environment: 'production' })`, at a store Agentcard reviewed (ParaHouse Magazine, Psalms & Potions and Alexandra Hagel today), with `amount` and `currency`, the total the cardholder approves: the request names none. Wix has no test environment, so a test client's `prepare()` fails with `live_host_on_sandbox_row`. The sync now also asks for `capabilities=wix_payments_tokenize`. Once the cardholder approves, the frame's request pauses, the cardholder's device sends the card through the checkout relay, and the frame receives Wix's token. The frame sends its card request once more under a fresh site instance after Wix answers 401 or 403: the SDK refuses that second request in the browser and never answers it from the first approval. The SDK fails a paused request the cardholder's device has not sent 20 seconds after the pause, of the 30 seconds Wix waits, with reason `wix_payments_send_window_closed`, as it fails a prepared Secure Acceptance token create, and the device sends nothing past its own earlier boundary.
|
|
141
145
|
|
|
146
|
+
- Keep a GoPay card form's token out of your events and error messages. GoPay's card frame sends the card to its own URL, `https://secure.gopay.com/gp-card-comm/h/<token>?lang=..&ver=..&SG=<signature>`, and that token, with the signature in the query, opens that payment's card form to anyone who holds it. `redactUrl`, every event and every error message now show it as `{token}` on GoPay's card frame hosts, as in `https://secure.gopay.com/gp-card-comm/h/{token}`, and drop the query as before. The SDK does not pause a GoPay payment yet. No API or Vault release is needed.
|
|
147
|
+
|
|
142
148
|
## 0.23.0
|
|
143
149
|
|
|
144
150
|
### Upgrading from 0.22.0
|
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: 0fcc0e758acec92fba9d21092bcb3704b8a73ec864647ea194ca44eba57cb96d
|
|
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,230 @@ 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
|
+
}
|
|
1445
|
+
// src/gopay.js
|
|
1446
|
+
var GOPAY_PSP = "gopay";
|
|
1447
|
+
var GOPAY_CARD_FORM_HOSTS = /* @__PURE__ */ Object.freeze({ "secure.gopay.com": "production", "secure.sandbox.gopay.com": "test" });
|
|
1448
|
+
var GOPAY_CARD_FORM_PATH = "/gp-card-comm/h/";
|
|
1449
|
+
var BODY_LIMIT7 = 1024;
|
|
1450
|
+
var ROOT_KEYS4 = /* @__PURE__ */ Object.freeze(["SG", "cardData", "type"]);
|
|
1451
|
+
var CARD_KEYS6 = /* @__PURE__ */ Object.freeze(["cardNumber", "cvc", "expiration"]);
|
|
1452
|
+
var TOKEN2 = /^[0-9A-Fa-f]{16,1024}$/;
|
|
1453
|
+
var SIGNATURE = /^[0-9A-Fa-f]{64}$/;
|
|
1454
|
+
var LANGUAGE = /^[a-z]{2}(?:-[A-Za-z]{2})?$/;
|
|
1455
|
+
var VERSION = /^[A-Za-z0-9]{1,16}$/;
|
|
1456
|
+
var QUERY_NAMES = /* @__PURE__ */ Object.freeze(["lang", "ver", "SG"]);
|
|
1457
|
+
var CARD_NUMBER7 = /^[0-9]{16}$/;
|
|
1458
|
+
var CODE = /^[0-9]{3}$/;
|
|
1459
|
+
var EXPIRATION = /^[0-9]{2}(?:0[1-9]|1[0-2])$/;
|
|
1460
|
+
var CARD_PAYMENT = "PAYMENT_CARD";
|
|
1461
|
+
var record8 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1462
|
+
var sameKeys4 = (value, keys) => {
|
|
1463
|
+
const own2 = Object.keys(value);
|
|
1464
|
+
return own2.length === keys.length && own2.every((key, at) => key === keys[at]);
|
|
1465
|
+
};
|
|
1466
|
+
var refuse11 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
1467
|
+
var CARD10 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
1468
|
+
function luhn(digits) {
|
|
1469
|
+
let sum = 0;
|
|
1470
|
+
for (let at = 0; at < digits.length; at += 1) {
|
|
1471
|
+
let digit = Number(digits[digits.length - 1 - at]);
|
|
1472
|
+
if (at % 2 === 1) {
|
|
1473
|
+
digit *= 2;
|
|
1474
|
+
if (digit > 9)
|
|
1475
|
+
digit -= 9;
|
|
1476
|
+
}
|
|
1477
|
+
sum += digit;
|
|
1478
|
+
}
|
|
1479
|
+
return sum % 10 === 0;
|
|
1480
|
+
}
|
|
1481
|
+
function gopayCardFormUrl(url) {
|
|
1482
|
+
if (typeof url !== "string" || url.length > 2048 || url.includes("#"))
|
|
1483
|
+
return null;
|
|
1484
|
+
let parsed;
|
|
1485
|
+
try {
|
|
1486
|
+
parsed = new URL(url);
|
|
1487
|
+
}
|
|
1488
|
+
catch {
|
|
1489
|
+
return null;
|
|
1490
|
+
}
|
|
1491
|
+
if (parsed.href !== url || parsed.protocol !== "https:" || parsed.port || parsed.username || parsed.password)
|
|
1492
|
+
return null;
|
|
1493
|
+
const environment = Object.prototype.hasOwnProperty.call(GOPAY_CARD_FORM_HOSTS, parsed.hostname) ? GOPAY_CARD_FORM_HOSTS[parsed.hostname] : null;
|
|
1494
|
+
if (environment === null || !parsed.pathname.startsWith(GOPAY_CARD_FORM_PATH))
|
|
1495
|
+
return null;
|
|
1496
|
+
if (!TOKEN2.test(parsed.pathname.slice(GOPAY_CARD_FORM_PATH.length)))
|
|
1497
|
+
return null;
|
|
1498
|
+
const names = [...parsed.searchParams.keys()];
|
|
1499
|
+
if (names.some((name) => !QUERY_NAMES.includes(name)) || new Set(names).size !== names.length)
|
|
1500
|
+
return null;
|
|
1501
|
+
const signature = parsed.searchParams.get("SG");
|
|
1502
|
+
const language = parsed.searchParams.get("lang");
|
|
1503
|
+
const version = parsed.searchParams.get("ver");
|
|
1504
|
+
if (signature === null || !SIGNATURE.test(signature) || language !== null && !LANGUAGE.test(language) || version !== null && !VERSION.test(version))
|
|
1505
|
+
return null;
|
|
1506
|
+
return Object.freeze({ host: parsed.hostname, environment, signature });
|
|
1507
|
+
}
|
|
1508
|
+
function readBody4(body) {
|
|
1509
|
+
if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT7)
|
|
1510
|
+
return null;
|
|
1511
|
+
let value;
|
|
1512
|
+
try {
|
|
1513
|
+
value = JSON.parse(body);
|
|
1514
|
+
if (!record8(value) || JSON.stringify(value) !== body)
|
|
1515
|
+
return null;
|
|
1516
|
+
}
|
|
1517
|
+
catch {
|
|
1518
|
+
return null;
|
|
1519
|
+
}
|
|
1520
|
+
return value;
|
|
1521
|
+
}
|
|
1522
|
+
function gopayBodyVerdict(body, url) {
|
|
1523
|
+
let target;
|
|
1524
|
+
if (url !== void 0) {
|
|
1525
|
+
target = gopayCardFormUrl(url);
|
|
1526
|
+
if (target === null)
|
|
1527
|
+
return refuse11("gopay_url_refused");
|
|
1528
|
+
}
|
|
1529
|
+
const value = readBody4(body);
|
|
1530
|
+
if (value === null || !sameKeys4(value, ROOT_KEYS4) || !record8(value.cardData) || !sameKeys4(value.cardData, CARD_KEYS6)) {
|
|
1531
|
+
return refuse11("gopay_body_unsupported");
|
|
1532
|
+
}
|
|
1533
|
+
const card = value.cardData;
|
|
1534
|
+
if (typeof value.SG !== "string" || !SIGNATURE.test(value.SG) || typeof card.cardNumber !== "string" || !CARD_NUMBER7.test(card.cardNumber) || !luhn(card.cardNumber) || typeof card.cvc !== "string" || !CODE.test(card.cvc) || typeof card.expiration !== "string" || !EXPIRATION.test(card.expiration) || typeof value.type !== "string") {
|
|
1535
|
+
return refuse11("gopay_body_unsupported");
|
|
1536
|
+
}
|
|
1537
|
+
if (value.type !== CARD_PAYMENT)
|
|
1538
|
+
return refuse11("gopay_card_saving_refused");
|
|
1539
|
+
if (target !== void 0 && value.SG !== target.signature)
|
|
1540
|
+
return refuse11("gopay_signature_mismatch");
|
|
1541
|
+
return CARD10;
|
|
1542
|
+
}
|
|
1314
1543
|
// src/card-fields.js
|
|
1315
1544
|
function processorGate(recognizer, url, body) {
|
|
1316
1545
|
if (recognizer && recognizer.psp === LYRA_PSP)
|
|
@@ -1336,6 +1565,14 @@ function processorGate(recognizer, url, body) {
|
|
|
1336
1565
|
return { verdict: "refuse", code: "paddle_tokenex_pay_unsupported" };
|
|
1337
1566
|
return paddleTokenexBodyVerdict(body, target ? target.line : void 0);
|
|
1338
1567
|
}
|
|
1568
|
+
if (recognizer && recognizer.psp === PAYMONGO_PSP) {
|
|
1569
|
+
const session = url !== void 0 ? paymongoCheckoutSession(url) : void 0;
|
|
1570
|
+
if (url !== void 0 && session === null)
|
|
1571
|
+
return { verdict: "refuse", code: "paymongo_url_refused" };
|
|
1572
|
+
return paymongoBodyVerdict(body, session);
|
|
1573
|
+
}
|
|
1574
|
+
if (recognizer && recognizer.psp === GOPAY_PSP)
|
|
1575
|
+
return gopayBodyVerdict(body, url);
|
|
1339
1576
|
if (recognizer && recognizer.psp === GRAVY_PSP)
|
|
1340
1577
|
return gravyBodyVerdict(body);
|
|
1341
1578
|
if (recognizer && recognizer.psp === MONERIS_PSP) {
|
|
@@ -1453,7 +1690,7 @@ function splitCardLeg(recognizer, body) {
|
|
|
1453
1690
|
const absorb = list(split.absorb);
|
|
1454
1691
|
const pass = list(split.pass);
|
|
1455
1692
|
const sibling = list(split.siblingKeys);
|
|
1456
|
-
const
|
|
1693
|
+
const own2 = [];
|
|
1457
1694
|
const seen = /* @__PURE__ */ new Set();
|
|
1458
1695
|
for (const chunk of body.split("&")) {
|
|
1459
1696
|
const eq = chunk.indexOf("=");
|
|
@@ -1470,13 +1707,13 @@ function splitCardLeg(recognizer, body) {
|
|
|
1470
1707
|
return null;
|
|
1471
1708
|
seen.add(key);
|
|
1472
1709
|
if (!sibling.includes(key))
|
|
1473
|
-
|
|
1710
|
+
own2.push(key);
|
|
1474
1711
|
}
|
|
1475
|
-
if (
|
|
1712
|
+
if (own2.length === 0)
|
|
1476
1713
|
return null;
|
|
1477
|
-
if (
|
|
1714
|
+
if (own2.every((key) => absorb.includes(key)))
|
|
1478
1715
|
return "absorb";
|
|
1479
|
-
if (
|
|
1716
|
+
if (own2.every((key) => pass.includes(key)))
|
|
1480
1717
|
return "pass";
|
|
1481
1718
|
return null;
|
|
1482
1719
|
}
|
package/dist/cdp.js
CHANGED
|
@@ -7,7 +7,7 @@ import { MercadoCheckoutGate } from './mercado-checkout.js';
|
|
|
7
7
|
import { MERCADO_CHECKOUT_PATTERN } from './mercado-checkout.generated.js';
|
|
8
8
|
import { JuspayCheckoutGate } from './juspay-gate.js';
|
|
9
9
|
import { ACI_CHECKOUT_REUSED, AciGate, playwrightReplacesNavigationBody } from './aci.js';
|
|
10
|
-
import { EprotectGate } from './eprotect.js';
|
|
10
|
+
import { EprotectGate, classifyEprotectRequest, eprotectScriptAnswer } from './eprotect.js';
|
|
11
11
|
import { dispatchPreClaim, eventUrl, isArmedCreditguardCall } from './pre-claim.js';
|
|
12
12
|
import { GMO_TOKEN_V1_PATTERNS, gmoRequestShop, isGmoTokenV1Request, recognizesGmo } from './gmo.js';
|
|
13
13
|
import { isPaddleVgsCardsUrl } from './paddle-vgs.generated.js';
|
|
@@ -1549,6 +1549,23 @@ function replayOf(result, timeoutMs) {
|
|
|
1549
1549
|
return { mode: 'token', authorizationId, status: response.status, headers: { ...response.headers }, body: response.body, amountVerified: null,
|
|
1550
1550
|
...(result.processor_context !== undefined ? { processorContext: result.processor_context } : {}), ...execution };
|
|
1551
1551
|
}
|
|
1552
|
+
/**
|
|
1553
|
+
* Worldpay eProtect's registration, resumed by the business. The read carries the registration as the API
|
|
1554
|
+
* stored it, bare JSON, and the paused request is the vendor's JSONP <script>, which runs its answer and reads
|
|
1555
|
+
* the registration only through the callback the paused URL named. So it gets the answer authorize() gives on
|
|
1556
|
+
* the create path (client.ts): the registration inside that callback, under Worldpay's script type, and only a
|
|
1557
|
+
* registration payment-core's judge accepts for that URL. A read in any other mode never answers it either. The
|
|
1558
|
+
* paused URL alone decides, whatever recognizer claimed it. Any other request's replay passes as it came, and a
|
|
1559
|
+
* create's replay never comes here, so no answer is wrapped twice.
|
|
1560
|
+
*/
|
|
1561
|
+
function eprotectResumedReplay(replay, request) {
|
|
1562
|
+
if (classifyEprotectRequest(request.url, request.method)?.kind !== 'registration')
|
|
1563
|
+
return replay;
|
|
1564
|
+
const answer = replay.mode === 'token' ? eprotectScriptAnswer(request.url, replay) : null;
|
|
1565
|
+
if (replay.mode !== 'token' || !answer)
|
|
1566
|
+
throw new PaymentOutcomeUnknownError(replay.authorizationId, 'eprotect_registration_unconfirmed');
|
|
1567
|
+
return { ...replay, status: answer.status, headers: answer.headers, body: answer.body };
|
|
1568
|
+
}
|
|
1552
1569
|
/** The recogniser that claimed a paused request, by processor name, so a caller never re-matches the URL; null on a duck-typed vault without the read. */
|
|
1553
1570
|
function recognizerNameOf(opts, url, method) {
|
|
1554
1571
|
const read = opts.vault.recognizerOf;
|
|
@@ -2763,7 +2780,7 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2763
2780
|
// through controller.resume; the replay it hands back goes down the same delivery path as a create's.
|
|
2764
2781
|
const pause = opts.pauseOnly ? pauseForBusiness(opts, lifecycle, attempt.signal, { url: request.url, method: request.method, headers: request.headers ?? {}, body }, pageOrigin) : null;
|
|
2765
2782
|
businessPause = pause;
|
|
2766
|
-
const replay = pause ? await pause.replay : await opts.vault.authorize({
|
|
2783
|
+
const replay = pause ? eprotectResumedReplay(await pause.replay, request) : await opts.vault.authorize({
|
|
2767
2784
|
user: opts.user,
|
|
2768
2785
|
merchant: opts.merchant,
|
|
2769
2786
|
amount: opts.amount,
|
package/dist/client.d.ts
CHANGED
|
@@ -1174,6 +1174,8 @@ 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}.
|
|
1178
|
+
* On GoPay's card frame hosts the card form's token reads `{token}`: https://secure.gopay.com/gp-card-comm/h/{token}.
|
|
1177
1179
|
* A string that is not a URL keeps the part before any `?` or `#`, with the
|
|
1178
1180
|
* same segments masked.
|
|
1179
1181
|
*/
|
package/dist/client.js
CHANGED
|
@@ -1102,6 +1102,31 @@ 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
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* GoPay's card frame posts the card to its own URL,
|
|
1120
|
+
* https://secure.gopay.com/gp-card-comm/h/<hex token>?lang=..&ver=..&SG=<64 hex> (payment-core gopay.js). The
|
|
1121
|
+
* token names that payment's card form, and with the SG, which redactUrl drops with the query, anyone who holds
|
|
1122
|
+
* the URL can open the form and post a card to it. On GoPay's two card frame hosts the segment after /h/, in any
|
|
1123
|
+
* case or after an escaped slash, reads {token}: https://secure.gopay.com/gp-card-comm/h/{token}.
|
|
1124
|
+
*/
|
|
1125
|
+
const GOPAY_CARD_FORM_HOST = /^secure(?:\.sandbox)?\.gopay\.com\.?$/i;
|
|
1126
|
+
const GOPAY_CARD_FORM_TOKEN = /(\/h(?:\/|%2f))[^/?#]*/gi;
|
|
1127
|
+
function maskGopayToken(path) {
|
|
1128
|
+
return path.replace(GOPAY_CARD_FORM_TOKEN, '$1{token}');
|
|
1129
|
+
}
|
|
1105
1130
|
/**
|
|
1106
1131
|
* The processors whose card request carries its own credential in the URL path, with the hosts
|
|
1107
1132
|
* that send it, the text that names those hosts in a string that is not a URL, and the mask. The
|
|
@@ -1141,6 +1166,8 @@ const PATH_CREDENTIALS = [
|
|
|
1141
1166
|
{ host: REDSYS_HOST, named: /redsys\.es/i, mask: maskRedsysSession },
|
|
1142
1167
|
{ host: LYRA_HOST, named: /(?:payzen\.eu|systempay\.fr|societegenerale\.eu|lyra\.com)/i, mask: maskLyraSession },
|
|
1143
1168
|
{ host: PAYPAL_HOST, named: /paypal\.com/i, mask: maskPaypalOrder },
|
|
1169
|
+
{ host: PAYMONGO_CHECKOUT_HOST, named: /checkout\.paymongo\.com/i, mask: maskPaymongoSession },
|
|
1170
|
+
{ host: GOPAY_CARD_FORM_HOST, named: /secure(?:\.sandbox)?\.gopay\.com/i, mask: maskGopayToken },
|
|
1144
1171
|
];
|
|
1145
1172
|
/**
|
|
1146
1173
|
* A URL as it may appear in an error message: origin + path only. A paused
|
|
@@ -1163,6 +1190,8 @@ const PATH_CREDENTIALS = [
|
|
|
1163
1190
|
* On Lyra's API hosts the form token's session id reads `{session}`.
|
|
1164
1191
|
* On PayPal's hosts an order id reads `{order}`:
|
|
1165
1192
|
* https://www.paypal.com/v2/checkout/orders/{order}/confirm-payment-source.
|
|
1193
|
+
* On PayMongo's checkout host the session id reads `{session}`: https://checkout.paymongo.com/{session}.
|
|
1194
|
+
* On GoPay's card frame hosts the card form's token reads `{token}`: https://secure.gopay.com/gp-card-comm/h/{token}.
|
|
1166
1195
|
* A string that is not a URL keeps the part before any `?` or `#`, with the
|
|
1167
1196
|
* same segments masked.
|
|
1168
1197
|
*/
|
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: c3b720efaa20837c1bf50cc511610282f324867e9fe5538ac155b2ff79f2d9b4
|
|
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,230 @@ 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
|
+
}
|
|
1453
|
+
// src/gopay.js
|
|
1454
|
+
var GOPAY_PSP = "gopay";
|
|
1455
|
+
var GOPAY_CARD_FORM_HOSTS = /* @__PURE__ */ Object.freeze({ "secure.gopay.com": "production", "secure.sandbox.gopay.com": "test" });
|
|
1456
|
+
var GOPAY_CARD_FORM_PATH = "/gp-card-comm/h/";
|
|
1457
|
+
var BODY_LIMIT7 = 1024;
|
|
1458
|
+
var ROOT_KEYS4 = /* @__PURE__ */ Object.freeze(["SG", "cardData", "type"]);
|
|
1459
|
+
var CARD_KEYS6 = /* @__PURE__ */ Object.freeze(["cardNumber", "cvc", "expiration"]);
|
|
1460
|
+
var TOKEN2 = /^[0-9A-Fa-f]{16,1024}$/;
|
|
1461
|
+
var SIGNATURE = /^[0-9A-Fa-f]{64}$/;
|
|
1462
|
+
var LANGUAGE = /^[a-z]{2}(?:-[A-Za-z]{2})?$/;
|
|
1463
|
+
var VERSION = /^[A-Za-z0-9]{1,16}$/;
|
|
1464
|
+
var QUERY_NAMES = /* @__PURE__ */ Object.freeze(["lang", "ver", "SG"]);
|
|
1465
|
+
var CARD_NUMBER7 = /^[0-9]{16}$/;
|
|
1466
|
+
var CODE = /^[0-9]{3}$/;
|
|
1467
|
+
var EXPIRATION = /^[0-9]{2}(?:0[1-9]|1[0-2])$/;
|
|
1468
|
+
var CARD_PAYMENT = "PAYMENT_CARD";
|
|
1469
|
+
var record8 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1470
|
+
var sameKeys4 = (value, keys) => {
|
|
1471
|
+
const own2 = Object.keys(value);
|
|
1472
|
+
return own2.length === keys.length && own2.every((key, at) => key === keys[at]);
|
|
1473
|
+
};
|
|
1474
|
+
var refuse11 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
1475
|
+
var CARD10 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
1476
|
+
function luhn(digits2) {
|
|
1477
|
+
let sum = 0;
|
|
1478
|
+
for (let at = 0; at < digits2.length; at += 1) {
|
|
1479
|
+
let digit = Number(digits2[digits2.length - 1 - at]);
|
|
1480
|
+
if (at % 2 === 1) {
|
|
1481
|
+
digit *= 2;
|
|
1482
|
+
if (digit > 9)
|
|
1483
|
+
digit -= 9;
|
|
1484
|
+
}
|
|
1485
|
+
sum += digit;
|
|
1486
|
+
}
|
|
1487
|
+
return sum % 10 === 0;
|
|
1488
|
+
}
|
|
1489
|
+
function gopayCardFormUrl(url) {
|
|
1490
|
+
if (typeof url !== "string" || url.length > 2048 || url.includes("#"))
|
|
1491
|
+
return null;
|
|
1492
|
+
let parsed;
|
|
1493
|
+
try {
|
|
1494
|
+
parsed = new URL(url);
|
|
1495
|
+
}
|
|
1496
|
+
catch {
|
|
1497
|
+
return null;
|
|
1498
|
+
}
|
|
1499
|
+
if (parsed.href !== url || parsed.protocol !== "https:" || parsed.port || parsed.username || parsed.password)
|
|
1500
|
+
return null;
|
|
1501
|
+
const environment = Object.prototype.hasOwnProperty.call(GOPAY_CARD_FORM_HOSTS, parsed.hostname) ? GOPAY_CARD_FORM_HOSTS[parsed.hostname] : null;
|
|
1502
|
+
if (environment === null || !parsed.pathname.startsWith(GOPAY_CARD_FORM_PATH))
|
|
1503
|
+
return null;
|
|
1504
|
+
if (!TOKEN2.test(parsed.pathname.slice(GOPAY_CARD_FORM_PATH.length)))
|
|
1505
|
+
return null;
|
|
1506
|
+
const names = [...parsed.searchParams.keys()];
|
|
1507
|
+
if (names.some((name) => !QUERY_NAMES.includes(name)) || new Set(names).size !== names.length)
|
|
1508
|
+
return null;
|
|
1509
|
+
const signature = parsed.searchParams.get("SG");
|
|
1510
|
+
const language = parsed.searchParams.get("lang");
|
|
1511
|
+
const version = parsed.searchParams.get("ver");
|
|
1512
|
+
if (signature === null || !SIGNATURE.test(signature) || language !== null && !LANGUAGE.test(language) || version !== null && !VERSION.test(version))
|
|
1513
|
+
return null;
|
|
1514
|
+
return Object.freeze({ host: parsed.hostname, environment, signature });
|
|
1515
|
+
}
|
|
1516
|
+
function readBody4(body) {
|
|
1517
|
+
if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT7)
|
|
1518
|
+
return null;
|
|
1519
|
+
let value;
|
|
1520
|
+
try {
|
|
1521
|
+
value = JSON.parse(body);
|
|
1522
|
+
if (!record8(value) || JSON.stringify(value) !== body)
|
|
1523
|
+
return null;
|
|
1524
|
+
}
|
|
1525
|
+
catch {
|
|
1526
|
+
return null;
|
|
1527
|
+
}
|
|
1528
|
+
return value;
|
|
1529
|
+
}
|
|
1530
|
+
function gopayBodyVerdict(body, url) {
|
|
1531
|
+
let target;
|
|
1532
|
+
if (url !== void 0) {
|
|
1533
|
+
target = gopayCardFormUrl(url);
|
|
1534
|
+
if (target === null)
|
|
1535
|
+
return refuse11("gopay_url_refused");
|
|
1536
|
+
}
|
|
1537
|
+
const value = readBody4(body);
|
|
1538
|
+
if (value === null || !sameKeys4(value, ROOT_KEYS4) || !record8(value.cardData) || !sameKeys4(value.cardData, CARD_KEYS6)) {
|
|
1539
|
+
return refuse11("gopay_body_unsupported");
|
|
1540
|
+
}
|
|
1541
|
+
const card = value.cardData;
|
|
1542
|
+
if (typeof value.SG !== "string" || !SIGNATURE.test(value.SG) || typeof card.cardNumber !== "string" || !CARD_NUMBER7.test(card.cardNumber) || !luhn(card.cardNumber) || typeof card.cvc !== "string" || !CODE.test(card.cvc) || typeof card.expiration !== "string" || !EXPIRATION.test(card.expiration) || typeof value.type !== "string") {
|
|
1543
|
+
return refuse11("gopay_body_unsupported");
|
|
1544
|
+
}
|
|
1545
|
+
if (value.type !== CARD_PAYMENT)
|
|
1546
|
+
return refuse11("gopay_card_saving_refused");
|
|
1547
|
+
if (target !== void 0 && value.SG !== target.signature)
|
|
1548
|
+
return refuse11("gopay_signature_mismatch");
|
|
1549
|
+
return CARD10;
|
|
1550
|
+
}
|
|
1322
1551
|
// src/card-fields.js
|
|
1323
1552
|
function processorGate(recognizer, url, body) {
|
|
1324
1553
|
if (recognizer && recognizer.psp === LYRA_PSP)
|
|
@@ -1344,6 +1573,14 @@ function processorGate(recognizer, url, body) {
|
|
|
1344
1573
|
return { verdict: "refuse", code: "paddle_tokenex_pay_unsupported" };
|
|
1345
1574
|
return paddleTokenexBodyVerdict(body, target ? target.line : void 0);
|
|
1346
1575
|
}
|
|
1576
|
+
if (recognizer && recognizer.psp === PAYMONGO_PSP) {
|
|
1577
|
+
const session = url !== void 0 ? paymongoCheckoutSession(url) : void 0;
|
|
1578
|
+
if (url !== void 0 && session === null)
|
|
1579
|
+
return { verdict: "refuse", code: "paymongo_url_refused" };
|
|
1580
|
+
return paymongoBodyVerdict(body, session);
|
|
1581
|
+
}
|
|
1582
|
+
if (recognizer && recognizer.psp === GOPAY_PSP)
|
|
1583
|
+
return gopayBodyVerdict(body, url);
|
|
1347
1584
|
if (recognizer && recognizer.psp === GRAVY_PSP)
|
|
1348
1585
|
return gravyBodyVerdict(body);
|
|
1349
1586
|
if (recognizer && recognizer.psp === MONERIS_PSP) {
|
|
@@ -1464,12 +1701,12 @@ var KEEP_ON_FILE_REFUSAL = "Refusing to pay: this Primer payment would keep the
|
|
|
1464
1701
|
var UNSUPPORTED_SDK_REFUSAL = "Refusing to pay: this checkout uses a Primer SDK Agentcard does not support yet. Nothing was sent.";
|
|
1465
1702
|
var NETWORK_MISMATCH_REFUSAL = "Refusing to pay: this Primer payment names a card network that is not your card's. Nothing was sent.";
|
|
1466
1703
|
var FILL_REFUSAL = "Could not fill the card fields for Primer. Nothing was sent.";
|
|
1467
|
-
var
|
|
1704
|
+
var BODY_LIMIT8 = 16384;
|
|
1468
1705
|
var RESPONSE_LIMIT = 32768;
|
|
1469
|
-
var
|
|
1706
|
+
var ROOT_KEYS5 = ["paymentInstrument", "userDescription", "tokenType", "paymentFlow"];
|
|
1470
1707
|
var INSTRUMENT_KEYS = ["number", "cvv", "expirationMonth", "expirationYear", "cardholderName", "preferredNetwork"];
|
|
1471
1708
|
var REQUIRED_INSTRUMENT_KEYS = ["number", "cvv", "expirationMonth", "expirationYear"];
|
|
1472
|
-
var
|
|
1709
|
+
var record9 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1473
1710
|
var owns5 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
1474
1711
|
var exact = (pattern, value) => typeof value === "string" && pattern.exec(value)?.[0] === value;
|
|
1475
1712
|
var text2 = (value, max) => typeof value === "string" && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value);
|
|
@@ -1492,7 +1729,7 @@ function isPrimerTokenizeUrl(url, method = "POST") {
|
|
|
1492
1729
|
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
1730
|
}
|
|
1494
1731
|
function judgeBody2(body) {
|
|
1495
|
-
if (typeof body !== "string" || body === "" || body.length >
|
|
1732
|
+
if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT8)
|
|
1496
1733
|
return null;
|
|
1497
1734
|
let value;
|
|
1498
1735
|
try {
|
|
@@ -1501,12 +1738,12 @@ function judgeBody2(body) {
|
|
|
1501
1738
|
catch {
|
|
1502
1739
|
return null;
|
|
1503
1740
|
}
|
|
1504
|
-
if (!
|
|
1741
|
+
if (!record9(value) || !roundTrips(value, body))
|
|
1505
1742
|
return null;
|
|
1506
|
-
if (!Object.keys(value).every((key) =>
|
|
1743
|
+
if (!Object.keys(value).every((key) => ROOT_KEYS5.includes(key)) || !owns5(value, "paymentInstrument"))
|
|
1507
1744
|
return null;
|
|
1508
1745
|
const instrument = value.paymentInstrument;
|
|
1509
|
-
if (!
|
|
1746
|
+
if (!record9(instrument) || !Object.keys(instrument).every((key) => INSTRUMENT_KEYS.includes(key)) || !REQUIRED_INSTRUMENT_KEYS.every((key) => owns5(instrument, key)))
|
|
1510
1747
|
return null;
|
|
1511
1748
|
if (!digits(instrument.number, 12, 19) || !digits(instrument.cvv, 3, 4) || !exact(MONTH3, instrument.expirationMonth) || !exact(YEAR3, instrument.expirationYear))
|
|
1512
1749
|
return null;
|
|
@@ -1534,7 +1771,7 @@ var CLIENT_TOKEN = /^[A-Za-z0-9._~+/=-]{16,4096}$/;
|
|
|
1534
1771
|
var REQUEST_ID = /^[A-Za-z0-9-]{1,64}$/;
|
|
1535
1772
|
var API_VERSION = /^2\.[0-9]{1,2}$/;
|
|
1536
1773
|
function readHeaders(headers) {
|
|
1537
|
-
if (!
|
|
1774
|
+
if (!record9(headers))
|
|
1538
1775
|
return null;
|
|
1539
1776
|
const folded = /* @__PURE__ */ new Map();
|
|
1540
1777
|
for (const [name, value] of Object.entries(headers)) {
|
|
@@ -1581,7 +1818,7 @@ function onTokenizePath(url) {
|
|
|
1581
1818
|
}
|
|
1582
1819
|
}
|
|
1583
1820
|
function primerTokenizeRefusal(request) {
|
|
1584
|
-
if (!
|
|
1821
|
+
if (!record9(request) || request.method !== "POST" || !isPrimerTokenizeUrl(request.url))
|
|
1585
1822
|
return "unsupported";
|
|
1586
1823
|
return headersRefusal(request.headers) ?? primerBodyRefusal(request.body);
|
|
1587
1824
|
}
|
|
@@ -1596,7 +1833,7 @@ function primerCardOnFileRefusal(url, options, policy = PRIMER_SESSION_CARD_ON_F
|
|
|
1596
1833
|
return "unsupported";
|
|
1597
1834
|
if (new URL(url).hostname !== LIVE_PCI_HOST)
|
|
1598
1835
|
return null;
|
|
1599
|
-
return primerCardOnFileDisclosure(url, policy) &&
|
|
1836
|
+
return primerCardOnFileDisclosure(url, policy) && record9(options) && options.keepOnFile === true ? null : "keep_on_file";
|
|
1600
1837
|
}
|
|
1601
1838
|
function primerRefusalMessage(reason) {
|
|
1602
1839
|
if (reason === "keep_on_file")
|
|
@@ -1655,9 +1892,9 @@ function injectPrimerCard(body, card) {
|
|
|
1655
1892
|
}
|
|
1656
1893
|
return { body: JSON.stringify(judged.value), where };
|
|
1657
1894
|
}
|
|
1658
|
-
var
|
|
1895
|
+
var TOKEN3 = /^[A-Za-z0-9_-]{8,256}$/;
|
|
1659
1896
|
var ERROR_ID = /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/;
|
|
1660
|
-
var
|
|
1897
|
+
var CODE2 = /^[A-Z][A-Z0-9_]{0,63}$/;
|
|
1661
1898
|
var NETWORK_NAME = /^[A-Za-z][A-Za-z0-9 _-]{0,31}$/;
|
|
1662
1899
|
var RESPONSE_MONTH = /^(?:0?[1-9]|1[0-2])$/;
|
|
1663
1900
|
var RESPONSE_YEAR = /^(?:[0-9]{2}|20[0-9]{2})$/;
|
|
@@ -1680,25 +1917,25 @@ function readAnswer(body) {
|
|
|
1680
1917
|
return null;
|
|
1681
1918
|
try {
|
|
1682
1919
|
const value = JSON.parse(body);
|
|
1683
|
-
return
|
|
1920
|
+
return record9(value) ? value : null;
|
|
1684
1921
|
}
|
|
1685
1922
|
catch {
|
|
1686
1923
|
return null;
|
|
1687
1924
|
}
|
|
1688
1925
|
}
|
|
1689
|
-
var tokenValue = (value) => exact(
|
|
1926
|
+
var tokenValue = (value) => exact(TOKEN3, value) && !panLike(value);
|
|
1690
1927
|
var optional = (value, key, check) => !owns5(value, key) || value[key] === null || check(value[key]);
|
|
1691
1928
|
function cardDetails(data) {
|
|
1692
|
-
if (!
|
|
1929
|
+
if (!record9(data) || !exact(/^[0-9]{4}$/, data.last4Digits))
|
|
1693
1930
|
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",
|
|
1931
|
+
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", record9))
|
|
1695
1932
|
return null;
|
|
1696
1933
|
const safe = { last4Digits: data.last4Digits };
|
|
1697
1934
|
for (const key of ["first6Digits", "expirationMonth", "expirationYear", "cardholderName", "network", "isNetworkTokenized"]) {
|
|
1698
1935
|
if (owns5(data, key) && data[key] !== null)
|
|
1699
1936
|
safe[key] = data[key];
|
|
1700
1937
|
}
|
|
1701
|
-
if (
|
|
1938
|
+
if (record9(data.binData)) {
|
|
1702
1939
|
const bin = {};
|
|
1703
1940
|
for (const key of BIN_KEYS) {
|
|
1704
1941
|
if (!owns5(data.binData, key) || data.binData[key] === null)
|
|
@@ -1714,7 +1951,7 @@ function cardDetails(data) {
|
|
|
1714
1951
|
function threeDsSummary(value) {
|
|
1715
1952
|
if (value === void 0 || value === null)
|
|
1716
1953
|
return null;
|
|
1717
|
-
if (!
|
|
1954
|
+
if (!record9(value) || !exact(CODE2, value.responseCode) || !optional(value, "reasonCode", (v) => exact(CODE2, v)) || !optional(value, "reasonText", (v) => plain(v, 256)) || !optional(value, "protocolVersion", (v) => v === "" || exact(PROTOCOL, v)) || !optional(value, "challengeIssued", (v) => typeof v === "boolean"))
|
|
1718
1955
|
return void 0;
|
|
1719
1956
|
const safe = { responseCode: value.responseCode };
|
|
1720
1957
|
for (const key of ["reasonCode", "reasonText", "protocolVersion", "challengeIssued"]) {
|
|
@@ -1742,7 +1979,7 @@ function rebuildToken(value) {
|
|
|
1742
1979
|
}
|
|
1743
1980
|
function primerErrorId(body) {
|
|
1744
1981
|
const value = readAnswer(body);
|
|
1745
|
-
const id = value &&
|
|
1982
|
+
const id = value && record9(value.error) ? value.error.errorId : null;
|
|
1746
1983
|
return exact(ERROR_ID, id) && !panLike(id) ? id : null;
|
|
1747
1984
|
}
|
|
1748
1985
|
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: 7af6b8dae5c779ea1901a4deac135effd97b00ea0925db7a269c82330e110cc9
|
|
4
4
|
// src/deep-freeze.js
|
|
5
5
|
function deepFreeze(value) {
|
|
6
6
|
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
@@ -1685,6 +1685,64 @@ 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}"
|
|
1718
|
+
},
|
|
1719
|
+
{
|
|
1720
|
+
psp: "gopay",
|
|
1721
|
+
// GoPay's payment gateway (Czech Republic and Slovakia), which a shop opens as a page or as an overlay frame
|
|
1722
|
+
// (embed.js). The gateway loads GoPay's card frame from https://secure.gopay.com/gp-card-comm/h/<hex
|
|
1723
|
+
// token>?lang=..&ver=..&SG=<64 hex>, and on Pay the frame posts the card as one JSON body to its own URL:
|
|
1724
|
+
// {"SG":<the URL's SG>,"cardData":{"cardNumber":<16 digits>,"cvc":<3 digits>,"expiration":<YYMM>},
|
|
1725
|
+
// "type":"PAYMENT_CARD"}, unencrypted. gopay.js holds the request rule the pause decision, the API's create
|
|
1726
|
+
// and the device all read: exactly a card frame URL, a JSON Content-Type, and the frame's body byte for byte,
|
|
1727
|
+
// with a body that names Click to Pay (the frame's "save the card" box) refused. The body names no amount
|
|
1728
|
+
// and no shop: the payment GoPay keeps on its side holds both, so a live request is taken only for a
|
|
1729
|
+
// checkout origin a reviewed pin lists. The frame's host answers no browser origin, so the card leaves the
|
|
1730
|
+
// cardholder's device only through the checkout relay. The answer tells the gateway only whether GoPay took
|
|
1731
|
+
// the card; the gateway then reads the payment's end from its own API and runs 3-D Secure by redirect.
|
|
1732
|
+
// secure.sandbox.gopay.com is GoPay's sandbox: pinned (S7) to test rows, which send only test cards.
|
|
1733
|
+
// Ships dark: served only to a client that declares `gopay_card_form`, gated off for every org until an
|
|
1734
|
+
// operator lists one (`gopay:<org>`), and autopilot never sends this request (autopilot.js
|
|
1735
|
+
// DEVICE_REPLAY_REFUSED_OPERATIONS). `(?![\w/.%-])` ends the path for a consumer that tests `match`
|
|
1736
|
+
// unanchored.
|
|
1737
|
+
capability: "gopay_card_form",
|
|
1738
|
+
match: "secure(?:\\.sandbox)?\\.gopay\\.com/gp-card-comm/h/[0-9A-Fa-f]{16,1024}(?![\\w/.%-])",
|
|
1739
|
+
hosts: ["^secure\\.gopay\\.com$", "^secure\\.sandbox\\.gopay\\.com$"],
|
|
1740
|
+
sandboxHosts: ["^secure\\.sandbox\\.gopay\\.com$"],
|
|
1741
|
+
hostEnvironmentPinned: true,
|
|
1742
|
+
encoding: "json",
|
|
1743
|
+
// The frame's own Accept rides as it sent it; origin, referer and cookies never do.
|
|
1744
|
+
passthroughHeaders: ["^accept$"],
|
|
1745
|
+
jsonCardFields: [["cardData", "cardNumber"]]
|
|
1688
1746
|
}
|
|
1689
1747
|
]);
|
|
1690
1748
|
var PSP_RECOGNIZERS = deepFreeze(PROCESSOR_DEFINITIONS.map(({ compose, encryptedCardFields, deviceSteps, deviceEncrypt, ...definition }) => definition));
|
|
@@ -1807,6 +1865,19 @@ var PAYFAST_PAY_HOSTS = /* @__PURE__ */ Object.freeze({
|
|
|
1807
1865
|
production: Object.freeze(["payment.payfast.io", "www.payfast.co.za"]),
|
|
1808
1866
|
sandbox: Object.freeze(["sandbox.payfast.co.za"])
|
|
1809
1867
|
});
|
|
1868
|
+
var PAYFAST_BODY_KEYS = /* @__PURE__ */ Object.freeze([
|
|
1869
|
+
"card_number",
|
|
1870
|
+
"cardholder_name",
|
|
1871
|
+
"consent_check",
|
|
1872
|
+
"cvv",
|
|
1873
|
+
"expiry_month",
|
|
1874
|
+
"expiry_year",
|
|
1875
|
+
"g-recaptcha-response",
|
|
1876
|
+
"captcha_version",
|
|
1877
|
+
"captcha_action"
|
|
1878
|
+
]);
|
|
1879
|
+
var CAPTCHA_ANSWER = "g-recaptcha-response";
|
|
1880
|
+
var UNANSWERED_KEYS = /* @__PURE__ */ Object.freeze(PAYFAST_BODY_KEYS.filter((key) => key !== CAPTCHA_ANSWER));
|
|
1810
1881
|
// src/rsa-pkcs1v15.js
|
|
1811
1882
|
var TYPED_ARRAY = Object.getPrototypeOf(Uint8Array.prototype);
|
|
1812
1883
|
var typedArrayName = Object.getOwnPropertyDescriptor(TYPED_ARRAY, Symbol.toStringTag).get;
|
|
@@ -1849,6 +1920,9 @@ var WIX_PAYMENTS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
|
|
|
1849
1920
|
pin3({ id: "psalms_and_potions", merchant: "Psalms & Potions", origins: ["https://www.psalmsandpotions.com"] }),
|
|
1850
1921
|
pin3({ id: "alexandra_hagel", merchant: "Alexandra Hagel, LLC", origins: ["https://www.alexandrahagel.com"] })
|
|
1851
1922
|
]);
|
|
1923
|
+
// src/paymongo.js
|
|
1924
|
+
var PAYMONGO_CHECKOUT_HOST = "checkout.paymongo.com";
|
|
1925
|
+
var ORIGIN = `https://${PAYMONGO_CHECKOUT_HOST}/`;
|
|
1852
1926
|
// src/cybersource-secure-acceptance.js
|
|
1853
1927
|
var SA_BODY_LIMIT = 32 * 1024;
|
|
1854
1928
|
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.17",
|
|
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",
|