@agent-cards/sdk 0.23.9 → 0.23.11
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 +48 -0
- package/PREFLIGHT.md +7 -5
- package/README.md +211 -102
- package/dist/aci.d.ts +7 -5
- package/dist/aci.generated.d.ts +2 -1
- package/dist/aci.generated.js +14 -4
- package/dist/aci.js +28 -7
- package/dist/amadeus-checkout.generated.js +2 -2
- package/dist/builtin-registry.generated.js +1 -1
- package/dist/card-fields.generated.js +165 -10
- package/dist/cdp.js +86 -34
- package/dist/client.d.ts +73 -33
- package/dist/client.js +136 -45
- package/dist/creditguard.generated.d.ts +1 -1
- package/dist/creditguard.generated.js +1 -3
- package/dist/cse-body.d.ts +7 -6
- package/dist/cse-body.js +8 -7
- package/dist/datatrans.generated.js +8 -5
- package/dist/elavon-converge.d.ts +5 -2
- package/dist/elavon-converge.generated.js +9 -5
- package/dist/elavon-converge.js +8 -4
- package/dist/fiserv.d.ts +9 -5
- package/dist/fiserv.generated.d.ts +3 -3
- package/dist/fiserv.generated.js +50 -20
- package/dist/fiserv.js +10 -6
- package/dist/lifecycle.d.ts +9 -1
- package/dist/lifecycle.js +34 -0
- package/dist/payrails.d.ts +78 -18
- package/dist/payrails.generated.d.ts +12 -2
- package/dist/payrails.generated.js +213 -18
- package/dist/payrails.js +165 -44
- package/dist/pre-claim.d.ts +7 -0
- package/dist/pre-claim.js +98 -36
- package/dist/preflight-capabilities.generated.d.ts +0 -106
- package/dist/preflight-capabilities.generated.js +0 -153
- package/dist/preflight-catalog.json +61 -181
- package/dist/preflight-schemas.json +6 -6
- package/dist/preflight.generated.d.ts +0 -106
- package/dist/preflight.generated.js +27 -15
- package/dist/preparation.d.ts +2 -0
- package/dist/preparation.js +4 -2
- package/dist/prepared-processor.d.ts +19 -5
- package/dist/prepared-processor.js +21 -5
- package/dist/primer.generated.d.ts +5 -1
- package/dist/primer.generated.js +202 -34
- package/dist/registry.d.ts +18 -12
- package/dist/registry.js +11 -8
- package/dist/shiji-sts.d.ts +24 -22
- package/dist/shiji-sts.generated.d.ts +1 -1
- package/dist/shiji-sts.generated.js +12 -8
- package/dist/shiji-sts.js +49 -46
- package/dist/stripe-checkout.generated.js +157 -49
- package/dist/verifone.generated.js +192 -17
- package/dist/verifone.js +1 -1
- package/dist/worldline-mycheckout.d.ts +27 -0
- package/dist/worldline-mycheckout.generated.d.ts +29 -0
- package/dist/worldline-mycheckout.generated.js +162 -0
- package/dist/worldline-mycheckout.js +93 -0
- package/dist/wspay.d.ts +17 -16
- package/dist/wspay.generated.d.ts +6 -1
- package/dist/wspay.generated.js +13 -3
- package/dist/wspay.js +9 -11
- package/examples/preflight/kernel-native/README.md +1 -1
- package/examples/preflight/kernel-native/inventory.json +1 -19
- package/package.json +3 -3
|
@@ -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: db732bdbb517b37ea27d05debafb7659bb456f686edd0c90d78b2fbdb993d68d
|
|
4
4
|
// src/deep-freeze.js
|
|
5
5
|
function deepFreeze(value) {
|
|
6
6
|
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
@@ -415,18 +415,44 @@ var VERIFONE_MERCHANT_PROFILES = /* @__PURE__ */ deepFreeze([
|
|
|
415
415
|
// employee variants (featureConfig verifoneCCEnabled, verifonePLCCEnabled,
|
|
416
416
|
// verifoneEmployeeCCEnabled, verifoneEmployeePLCCEnabled; live from late 2023, still on
|
|
417
417
|
// 2026-09-19). Checkout is www.containerstore.com/checkout?fulfillmentMethod=STANDARD|PICKUP,
|
|
418
|
-
// on the legacy app behind PerimeterX.
|
|
419
|
-
//
|
|
420
|
-
//
|
|
421
|
-
//
|
|
422
|
-
//
|
|
418
|
+
// on the legacy app behind PerimeterX.
|
|
419
|
+
//
|
|
420
|
+
// The card request, read from TCS's own CheckoutV2 build 3.340.1.16022 (its public source
|
|
421
|
+
// map: CheckoutForm and the checkout service) and its live guest checkout on 2026-10-10:
|
|
422
|
+
// Place Order encrypts the card once with verifone.js, and axios posts the order once, as
|
|
423
|
+
// JSON, to /checkout/api/submit, with the new card last in paymentMethods after any gift
|
|
424
|
+
// card, as {defaultWallet, cardNumber (masked but its last four), encryptedCard, type
|
|
425
|
+
// 'CREDIT_CARD', cardType}. A saved card is type 'COF' with a walletId, TCS's own card
|
|
426
|
+
// 'TCS_CREDIT_CARD'; defaultWallet saves the card to the shopper's wallet, and
|
|
427
|
+
// popTermsAndConditionsAccepted (the form's popEnroll box) enrolls them in TCS's loyalty
|
|
428
|
+
// program, Organized Insider (once POP!). A gift card's balance check posts its own blob to
|
|
429
|
+
// /checkout/api/gift-card, which aborts like any request off the leg.
|
|
430
|
+
//
|
|
431
|
+
// The body names no amount (TCS's total is only in its cart response), and the key the page
|
|
432
|
+
// encrypts under, its verifoneEncryptionKey, is not pinned (verifone-keys.js). So the profile
|
|
433
|
+
// stays in observe: the leg's requests abort as unreviewed until a person pins the key and the
|
|
434
|
+
// merchant's total has a reviewed source.
|
|
423
435
|
id: "containerstore_checkout",
|
|
424
436
|
merchant: "The Container Store",
|
|
425
437
|
processor: "verifone",
|
|
426
438
|
status: "observe",
|
|
427
439
|
environment: "production",
|
|
428
440
|
origins: [{ checkout: "https://www.containerstore.com", endpoint: "https://www.containerstore.com" }],
|
|
429
|
-
legs: [
|
|
441
|
+
legs: [{
|
|
442
|
+
name: "card",
|
|
443
|
+
method: "POST",
|
|
444
|
+
path: "/checkout/api/submit",
|
|
445
|
+
query: {},
|
|
446
|
+
requires: [{ path: ["paymentMethods", 0, "type"], equals: "CREDIT_CARD" }],
|
|
447
|
+
refuse: [
|
|
448
|
+
{ path: ["paymentMethods", 1], when: "present" },
|
|
449
|
+
{ path: ["paymentMethods", 0, "defaultWallet"], when: "true" },
|
|
450
|
+
{ path: ["paymentMethods", 0, "walletId"], when: "present" },
|
|
451
|
+
{ path: ["popTermsAndConditionsAccepted"], when: "true" }
|
|
452
|
+
],
|
|
453
|
+
holder: ["paymentMethods", 0],
|
|
454
|
+
field: "encryptedCard"
|
|
455
|
+
}],
|
|
430
456
|
keyPin: null,
|
|
431
457
|
amount: null
|
|
432
458
|
}
|
|
@@ -652,8 +678,143 @@ function decodeBase64(text) {
|
|
|
652
678
|
}
|
|
653
679
|
return encodeBase64(out) === text ? out : null;
|
|
654
680
|
}
|
|
681
|
+
// src/secp256k1.js
|
|
682
|
+
var P = (1n << 256n) - (1n << 32n) - 977n;
|
|
683
|
+
var N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
|
|
684
|
+
var G = Object.freeze({
|
|
685
|
+
x: 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n,
|
|
686
|
+
y: 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n
|
|
687
|
+
});
|
|
688
|
+
var BYTES = 32;
|
|
689
|
+
var MAX_DRAWS = 16;
|
|
690
|
+
var mod = (a) => {
|
|
691
|
+
const r = a % P;
|
|
692
|
+
return r < 0n ? r + P : r;
|
|
693
|
+
};
|
|
694
|
+
function toBigInt(bytes) {
|
|
695
|
+
let hex2 = "0";
|
|
696
|
+
for (let i = 0; i < bytes.length; i++)
|
|
697
|
+
hex2 += (bytes[i] < 16 ? "0" : "") + bytes[i].toString(16);
|
|
698
|
+
return BigInt(`0x${hex2}`);
|
|
699
|
+
}
|
|
700
|
+
function toBytes(value) {
|
|
701
|
+
const hex2 = value.toString(16).padStart(BYTES * 2, "0");
|
|
702
|
+
const out = new Uint8Array(BYTES);
|
|
703
|
+
for (let i = 0; i < BYTES; i++)
|
|
704
|
+
out[i] = parseInt(hex2.slice(2 * i, 2 * i + 2), 16);
|
|
705
|
+
return out;
|
|
706
|
+
}
|
|
707
|
+
function decodePoint(bytes) {
|
|
708
|
+
if (!(bytes instanceof Uint8Array) || bytes.length !== 1 + 2 * BYTES || bytes[0] !== 4)
|
|
709
|
+
return null;
|
|
710
|
+
const x = toBigInt(bytes.subarray(1, 1 + BYTES));
|
|
711
|
+
const y = toBigInt(bytes.subarray(1 + BYTES));
|
|
712
|
+
if (x >= P || y >= P)
|
|
713
|
+
return null;
|
|
714
|
+
return mod(y * y) === mod(x * x * x + 7n) ? { x, y } : null;
|
|
715
|
+
}
|
|
716
|
+
function isSecp256k1Point(bytes) {
|
|
717
|
+
return decodePoint(bytes) !== null;
|
|
718
|
+
}
|
|
719
|
+
var INFINITY = Object.freeze({ X: 0n, Y: 1n, Z: 0n });
|
|
720
|
+
function double(p) {
|
|
721
|
+
if (p.Z === 0n || p.Y === 0n)
|
|
722
|
+
return INFINITY;
|
|
723
|
+
const a = mod(p.X * p.X);
|
|
724
|
+
const b = mod(p.Y * p.Y);
|
|
725
|
+
const c = mod(b * b);
|
|
726
|
+
const d = mod(2n * (mod((p.X + b) * (p.X + b)) - a - c));
|
|
727
|
+
const e = mod(3n * a);
|
|
728
|
+
const X = mod(e * e - 2n * d);
|
|
729
|
+
return { X, Y: mod(e * (d - X) - 8n * c), Z: mod(2n * p.Y * p.Z) };
|
|
730
|
+
}
|
|
731
|
+
function add(p, q) {
|
|
732
|
+
if (p.Z === 0n)
|
|
733
|
+
return q;
|
|
734
|
+
if (q.Z === 0n)
|
|
735
|
+
return p;
|
|
736
|
+
const z1z1 = mod(p.Z * p.Z);
|
|
737
|
+
const z2z2 = mod(q.Z * q.Z);
|
|
738
|
+
const u1 = mod(p.X * z2z2);
|
|
739
|
+
const u2 = mod(q.X * z1z1);
|
|
740
|
+
const s1 = mod(p.Y * q.Z * z2z2);
|
|
741
|
+
const s2 = mod(q.Y * p.Z * z1z1);
|
|
742
|
+
const h = mod(u2 - u1);
|
|
743
|
+
const r = mod(2n * (s2 - s1));
|
|
744
|
+
if (h === 0n)
|
|
745
|
+
return r === 0n ? double(p) : INFINITY;
|
|
746
|
+
const i = mod(4n * h * h);
|
|
747
|
+
const j = mod(h * i);
|
|
748
|
+
const v = mod(u1 * i);
|
|
749
|
+
const X = mod(r * r - j - 2n * v);
|
|
750
|
+
return { X, Y: mod(r * (v - X) - 2n * s1 * j), Z: mod((mod((p.Z + q.Z) * (p.Z + q.Z)) - z1z1 - z2z2) * h) };
|
|
751
|
+
}
|
|
752
|
+
function power(base, exponent) {
|
|
753
|
+
let result = 1n;
|
|
754
|
+
let square = mod(base);
|
|
755
|
+
for (let rest = exponent; rest > 0n; rest >>= 1n) {
|
|
756
|
+
if (rest & 1n)
|
|
757
|
+
result = mod(result * square);
|
|
758
|
+
square = mod(square * square);
|
|
759
|
+
}
|
|
760
|
+
return result;
|
|
761
|
+
}
|
|
762
|
+
function multiply(k, point) {
|
|
763
|
+
let r0 = INFINITY;
|
|
764
|
+
let r1 = { X: point.x, Y: point.y, Z: 1n };
|
|
765
|
+
for (let i = 255; i >= 0; i--) {
|
|
766
|
+
if (k >> BigInt(i) & 1n) {
|
|
767
|
+
r0 = add(r0, r1);
|
|
768
|
+
r1 = double(r1);
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
r1 = add(r0, r1);
|
|
772
|
+
r0 = double(r0);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
if (r0.Z === 0n)
|
|
776
|
+
return null;
|
|
777
|
+
const inverse = power(r0.Z, P - 2n);
|
|
778
|
+
const inverse2 = mod(inverse * inverse);
|
|
779
|
+
return { x: mod(r0.X * inverse2), y: mod(r0.Y * inverse2 * inverse) };
|
|
780
|
+
}
|
|
781
|
+
function ephemeralScalar(random) {
|
|
782
|
+
if (!random || typeof random.getRandomValues !== "function")
|
|
783
|
+
throw new Error("Refusing to encrypt: no random source.");
|
|
784
|
+
const draw = new Uint8Array(BYTES);
|
|
785
|
+
try {
|
|
786
|
+
for (let round = 0; round < MAX_DRAWS; round++) {
|
|
787
|
+
random.getRandomValues(draw);
|
|
788
|
+
const k = toBigInt(draw);
|
|
789
|
+
if (k > 0n && k < N)
|
|
790
|
+
return k;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
finally {
|
|
794
|
+
draw.fill(0);
|
|
795
|
+
}
|
|
796
|
+
throw new Error("Refusing to encrypt: the random source drew no usable key.");
|
|
797
|
+
}
|
|
798
|
+
function secp256k1Agreement(recipient, random) {
|
|
799
|
+
const q = decodePoint(recipient);
|
|
800
|
+
if (!q)
|
|
801
|
+
throw new Error("Refusing to encrypt: the key is not a point on secp256k1.");
|
|
802
|
+
const k = ephemeralScalar(random);
|
|
803
|
+
const v = multiply(k, G);
|
|
804
|
+
const s = multiply(k, q);
|
|
805
|
+
if (!v || !s)
|
|
806
|
+
throw new Error("Refusing to encrypt: the key agreement reached the point at infinity.");
|
|
807
|
+
const ephemeralPoint = new Uint8Array(1 + 2 * BYTES);
|
|
808
|
+
ephemeralPoint[0] = 4;
|
|
809
|
+
ephemeralPoint.set(toBytes(v.x), 1);
|
|
810
|
+
ephemeralPoint.set(toBytes(v.y), 1 + BYTES);
|
|
811
|
+
return { ephemeralPoint, sharedSecret: toBytes(s.x) };
|
|
812
|
+
}
|
|
655
813
|
// src/verifone-pgp.js
|
|
656
|
-
var
|
|
814
|
+
var CURVE_OIDS = Object.freeze({
|
|
815
|
+
"P-256": Object.freeze([42, 134, 72, 206, 61, 3, 1, 7]),
|
|
816
|
+
secp256k1: Object.freeze([43, 129, 4, 0, 10])
|
|
817
|
+
});
|
|
657
818
|
var ALGORITHM_ECDH = 18;
|
|
658
819
|
var ENCRYPTING_ALGORITHMS = Object.freeze([1, 2, 16, 18]);
|
|
659
820
|
var KNOWN_SYMMETRIC = Object.freeze([2, 3, 4, 7, 8, 9, 10]);
|
|
@@ -863,7 +1024,8 @@ function keyPacket(body) {
|
|
|
863
1024
|
function ecdhFields(fields) {
|
|
864
1025
|
const oidLength = fields[0];
|
|
865
1026
|
const oid = fields.subarray(1, 1 + oidLength);
|
|
866
|
-
|
|
1027
|
+
const curve = sameBytes(oid, CURVE_OIDS["P-256"]) ? "P-256" : sameBytes(oid, CURVE_OIDS.secp256k1) ? "secp256k1" : null;
|
|
1028
|
+
if (!curve)
|
|
867
1029
|
return { refusal: "verifone_key_curve_unsupported" };
|
|
868
1030
|
let at = 1 + oidLength;
|
|
869
1031
|
const bits = fields[at] << 8 | fields[at + 1];
|
|
@@ -871,12 +1033,14 @@ function ecdhFields(fields) {
|
|
|
871
1033
|
at += 2 + point.length;
|
|
872
1034
|
if (bits !== 515 || point.length !== 65 || point[0] !== 4)
|
|
873
1035
|
return { refusal: "verifone_key_malformed" };
|
|
1036
|
+
if (curve === "secp256k1" && !isSecp256k1Point(point))
|
|
1037
|
+
return { refusal: "verifone_key_malformed" };
|
|
874
1038
|
const kdf = fields.subarray(at, at + 1 + fields[at]);
|
|
875
1039
|
if (kdf.length !== 4 || kdf[0] !== 3 || kdf[1] !== 1 || at + 4 !== fields.length)
|
|
876
1040
|
return { refusal: "verifone_key_malformed" };
|
|
877
1041
|
if (!KDF_HASHES[kdf[2]] || !AES[kdf[3]])
|
|
878
1042
|
return { refusal: "verifone_key_kdf_unsupported" };
|
|
879
|
-
return { point: Uint8Array.from(point), kdfHash: kdf[2], kekAlgorithm: kdf[3] };
|
|
1043
|
+
return { curve, point: Uint8Array.from(point), kdfHash: kdf[2], kekAlgorithm: kdf[3] };
|
|
880
1044
|
}
|
|
881
1045
|
async function fingerprintOf(body, subtle) {
|
|
882
1046
|
return new Uint8Array(await subtle.digest("SHA-1", concat(Uint8Array.of(153), u16(body.length), body)));
|
|
@@ -962,6 +1126,7 @@ async function readVerifonePublicKey(publicKeyBase64, facilities) {
|
|
|
962
1126
|
primaryFingerprint: hex(primaryFingerprint),
|
|
963
1127
|
encryptionSubkeyId: hex(fingerprint.subarray(12, 20)),
|
|
964
1128
|
encryptionFingerprint: fingerprint,
|
|
1129
|
+
curve: ecdh.curve,
|
|
965
1130
|
point: ecdh.point,
|
|
966
1131
|
kdfHash: ecdh.kdfHash,
|
|
967
1132
|
kekAlgorithm: ecdh.kekAlgorithm,
|
|
@@ -999,21 +1164,31 @@ ${MESSAGE_END}\r
|
|
|
999
1164
|
`;
|
|
1000
1165
|
return encodeBase64(asciiBytes(armored));
|
|
1001
1166
|
}
|
|
1167
|
+
async function agree(curve, point, crypto) {
|
|
1168
|
+
if (curve === "secp256k1") {
|
|
1169
|
+
const { ephemeralPoint: ephemeralPoint2, sharedSecret } = secp256k1Agreement(point, crypto);
|
|
1170
|
+
return { ephemeralPoint: ephemeralPoint2, shared: sharedSecret };
|
|
1171
|
+
}
|
|
1172
|
+
const subtle = crypto.subtle;
|
|
1173
|
+
const ephemeral = await subtle.generateKey({ name: "ECDH", namedCurve: "P-256" }, true, ["deriveBits"]);
|
|
1174
|
+
const ephemeralPoint = new Uint8Array(await subtle.exportKey("raw", ephemeral.publicKey));
|
|
1175
|
+
const recipient = await subtle.importKey("raw", point, { name: "ECDH", namedCurve: "P-256" }, false, []);
|
|
1176
|
+
const shared = new Uint8Array(await subtle.deriveBits({ name: "ECDH", public: recipient }, ephemeral.privateKey, 256));
|
|
1177
|
+
return { ephemeralPoint, shared };
|
|
1178
|
+
}
|
|
1002
1179
|
async function verifoneEncryptText(key, text, facilities) {
|
|
1003
1180
|
const crypto = facilities?.crypto;
|
|
1004
1181
|
const subtle = crypto?.subtle;
|
|
1005
1182
|
if (!subtle || typeof crypto.getRandomValues !== "function")
|
|
1006
1183
|
throw new Error("Refusing to encrypt: no WebCrypto.");
|
|
1007
|
-
|
|
1184
|
+
const oid = key && Object.prototype.hasOwnProperty.call(CURVE_OIDS, key.curve) ? CURVE_OIDS[key.curve] : null;
|
|
1185
|
+
if (!key || key.refusal || !oid || !(key.point instanceof Uint8Array) || !AES[key.sessionCipher] || !AES[key.kekAlgorithm] || !KDF_HASHES[key.kdfHash]) {
|
|
1008
1186
|
throw new Error("Refusing to encrypt: the Verifone key is not one this codec reads.");
|
|
1009
1187
|
}
|
|
1010
1188
|
if (!Number.isSafeInteger(facilities.nowMs) || facilities.nowMs < 0)
|
|
1011
1189
|
throw new Error("Refusing to encrypt: no clock.");
|
|
1012
|
-
const
|
|
1013
|
-
const
|
|
1014
|
-
const recipient = await subtle.importKey("raw", key.point, { name: "ECDH", namedCurve: "P-256" }, false, []);
|
|
1015
|
-
const shared = new Uint8Array(await subtle.deriveBits({ name: "ECDH", public: recipient }, ephemeral.privateKey, 256));
|
|
1016
|
-
const param = concat(Uint8Array.of(P256_OID.length), Uint8Array.from(P256_OID), Uint8Array.of(ALGORITHM_ECDH, 3, 1, key.kdfHash, key.kekAlgorithm), asciiBytes("Anonymous Sender "), key.encryptionFingerprint);
|
|
1190
|
+
const { ephemeralPoint, shared } = await agree(key.curve, key.point, crypto);
|
|
1191
|
+
const param = concat(Uint8Array.of(oid.length), Uint8Array.from(oid), Uint8Array.of(ALGORITHM_ECDH, 3, 1, key.kdfHash, key.kekAlgorithm), asciiBytes("Anonymous Sender "), key.encryptionFingerprint);
|
|
1017
1192
|
const derived = new Uint8Array(await subtle.digest(KDF_HASHES[key.kdfHash], concat(u32(1), shared, param)));
|
|
1018
1193
|
const kek = await subtle.importKey("raw", derived.subarray(0, AES[key.kekAlgorithm]), "AES-KW", false, ["wrapKey"]);
|
|
1019
1194
|
const sessionKey = crypto.getRandomValues(new Uint8Array(AES[key.sessionCipher]));
|
|
@@ -1142,7 +1317,7 @@ function verifoneClockSkewRefusal(deviceNowMs, serverNowMs) {
|
|
|
1142
1317
|
}
|
|
1143
1318
|
var KEY_REFUSALS = Object.freeze({
|
|
1144
1319
|
verifone_key_unpinned: "Refusing to encrypt: Agentcard has not pinned this Verifone key.",
|
|
1145
|
-
verifone_key_curve_unsupported: "Refusing to encrypt: the Verifone key is
|
|
1320
|
+
verifone_key_curve_unsupported: "Refusing to encrypt: the Verifone key is on neither NIST P-256 nor secp256k1.",
|
|
1146
1321
|
verifone_key_expired: "Refusing to encrypt: this Verifone key has expired.",
|
|
1147
1322
|
verifone_key_revoked: "Refusing to encrypt: this Verifone key was revoked."
|
|
1148
1323
|
});
|
package/dist/verifone.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* key paths, never a value) and aborts it, so the agent's placeholder card never
|
|
19
19
|
* reaches the merchant and nothing is charged; 'enabled' lets a prepared checkout
|
|
20
20
|
* pay the reviewed leg. A profile is 'enabled' only when the API and this build both
|
|
21
|
-
* say so. The Container Store ships in 'observe',
|
|
21
|
+
* say so. The Container Store ships in 'observe', its card request reviewed and no key pinned.
|
|
22
22
|
*/
|
|
23
23
|
import * as generated from './verifone.generated.js';
|
|
24
24
|
import { sha256Hex as sharedSha256Hex } from './adyen-merchant-hosted.generated.js';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Whether `url` is one of MyCheckout's four card-form endpoints, written exactly (payment-core's rule). */
|
|
2
|
+
export declare function isWorldlineMyCheckoutEndpoint(url: string): boolean;
|
|
3
|
+
/** The `blocked` detail for a card submit whose product is not the one its page chose. */
|
|
4
|
+
export declare const WORLDLINE_MYCHECKOUT_PRODUCT_CHANGED = "worldline_mycheckout_product_changed";
|
|
5
|
+
/** The `blocked` detail for a card submit that names no product, on a hosted checkout whose page chose one. */
|
|
6
|
+
export declare const WORLDLINE_MYCHECKOUT_PRODUCT_MISSING = "worldline_mycheckout_product_missing";
|
|
7
|
+
export type WorldlineMyCheckoutRefusal = typeof WORLDLINE_MYCHECKOUT_PRODUCT_CHANGED | typeof WORLDLINE_MYCHECKOUT_PRODUCT_MISSING;
|
|
8
|
+
/**
|
|
9
|
+
* The card products one attachment saw chosen, per hosted checkout: its `publicMerchantId` and
|
|
10
|
+
* `token`, the pair payment-core's duplicate guard reads as one hosted checkout
|
|
11
|
+
* (WORLDLINE_MYCHECKOUT_DEDUPE).
|
|
12
|
+
*/
|
|
13
|
+
export declare class WorldlineMyCheckoutProducts {
|
|
14
|
+
private readonly chosen;
|
|
15
|
+
/**
|
|
16
|
+
* Read one POST to a card-form endpoint. A product selection (no card, and neither the Pay nor
|
|
17
|
+
* the cancel pair) records the product it chose for its hosted checkout, or forgets the choice
|
|
18
|
+
* when it picks the card group, which leaves the product to the card. A card submit is judged:
|
|
19
|
+
* WORLDLINE_MYCHECKOUT_PRODUCT_CHANGED when its paymentProductId is another product than the one
|
|
20
|
+
* chosen for its hosted checkout, WORLDLINE_MYCHECKOUT_PRODUCT_MISSING when it names none. Null for
|
|
21
|
+
* every other post, and for a card submit on the card group or on a hosted checkout whose choice
|
|
22
|
+
* this attachment never saw: the API and the device judge those as before. A body payment-core
|
|
23
|
+
* cannot read is never judged here either.
|
|
24
|
+
*/
|
|
25
|
+
judge(url: string, body: string | null): WorldlineMyCheckoutRefusal | null;
|
|
26
|
+
private remember;
|
|
27
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
declare var WORLDLINE_MYCHECKOUT_PSP: string;
|
|
2
|
+
declare var WORLDLINE_MYCHECKOUT_CAPABILITY: string;
|
|
3
|
+
declare var WORLDLINE_MYCHECKOUT_ENDPOINTS: Readonly<{
|
|
4
|
+
production: readonly string[];
|
|
5
|
+
sandbox: readonly string[];
|
|
6
|
+
}>;
|
|
7
|
+
declare function worldlineMyCheckoutEnvironment(url: any): "production" | "sandbox" | undefined;
|
|
8
|
+
declare function isWorldlineMyCheckoutUrl(url: any): boolean;
|
|
9
|
+
declare var WORLDLINE_MYCHECKOUT_CARD_PRODUCTS: Readonly<{
|
|
10
|
+
visa: readonly string[];
|
|
11
|
+
mastercard: readonly string[];
|
|
12
|
+
amex: readonly string[];
|
|
13
|
+
discover: readonly string[];
|
|
14
|
+
jcb: readonly string[];
|
|
15
|
+
diners: readonly string[];
|
|
16
|
+
}>;
|
|
17
|
+
declare function worldlineMyCheckoutProductNetwork(productId: any): string | null;
|
|
18
|
+
declare function parseWorldlineMyCheckoutForm(body: any): {
|
|
19
|
+
refusal: string;
|
|
20
|
+
pairs?: undefined;
|
|
21
|
+
} | {
|
|
22
|
+
pairs: string[][];
|
|
23
|
+
refusal?: undefined;
|
|
24
|
+
};
|
|
25
|
+
declare function worldlineMyCheckoutCardholderName(name: any): string | null;
|
|
26
|
+
declare function worldlineMyCheckoutTemplateRefusal(body: any): string | null;
|
|
27
|
+
declare function worldlineMyCheckoutRequestRefusal(request: any): string | null;
|
|
28
|
+
declare var WORLDLINE_MYCHECKOUT_DEDUPE: readonly string[];
|
|
29
|
+
export { WORLDLINE_MYCHECKOUT_CAPABILITY, WORLDLINE_MYCHECKOUT_CARD_PRODUCTS, WORLDLINE_MYCHECKOUT_DEDUPE, WORLDLINE_MYCHECKOUT_ENDPOINTS, WORLDLINE_MYCHECKOUT_PSP, isWorldlineMyCheckoutUrl, parseWorldlineMyCheckoutForm, worldlineMyCheckoutCardholderName, worldlineMyCheckoutEnvironment, worldlineMyCheckoutProductNetwork, worldlineMyCheckoutRequestRefusal, worldlineMyCheckoutTemplateRefusal };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
+
// artifact-sha256: 486a32aa0f96f59666d4a43e42fee9f493cd3059b27ad3f92b97ad6b249133f5
|
|
4
|
+
// src/worldline-mycheckout.js
|
|
5
|
+
var WORLDLINE_MYCHECKOUT_PSP = "worldline_mycheckout";
|
|
6
|
+
var WORLDLINE_MYCHECKOUT_CAPABILITY = "worldline_mycheckout_form";
|
|
7
|
+
var WORLDLINE_MYCHECKOUT_ENDPOINTS = /* @__PURE__ */ Object.freeze({
|
|
8
|
+
production: /* @__PURE__ */ Object.freeze([
|
|
9
|
+
"https://payment.pay1.checkout.worldline-solutions.com/checkout",
|
|
10
|
+
"https://payment.pay2.checkout.worldline-solutions.com/checkout"
|
|
11
|
+
]),
|
|
12
|
+
sandbox: /* @__PURE__ */ Object.freeze([
|
|
13
|
+
"https://payment.pay1.preprod.checkout.worldline-solutions.com/checkout",
|
|
14
|
+
"https://payment.pay2.preprod.checkout.worldline-solutions.com/checkout"
|
|
15
|
+
])
|
|
16
|
+
});
|
|
17
|
+
function worldlineMyCheckoutEnvironment(url) {
|
|
18
|
+
if (WORLDLINE_MYCHECKOUT_ENDPOINTS.production.includes(url))
|
|
19
|
+
return "production";
|
|
20
|
+
if (WORLDLINE_MYCHECKOUT_ENDPOINTS.sandbox.includes(url))
|
|
21
|
+
return "sandbox";
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
function isWorldlineMyCheckoutUrl(url) {
|
|
25
|
+
return worldlineMyCheckoutEnvironment(url) !== void 0;
|
|
26
|
+
}
|
|
27
|
+
var WORLDLINE_MYCHECKOUT_CARD_PRODUCTS = /* @__PURE__ */ Object.freeze({
|
|
28
|
+
visa: /* @__PURE__ */ Object.freeze(["1", "114", "122"]),
|
|
29
|
+
mastercard: /* @__PURE__ */ Object.freeze(["3", "119"]),
|
|
30
|
+
amex: /* @__PURE__ */ Object.freeze(["2"]),
|
|
31
|
+
discover: /* @__PURE__ */ Object.freeze(["128"]),
|
|
32
|
+
jcb: /* @__PURE__ */ Object.freeze(["125"]),
|
|
33
|
+
diners: /* @__PURE__ */ Object.freeze(["132"])
|
|
34
|
+
});
|
|
35
|
+
function worldlineMyCheckoutProductNetwork(productId) {
|
|
36
|
+
for (const [network, ids] of Object.entries(WORLDLINE_MYCHECKOUT_CARD_PRODUCTS)) {
|
|
37
|
+
if (ids.includes(productId))
|
|
38
|
+
return network;
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
43
|
+
var FIELDS = {
|
|
44
|
+
publicMerchantId: /^[1-9]\d{0,9}$/,
|
|
45
|
+
variantCode: /^\d{1,10}$/,
|
|
46
|
+
locale: /^[a-z]{2}_[A-Z]{2}$/,
|
|
47
|
+
idempotenceKey: UUID,
|
|
48
|
+
// <merchant id>-<32 hex>:<hosted checkout id>:<32 hex>, the hosted checkout's own id.
|
|
49
|
+
token: /^[1-9]\d{0,9}-[0-9a-f]{32}:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}:[0-9a-f]{32}$/,
|
|
50
|
+
requestToken: UUID,
|
|
51
|
+
isPaymentProductDetailsShown: /^(?:true|false)$/,
|
|
52
|
+
isPaymentProductFiscalNumberShown: /^(?:true|false)$/,
|
|
53
|
+
jsEnabled: /^true$/,
|
|
54
|
+
isFramed: /^(?:true|false)$/,
|
|
55
|
+
paymentProductId: /^\d{1,4}$/,
|
|
56
|
+
paymentProductGroupId: /^cards$/,
|
|
57
|
+
colorDepth: /^\d{0,3}$/,
|
|
58
|
+
screenHeight: /^\d{0,5}$/,
|
|
59
|
+
screenWidth: /^\d{0,5}$/,
|
|
60
|
+
innerHeight: /^\d{0,5}$/,
|
|
61
|
+
innerWidth: /^\d{0,5}$/,
|
|
62
|
+
timezoneOffsetUtcMinutes: /^-?\d{0,4}$/,
|
|
63
|
+
browserLocale: /^[A-Za-z0-9-]{0,35}$/,
|
|
64
|
+
javaEnabled: /^(?:true|false)?$/,
|
|
65
|
+
cardNumber: /^[0-9 ]{12,23}$/,
|
|
66
|
+
// MM/YY as the page's mask writes it; the delimiter may be absent or padded.
|
|
67
|
+
expiryDate: /^(?:0[1-9]|1[0-2])(?: ?\/ ?)?\d{2}$/,
|
|
68
|
+
cvv: /^\d{3,4}$/,
|
|
69
|
+
cardholderName: /^[^\u0000-\u001f\u007f<>]{0,100}$/,
|
|
70
|
+
// The autofill helpers the page folds into expiryDate and then empties.
|
|
71
|
+
expiryDateMonth: /^$/,
|
|
72
|
+
expiryDateYear: /^$/,
|
|
73
|
+
// A checked "remember" asks Worldline to keep the card for later purchases.
|
|
74
|
+
remember: /^$/,
|
|
75
|
+
// Present, empty, on a page that offers Click to Pay; a value is a Click to Pay payment.
|
|
76
|
+
clickToPayCheckoutResponseSignature: /^$/,
|
|
77
|
+
// The Pay button's own pair. The click handler writes it into #buttonValue and appends one
|
|
78
|
+
// more copy per click, so it may repeat, always as doPayment=doPayment.
|
|
79
|
+
doPayment: /^doPayment$/
|
|
80
|
+
};
|
|
81
|
+
var REPEATABLE = /* @__PURE__ */ new Set(["doPayment"]);
|
|
82
|
+
var REQUIRED = ["publicMerchantId", "token", "requestToken", "idempotenceKey", "doPayment", "cardNumber", "expiryDate", "cvv"];
|
|
83
|
+
var NOT_A_CARD_SALE = /* @__PURE__ */ new Set(["cancelPayment", "encryptedPaymentData", "mobileCardholderName"]);
|
|
84
|
+
var BODY_LIMIT = 16384;
|
|
85
|
+
var VALUE_LIMIT = 2048;
|
|
86
|
+
function parseWorldlineMyCheckoutForm(body) {
|
|
87
|
+
if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT || /[^\x21-\x7e]/.test(body))
|
|
88
|
+
return { refusal: "body" };
|
|
89
|
+
const pairs = [];
|
|
90
|
+
const seen = /* @__PURE__ */ new Set();
|
|
91
|
+
for (const chunk of body.split("&")) {
|
|
92
|
+
const eq = chunk.indexOf("=");
|
|
93
|
+
if (eq < 1)
|
|
94
|
+
return { refusal: "body" };
|
|
95
|
+
let key;
|
|
96
|
+
let value;
|
|
97
|
+
try {
|
|
98
|
+
key = decodeURIComponent(chunk.slice(0, eq).replace(/\+/g, " "));
|
|
99
|
+
value = decodeURIComponent(chunk.slice(eq + 1).replace(/\+/g, " "));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return { refusal: "body" };
|
|
103
|
+
}
|
|
104
|
+
if (value.length > VALUE_LIMIT || /[\u0000-\u001f\u007f]/.test(value))
|
|
105
|
+
return { refusal: "body" };
|
|
106
|
+
if (seen.has(key) && !REPEATABLE.has(key))
|
|
107
|
+
return { refusal: "body" };
|
|
108
|
+
seen.add(key);
|
|
109
|
+
pairs.push([key, value]);
|
|
110
|
+
}
|
|
111
|
+
return { pairs };
|
|
112
|
+
}
|
|
113
|
+
var SESSION_FIELDS = /* @__PURE__ */ new Set(["publicMerchantId", "token", "requestToken", "idempotenceKey"]);
|
|
114
|
+
var CARD_FIELDS = /* @__PURE__ */ new Set(["cardNumber", "expiryDate", "cvv", "cardholderName", "expiryDateMonth", "expiryDateYear"]);
|
|
115
|
+
function worldlineMyCheckoutCardholderName(name) {
|
|
116
|
+
if (typeof name !== "string")
|
|
117
|
+
return null;
|
|
118
|
+
const trimmed = name.trim();
|
|
119
|
+
return trimmed !== "" && FIELDS.cardholderName.test(trimmed) ? trimmed : null;
|
|
120
|
+
}
|
|
121
|
+
function worldlineMyCheckoutTemplateRefusal(body) {
|
|
122
|
+
const parsed = parseWorldlineMyCheckoutForm(body);
|
|
123
|
+
if (parsed.refusal)
|
|
124
|
+
return parsed.refusal;
|
|
125
|
+
const values = /* @__PURE__ */ new Map();
|
|
126
|
+
for (const [key, value] of parsed.pairs) {
|
|
127
|
+
if (NOT_A_CARD_SALE.has(key) || key === "clickToPayCheckoutResponseSignature" && value !== "")
|
|
128
|
+
return "not_a_card_sale";
|
|
129
|
+
if (key === "remember" && value !== "")
|
|
130
|
+
return "stored_card";
|
|
131
|
+
if (!Object.hasOwn(FIELDS, key))
|
|
132
|
+
return "unknown_field";
|
|
133
|
+
values.set(key, value);
|
|
134
|
+
}
|
|
135
|
+
for (const key of REQUIRED)
|
|
136
|
+
if (!values.has(key))
|
|
137
|
+
return "missing_field";
|
|
138
|
+
for (const [key, value] of parsed.pairs) {
|
|
139
|
+
if (FIELDS[key].test(value))
|
|
140
|
+
continue;
|
|
141
|
+
return SESSION_FIELDS.has(key) ? "session" : CARD_FIELDS.has(key) ? "card_fields" : "field_value";
|
|
142
|
+
}
|
|
143
|
+
if (values.get("token").split("-")[0] !== values.get("publicMerchantId"))
|
|
144
|
+
return "session";
|
|
145
|
+
const product = values.get("paymentProductId");
|
|
146
|
+
if (product === void 0 ? !values.has("paymentProductGroupId") : worldlineMyCheckoutProductNetwork(product) === null)
|
|
147
|
+
return "product";
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
function worldlineMyCheckoutRequestRefusal(request) {
|
|
151
|
+
if (!request || typeof request !== "object" || !isWorldlineMyCheckoutUrl(request.url))
|
|
152
|
+
return "endpoint";
|
|
153
|
+
if (request.method !== "POST")
|
|
154
|
+
return "method";
|
|
155
|
+
const headers = request.headers && typeof request.headers === "object" ? request.headers : {};
|
|
156
|
+
const types = Object.entries(headers).filter(([name]) => name.toLowerCase() === "content-type");
|
|
157
|
+
if (types.length !== 1 || typeof types[0][1] !== "string" || !/^application\/x-www-form-urlencoded(?:;\s*charset=utf-8)?$/i.test(types[0][1].trim()))
|
|
158
|
+
return "content_type";
|
|
159
|
+
return worldlineMyCheckoutTemplateRefusal(request.body);
|
|
160
|
+
}
|
|
161
|
+
var WORLDLINE_MYCHECKOUT_DEDUPE = /* @__PURE__ */ Object.freeze(["publicMerchantId", "token"]);
|
|
162
|
+
export { WORLDLINE_MYCHECKOUT_CAPABILITY, WORLDLINE_MYCHECKOUT_CARD_PRODUCTS, WORLDLINE_MYCHECKOUT_DEDUPE, WORLDLINE_MYCHECKOUT_ENDPOINTS, WORLDLINE_MYCHECKOUT_PSP, isWorldlineMyCheckoutUrl, parseWorldlineMyCheckoutForm, worldlineMyCheckoutCardholderName, worldlineMyCheckoutEnvironment, worldlineMyCheckoutProductNetwork, worldlineMyCheckoutRequestRefusal, worldlineMyCheckoutTemplateRefusal };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Worldline Connect MyCheckout (psp `worldline_mycheckout`): the hosted payment page whose card
|
|
2
|
+
// form posts the card as a top-level navigation to /checkout on Worldline's own host. The form
|
|
3
|
+
// rules are payment-core's (worldline-mycheckout.generated.ts): the API applies them at create,
|
|
4
|
+
// and the cardholder's device applies them again before it rebuilds the form with the real card.
|
|
5
|
+
// This module holds the one thing only the SDK can decide: which card product the page chose.
|
|
6
|
+
//
|
|
7
|
+
// A MyCheckout page that offers several card products asks the shopper to choose one first (its
|
|
8
|
+
// select-payment-product page: Visa 1, Mastercard 3, Visa Electron 122, and so on). That choice
|
|
9
|
+
// posts to the same /checkout URL with no card, and continues untouched. The card form it opens
|
|
10
|
+
// then looks the typed number's IIN up with Worldline and, when the answer names another product
|
|
11
|
+
// the page offers, switches its own paymentProductId to that product. With the agent's placeholder
|
|
12
|
+
// typed, the answer describes the placeholder, never the card that pays: on 2026-10-12 CLZ's page
|
|
13
|
+
// read the SDK's Visa placeholder (411111) as Visa Electron (122) and switched the form from the
|
|
14
|
+
// Visa (1) the agent chose. The cardholder's device checks only that the product belongs to the
|
|
15
|
+
// real card's network (Visa takes 1, 114 and 122), so it would have submitted a Visa credit card
|
|
16
|
+
// as Visa Electron. The device never sees the choice and the API stores only the card form; this
|
|
17
|
+
// attachment sees both posts, so the SDK holds the card submit to the product chosen for its
|
|
18
|
+
// hosted checkout.
|
|
19
|
+
//
|
|
20
|
+
// The processor ships dark: the API serves the recognizer only to a build that declares
|
|
21
|
+
// `worldline_mycheckout_form`, for an org Agentcard turned Worldline on for. Without it no
|
|
22
|
+
// MyCheckout post pauses, and none reaches this module.
|
|
23
|
+
import { isWorldlineMyCheckoutUrl, parseWorldlineMyCheckoutForm } from './worldline-mycheckout.generated.js';
|
|
24
|
+
/** Whether `url` is one of MyCheckout's four card-form endpoints, written exactly (payment-core's rule). */
|
|
25
|
+
export function isWorldlineMyCheckoutEndpoint(url) {
|
|
26
|
+
return isWorldlineMyCheckoutUrl(url) === true;
|
|
27
|
+
}
|
|
28
|
+
/** The `blocked` detail for a card submit whose product is not the one its page chose. */
|
|
29
|
+
export const WORLDLINE_MYCHECKOUT_PRODUCT_CHANGED = 'worldline_mycheckout_product_changed';
|
|
30
|
+
/** The `blocked` detail for a card submit that names no product, on a hosted checkout whose page chose one. */
|
|
31
|
+
export const WORLDLINE_MYCHECKOUT_PRODUCT_MISSING = 'worldline_mycheckout_product_missing';
|
|
32
|
+
/** How many hosted checkouts one attachment remembers a choice for; the oldest is forgotten first. */
|
|
33
|
+
const REMEMBERED_CHECKOUTS = 32;
|
|
34
|
+
const PRODUCT_ID = /^\d{1,4}$/;
|
|
35
|
+
/**
|
|
36
|
+
* The card products one attachment saw chosen, per hosted checkout: its `publicMerchantId` and
|
|
37
|
+
* `token`, the pair payment-core's duplicate guard reads as one hosted checkout
|
|
38
|
+
* (WORLDLINE_MYCHECKOUT_DEDUPE).
|
|
39
|
+
*/
|
|
40
|
+
export class WorldlineMyCheckoutProducts {
|
|
41
|
+
chosen = new Map();
|
|
42
|
+
/**
|
|
43
|
+
* Read one POST to a card-form endpoint. A product selection (no card, and neither the Pay nor
|
|
44
|
+
* the cancel pair) records the product it chose for its hosted checkout, or forgets the choice
|
|
45
|
+
* when it picks the card group, which leaves the product to the card. A card submit is judged:
|
|
46
|
+
* WORLDLINE_MYCHECKOUT_PRODUCT_CHANGED when its paymentProductId is another product than the one
|
|
47
|
+
* chosen for its hosted checkout, WORLDLINE_MYCHECKOUT_PRODUCT_MISSING when it names none. Null for
|
|
48
|
+
* every other post, and for a card submit on the card group or on a hosted checkout whose choice
|
|
49
|
+
* this attachment never saw: the API and the device judge those as before. A body payment-core
|
|
50
|
+
* cannot read is never judged here either.
|
|
51
|
+
*/
|
|
52
|
+
judge(url, body) {
|
|
53
|
+
if (typeof body !== 'string' || !isWorldlineMyCheckoutUrl(url))
|
|
54
|
+
return null;
|
|
55
|
+
const parsed = parseWorldlineMyCheckoutForm(body);
|
|
56
|
+
if (!parsed.pairs)
|
|
57
|
+
return null;
|
|
58
|
+
const values = new Map();
|
|
59
|
+
for (const [key, value] of parsed.pairs)
|
|
60
|
+
if (!values.has(key))
|
|
61
|
+
values.set(key, value);
|
|
62
|
+
const merchant = values.get('publicMerchantId');
|
|
63
|
+
const token = values.get('token');
|
|
64
|
+
if (!merchant || !token)
|
|
65
|
+
return null;
|
|
66
|
+
const checkout = JSON.stringify([merchant, token]);
|
|
67
|
+
const product = values.get('paymentProductId');
|
|
68
|
+
// A card is a non-empty cardNumber, as the recognizer's card fields read it (card-fields.js).
|
|
69
|
+
if (!values.get('cardNumber')) {
|
|
70
|
+
if (values.has('doPayment') || values.has('cancelPayment'))
|
|
71
|
+
return null;
|
|
72
|
+
if (product !== undefined && PRODUCT_ID.test(product))
|
|
73
|
+
this.remember(checkout, product);
|
|
74
|
+
else if (values.get('paymentProductGroupId') === 'cards')
|
|
75
|
+
this.chosen.delete(checkout);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
if (values.has('paymentProductGroupId'))
|
|
79
|
+
return null;
|
|
80
|
+
const chosen = this.chosen.get(checkout);
|
|
81
|
+
if (chosen === undefined)
|
|
82
|
+
return null;
|
|
83
|
+
if (!product)
|
|
84
|
+
return WORLDLINE_MYCHECKOUT_PRODUCT_MISSING;
|
|
85
|
+
return product === chosen ? null : WORLDLINE_MYCHECKOUT_PRODUCT_CHANGED;
|
|
86
|
+
}
|
|
87
|
+
remember(checkout, product) {
|
|
88
|
+
this.chosen.delete(checkout);
|
|
89
|
+
this.chosen.set(checkout, product);
|
|
90
|
+
while (this.chosen.size > REMEMBERED_CHECKOUTS)
|
|
91
|
+
this.chosen.delete(this.chosen.keys().next().value);
|
|
92
|
+
}
|
|
93
|
+
}
|
package/dist/wspay.d.ts
CHANGED
|
@@ -4,27 +4,28 @@ export declare const WSPAY_PSP = "wspay";
|
|
|
4
4
|
export declare const WSPAY_CAPABILITY = "wspay_confirm";
|
|
5
5
|
/**
|
|
6
6
|
* Why the SDK refuses a WSPay request before anything is claimed, created or prompted:
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
7
|
+
* `unsupported_wspay_payment_method`, not the card frame's new-card confirm. That covers a
|
|
8
|
+
* saved_card or temp_card_id body (no new card to fill), a card-shaped object the page built
|
|
9
|
+
* into the transaction, another URL shape on the host, or headers the frame does not send. The
|
|
10
|
+
* cardholder's device could never fill it, and the API refuses the same requests at create
|
|
11
|
+
* (invalid_wspay_request), so a refusal here only saves the round trip.
|
|
12
|
+
*
|
|
13
|
+
* A sound confirm that asks WSPay to keep the card on file (tokenize_pan true) pauses like a
|
|
14
|
+
* one-time one: the approval that follows asks the cardholder whether the merchant may keep the
|
|
15
|
+
* card. The API offers it for that approval only as a hotel's EUR 1.00 card guarantee paid with a
|
|
16
|
+
* card the cardholder stored, and refuses any other at create (wspay_keep_on_file_unsupported,
|
|
17
|
+
* card_not_stored).
|
|
16
18
|
*/
|
|
17
|
-
export type WspayRefusalReason = 'unsupported_wspay_payment_method'
|
|
19
|
+
export type WspayRefusalReason = 'unsupported_wspay_payment_method';
|
|
18
20
|
/** True when the URL's host is one of WSPay's two Components hosts. */
|
|
19
21
|
export declare function isWspayHost(url: string): boolean;
|
|
20
22
|
/**
|
|
21
23
|
* The pause decision for a POST the registry recognized on a WSPay host. Null when it is the
|
|
22
|
-
* card frame's confirm of a new card
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* the cardholder.
|
|
24
|
+
* card frame's confirm of a new card, which pauses for approval; otherwise the reason it is
|
|
25
|
+
* refused. It judges the whole request (the exact URL, the frame's content type, Accept and
|
|
26
|
+
* WP3-v2-Client authorization, and a body of the frame's own keys bound to the URL's
|
|
27
|
+
* client_secret) with payment-core's wspayConfirmRefusal, the check the API runs at create.
|
|
28
|
+
* It passes keep-on-file consent because the approval that follows asks the cardholder for it.
|
|
28
29
|
*/
|
|
29
30
|
export declare function wspayRefusal(request: {
|
|
30
31
|
url: string;
|