@agent-cards/sdk 0.23.18 → 0.23.19
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 +10 -0
- package/README.md +12 -3
- package/dist/aci.d.ts +0 -7
- package/dist/aci.js +0 -19
- package/dist/card-fields.generated.js +498 -35
- package/dist/client.d.ts +13 -1
- package/dist/client.js +110 -31
- package/dist/datatrans.d.ts +2 -2
- package/dist/datatrans.generated.js +16 -11
- package/dist/datatrans.js +2 -2
- package/dist/pelecard.generated.d.ts +2 -1
- package/dist/pelecard.generated.js +14 -5
- package/dist/primer.generated.js +515 -52
- package/dist/stripe-checkout.generated.js +197 -1
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -1187,8 +1187,20 @@ export declare class CheckoutAuthError extends Error {
|
|
|
1187
1187
|
* On komoju.com a session id reads `{session}`: https://komoju.com/api/v1/sessions/{session}/pay.
|
|
1188
1188
|
* On Przelewy24's hosts the transaction's token reads `{token}`:
|
|
1189
1189
|
* https://secure.przelewy24.pl/trnRequest/{token}.
|
|
1190
|
+
* On Revolut's card payment and hosted checkout hosts an order or payment token reads `{token}`:
|
|
1191
|
+
* https://merchant.revolut.com/api/public/orders/{token}/payments.
|
|
1192
|
+
* On SumUp's API host a checkout id reads `{checkout}`: https://api.sumup.com/v0.2/checkouts/{checkout}.
|
|
1193
|
+
* On Paytrail's hosts a payment's id reads `{transaction}`:
|
|
1194
|
+
* https://services.paytrail.com/payments/{transaction}/click-to-pay/loading-and-redirect, and on Payment
|
|
1195
|
+
* Highway's a card page's session reads `{session}`.
|
|
1190
1196
|
* A string that is not a URL keeps the part before any `?` or `#`, with the
|
|
1191
|
-
* same segments masked.
|
|
1197
|
+
* same segments masked for every processor whose host it names.
|
|
1198
|
+
* A URL is read by its host only when the parser finds one. Text before a URL
|
|
1199
|
+
* can read as a scheme: `see:https://api.stripe.com/...` parses as a URL with
|
|
1200
|
+
* the scheme `see`, no host, and the whole Stripe URL as its path, so a parse
|
|
1201
|
+
* with no host is read as a string that is not a URL. A path can carry a URL of
|
|
1202
|
+
* its own, as `http:https://api.stripe.com/...` does once `https` reads as its
|
|
1203
|
+
* host, so every processor whose host the path names masks the URL too.
|
|
1192
1204
|
*/
|
|
1193
1205
|
export declare function redactUrl(raw: string): string;
|
|
1194
1206
|
export interface VaultClientOptions {
|
package/dist/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BUILTIN_REGISTRY, cardUrlPatterns as deriveCardUrlPatterns, LOOPBACK_URL_PATTERNS, findRecognizer, isRecognizerRequest, recognizerRequestMethod, recognizerRequiresPreparation, urlsOnProcessorHosts, } from './registry.js';
|
|
2
2
|
import { armServedProfiles, classifyProfileRequest, declareSandboxMerchant, merchantProfileEnvironment, merchantProfileFor, merchantProfileUrlPatterns, } from './merchant-hosted.js';
|
|
3
3
|
import { merchantAmountExchanges, merchantAmountFromResponses, merchantProfilePricedBySource, merchantTotalWire, MerchantTotalWait, } from './merchant-total.js';
|
|
4
|
-
import { ACI_CAPABILITY, ACI_PSP, aciCardLegAnswer
|
|
4
|
+
import { ACI_CAPABILITY, ACI_PSP, aciCardLegAnswer } from './aci.js';
|
|
5
5
|
import { EPROTECT_CAPABILITY, EPROTECT_PSP, eprotectCheckoutRefusal, eprotectScriptAnswer, isEprotectRegistration } from './eprotect.js';
|
|
6
6
|
import { isMercadoTokenRequest, parseMercadoCheckoutContext, validateMercadoProcessorContext } from './mercado-checkout.generated.js';
|
|
7
7
|
import { matchesPreparation, preparationMode, validPreparationEnvironment } from './prepared-processor.js';
|
|
@@ -1041,9 +1041,12 @@ function maskCreditguardTxId(path) {
|
|
|
1041
1041
|
* /v2/payment/{client_secret}/confirm (and /show, /status), and with the page's
|
|
1042
1042
|
* public token that secret confirms the payment and reads its status.
|
|
1043
1043
|
*/
|
|
1044
|
-
const
|
|
1044
|
+
const PATH_SECRET_HOST = /^(?:proxy(?:-test)?\.wspay\.biz|ipg(?:test)?\.monri\.com)\.?$/i;
|
|
1045
1045
|
/** The segment after a `payment` segment, in any case (the WSPay proxy is IIS) and any spelling of its slashes. */
|
|
1046
1046
|
const PATH_SECRET = new RegExp(`(${SLASH}payment${SLASH}+)[^/]*`, 'gi');
|
|
1047
|
+
function maskPathSecret(path) {
|
|
1048
|
+
return path.replace(PATH_SECRET, '$1{client_secret}');
|
|
1049
|
+
}
|
|
1047
1050
|
/**
|
|
1048
1051
|
* Gravy (Gr4vy) gives every merchant hosts of its own under gr4vy.app, and Secure Fields sends the
|
|
1049
1052
|
* card to /checkout/sessions/<session id>/fields with no auth header, so the session id is that
|
|
@@ -1084,10 +1087,11 @@ function maskStripeSession(path) {
|
|
|
1084
1087
|
return path.replace(STRIPE_PAYMENT_PAGE, '$1{session}');
|
|
1085
1088
|
}
|
|
1086
1089
|
/**
|
|
1087
|
-
*
|
|
1088
|
-
*
|
|
1089
|
-
*
|
|
1090
|
-
* ACI's shape
|
|
1090
|
+
* ACI's checkout id is that checkout's only capability: once the card-number request carried the real
|
|
1091
|
+
* card, anyone holding it can post /payment against that card for the rest of the checkout's 30
|
|
1092
|
+
* minutes. On ACI's hosts, with a trailing dot, an escaped or doubled slash, in another case or in a
|
|
1093
|
+
* string that is not a URL, the segment after /v1/checkouts/ and any id of ACI's shape
|
|
1094
|
+
* (<32 hex>.<cluster>-vm-tx<nn>) read {checkoutId}.
|
|
1091
1095
|
*/
|
|
1092
1096
|
const ACI_HOST = /^(?:eu-prod\.|eu-test\.|test\.)?oppwa\.com\.?$/i;
|
|
1093
1097
|
const ACI_CHECKOUT = new RegExp(`(${SLASH}v1${SLASH}checkouts${SLASH}+)[^/]*`, 'gi');
|
|
@@ -1162,21 +1166,36 @@ function maskGopayToken(path) {
|
|
|
1162
1166
|
* the merchant's publishable key, which is public (payment-core komoju.js): with the session id, anyone
|
|
1163
1167
|
* can read that session's amount and buyer email and pay it. On komoju.com the segment after /sessions,
|
|
1164
1168
|
* in any case and after any run of slashes, each written plain or escaped once or more, reads {session}:
|
|
1165
|
-
* https://komoju.com/api/v1/sessions/{session}/pay. Then
|
|
1166
|
-
* {session}
|
|
1167
|
-
* masked as well;
|
|
1169
|
+
* https://komoju.com/api/v1/sessions/{session}/pay. Then every run of 25 or more letters and digits left
|
|
1170
|
+
* reads {session}, so an id after a dot segment, a backslash or an escape KOMOJU's frame never writes is
|
|
1171
|
+
* masked as well; a percent escape right before the run stays when 25 or more remain after it. No other
|
|
1172
|
+
* path KOMOJU's card fields use holds such a run. The whole run reads {session}, never only its last 25
|
|
1173
|
+
* characters, so a longer id of another processor in the same text is never cut in two before that
|
|
1174
|
+
* processor's mask reads it.
|
|
1168
1175
|
*/
|
|
1169
1176
|
const KOMOJU_HOST = /^komoju\.com\.?$/i;
|
|
1170
1177
|
const KOMOJU_SESSION = new RegExp(`(${SLASH}sessions)(${SLASH}+)[^/]*`, 'gi');
|
|
1171
|
-
const KOMOJU_SESSION_ID = /[
|
|
1178
|
+
const KOMOJU_SESSION_ID = /(%(?:25)*[0-9a-f]{2})?[a-z0-9]{25,}/gi;
|
|
1172
1179
|
function maskKomojuSession(path) {
|
|
1173
|
-
return path.replace(KOMOJU_SESSION, '$1$2{session}').replace(KOMOJU_SESSION_ID, '{session}
|
|
1180
|
+
return path.replace(KOMOJU_SESSION, '$1$2{session}').replace(KOMOJU_SESSION_ID, (_run, escape) => `${escape ?? ''}{session}`);
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* SumUp's Payment Widget sends the card to https://api.sumup.com/v0.2/checkouts/<checkout id> with no cookie and
|
|
1184
|
+
* no auth header (payment-core sumup.js), so the checkout id is that request's only credential: with it anyone can
|
|
1185
|
+
* read the checkout's amount and merchant and pay it. On api.sumup.com the segment after /checkouts/, in any case
|
|
1186
|
+
* and any spelling of the slashes before it, reads {checkout}, as does any other id shaped like SumUp's UUID
|
|
1187
|
+
* checkout ids, in every spelling maskUuids reads.
|
|
1188
|
+
*/
|
|
1189
|
+
const SUMUP_API_HOST = /^api\.sumup\.com\.?$/i;
|
|
1190
|
+
const SUMUP_CHECKOUT = new RegExp(`(${SLASH}checkouts${SLASH}+)[^/]*`, 'gi');
|
|
1191
|
+
function maskSumupCheckout(path) {
|
|
1192
|
+
return maskUuids(path.replace(SUMUP_CHECKOUT, '$1{checkout}'), '{checkout}');
|
|
1174
1193
|
}
|
|
1175
1194
|
/**
|
|
1176
1195
|
* The processors whose card request carries its own credential in the URL path, with the hosts
|
|
1177
|
-
* that send it, the text that names those hosts
|
|
1178
|
-
* guard in redact-url.test.mjs fails for a
|
|
1179
|
-
* this list or out of it.
|
|
1196
|
+
* that send it, the text that names those hosts anywhere else (a string that is not a URL, or a
|
|
1197
|
+
* path that carries a URL of its own), and the mask. The guard in redact-url.test.mjs fails for a
|
|
1198
|
+
* payment-core recognizer that no one has sorted into this list or out of it.
|
|
1180
1199
|
*/
|
|
1181
1200
|
/**
|
|
1182
1201
|
* Redsys's hosted payment page names its session in the path of every page and post after the entry
|
|
@@ -1212,7 +1231,45 @@ const PRZELEWY24_TOKEN = new RegExp(`[0-9a-f]{10}${HYPHEN}[0-9a-f]{6}${HYPHEN}[0
|
|
|
1212
1231
|
function maskPrzelewy24Token(path) {
|
|
1213
1232
|
return path.replace(PRZELEWY24_TOKEN, '{token}');
|
|
1214
1233
|
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Revolut's card field and card popup send the card payment to /api/public/orders/<order token>/payments on
|
|
1236
|
+
* merchant.revolut.com (sandbox-merchant.revolut.com in test), and the frame polls /api/public/payments/<payment
|
|
1237
|
+
* token> (payment-core revolut.js). Neither carries a header or cookie that binds it: the order token alone
|
|
1238
|
+
* opens that order's checkout, its amount and its merchant, and pays it, and the payment token reads the payment.
|
|
1239
|
+
* Revolut's hosted checkout page names the same order token in its own paths (/payment-link/<order token>), and the
|
|
1240
|
+
* frame reads the order at merchant-mgmt.revolut.com/orders/token/<order token>. On those hosts every id of their
|
|
1241
|
+
* uuid shape, in every spelling maskUuids reads, reads {token}.
|
|
1242
|
+
*/
|
|
1243
|
+
const REVOLUT_HOST = /^(?:sandbox-)?(?:merchant|checkout|merchant-mgmt)\.revolut\.com\.?$/i;
|
|
1244
|
+
function maskRevolutToken(path) {
|
|
1245
|
+
return maskUuids(path, '{token}');
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Paytrail names its payment in the path of every route of its card method
|
|
1249
|
+
* (/payments/<transaction>/click-to-pay/loading-and-redirect, and its success and cancel routes), and the same id
|
|
1250
|
+
* opens Paytrail's own payment wall for that payment (pay.paytrail.com/pay/<transaction>), its amount, its
|
|
1251
|
+
* merchant and every method's form, to anyone who holds it (payment-core processors/paytrail.js). On every host
|
|
1252
|
+
* Paytrail owns, any id of its shape, with or without its hyphens, in any case, after an escaped slash or with its
|
|
1253
|
+
* hyphens escaped, reads {transaction}. Payment Highway, Paytrail's card platform, names its card page's session
|
|
1254
|
+
* the same way (/form/<session>/pay_with_click_to_pay), and that path is what carries a card post into the payment,
|
|
1255
|
+
* so on its hosts the same shape reads {session}. The pattern reads a percent escape as one unit and keeps it, so
|
|
1256
|
+
* the hex digits of an escaped separator (%2F, %252F, %3D) never start an id and leave part of it showing.
|
|
1257
|
+
*/
|
|
1258
|
+
const PAYTRAIL_HOST = /(?:^|\.)paytrail\.com\.?$/i;
|
|
1259
|
+
const PAYMENT_HIGHWAY_HOST = /(?:^|\.)(?:paymenthighway\.io|sph-test-solinor\.com)\.?$/i;
|
|
1260
|
+
const PAYTRAIL_ID_HYPHEN = '(?:-|%(?:25)*2d)?';
|
|
1261
|
+
const PAYTRAIL_ID = new RegExp(`%(?:25)*[0-9a-f]{2}|[0-9a-f]{8}${PAYTRAIL_ID_HYPHEN}[0-9a-f]{4}${PAYTRAIL_ID_HYPHEN}[0-9a-f]{4}${PAYTRAIL_ID_HYPHEN}[0-9a-f]{4}${PAYTRAIL_ID_HYPHEN}[0-9a-f]{12}`, 'gi');
|
|
1262
|
+
function maskPaytrailId(path, label) {
|
|
1263
|
+
return path.replace(PAYTRAIL_ID, (token) => (token.startsWith('%') ? token : label));
|
|
1264
|
+
}
|
|
1265
|
+
function maskPaytrailTransaction(path) {
|
|
1266
|
+
return maskPaytrailId(path, '{transaction}');
|
|
1267
|
+
}
|
|
1268
|
+
function maskPaymentHighwaySession(path) {
|
|
1269
|
+
return maskPaytrailId(path, '{session}');
|
|
1270
|
+
}
|
|
1215
1271
|
const PATH_CREDENTIALS = [
|
|
1272
|
+
{ host: PATH_SECRET_HOST, named: /(?:wspay\.biz|monri\.com)/i, mask: maskPathSecret },
|
|
1216
1273
|
{ host: CREDITGUARD_HOST, named: /creditguard\.co\.il/i, mask: maskCreditguardTxId },
|
|
1217
1274
|
{ host: GRAVY_HOST, named: /gr4vy\.app/i, mask: maskGravySession },
|
|
1218
1275
|
{ host: RAPYD_HOST, named: /rapyd\.net/i, mask: maskRapydToken },
|
|
@@ -1227,6 +1284,10 @@ const PATH_CREDENTIALS = [
|
|
|
1227
1284
|
{ host: GOPAY_CARD_FORM_HOST, named: /secure(?:\.sandbox)?\.gopay\.com/i, mask: maskGopayToken },
|
|
1228
1285
|
{ host: KOMOJU_HOST, named: /komoju\.com/i, mask: maskKomojuSession },
|
|
1229
1286
|
{ host: PRZELEWY24_HOST, named: /przelewy24\.pl/i, mask: maskPrzelewy24Token },
|
|
1287
|
+
{ host: REVOLUT_HOST, named: /(?:merchant|checkout|merchant-mgmt)\.revolut\.com/i, mask: maskRevolutToken },
|
|
1288
|
+
{ host: SUMUP_API_HOST, named: /api\.sumup\.com/i, mask: maskSumupCheckout },
|
|
1289
|
+
{ host: PAYTRAIL_HOST, named: /paytrail\.com/i, mask: maskPaytrailTransaction },
|
|
1290
|
+
{ host: PAYMENT_HIGHWAY_HOST, named: /(?:paymenthighway\.io|sph-test-solinor\.com)/i, mask: maskPaymentHighwaySession },
|
|
1230
1291
|
];
|
|
1231
1292
|
/**
|
|
1232
1293
|
* A URL as it may appear in an error message: origin + path only. A paused
|
|
@@ -1254,30 +1315,48 @@ const PATH_CREDENTIALS = [
|
|
|
1254
1315
|
* On komoju.com a session id reads `{session}`: https://komoju.com/api/v1/sessions/{session}/pay.
|
|
1255
1316
|
* On Przelewy24's hosts the transaction's token reads `{token}`:
|
|
1256
1317
|
* https://secure.przelewy24.pl/trnRequest/{token}.
|
|
1318
|
+
* On Revolut's card payment and hosted checkout hosts an order or payment token reads `{token}`:
|
|
1319
|
+
* https://merchant.revolut.com/api/public/orders/{token}/payments.
|
|
1320
|
+
* On SumUp's API host a checkout id reads `{checkout}`: https://api.sumup.com/v0.2/checkouts/{checkout}.
|
|
1321
|
+
* On Paytrail's hosts a payment's id reads `{transaction}`:
|
|
1322
|
+
* https://services.paytrail.com/payments/{transaction}/click-to-pay/loading-and-redirect, and on Payment
|
|
1323
|
+
* Highway's a card page's session reads `{session}`.
|
|
1257
1324
|
* A string that is not a URL keeps the part before any `?` or `#`, with the
|
|
1258
|
-
* same segments masked.
|
|
1325
|
+
* same segments masked for every processor whose host it names.
|
|
1326
|
+
* A URL is read by its host only when the parser finds one. Text before a URL
|
|
1327
|
+
* can read as a scheme: `see:https://api.stripe.com/...` parses as a URL with
|
|
1328
|
+
* the scheme `see`, no host, and the whole Stripe URL as its path, so a parse
|
|
1329
|
+
* with no host is read as a string that is not a URL. A path can carry a URL of
|
|
1330
|
+
* its own, as `http:https://api.stripe.com/...` does once `https` reads as its
|
|
1331
|
+
* host, so every processor whose host the path names masks the URL too.
|
|
1259
1332
|
*/
|
|
1260
1333
|
export function redactUrl(raw) {
|
|
1261
|
-
|
|
1262
|
-
const aci = redactAciCheckoutId(raw);
|
|
1263
|
-
if (aci !== null)
|
|
1264
|
-
return aci;
|
|
1334
|
+
let url;
|
|
1265
1335
|
try {
|
|
1266
|
-
|
|
1267
|
-
// A trailing dot names the same host, so it cannot let the secret through.
|
|
1268
|
-
let path = PATH_SECRET_HOSTS.includes(u.hostname.replace(/\.$/, '')) ? u.pathname.replace(PATH_SECRET, '$1{client_secret}') : u.pathname;
|
|
1269
|
-
for (const rule of PATH_CREDENTIALS)
|
|
1270
|
-
if (rule.host.test(u.hostname))
|
|
1271
|
-
path = rule.mask(path);
|
|
1272
|
-
return `${u.origin}${path}`;
|
|
1336
|
+
url = new URL(raw);
|
|
1273
1337
|
}
|
|
1274
1338
|
catch {
|
|
1275
|
-
|
|
1276
|
-
for (const rule of PATH_CREDENTIALS)
|
|
1277
|
-
if (rule.named.test(bare))
|
|
1278
|
-
bare = rule.mask(bare);
|
|
1279
|
-
return bare;
|
|
1339
|
+
url = null;
|
|
1280
1340
|
}
|
|
1341
|
+
if (!url?.hostname) {
|
|
1342
|
+
const text = raw.split(/[?#]/)[0];
|
|
1343
|
+
return maskNamed(maskPathSecret(text), text);
|
|
1344
|
+
}
|
|
1345
|
+
let path = url.pathname;
|
|
1346
|
+
for (const rule of PATH_CREDENTIALS)
|
|
1347
|
+
if (rule.host.test(url.hostname))
|
|
1348
|
+
path = rule.mask(path);
|
|
1349
|
+
return maskNamed(`${url.origin}${path}`, url.pathname);
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* `text` with the mask of every processor whose host `names` names. `names` is read before any mask
|
|
1353
|
+
* runs, so a mask that rewrites another processor's host cannot keep that processor's mask from running.
|
|
1354
|
+
*/
|
|
1355
|
+
function maskNamed(text, names) {
|
|
1356
|
+
for (const rule of PATH_CREDENTIALS)
|
|
1357
|
+
if (rule.named.test(names))
|
|
1358
|
+
text = rule.mask(text);
|
|
1359
|
+
return text;
|
|
1281
1360
|
}
|
|
1282
1361
|
/**
|
|
1283
1362
|
* Refusals a preparation create answers before any preparation exists, surfaced
|
package/dist/datatrans.d.ts
CHANGED
|
@@ -69,8 +69,8 @@ export interface DatatransRepeatAnswer {
|
|
|
69
69
|
* The approved Datatrans answers one attachment handed to a card post, per frame. `frame` is the
|
|
70
70
|
* adapter's identity for the request's frame (the CDP frameId, the Playwright Frame); a request
|
|
71
71
|
* whose frame is unknown is never remembered and never answered. Only an answer payment-core reads
|
|
72
|
-
* as approved (
|
|
73
|
-
* question, so the page can try again.
|
|
72
|
+
* as approved (the token, with cardInfo when Datatrans sends one) is kept: an error answer leaves
|
|
73
|
+
* the next post a new question, so the page can try again.
|
|
74
74
|
*/
|
|
75
75
|
export declare class DatatransRepeats {
|
|
76
76
|
private readonly windowMs;
|
|
@@ -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: 829167e03a7ffe9fc109ababe9755f1263a840fd5294ebec970e08d41aa9741b
|
|
4
4
|
// src/datatrans.js
|
|
5
5
|
var DATATRANS_HOSTS = Object.freeze(["pay.datatrans.com", "pay.sandbox.datatrans.com"]);
|
|
6
6
|
var DATATRANS_TOKENIZE_PATH = "/upp/payment/SecureFields/paymentField";
|
|
@@ -346,7 +346,7 @@ function injectDatatransCard(body, card, policy = DATATRANS_REGISTRATION_POLICY)
|
|
|
346
346
|
var TRANSACTION_ID = /^[0-9]{18}$/;
|
|
347
347
|
var CARD_WORD = /^[A-Za-z][A-Za-z _-]{0,31}$/;
|
|
348
348
|
var COUNTRY = /^[A-Z]{2}$/;
|
|
349
|
-
var CARD_INFO_KEYS = ["brand", "type", "usage", "country", "issuer", "coBrand"];
|
|
349
|
+
var CARD_INFO_KEYS = ["brand", "type", "usage", "country", "issuer", "coBrand", "accountType", "binLength"];
|
|
350
350
|
var UNFOLLOWED_KEYS = ["tds2EnrollmentResult", "redirect", "dcc", "mcp"];
|
|
351
351
|
var ERROR_CODES = [
|
|
352
352
|
[/^Invalid merchantId$/, "invalid_merchant_id"],
|
|
@@ -368,10 +368,11 @@ function readAnswer(body) {
|
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
370
|
var optionalText = (value, pattern) => value === null || exact(pattern, value);
|
|
371
|
+
var optionalBinLength = (value) => value === null || Number.isInteger(value) && value >= 1 && value <= 19;
|
|
371
372
|
function rebuildCardInfo(info) {
|
|
372
|
-
if (!record(info) || !Object.keys(info).every((key) => CARD_INFO_KEYS.includes(key))
|
|
373
|
+
if (!record(info) || !Object.keys(info).every((key) => CARD_INFO_KEYS.includes(key)))
|
|
373
374
|
return null;
|
|
374
|
-
if (owns(info, "type") && !optionalText(info.type, CARD_WORD) || owns(info, "usage") && !optionalText(info.usage, CARD_WORD) || owns(info, "country") && !optionalText(info.country, COUNTRY) || owns(info, "issuer") && info.issuer !== null && !plain(info.issuer, 128) || owns(info, "coBrand") && info.coBrand !== null && !plain(info.coBrand, 64))
|
|
375
|
+
if (owns(info, "brand") && !optionalText(info.brand, CARD_WORD) || owns(info, "type") && !optionalText(info.type, CARD_WORD) || owns(info, "usage") && !optionalText(info.usage, CARD_WORD) || owns(info, "country") && !optionalText(info.country, COUNTRY) || owns(info, "issuer") && info.issuer !== null && !plain(info.issuer, 128) || owns(info, "coBrand") && info.coBrand !== null && !plain(info.coBrand, 64) || owns(info, "accountType") && !optionalText(info.accountType, CARD_WORD) || owns(info, "binLength") && !optionalBinLength(info.binLength))
|
|
375
376
|
return null;
|
|
376
377
|
const safe = {};
|
|
377
378
|
for (const key of CARD_INFO_KEYS)
|
|
@@ -380,10 +381,14 @@ function rebuildCardInfo(info) {
|
|
|
380
381
|
return safe;
|
|
381
382
|
}
|
|
382
383
|
function rebuildSuccess(value) {
|
|
383
|
-
if (Object.keys(value).
|
|
384
|
-
return
|
|
384
|
+
if (!Object.keys(value).every((key) => key === "transactionId" || key === "cardInfo"))
|
|
385
|
+
return { reason: "datatrans_unexpected_key" };
|
|
386
|
+
if (!exact(TRANSACTION_ID, value.transactionId))
|
|
387
|
+
return { reason: "datatrans_unexpected_token" };
|
|
388
|
+
if (!owns(value, "cardInfo") || value.cardInfo === null)
|
|
389
|
+
return { answer: { transactionId: value.transactionId } };
|
|
385
390
|
const cardInfo = rebuildCardInfo(value.cardInfo);
|
|
386
|
-
return cardInfo ? { transactionId: value.transactionId, cardInfo } :
|
|
391
|
+
return cardInfo ? { answer: { transactionId: value.transactionId, cardInfo } } : { reason: "datatrans_unexpected_card_info" };
|
|
387
392
|
}
|
|
388
393
|
function rebuildError(value) {
|
|
389
394
|
return Object.keys(value).length === 1 && plain(value.error, 256) && value.error.trim() !== "" ? { error: value.error } : null;
|
|
@@ -407,15 +412,15 @@ function datatransResponseOutcome(status, body, card) {
|
|
|
407
412
|
if (owns(value, "error")) {
|
|
408
413
|
const error = rebuildError(value);
|
|
409
414
|
if (!error)
|
|
410
|
-
return { outcome: "unknown", reason: "
|
|
415
|
+
return { outcome: "unknown", reason: "datatrans_unexpected_error" };
|
|
411
416
|
const code = datatransErrorCode(error.error);
|
|
412
417
|
return code === "too_many_requests" ? { outcome: "unknown", reason: "datatrans_repeat_request" } : { outcome: "refused", code };
|
|
413
418
|
}
|
|
414
419
|
if (UNFOLLOWED_KEYS.some((key) => owns(value, key)))
|
|
415
420
|
return { outcome: "unknown", reason: "datatrans_unsupported_flow" };
|
|
416
|
-
const safe = rebuildSuccess(value);
|
|
421
|
+
const { answer: safe, reason } = rebuildSuccess(value);
|
|
417
422
|
if (!safe)
|
|
418
|
-
return { outcome: "unknown", reason
|
|
423
|
+
return { outcome: "unknown", reason };
|
|
419
424
|
if (card && digits(card.number, 12, 19) && safe.transactionId.includes(card.number)) {
|
|
420
425
|
return { outcome: "unknown", reason: "datatrans_token_mismatch" };
|
|
421
426
|
}
|
|
@@ -432,7 +437,7 @@ function sanitizeDatatransTokenResponse(body) {
|
|
|
432
437
|
return JSON.stringify(rebuildError(value) ?? fail());
|
|
433
438
|
if (UNFOLLOWED_KEYS.some((key) => owns(value, key)))
|
|
434
439
|
return fail();
|
|
435
|
-
return JSON.stringify(rebuildSuccess(value) ?? fail());
|
|
440
|
+
return JSON.stringify(rebuildSuccess(value).answer ?? fail());
|
|
436
441
|
}
|
|
437
442
|
function datatransFacts(status, body) {
|
|
438
443
|
const verdict = datatransResponseOutcome(status, body);
|
package/dist/datatrans.js
CHANGED
|
@@ -112,8 +112,8 @@ function sameKey(a, b) {
|
|
|
112
112
|
* The approved Datatrans answers one attachment handed to a card post, per frame. `frame` is the
|
|
113
113
|
* adapter's identity for the request's frame (the CDP frameId, the Playwright Frame); a request
|
|
114
114
|
* whose frame is unknown is never remembered and never answered. Only an answer payment-core reads
|
|
115
|
-
* as approved (
|
|
116
|
-
* question, so the page can try again.
|
|
115
|
+
* as approved (the token, with cardInfo when Datatrans sends one) is kept: an error answer leaves
|
|
116
|
+
* the next post a new question, so the page can try again.
|
|
117
117
|
*/
|
|
118
118
|
export class DatatransRepeats {
|
|
119
119
|
windowMs;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
declare var PELECARD_TRANSACTION_HOSTS: readonly string[];
|
|
2
2
|
declare var PELECARD_TRANSACTION_PATH: string;
|
|
3
3
|
declare var PELECARD_REFUSAL_REASONS: readonly string[];
|
|
4
|
+
declare var PELECARD_SUCCESS_CODES: readonly string[];
|
|
4
5
|
declare function isPelecardTransactionUrl(url: any, method?: string): boolean;
|
|
5
6
|
declare function pelecardBodyRefusal(body: any): "unsupported" | "keep_on_file" | null;
|
|
6
7
|
declare function isPelecardNewCardBody(body: any): boolean;
|
|
@@ -29,4 +30,4 @@ declare function pelecardFacts(status: any, body: any): {
|
|
|
29
30
|
outcome: string;
|
|
30
31
|
refusalCode: any;
|
|
31
32
|
};
|
|
32
|
-
export { PELECARD_REFUSAL_REASONS, PELECARD_TRANSACTION_HOSTS, PELECARD_TRANSACTION_PATH, classifyPelecardRequest, injectPelecardCard, isPelecardNewCardBody, isPelecardTransactionRequest, isPelecardTransactionUrl, pelecardBodyRefusal, pelecardFacts, pelecardKeepOnFileRequested, pelecardRefusalMessage, pelecardResponseOutcome, pelecardTransactionRefusal, sanitizePelecardResponse };
|
|
33
|
+
export { PELECARD_REFUSAL_REASONS, PELECARD_SUCCESS_CODES, PELECARD_TRANSACTION_HOSTS, PELECARD_TRANSACTION_PATH, classifyPelecardRequest, injectPelecardCard, isPelecardNewCardBody, isPelecardTransactionRequest, isPelecardTransactionUrl, pelecardBodyRefusal, pelecardFacts, pelecardKeepOnFileRequested, pelecardRefusalMessage, pelecardResponseOutcome, pelecardTransactionRefusal, sanitizePelecardResponse };
|
|
@@ -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: 30904474bf2bd6c0b0aac97deda8fe133514a241e7060e782118af5fba813fe4
|
|
4
4
|
// src/json-path.js
|
|
5
5
|
function roundTrips(parsed, body) {
|
|
6
6
|
try {
|
|
@@ -14,6 +14,7 @@ function roundTrips(parsed, body) {
|
|
|
14
14
|
var PELECARD_TRANSACTION_HOSTS = /* @__PURE__ */ Object.freeze(["gateway21.pelecard.biz", "gateway20.pelecard.biz"]);
|
|
15
15
|
var PELECARD_TRANSACTION_PATH = "/PaymentGW/Transaction";
|
|
16
16
|
var PELECARD_REFUSAL_REASONS = /* @__PURE__ */ Object.freeze(["unsupported", "keep_on_file"]);
|
|
17
|
+
var PELECARD_SUCCESS_CODES = /* @__PURE__ */ Object.freeze(["000", "302"]);
|
|
17
18
|
var UNSUPPORTED_REFUSAL = "Refusing to pay: unsupported Pelecard card payment request.";
|
|
18
19
|
var KEEP_ON_FILE_REFUSAL = "Refusing to pay: this Pelecard payment would keep the card on file without your consent. Nothing was sent.";
|
|
19
20
|
var TEST_CARD_REFUSAL = "Refusing to pay: a Pelecard checkout takes only a documented test card, because the merchant can have Pelecard keep the card without asking you. Nothing was sent.";
|
|
@@ -214,6 +215,9 @@ function landingUrl(value) {
|
|
|
214
215
|
return null;
|
|
215
216
|
}
|
|
216
217
|
}
|
|
218
|
+
function inPageDebit(value) {
|
|
219
|
+
return absent(value.UrlKashCash) && value.StatusDescription !== "Gama Completion Required" && (value.DisplayIshurPopUp === void 0 || value.DisplayIshurPopUp === null || value.DisplayIshurPopUp === false);
|
|
220
|
+
}
|
|
217
221
|
function pelecardResponseOutcome(status, body) {
|
|
218
222
|
if (Number.isInteger(status) && status >= 400 && status < 500)
|
|
219
223
|
return clientErrorOutcome(status, body);
|
|
@@ -225,7 +229,7 @@ function pelecardResponseOutcome(status, body) {
|
|
|
225
229
|
const code = value.StatusCode;
|
|
226
230
|
if (!exact(/^[0-9]{3}$/, code))
|
|
227
231
|
return { outcome: "unknown", reason: "pelecard_unexpected_response" };
|
|
228
|
-
if (code
|
|
232
|
+
if (!PELECARD_SUCCESS_CODES.includes(code)) {
|
|
229
233
|
return absent(value.ApprovalNo) ? { outcome: "refused", code } : { outcome: "unknown", reason: "pelecard_unconfirmed" };
|
|
230
234
|
}
|
|
231
235
|
if (!REDIRECT_KEYS.every((key) => typeof value[key] === "string") || owns(value, "ParamX") && value.ParamX !== null && typeof value.ParamX !== "string") {
|
|
@@ -233,6 +237,9 @@ function pelecardResponseOutcome(status, body) {
|
|
|
233
237
|
}
|
|
234
238
|
if (value.DisplayIshurPopUp === true || !absent(value.UrlOpenFinance))
|
|
235
239
|
return { outcome: "unknown", reason: "pelecard_unconfirmed" };
|
|
240
|
+
if (code === "302" && value.LandingURL === "") {
|
|
241
|
+
return inPageDebit(value) ? { outcome: "approved" } : { outcome: "unknown", reason: "pelecard_unconfirmed" };
|
|
242
|
+
}
|
|
236
243
|
const landing = landingUrl(value.LandingURL);
|
|
237
244
|
if (!landing)
|
|
238
245
|
return { outcome: "unknown", reason: "pelecard_unconfirmed" };
|
|
@@ -246,7 +253,7 @@ function clientErrorOutcome(status, body) {
|
|
|
246
253
|
if (!value || !owns(value, "StatusCode") && !owns(value, "ApprovalNo"))
|
|
247
254
|
return { outcome: "refused", code: `http_${status}` };
|
|
248
255
|
const code = value.StatusCode;
|
|
249
|
-
return exact(/^[0-9]{3}$/, code) && code
|
|
256
|
+
return exact(/^[0-9]{3}$/, code) && !PELECARD_SUCCESS_CODES.includes(code) && absent(value.ApprovalNo) ? { outcome: "refused", code } : { outcome: "unknown", reason: "pelecard_unconfirmed" };
|
|
250
257
|
}
|
|
251
258
|
function sanitizePelecardResponse(body) {
|
|
252
259
|
const fail = () => {
|
|
@@ -260,7 +267,7 @@ function sanitizePelecardResponse(body) {
|
|
|
260
267
|
const safe = {
|
|
261
268
|
LandingURL: value.LandingURL,
|
|
262
269
|
Target: value.Target,
|
|
263
|
-
StatusCode:
|
|
270
|
+
StatusCode: value.StatusCode,
|
|
264
271
|
StatusDescription: markupSafe(value.StatusDescription, 256) && !panLike(value.StatusDescription) ? value.StatusDescription : "",
|
|
265
272
|
ApprovalNo: value.ApprovalNo,
|
|
266
273
|
Token: "",
|
|
@@ -271,6 +278,8 @@ function sanitizePelecardResponse(body) {
|
|
|
271
278
|
safe.UserKey = value.UserKey;
|
|
272
279
|
safe.UseEndOfProcess = value.UseEndOfProcess;
|
|
273
280
|
safe.FeedbackDataTransferMethod = value.FeedbackDataTransferMethod;
|
|
281
|
+
if (value.LandingURL === "")
|
|
282
|
+
safe.UrlOpenFinance = "";
|
|
274
283
|
return JSON.stringify(safe);
|
|
275
284
|
}
|
|
276
285
|
function pelecardFacts(status, body) {
|
|
@@ -280,4 +289,4 @@ function pelecardFacts(status, body) {
|
|
|
280
289
|
refusalCode: verdict.outcome === "refused" && /^[0-9]{3}$/.test(verdict.code) ? verdict.code : null
|
|
281
290
|
};
|
|
282
291
|
}
|
|
283
|
-
export { PELECARD_REFUSAL_REASONS, PELECARD_TRANSACTION_HOSTS, PELECARD_TRANSACTION_PATH, classifyPelecardRequest, injectPelecardCard, isPelecardNewCardBody, isPelecardTransactionRequest, isPelecardTransactionUrl, pelecardBodyRefusal, pelecardFacts, pelecardKeepOnFileRequested, pelecardRefusalMessage, pelecardResponseOutcome, pelecardTransactionRefusal, sanitizePelecardResponse };
|
|
292
|
+
export { PELECARD_REFUSAL_REASONS, PELECARD_SUCCESS_CODES, PELECARD_TRANSACTION_HOSTS, PELECARD_TRANSACTION_PATH, classifyPelecardRequest, injectPelecardCard, isPelecardNewCardBody, isPelecardTransactionRequest, isPelecardTransactionUrl, pelecardBodyRefusal, pelecardFacts, pelecardKeepOnFileRequested, pelecardRefusalMessage, pelecardResponseOutcome, pelecardTransactionRefusal, sanitizePelecardResponse };
|