@agent-cards/sdk 0.23.17 → 0.23.18
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 +9 -1
- package/README.md +5 -1
- package/dist/card-fields.generated.js +471 -4
- package/dist/client.d.ts +11 -0
- package/dist/client.js +93 -31
- package/dist/primer.generated.js +495 -28
- package/dist/stripe-checkout.generated.js +295 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
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.
|
|
18
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}`.
|
|
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}`.
|
|
20
20
|
|
|
21
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.
|
|
22
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.
|
|
@@ -139,12 +139,20 @@
|
|
|
139
139
|
|
|
140
140
|
- Keep a Payfast payment's id out of your events and error messages. Payfast's hosted payment page sends the card to `/eng/method/CreditCard/<uuid>/pay` on its own host (`payment.payfast.io`, `www.payfast.co.za`, `sandbox.payfast.co.za`) with no auth header, so the payment's uuid is the only credential that request needs: anyone who holds it can read the payment page, its amount and its merchant. `redactUrl`, every event and every error message now show it as `{uuid}` on every host Payfast owns, on every route of the page, as in `https://payment.payfast.io/eng/method/CreditCard/{uuid}/pay`. The SDK does not pause a Payfast payment yet. No API or Vault release is needed.
|
|
141
141
|
|
|
142
|
+
- Keep a processor's id out of your events and error messages however the URL spells the slashes in front of it. A path keeps a percent escape as it was written, so a slash can reach `redactUrl` as `%2F` or `%2f`, escaped twice as `%252F`, or doubled. Earlier releases showed the last digits of a Payfast payment, a CreditGuard transaction or a Gravy session written without its hyphens after an escaped slash, as in `https://payment.payfast.io/eng/method/CreditCard%{uuid}7f/pay`, and all of one written with its hyphens escaped, as of a Przelewy24 transaction's token. They showed a Stripe Checkout Session, a PayPal order, a GoPay card form's token, a Paddle checkout and a WSPay or Monri client secret whole behind a slash escaped twice or doubled, or behind a route whose own slash was escaped, a Lyra session behind an escaped `;` or `=`, and a Redsys session behind one escaped twice. `redactUrl`, every event and every error message now show each id as its label in every one of these spellings, as in `https://payment.payfast.io/eng/method/CreditCard%2F{uuid}/pay`. No API or Vault release is needed.
|
|
143
|
+
|
|
142
144
|
- Pay a live Pelecard checkout, for a company Agentcard turned Pelecard on for. Pelecard's test and live terminals share `gateway21.pelecard.biz` and `gateway20.pelecard.biz`, and neither the payment page nor its request says which kind a page is on, so the API refused every live Pelecard create with `400 pelecard_live_unsupported`, before any row or prompt. On 2026-10-11 Agentcard accepted that a live payment page could sit on one of Pelecard's public test terminals, and a live create now parks like a test one. The cardholder approves under Pelecard's note, which says the merchant may have Pelecard keep the card, since a merchant can set `CreateToken` when it creates the payment page, and only a card the cardholder stored pays: the API refuses a card a company added as `card_not_stored`. A test-mode checkout still takes only a documented test card. No SDK upgrade is needed. A payment also needs the matching API, Vault and checkout relay releases, with `CHECKOUT_PELECARD_RELAY_ENABLED` on and the company listed for Pelecard.
|
|
143
145
|
|
|
144
146
|
- 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.
|
|
145
147
|
|
|
146
148
|
- 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
149
|
|
|
150
|
+
- Pay PayMongo's hosted checkout through the checkout relay, for a company Agentcard turns PayMongo on for (dark launch). Every PayMongo payment link, Storefront page and Checkout Session opens one page, `https://checkout.paymongo.com/<session>`, whose Pay button posts the card to that page's own URL as a Next.js Server Action. This build declares `paymongo_checkout_pay` on every sync, and the API serves the PayMongo entry only to a company Agentcard turned PayMongo on for, so every other company's PayMongo payments go through untouched, as for an unsupported processor. For an enabled company the SDK pauses only the checkout page's own card attach. The attach names no price, so `authorize()` refuses one without `amount` above zero and `currency` (`AmountRequiredError`): send the total PayMongo's checkout shows, in Philippine pesos. The API reads the checkout session from PayMongo and refuses a total that is not the session's to the centavo (`amount_mismatch`), a test-mode session on live credentials or the reverse (`paymongo_environment_mismatch`), and a session that has expired, is already being paid, takes no cards or adds PayMongo's fee on top (`paymongo_checkout_unpayable`), and it names PayMongo's merchant, whatever `merchant` you send. The cardholder's device sends the card through the checkout relay, and the page gets PayMongo's own answer: the paid payment, or the step toward PayMongo's 3-D Secure page, which the page then runs in the agent's browser, where a challenge needs a person at that browser. A PayMongo payment also needs the matching API, Vault and checkout relay releases.
|
|
151
|
+
|
|
152
|
+
- Keep a KOMOJU session's id out of your events and error messages. KOMOJU's Hosted Fields frame pays a session with `/api/v1/sessions/<id>/pay` on `komoju.com` under the merchant's publishable key, which is public, so the session id is what lets anyone read that session and pay it. `redactUrl`, every event and every error message show it as `{session}`, as in `https://komoju.com/api/v1/sessions/{session}/pay`. KOMOJU stays dark for every company Agentcard has not turned it on for, and no API or Vault release is needed.
|
|
153
|
+
|
|
154
|
+
- Keep a Przelewy24 transaction's token out of your events and error messages. Przelewy24's pay wall names its transaction by one token in every path (`/trnRequest/<token>`, its method links, `/card/p24form/<token>`, `/trnResult/<token>`), with no auth header, so the token is all a browser needs to open that transaction's pay wall, with its merchant, its amount and its card form. `redactUrl`, every event and every error message now show it as `{token}` on Przelewy24's hosts, on every route, as in `https://secure.przelewy24.pl/trnRequest/{token}`. The SDK does not pause a Przelewy24 payment yet. No API or Vault release is needed.
|
|
155
|
+
|
|
148
156
|
## 0.23.0
|
|
149
157
|
|
|
150
158
|
### Upgrading from 0.22.0
|
package/README.md
CHANGED
|
@@ -289,7 +289,7 @@ so new processors work without you shipping a release. `attachToCdp` derives the
|
|
|
289
289
|
`Fetch.enable` url patterns from that same list rather than a constant, which is
|
|
290
290
|
why the sync call belongs before the attach. `agentcard.checkout.cardUrlPatterns()` returns
|
|
291
291
|
those patterns if you arm a CDP connection yourself. Call
|
|
292
|
-
`GET /v2/checkout/recognizers?modes=token,cse,hosted_form,device_encrypt&capabilities=fiserv_card_capture,gmo_shared_pk,capture_context,juspay_txns.v1,datatrans_secure_fields_tokenize,authorize_net_dpm_form,pelecard_transaction,aci_split_card.v1,creditguard_pps_process,primer_payment_instruments,payrails_device_encrypt,converge_hpp_process,worldpay_eprotect_jsonp,wspay_confirm,shiji_sts_pinned_key,amadeus_checkout_actions.v1,worldline_mycheckout_form,verifone_card_capture,ekashu_hosted_payment_page,cybersource_sa_token_create,gravy_session_fields_put,moneris_hosted_tokenization,paddle_vgs_cards,redsys_hosted_payment_page`
|
|
292
|
+
`GET /v2/checkout/recognizers?modes=token,cse,hosted_form,device_encrypt&capabilities=fiserv_card_capture,gmo_shared_pk,capture_context,juspay_txns.v1,datatrans_secure_fields_tokenize,authorize_net_dpm_form,pelecard_transaction,aci_split_card.v1,creditguard_pps_process,primer_payment_instruments,payrails_device_encrypt,converge_hpp_process,worldpay_eprotect_jsonp,wspay_confirm,shiji_sts_pinned_key,amadeus_checkout_actions.v1,worldline_mycheckout_form,verifone_card_capture,ekashu_hosted_payment_page,cybersource_sa_token_create,gravy_session_fields_put,moneris_hosted_tokenization,paddle_vgs_cards,redsys_hosted_payment_page,paymongo_checkout_pay`
|
|
293
293
|
for the list this build syncs. The API serves Fiserv's card capture only to a
|
|
294
294
|
build that lists `fiserv_card_capture`, and only for a company Agentcard turned
|
|
295
295
|
Fiserv on for. An older SDK never pauses a Fiserv request it could not finish,
|
|
@@ -313,6 +313,10 @@ NMI never sees the card your agent typed. Redsys's hosted payment page
|
|
|
313
313
|
(`redsys_hosted_payment_page`) is served the same way: this build pauses the
|
|
314
314
|
merchant's signed entry form to `sis.redsys.es/sis/realizarPago` for the
|
|
315
315
|
cardholder's device and fails every other post your browser makes to Redsys.
|
|
316
|
+
PayMongo's hosted checkout (`paymongo_checkout_pay`) is served on the same terms:
|
|
317
|
+
this build pauses the checkout page's own card attach on `checkout.paymongo.com`,
|
|
318
|
+
the cardholder's device sends it through the checkout relay, and the page gets
|
|
319
|
+
PayMongo's answer.
|
|
316
320
|
CyberSource Secure Acceptance's token
|
|
317
321
|
create (`cybersource_sa_token_create`) is served on the same terms too; every
|
|
318
322
|
other company keeps the stop every build applies to a card posted to Secure
|
|
@@ -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: 0021d69ee6583030342d0f5ed625f1cb4bec88236f01fbf7450e41293f6f450d
|
|
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
|
|
@@ -663,10 +663,10 @@ var TYPED_ARRAY = Object.getPrototypeOf(Uint8Array.prototype);
|
|
|
663
663
|
var typedArrayName = Object.getOwnPropertyDescriptor(TYPED_ARRAY, Symbol.toStringTag).get;
|
|
664
664
|
function parseRsaPublicKey(key) {
|
|
665
665
|
const spki = key && typeof key === "object" ? key.spki : void 0;
|
|
666
|
-
const
|
|
666
|
+
const pin5 = key && typeof key === "object" ? key.sha256 : void 0;
|
|
667
667
|
if (typeof spki !== "string" || spki.length > 1024 || spki.length % 4 !== 0 || !BASE64.test(spki))
|
|
668
668
|
throw refuse6("key");
|
|
669
|
-
if (typeof
|
|
669
|
+
if (typeof pin5 !== "string" || !SHA256_HEX.test(pin5))
|
|
670
670
|
throw refuse6("key");
|
|
671
671
|
let binary;
|
|
672
672
|
try {
|
|
@@ -678,7 +678,7 @@ function parseRsaPublicKey(key) {
|
|
|
678
678
|
if (btoa(binary) !== spki)
|
|
679
679
|
throw refuse6("key");
|
|
680
680
|
const der = Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
681
|
-
if (sha256Hex(der) !==
|
|
681
|
+
if (sha256Hex(der) !== pin5)
|
|
682
682
|
throw refuse6("key");
|
|
683
683
|
const outer = readElement(der, 0, 48);
|
|
684
684
|
if (outer.end !== der.length || outer.start + RSA_ALGORITHM.length > der.length)
|
|
@@ -1540,6 +1540,460 @@ function gopayBodyVerdict(body, url) {
|
|
|
1540
1540
|
return refuse11("gopay_signature_mismatch");
|
|
1541
1541
|
return CARD10;
|
|
1542
1542
|
}
|
|
1543
|
+
// src/payrexx.js
|
|
1544
|
+
var PAYREXX_PSP = "payrexx";
|
|
1545
|
+
var PAYREXX_FRAME_HOST = "checkout.payrexx.com";
|
|
1546
|
+
var PAYREXX_PAY_PSP_ID = "44";
|
|
1547
|
+
var PAYREXX_BODY_KEYS = /* @__PURE__ */ Object.freeze([
|
|
1548
|
+
"instance",
|
|
1549
|
+
"pspId",
|
|
1550
|
+
"card[number]",
|
|
1551
|
+
"card[cardholder]",
|
|
1552
|
+
"card[exp_month]",
|
|
1553
|
+
"card[exp_year]",
|
|
1554
|
+
"card[cvc]",
|
|
1555
|
+
"card[brand]",
|
|
1556
|
+
"amount",
|
|
1557
|
+
"currency",
|
|
1558
|
+
"isTokenization",
|
|
1559
|
+
"isSubscription",
|
|
1560
|
+
"isPreAuthorization",
|
|
1561
|
+
"isMoto",
|
|
1562
|
+
"email"
|
|
1563
|
+
]);
|
|
1564
|
+
var BODY_LIMIT8 = 16384;
|
|
1565
|
+
var URL_LIMIT3 = 128;
|
|
1566
|
+
var FRAME_QUERY = /^\?pspId=([1-9][0-9]{0,3})&lang=([a-z]{2})$/;
|
|
1567
|
+
var GROUPED = /^[0-9]{4} [0-9]{4} [0-9]{4} [0-9]{4}$/;
|
|
1568
|
+
var VISA2 = /^4[0-9]{15}$/;
|
|
1569
|
+
var MASTERCARD2 = /^(?:5[1-5][0-9]{14}|2(?:2(?:2[1-9]|[3-9][0-9])|[3-6][0-9]{2}|7(?:[01][0-9]|20))[0-9]{12})$/;
|
|
1570
|
+
var SECURITY_CODE7 = /^[0-9]{3}$/;
|
|
1571
|
+
var MONTH3 = /^(?:0[1-9]|1[0-2])$/;
|
|
1572
|
+
var YEAR3 = /^20[0-9]{2}$/;
|
|
1573
|
+
var HOLDER2 = /^[\p{L}\p{M}][\p{L}\p{M} '.-]{0,44}$/u;
|
|
1574
|
+
var INSTANCE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
1575
|
+
var AMOUNT2 = /^[1-9][0-9]{0,8}$/;
|
|
1576
|
+
var CURRENCY = /^[A-Z]{3}$/;
|
|
1577
|
+
var EMAIL = /^[^\s@]{1,64}@[^\s@]{1,189}$/;
|
|
1578
|
+
var FLAGS = /* @__PURE__ */ Object.freeze(["isTokenization", "isSubscription", "isPreAuthorization", "isMoto"]);
|
|
1579
|
+
var CARD_DIGITS = /[0-9](?:[ -]?[0-9]){11}/;
|
|
1580
|
+
var THREE_DS_KEY = /^(?:threeDSServerTransID|threeDSVersion|postAuth|authData\[[A-Za-z]{1,32}\])$/;
|
|
1581
|
+
var refuse12 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
1582
|
+
var CARD11 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
1583
|
+
var CONTINUE5 = /* @__PURE__ */ Object.freeze({ verdict: "continue" });
|
|
1584
|
+
function payrexxFrameTarget(url) {
|
|
1585
|
+
if (typeof url !== "string" || url.length > URL_LIMIT3 || url.includes("#"))
|
|
1586
|
+
return null;
|
|
1587
|
+
let parsed;
|
|
1588
|
+
try {
|
|
1589
|
+
parsed = new URL(url);
|
|
1590
|
+
}
|
|
1591
|
+
catch {
|
|
1592
|
+
return null;
|
|
1593
|
+
}
|
|
1594
|
+
if (parsed.href !== url || parsed.protocol !== "https:" || parsed.port || parsed.username || parsed.password || parsed.hostname !== PAYREXX_FRAME_HOST || parsed.pathname !== "/")
|
|
1595
|
+
return null;
|
|
1596
|
+
const query = FRAME_QUERY.exec(parsed.search);
|
|
1597
|
+
return query ? Object.freeze({ pspId: query[1], lang: query[2] }) : null;
|
|
1598
|
+
}
|
|
1599
|
+
function readForm2(body) {
|
|
1600
|
+
if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT8)
|
|
1601
|
+
return null;
|
|
1602
|
+
try {
|
|
1603
|
+
const pairs = body.split("&").map((chunk) => {
|
|
1604
|
+
const eq = chunk.indexOf("=");
|
|
1605
|
+
if (eq <= 0)
|
|
1606
|
+
throw new URIError("not a form pair");
|
|
1607
|
+
return [decodeURIComponent(chunk.slice(0, eq)), decodeURIComponent(chunk.slice(eq + 1))];
|
|
1608
|
+
});
|
|
1609
|
+
return pairs.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&") === body ? pairs : null;
|
|
1610
|
+
}
|
|
1611
|
+
catch {
|
|
1612
|
+
return null;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
function cardNetwork2(digits) {
|
|
1616
|
+
if (VISA2.test(digits))
|
|
1617
|
+
return "visa";
|
|
1618
|
+
return MASTERCARD2.test(digits) ? "mastercard" : null;
|
|
1619
|
+
}
|
|
1620
|
+
function judgeBody2(body) {
|
|
1621
|
+
const pairs = readForm2(body);
|
|
1622
|
+
if (pairs === null)
|
|
1623
|
+
return { verdict: refuse12("payrexx_body_unsupported"), pairs: null };
|
|
1624
|
+
const keys = pairs.map(([key]) => key);
|
|
1625
|
+
const value = Object.fromEntries(pairs);
|
|
1626
|
+
if (!pairs.some(([key, entry]) => key.startsWith("card[") && key !== "card[brand]" && entry !== "")) {
|
|
1627
|
+
return pairs.some(([key, entry]) => key !== "encryptedCard" && CARD_DIGITS.test(entry)) ? { verdict: refuse12("payrexx_body_unsupported"), pairs: null } : { verdict: CONTINUE5, pairs: null };
|
|
1628
|
+
}
|
|
1629
|
+
if (new Set(keys).size !== keys.length)
|
|
1630
|
+
return { verdict: refuse12("payrexx_body_unsupported"), pairs: null };
|
|
1631
|
+
if (keys.includes("encryptedCard"))
|
|
1632
|
+
return { verdict: refuse12("payrexx_wallet_card"), pairs: null };
|
|
1633
|
+
if (keys.some((key) => THREE_DS_KEY.test(key)))
|
|
1634
|
+
return { verdict: refuse12("payrexx_three_ds_resend"), pairs: null };
|
|
1635
|
+
if (value.pspId !== PAYREXX_PAY_PSP_ID)
|
|
1636
|
+
return { verdict: refuse12("payrexx_other_provider"), pairs: null };
|
|
1637
|
+
if (keys.length !== PAYREXX_BODY_KEYS.length || !keys.every((key, at) => key === PAYREXX_BODY_KEYS[at])) {
|
|
1638
|
+
return { verdict: refuse12("payrexx_body_unsupported"), pairs: null };
|
|
1639
|
+
}
|
|
1640
|
+
const number = value["card[number]"];
|
|
1641
|
+
const network = GROUPED.test(number) ? cardNetwork2(number.replace(/ /g, "")) : null;
|
|
1642
|
+
if (network === null || value["card[brand]"] !== network || !SECURITY_CODE7.test(value["card[cvc]"]) || !MONTH3.test(value["card[exp_month]"]) || !YEAR3.test(value["card[exp_year]"]) || !HOLDER2.test(value["card[cardholder]"]) || !INSTANCE.test(value.instance) || !AMOUNT2.test(value.amount) || !CURRENCY.test(value.currency) || !(value.email === "" || value.email.length <= 254 && EMAIL.test(value.email)) || !FLAGS.every((flag) => value[flag] === "true" || value[flag] === "false")) {
|
|
1643
|
+
return { verdict: refuse12("payrexx_body_unsupported"), pairs: null };
|
|
1644
|
+
}
|
|
1645
|
+
if (value.isTokenization === "true" || value.isSubscription === "true")
|
|
1646
|
+
return { verdict: refuse12("payrexx_keep_on_file"), pairs: null };
|
|
1647
|
+
if (value.isPreAuthorization === "true" || value.isMoto === "true")
|
|
1648
|
+
return { verdict: refuse12("payrexx_not_a_sale"), pairs: null };
|
|
1649
|
+
return { verdict: CARD11, pairs };
|
|
1650
|
+
}
|
|
1651
|
+
function payrexxBodyVerdict(body) {
|
|
1652
|
+
return judgeBody2(body).verdict;
|
|
1653
|
+
}
|
|
1654
|
+
function payrexxGateVerdict(url, body) {
|
|
1655
|
+
const target = payrexxFrameTarget(url);
|
|
1656
|
+
if (target === null)
|
|
1657
|
+
return refuse12("payrexx_url_refused");
|
|
1658
|
+
const verdict = payrexxBodyVerdict(body);
|
|
1659
|
+
return verdict.verdict === "card" && target.pspId !== PAYREXX_PAY_PSP_ID ? refuse12("payrexx_other_provider") : verdict;
|
|
1660
|
+
}
|
|
1661
|
+
// src/komoju.js
|
|
1662
|
+
var KOMOJU_PSP = "komoju";
|
|
1663
|
+
var KOMOJU_HOST = "komoju.com";
|
|
1664
|
+
var KOMOJU_SECURE_TOKEN_URL = "https://komoju.com/api/v1/secure_tokens";
|
|
1665
|
+
var KOMOJU_TOKEN_URL = "https://komoju.com/api/v1/tokens";
|
|
1666
|
+
var KOMOJU_RETURN_URL_PREFIX = "https://multipay.komoju.com/secure-token-return.html?session_id=";
|
|
1667
|
+
var BODY_LIMIT9 = 4096;
|
|
1668
|
+
var URL_LIMIT4 = 128;
|
|
1669
|
+
var SESSION_ID = /^[a-z0-9]{25}$/;
|
|
1670
|
+
var SESSION_PAY_PATH = /^\/api\/v1\/sessions\/([a-z0-9]{25})\/pay$/;
|
|
1671
|
+
var TOKEN_ID = /^tok_[a-z0-9]{20,32}$/;
|
|
1672
|
+
var TOP_KEYS = /* @__PURE__ */ Object.freeze(["amount", "currency", "payment_details", "return_url"]);
|
|
1673
|
+
var KOMOJU_CARD_KEYS = /* @__PURE__ */ Object.freeze(["type", "name", "number", "month", "year", "verification_value", "email"]);
|
|
1674
|
+
var PAY_KEYS = /* @__PURE__ */ Object.freeze(["payment_details", "api_locale"]);
|
|
1675
|
+
var AMOUNT_MAX = 99999999;
|
|
1676
|
+
var CURRENCY2 = /^[A-Z]{3}$/;
|
|
1677
|
+
var NUMBER2 = /^[0-9]{12,19}$/;
|
|
1678
|
+
var MONTH4 = /^(?:0[1-9]|1[0-2])$/;
|
|
1679
|
+
var YEAR4 = /^[0-9]{2}$/;
|
|
1680
|
+
var SECURITY_CODE8 = /^[0-9]{3,4}$/;
|
|
1681
|
+
var PAGE_NAME = /^[\x20-\x7e]{1,64}$/;
|
|
1682
|
+
var NAME_DIGITS_MAX = 11;
|
|
1683
|
+
var EMAIL2 = /^[\x21-\x7e]{1,64}@[\x21-\x7e]{1,189}$/;
|
|
1684
|
+
var LOCALE = /^[a-z]{2}(?:-[A-Za-z]{2,4})?$/;
|
|
1685
|
+
var PAN_LIKE3 = /(?:[0-9][ _.:-]*){12,}/;
|
|
1686
|
+
var record9 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1687
|
+
var sameKeys5 = (value, keys) => {
|
|
1688
|
+
const own2 = Object.keys(value);
|
|
1689
|
+
return own2.length === keys.length && own2.every((key, at) => key === keys[at]);
|
|
1690
|
+
};
|
|
1691
|
+
var refuse13 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
1692
|
+
var CARD12 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
1693
|
+
var CONTINUE6 = /* @__PURE__ */ Object.freeze({ verdict: "continue" });
|
|
1694
|
+
function komojuTarget(url) {
|
|
1695
|
+
if (typeof url !== "string" || url.length > URL_LIMIT4 || url.includes("?") || url.includes("#"))
|
|
1696
|
+
return null;
|
|
1697
|
+
let parsed;
|
|
1698
|
+
try {
|
|
1699
|
+
parsed = new URL(url);
|
|
1700
|
+
}
|
|
1701
|
+
catch {
|
|
1702
|
+
return null;
|
|
1703
|
+
}
|
|
1704
|
+
if (parsed.href !== url || parsed.protocol !== "https:" || parsed.port || parsed.username || parsed.password || parsed.hostname !== KOMOJU_HOST)
|
|
1705
|
+
return null;
|
|
1706
|
+
if (url === KOMOJU_SECURE_TOKEN_URL)
|
|
1707
|
+
return Object.freeze({ kind: "secure_token" });
|
|
1708
|
+
if (url === KOMOJU_TOKEN_URL)
|
|
1709
|
+
return Object.freeze({ kind: "token" });
|
|
1710
|
+
const pay = SESSION_PAY_PATH.exec(parsed.pathname);
|
|
1711
|
+
return pay ? Object.freeze({ kind: "session_pay", session: pay[1] }) : null;
|
|
1712
|
+
}
|
|
1713
|
+
function readJson3(body) {
|
|
1714
|
+
if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT9)
|
|
1715
|
+
return null;
|
|
1716
|
+
try {
|
|
1717
|
+
const value = JSON.parse(body);
|
|
1718
|
+
return record9(value) && JSON.stringify(value) === body ? value : null;
|
|
1719
|
+
}
|
|
1720
|
+
catch {
|
|
1721
|
+
return null;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
function carriesCard(details) {
|
|
1725
|
+
return record9(details) && Object.keys(details).some((key) => /^(?:number|card_?number|pan)$/i.test(key));
|
|
1726
|
+
}
|
|
1727
|
+
function judgeSecureToken(value) {
|
|
1728
|
+
if (!sameKeys5(value, TOP_KEYS))
|
|
1729
|
+
return "komoju_body_unsupported";
|
|
1730
|
+
const details = value.payment_details;
|
|
1731
|
+
if (!Number.isSafeInteger(value.amount) || value.amount < 0 || value.amount > AMOUNT_MAX || typeof value.currency !== "string" || !CURRENCY2.test(value.currency) || typeof value.return_url !== "string" || !value.return_url.startsWith(KOMOJU_RETURN_URL_PREFIX) || !SESSION_ID.test(value.return_url.slice(KOMOJU_RETURN_URL_PREFIX.length)) || !record9(details) || !sameKeys5(details, KOMOJU_CARD_KEYS) || details.type !== "credit_card" || typeof details.name !== "string" || !PAGE_NAME.test(details.name) || details.name.trim() === "" || details.name.replace(/[^0-9]/g, "").length > NAME_DIGITS_MAX || typeof details.number !== "string" || !NUMBER2.test(details.number) || typeof details.month !== "string" || !MONTH4.test(details.month) || typeof details.year !== "string" || !YEAR4.test(details.year) || typeof details.verification_value !== "string" || !SECURITY_CODE8.test(details.verification_value) || typeof details.email !== "string" || !EMAIL2.test(details.email) || PAN_LIKE3.test(details.email)) {
|
|
1732
|
+
return "komoju_body_unsupported";
|
|
1733
|
+
}
|
|
1734
|
+
return value.amount === 0 ? "komoju_keeps_card" : null;
|
|
1735
|
+
}
|
|
1736
|
+
function tokenPay(value) {
|
|
1737
|
+
const keys = Object.keys(value);
|
|
1738
|
+
return (sameKeys5(value, PAY_KEYS) || keys.length === 1 && keys[0] === "payment_details") && typeof value.payment_details === "string" && TOKEN_ID.test(value.payment_details) && (value.api_locale === void 0 || typeof value.api_locale === "string" && LOCALE.test(value.api_locale));
|
|
1739
|
+
}
|
|
1740
|
+
function komojuBodyVerdict(body, url) {
|
|
1741
|
+
let target = null;
|
|
1742
|
+
if (url !== void 0) {
|
|
1743
|
+
target = komojuTarget(url);
|
|
1744
|
+
if (target === null)
|
|
1745
|
+
return refuse13("komoju_url_refused");
|
|
1746
|
+
}
|
|
1747
|
+
const value = readJson3(body);
|
|
1748
|
+
if (target !== null && target.kind !== "secure_token") {
|
|
1749
|
+
if (value !== null && target.kind === "session_pay" && tokenPay(value))
|
|
1750
|
+
return CONTINUE6;
|
|
1751
|
+
return refuse13(value !== null && carriesCard(value.payment_details) ? "komoju_fallback_refused" : "komoju_body_unsupported");
|
|
1752
|
+
}
|
|
1753
|
+
if (value === null)
|
|
1754
|
+
return refuse13("komoju_body_unsupported");
|
|
1755
|
+
if (target === null && tokenPay(value))
|
|
1756
|
+
return CONTINUE6;
|
|
1757
|
+
if (target === null && !Object.prototype.hasOwnProperty.call(value, "return_url") && carriesCard(value.payment_details)) {
|
|
1758
|
+
return refuse13("komoju_fallback_refused");
|
|
1759
|
+
}
|
|
1760
|
+
const code = judgeSecureToken(value);
|
|
1761
|
+
return code === null ? CARD12 : refuse13(code);
|
|
1762
|
+
}
|
|
1763
|
+
// src/paygent.js
|
|
1764
|
+
var PAYGENT_PSP = "paygent";
|
|
1765
|
+
var PAYGENT_TOKEN_HOSTS = /* @__PURE__ */ Object.freeze({
|
|
1766
|
+
production: Object.freeze(["token.paygent.co.jp"]),
|
|
1767
|
+
sandbox: Object.freeze(["sandbox.paygent.co.jp"])
|
|
1768
|
+
});
|
|
1769
|
+
var PAYGENT_TOKEN_PATH = "/n/token/request";
|
|
1770
|
+
var PAYGENT_BODY_KEYS = /* @__PURE__ */ Object.freeze([
|
|
1771
|
+
"merchant_id",
|
|
1772
|
+
"token_generate_key",
|
|
1773
|
+
"card_number",
|
|
1774
|
+
"card_expire_year",
|
|
1775
|
+
"card_expire_month",
|
|
1776
|
+
"card_cvc",
|
|
1777
|
+
"card_name"
|
|
1778
|
+
]);
|
|
1779
|
+
var BODY_LIMIT10 = 2048;
|
|
1780
|
+
var URL_LIMIT5 = 128;
|
|
1781
|
+
var MERCHANT_ID = /^[0-9]{5,6}$/;
|
|
1782
|
+
var LIVE_KEY = /^live_[A-Za-z0-9]{24}$/;
|
|
1783
|
+
var SANDBOX_KEY = /^(?!live_)[a-z]{1,8}_[A-Za-z0-9]{24}$/;
|
|
1784
|
+
var NUMBER3 = /^[0-9]{12,19}$/;
|
|
1785
|
+
var YEAR5 = /^[0-9]{2}$/;
|
|
1786
|
+
var MONTH5 = /^(?:0?[1-9]|1[0-2])$/;
|
|
1787
|
+
var SECURITY_CODE9 = /^[0-9]{3,4}$/;
|
|
1788
|
+
var PAGE_HOLDER = /^(?:[A-Za-z][A-Za-z .'-]{0,44})?$/;
|
|
1789
|
+
var refuse14 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
1790
|
+
var CARD13 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
1791
|
+
function paygentEnvironment(url) {
|
|
1792
|
+
if (typeof url !== "string" || url.length > URL_LIMIT5 || url.includes("?") || url.includes("#"))
|
|
1793
|
+
return null;
|
|
1794
|
+
let parsed;
|
|
1795
|
+
try {
|
|
1796
|
+
parsed = new URL(url);
|
|
1797
|
+
}
|
|
1798
|
+
catch {
|
|
1799
|
+
return null;
|
|
1800
|
+
}
|
|
1801
|
+
if (parsed.href !== url || parsed.protocol !== "https:" || parsed.port || parsed.username || parsed.password || parsed.pathname !== PAYGENT_TOKEN_PATH)
|
|
1802
|
+
return null;
|
|
1803
|
+
if (PAYGENT_TOKEN_HOSTS.production.includes(parsed.hostname))
|
|
1804
|
+
return "production";
|
|
1805
|
+
if (PAYGENT_TOKEN_HOSTS.sandbox.includes(parsed.hostname))
|
|
1806
|
+
return "sandbox";
|
|
1807
|
+
return null;
|
|
1808
|
+
}
|
|
1809
|
+
function readBody5(body) {
|
|
1810
|
+
if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT10)
|
|
1811
|
+
return null;
|
|
1812
|
+
try {
|
|
1813
|
+
const pairs = body.split("&").map((chunk) => {
|
|
1814
|
+
const eq = chunk.indexOf("=");
|
|
1815
|
+
if (eq <= 0)
|
|
1816
|
+
throw new URIError("not a pair");
|
|
1817
|
+
return [chunk.slice(0, eq), decodeURIComponent(chunk.slice(eq + 1))];
|
|
1818
|
+
});
|
|
1819
|
+
return pairs.map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&") === body ? pairs : null;
|
|
1820
|
+
}
|
|
1821
|
+
catch {
|
|
1822
|
+
return null;
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
var CVC_ONLY_KEYS = /* @__PURE__ */ Object.freeze(["merchant_id", "token_generate_key", "card_cvc", "cvc_only_flg"]);
|
|
1826
|
+
var sameKeys6 = (pairs, keys) => pairs.length === keys.length && pairs.every(([key], at) => key === keys[at]);
|
|
1827
|
+
function judgeBody3(body) {
|
|
1828
|
+
const pairs = readBody5(body);
|
|
1829
|
+
if (pairs === null)
|
|
1830
|
+
return { code: "paygent_body_unsupported", pairs: null };
|
|
1831
|
+
if (sameKeys6(pairs, CVC_ONLY_KEYS))
|
|
1832
|
+
return { code: "paygent_cvc_only", pairs: null };
|
|
1833
|
+
if (!sameKeys6(pairs, PAYGENT_BODY_KEYS))
|
|
1834
|
+
return { code: "paygent_body_unsupported", pairs: null };
|
|
1835
|
+
const value = Object.fromEntries(pairs);
|
|
1836
|
+
if (!MERCHANT_ID.test(value.merchant_id) || !(LIVE_KEY.test(value.token_generate_key) || SANDBOX_KEY.test(value.token_generate_key)) || !NUMBER3.test(value.card_number) || !YEAR5.test(value.card_expire_year) || !MONTH5.test(value.card_expire_month) || !SECURITY_CODE9.test(value.card_cvc) || !PAGE_HOLDER.test(value.card_name)) {
|
|
1837
|
+
return { code: "paygent_body_unsupported", pairs: null };
|
|
1838
|
+
}
|
|
1839
|
+
return { code: null, pairs };
|
|
1840
|
+
}
|
|
1841
|
+
function paygentBodyVerdict(body) {
|
|
1842
|
+
const { code } = judgeBody3(body);
|
|
1843
|
+
return code === null ? CARD13 : refuse14(code);
|
|
1844
|
+
}
|
|
1845
|
+
function paygentRequestVerdict(url, body) {
|
|
1846
|
+
const environment = paygentEnvironment(url);
|
|
1847
|
+
if (environment === null)
|
|
1848
|
+
return refuse14("paygent_url_refused");
|
|
1849
|
+
const { code, pairs } = judgeBody3(body);
|
|
1850
|
+
if (code !== null)
|
|
1851
|
+
return refuse14(code);
|
|
1852
|
+
const key = pairs[1][1];
|
|
1853
|
+
return (environment === "production" ? LIVE_KEY : SANDBOX_KEY).test(key) ? CARD13 : refuse14("paygent_key_environment");
|
|
1854
|
+
}
|
|
1855
|
+
// src/multipart-form.js
|
|
1856
|
+
var BODY_LIMIT11 = 65536;
|
|
1857
|
+
var CRLF = "\r\n";
|
|
1858
|
+
var BOUNDARY_CHARACTERS = "[A-Za-z0-9'+_.-]{1,70}";
|
|
1859
|
+
var BOUNDARY = new RegExp(`^${BOUNDARY_CHARACTERS}$`);
|
|
1860
|
+
var CONTENT_TYPE = new RegExp(`^multipart/form-data; ?boundary=(${BOUNDARY_CHARACTERS})$`, "i");
|
|
1861
|
+
var DISPOSITION = /^content-disposition: ?form-data; name="([^"\\\r\n]*)"$/i;
|
|
1862
|
+
var FILE_HINT = /^content-disposition:.*;\s*filename\*?=|^content-type:/i;
|
|
1863
|
+
var refusal = (code) => Object.freeze({ refusal: code });
|
|
1864
|
+
var listed = (list, name) => Array.isArray(list) && list.includes(name);
|
|
1865
|
+
function multipartBoundary(contentType) {
|
|
1866
|
+
const match = typeof contentType === "string" ? CONTENT_TYPE.exec(contentType) : null;
|
|
1867
|
+
return match ? match[1] : null;
|
|
1868
|
+
}
|
|
1869
|
+
function boundaryOf(body, contentType) {
|
|
1870
|
+
const lineEnd = body.indexOf(CRLF);
|
|
1871
|
+
const firstLine = body.startsWith("--") && lineEnd !== -1 ? body.slice(2, lineEnd) : null;
|
|
1872
|
+
if (firstLine === null || !BOUNDARY.test(firstLine))
|
|
1873
|
+
return null;
|
|
1874
|
+
if (contentType === void 0 || contentType === null)
|
|
1875
|
+
return firstLine;
|
|
1876
|
+
return multipartBoundary(contentType) === firstLine ? firstLine : null;
|
|
1877
|
+
}
|
|
1878
|
+
function readMultipartForm(body, contentType) {
|
|
1879
|
+
if (typeof body !== "string" || body.length > BODY_LIMIT11)
|
|
1880
|
+
return refusal("multipart_not_form");
|
|
1881
|
+
const boundary = boundaryOf(body, contentType);
|
|
1882
|
+
if (boundary === null)
|
|
1883
|
+
return refusal("multipart_not_form");
|
|
1884
|
+
const dash = `--${boundary}`;
|
|
1885
|
+
const delimiter = `${CRLF}${dash}`;
|
|
1886
|
+
const parts = [];
|
|
1887
|
+
let position = dash.length + CRLF.length;
|
|
1888
|
+
for (;;) {
|
|
1889
|
+
const headerEnd = body.indexOf(`${CRLF}${CRLF}`, position);
|
|
1890
|
+
if (headerEnd === -1)
|
|
1891
|
+
return refusal("multipart_not_form");
|
|
1892
|
+
const headers = body.slice(position, headerEnd).split(CRLF);
|
|
1893
|
+
if (headers.some((line) => FILE_HINT.test(line)))
|
|
1894
|
+
return refusal("multipart_file_part");
|
|
1895
|
+
const disposition = headers.length === 1 ? DISPOSITION.exec(headers[0]) : null;
|
|
1896
|
+
if (!disposition)
|
|
1897
|
+
return refusal("multipart_not_form");
|
|
1898
|
+
const start = headerEnd + 2 * CRLF.length;
|
|
1899
|
+
const end = body.indexOf(delimiter, start);
|
|
1900
|
+
if (end === -1)
|
|
1901
|
+
return refusal("multipart_not_form");
|
|
1902
|
+
parts.push(Object.freeze({ name: disposition[1], value: body.slice(start, end), start, end }));
|
|
1903
|
+
position = end + delimiter.length;
|
|
1904
|
+
if (body.startsWith("--", position)) {
|
|
1905
|
+
const rest = body.slice(position + 2);
|
|
1906
|
+
return rest === "" || rest === CRLF ? Object.freeze({ boundary, parts: Object.freeze(parts) }) : refusal("multipart_not_form");
|
|
1907
|
+
}
|
|
1908
|
+
if (!body.startsWith(CRLF, position))
|
|
1909
|
+
return refusal("multipart_not_form");
|
|
1910
|
+
position += CRLF.length;
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
function soundRule(rule) {
|
|
1914
|
+
if (!rule || typeof rule !== "object" || !Array.isArray(rule.parts) || !Array.isArray(rule.card) || rule.card.length === 0)
|
|
1915
|
+
return false;
|
|
1916
|
+
const names = [...rule.parts, ...rule.card];
|
|
1917
|
+
if (!names.every((name) => typeof name === "string" && name !== ""))
|
|
1918
|
+
return false;
|
|
1919
|
+
return new Set(rule.card).size === rule.card.length && rule.card.every((name) => rule.parts.includes(name));
|
|
1920
|
+
}
|
|
1921
|
+
function judge(body, contentType, rule) {
|
|
1922
|
+
if (!soundRule(rule))
|
|
1923
|
+
throw new TypeError("A multipart rule lists its part names and, among them, its card parts.");
|
|
1924
|
+
const read = readMultipartForm(body, contentType);
|
|
1925
|
+
if (read.refusal)
|
|
1926
|
+
return read;
|
|
1927
|
+
if (read.parts.some((part) => !listed(rule.parts, part.name)))
|
|
1928
|
+
return refusal("multipart_unknown_part");
|
|
1929
|
+
for (const name of rule.card) {
|
|
1930
|
+
const count = read.parts.filter((part) => part.name === name).length;
|
|
1931
|
+
if (count === 0)
|
|
1932
|
+
return refusal("multipart_missing_card_part");
|
|
1933
|
+
if (count > 1)
|
|
1934
|
+
return refusal("multipart_duplicate_card_part");
|
|
1935
|
+
}
|
|
1936
|
+
return read;
|
|
1937
|
+
}
|
|
1938
|
+
function multipartFormRefusal(body, contentType, rule) {
|
|
1939
|
+
const read = judge(body, contentType, rule);
|
|
1940
|
+
return read.refusal ?? null;
|
|
1941
|
+
}
|
|
1942
|
+
// src/pay-nl.js
|
|
1943
|
+
var PAY_NL_PSP = "pay_nl";
|
|
1944
|
+
var PAY_NL_TOKEN_URL = "https://payment.pay.nl/v1/Payment/getCardToken/json";
|
|
1945
|
+
var PAY_NL_PARTS = /* @__PURE__ */ Object.freeze([
|
|
1946
|
+
"serviceId",
|
|
1947
|
+
"requestToken",
|
|
1948
|
+
"payment[method]",
|
|
1949
|
+
"payment[card][number]",
|
|
1950
|
+
"payment[card][name]",
|
|
1951
|
+
"payment[card][cvc]",
|
|
1952
|
+
"payment[card][expire_month]",
|
|
1953
|
+
"payment[card][expire_year]"
|
|
1954
|
+
]);
|
|
1955
|
+
var PAY_NL_CARD_PARTS = /* @__PURE__ */ Object.freeze([
|
|
1956
|
+
"payment[card][number]",
|
|
1957
|
+
"payment[card][name]",
|
|
1958
|
+
"payment[card][cvc]",
|
|
1959
|
+
"payment[card][expire_month]",
|
|
1960
|
+
"payment[card][expire_year]"
|
|
1961
|
+
]);
|
|
1962
|
+
var pin4 = (fields) => Object.freeze({ ...fields, origins: Object.freeze([...fields.origins]), serviceIds: Object.freeze([...fields.serviceIds]) });
|
|
1963
|
+
var PAY_NL_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
|
|
1964
|
+
pin4({ id: "weeztix", merchant: "Weeztix", origins: ["https://shop.weeztix.com"], serviceIds: ["SL-6399-2740"] })
|
|
1965
|
+
]);
|
|
1966
|
+
var RULE = /* @__PURE__ */ Object.freeze({ parts: PAY_NL_PARTS, card: PAY_NL_CARD_PARTS });
|
|
1967
|
+
var SERVICE_ID = /^SL-\d{4}-\d{4}$/;
|
|
1968
|
+
var REQUEST_TOKEN = /^[0-9a-f]{40}$/i;
|
|
1969
|
+
var NUMBER4 = /^[0-9]{8,19}$/;
|
|
1970
|
+
var HOLDER3 = /^\p{L}+(?: \p{L}+)*$/u;
|
|
1971
|
+
var HOLDER_LIMIT2 = 256;
|
|
1972
|
+
var SECURITY_CODE10 = /^[0-9]{3,4}$/;
|
|
1973
|
+
var MONTH6 = /^(?:0[1-9]|1[0-2])$/;
|
|
1974
|
+
var YEAR6 = /^[0-9]{2}$/;
|
|
1975
|
+
var refuse15 = (code) => Object.freeze({ verdict: "refuse", code });
|
|
1976
|
+
var CARD14 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
|
|
1977
|
+
function isPayNlTokenUrl(url) {
|
|
1978
|
+
return url === PAY_NL_TOKEN_URL;
|
|
1979
|
+
}
|
|
1980
|
+
function readBody6(body, contentType) {
|
|
1981
|
+
if (multipartFormRefusal(body, contentType, RULE) !== null)
|
|
1982
|
+
return null;
|
|
1983
|
+
const { parts } = readMultipartForm(body, contentType);
|
|
1984
|
+
if (parts.length !== PAY_NL_PARTS.length || !parts.every((part, at) => part.name === PAY_NL_PARTS[at]))
|
|
1985
|
+
return null;
|
|
1986
|
+
const value = Object.fromEntries(parts.map((part) => [part.name, part.value]));
|
|
1987
|
+
const name = value["payment[card][name]"];
|
|
1988
|
+
if (!SERVICE_ID.test(value.serviceId) || !REQUEST_TOKEN.test(value.requestToken) || value["payment[method]"] !== "card" || !NUMBER4.test(value["payment[card][number]"]) || name.length > HOLDER_LIMIT2 || !HOLDER3.test(name) || !SECURITY_CODE10.test(value["payment[card][cvc]"]) || !MONTH6.test(value["payment[card][expire_month]"]) || !YEAR6.test(value["payment[card][expire_year]"]))
|
|
1989
|
+
return null;
|
|
1990
|
+
return value;
|
|
1991
|
+
}
|
|
1992
|
+
function payNlBodyVerdict(body, contentType) {
|
|
1993
|
+
return readBody6(body, contentType) === null ? refuse15("pay_nl_body_unsupported") : CARD14;
|
|
1994
|
+
}
|
|
1995
|
+
var ERROR_KEYS = /* @__PURE__ */ Object.freeze(["errorId", "errorTag", "errorMessage", "errorFieldName"]);
|
|
1996
|
+
var REQUEST_KEYS = /* @__PURE__ */ Object.freeze(["result", ...ERROR_KEYS]);
|
|
1543
1997
|
// src/card-fields.js
|
|
1544
1998
|
function processorGate(recognizer, url, body) {
|
|
1545
1999
|
if (recognizer && recognizer.psp === LYRA_PSP)
|
|
@@ -1549,6 +2003,8 @@ function processorGate(recognizer, url, body) {
|
|
|
1549
2003
|
return { verdict: "refuse", code: "wix_payments_url_refused" };
|
|
1550
2004
|
return wixPaymentsBodyVerdict(body);
|
|
1551
2005
|
}
|
|
2006
|
+
if (recognizer && recognizer.psp === KOMOJU_PSP)
|
|
2007
|
+
return komojuBodyVerdict(body, url);
|
|
1552
2008
|
if (recognizer && recognizer.psp === PADDLE_VGS_PSP) {
|
|
1553
2009
|
if (url !== void 0 && !isPaddleVgsCardsUrl(url))
|
|
1554
2010
|
return { verdict: "refuse", code: "paddle_vgs_url_refused" };
|
|
@@ -1559,6 +2015,17 @@ function processorGate(recognizer, url, body) {
|
|
|
1559
2015
|
return { verdict: "refuse", code: "payfast_url_refused" };
|
|
1560
2016
|
return payfastBodyVerdict(body);
|
|
1561
2017
|
}
|
|
2018
|
+
if (recognizer && recognizer.psp === PAYREXX_PSP) {
|
|
2019
|
+
return url !== void 0 ? payrexxGateVerdict(url, body) : payrexxBodyVerdict(body);
|
|
2020
|
+
}
|
|
2021
|
+
if (recognizer && recognizer.psp === PAYGENT_PSP) {
|
|
2022
|
+
return url !== void 0 ? paygentRequestVerdict(url, body) : paygentBodyVerdict(body);
|
|
2023
|
+
}
|
|
2024
|
+
if (recognizer && recognizer.psp === PAY_NL_PSP) {
|
|
2025
|
+
if (url !== void 0 && !isPayNlTokenUrl(url))
|
|
2026
|
+
return { verdict: "refuse", code: "pay_nl_url_refused" };
|
|
2027
|
+
return payNlBodyVerdict(body);
|
|
2028
|
+
}
|
|
1562
2029
|
if (recognizer && recognizer.psp === PADDLE_TOKENEX_PSP) {
|
|
1563
2030
|
const target = url !== void 0 ? paddleTokenexPayTarget(url) : null;
|
|
1564
2031
|
if (url !== void 0 && target === null)
|
package/dist/client.d.ts
CHANGED
|
@@ -249,6 +249,14 @@ export declare const SUPPORTED_REGISTRY_FEATURES: readonly string[];
|
|
|
249
249
|
* Wix's token. A second card request from the frame, which Wix's frame sends after a 401
|
|
250
250
|
* or 403 under a fresh instance, is never answered from the same approval. Served on the
|
|
251
251
|
* same terms: only to a build that lists it, for a company Agentcard turned Wix on for.
|
|
252
|
+
*
|
|
253
|
+
* `paymongo_checkout_pay`: the card attach PayMongo's hosted checkout sends to its own page
|
|
254
|
+
* URL (checkout.paymongo.com/<session>) as a Next.js Server Action. This build pauses only the
|
|
255
|
+
* page's own card attach and answers it with PayMongo's answer as the API recorded it, under the
|
|
256
|
+
* RSC type the page reads: a paid payment intent, or the step toward PayMongo's 3-D Secure page
|
|
257
|
+
* that the page then runs in the agent's browser. The cardholder's device sends the card through
|
|
258
|
+
* the checkout relay. Served on the same terms: only to a build that lists it, for a company
|
|
259
|
+
* Agentcard turned PayMongo on for.
|
|
252
260
|
*/
|
|
253
261
|
export declare const SUPPORTED_CAPABILITIES: readonly string[];
|
|
254
262
|
/**
|
|
@@ -1176,6 +1184,9 @@ export declare class CheckoutAuthError extends Error {
|
|
|
1176
1184
|
* https://www.paypal.com/v2/checkout/orders/{order}/confirm-payment-source.
|
|
1177
1185
|
* On PayMongo's checkout host the session id reads `{session}`: https://checkout.paymongo.com/{session}.
|
|
1178
1186
|
* On GoPay's card frame hosts the card form's token reads `{token}`: https://secure.gopay.com/gp-card-comm/h/{token}.
|
|
1187
|
+
* On komoju.com a session id reads `{session}`: https://komoju.com/api/v1/sessions/{session}/pay.
|
|
1188
|
+
* On Przelewy24's hosts the transaction's token reads `{token}`:
|
|
1189
|
+
* https://secure.przelewy24.pl/trnRequest/{token}.
|
|
1179
1190
|
* A string that is not a URL keeps the part before any `?` or `#`, with the
|
|
1180
1191
|
* same segments masked.
|
|
1181
1192
|
*/
|