@agent-cards/sdk 0.23.9 → 0.23.10

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/PREFLIGHT.md +7 -5
  3. package/README.md +181 -92
  4. package/dist/aci.d.ts +7 -5
  5. package/dist/aci.generated.d.ts +2 -1
  6. package/dist/aci.generated.js +14 -4
  7. package/dist/aci.js +28 -7
  8. package/dist/amadeus-checkout.generated.js +2 -2
  9. package/dist/builtin-registry.generated.js +1 -1
  10. package/dist/card-fields.generated.js +163 -9
  11. package/dist/cdp.js +79 -34
  12. package/dist/client.d.ts +61 -31
  13. package/dist/client.js +99 -42
  14. package/dist/creditguard.generated.d.ts +1 -1
  15. package/dist/creditguard.generated.js +1 -3
  16. package/dist/cse-body.d.ts +7 -6
  17. package/dist/cse-body.js +8 -7
  18. package/dist/datatrans.generated.js +8 -5
  19. package/dist/elavon-converge.d.ts +1 -1
  20. package/dist/elavon-converge.generated.js +7 -4
  21. package/dist/elavon-converge.js +4 -3
  22. package/dist/fiserv.d.ts +9 -5
  23. package/dist/fiserv.generated.d.ts +3 -3
  24. package/dist/fiserv.generated.js +50 -20
  25. package/dist/fiserv.js +10 -6
  26. package/dist/payrails.d.ts +78 -18
  27. package/dist/payrails.generated.d.ts +12 -2
  28. package/dist/payrails.generated.js +213 -18
  29. package/dist/payrails.js +165 -44
  30. package/dist/pre-claim.js +51 -29
  31. package/dist/preflight-capabilities.generated.d.ts +0 -106
  32. package/dist/preflight-capabilities.generated.js +0 -153
  33. package/dist/preflight-catalog.json +61 -181
  34. package/dist/preflight-schemas.json +6 -6
  35. package/dist/preflight.generated.d.ts +0 -106
  36. package/dist/preflight.generated.js +27 -15
  37. package/dist/preparation.d.ts +2 -0
  38. package/dist/preparation.js +4 -2
  39. package/dist/prepared-processor.d.ts +19 -5
  40. package/dist/prepared-processor.js +21 -5
  41. package/dist/primer.generated.d.ts +5 -1
  42. package/dist/primer.generated.js +200 -33
  43. package/dist/registry.d.ts +18 -12
  44. package/dist/registry.js +11 -8
  45. package/dist/shiji-sts.d.ts +24 -22
  46. package/dist/shiji-sts.generated.d.ts +1 -1
  47. package/dist/shiji-sts.generated.js +12 -8
  48. package/dist/shiji-sts.js +49 -46
  49. package/dist/stripe-checkout.generated.js +146 -49
  50. package/dist/verifone.generated.js +192 -17
  51. package/dist/verifone.js +1 -1
  52. package/dist/wspay.d.ts +17 -16
  53. package/dist/wspay.generated.d.ts +6 -1
  54. package/dist/wspay.generated.js +13 -3
  55. package/dist/wspay.js +9 -11
  56. package/examples/preflight/kernel-native/README.md +1 -1
  57. package/examples/preflight/kernel-native/inventory.json +1 -19
  58. 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: cf655438763f54e5d5bfea2555f76941ef80dbfd5aa7bc6342a2a75d1ac9cd39
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. Its card request has never been observed: the
419
- // CheckoutV2 bundle was never archived and the page is session-gated. So no leg and no key
420
- // are pinned, and every blob-bearing request to www.containerstore.com is aborted, until one
421
- // person-run checkout captured as a HAR pins the endpoint, the member holding the blob, the
422
- // key and its alias, and whether the blob is sent once or twice.
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 P256_OID = Object.freeze([42, 134, 72, 206, 61, 3, 1, 7]);
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
- if (oidLength !== P256_OID.length || !sameBytes(oid, P256_OID))
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
- if (!key || key.refusal || !(key.point instanceof Uint8Array) || !AES[key.sessionCipher] || !AES[key.kekAlgorithm] || !KDF_HASHES[key.kdfHash]) {
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 ephemeral = await subtle.generateKey({ name: "ECDH", namedCurve: "P-256" }, true, ["deriveBits"]);
1013
- const ephemeralPoint = new Uint8Array(await subtle.exportKey("raw", ephemeral.publicKey));
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 not on NIST P-256.",
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', with no leg and no key reviewed.
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';
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
- * - `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
9
- * built into the transaction, another URL shape on the host, or headers the frame does not
10
- * send. The cardholder's device could never fill it.
11
- * - `wspay_keep_on_file_unsupported`: a sound confirm that asks WSPay to keep the card on file
12
- * (tokenize_pan true), so the merchant could charge it later without another approval. Only
13
- * the cardholder can agree to that, and no approval asks them yet.
14
- * The API refuses the same requests at create (invalid_wspay_request and
15
- * wspay_keep_on_file_unsupported), so a refusal here only saves the round trip.
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' | 'wspay_keep_on_file_unsupported';
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 for a one-time payment, which pauses for approval;
23
- * otherwise the reason it is refused. It judges the whole request (the exact URL, the frame's
24
- * content type, Accept and WP3-v2-Client authorization, and a body of the frame's own keys
25
- * bound to the URL's client_secret) with payment-core's wspayConfirmRefusal, the check the API
26
- * runs at create. It never passes keep-on-file consent: that waits for an approval that asks
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;
@@ -6,6 +6,11 @@ declare function isWspayNewCardBody(body: any, clientSecret: any, options: any):
6
6
  declare function classifyWspayRequest(url: any, method: any, body: any, options: any): "card" | "unsupported" | null;
7
7
  declare function isWspayConfirmRequest(request: any, options: any): any;
8
8
  declare function wspayStoredCredentialRequested(body: any): boolean | null;
9
+ declare var WSPAY_GUARANTEE: Readonly<{
10
+ amount: 100;
11
+ currency: "eur";
12
+ }>;
13
+ declare function wspayKeepOnFileRefusal(body: any, amount: any, currency: any): "keep_on_file" | null;
9
14
  declare function wspayConfirmRefusal(request: any, options: any): "unsupported" | "keep_on_file" | null;
10
15
  declare function wspayRefusalMessage(reason: any): string;
11
16
  declare function injectWspayCard(body: any, card: any, options: any): {
@@ -38,4 +43,4 @@ declare function wspayFacts(status: any, text2: any): {
38
43
  outcome: string;
39
44
  refusalCode: any;
40
45
  };
41
- export { WSPAY_CONFIRM_HOSTS, WSPAY_TEST_HOST, classifyWspayRequest, injectWspayCard, isWspayConfirmRequest, isWspayConfirmUrl, isWspayNewCardBody, sanitizeWspayResponse, wspayConfirmClientSecret, wspayConfirmRefusal, wspayFacts, wspayRefusalMessage, wspayResponseOutcome, wspayStoredCredentialRequested };
46
+ export { WSPAY_CONFIRM_HOSTS, WSPAY_GUARANTEE, WSPAY_TEST_HOST, classifyWspayRequest, injectWspayCard, isWspayConfirmRequest, isWspayConfirmUrl, isWspayNewCardBody, sanitizeWspayResponse, wspayConfirmClientSecret, wspayConfirmRefusal, wspayFacts, wspayKeepOnFileRefusal, wspayRefusalMessage, wspayResponseOutcome, wspayStoredCredentialRequested };
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: 560fa6d54ff04e65a21dc08d3046495f67499274b35c83053f8b25370f13a17a
3
+ // artifact-sha256: 9ba65916c5dc5ee3ca830d630cafbde62d537e61991c33c80f1520ac93ef0b0d
4
4
  // src/wspay.js
5
5
  var WSPAY_CONFIRM_HOSTS = Object.freeze(["proxy.wspay.biz", "proxy-test.wspay.biz"]);
6
6
  var WSPAY_TEST_HOST = "proxy-test.wspay.biz";
@@ -224,6 +224,12 @@ function wspayStoredCredentialRequested(body) {
224
224
  const value = confirmBody(body);
225
225
  return value === null ? null : value.payment_method.data.tokenize_pan === true;
226
226
  }
227
+ var WSPAY_GUARANTEE = Object.freeze({ amount: 100, currency: "eur" });
228
+ function wspayKeepOnFileRefusal(body, amount, currency) {
229
+ if (wspayStoredCredentialRequested(body) !== true)
230
+ return null;
231
+ return amount === WSPAY_GUARANTEE.amount && typeof currency === "string" && currency.toLowerCase() === WSPAY_GUARANTEE.currency ? null : "keep_on_file";
232
+ }
227
233
  function wspayConfirmRefusal(request, options) {
228
234
  if (!isWspayConfirmRequest(request, { keepOnFile: true }))
229
235
  return "unsupported";
@@ -358,7 +364,11 @@ function sanitizeWspayResponse(text2, options) {
358
364
  if (pm !== null && pm !== void 0) {
359
365
  if (!keysWithin(pm, ["type", "data"]) || pm.type !== "card" || !keysWithin(data, ["brand", "issuer", "masked", "expiration_date", "token"]) || !opaqueId(data.token) || owns(data, "brand") && !opaqueId(data.brand, 32) || owns(data, "issuer") && !opaqueId(data.issuer, 64) || owns(data, "expiration_date") && !yymm(data.expiration_date) || owns(data, "masked") && !exact(/^[0-9]{0,6}[-* xX]{4,16}[0-9]{4}$/, data.masked))
360
366
  return fail();
361
- safe.payment_method = { type: "card", data: { ...data } };
367
+ const card = {};
368
+ for (const key of ["brand", "issuer", "token"])
369
+ if (owns(data, key))
370
+ card[key] = data[key];
371
+ safe.payment_method = { type: "card", data: card };
362
372
  }
363
373
  }
364
374
  return JSON.stringify({ status: "approved", action_required: null, payment_result: safe });
@@ -367,4 +377,4 @@ function wspayFacts(status, text2) {
367
377
  const verdict = wspayResponseOutcome(status, text2);
368
378
  return { outcome: verdict.outcome, refusalCode: verdict.outcome === "refused" ? verdict.code : null };
369
379
  }
370
- export { WSPAY_CONFIRM_HOSTS, WSPAY_TEST_HOST, classifyWspayRequest, injectWspayCard, isWspayConfirmRequest, isWspayConfirmUrl, isWspayNewCardBody, sanitizeWspayResponse, wspayConfirmClientSecret, wspayConfirmRefusal, wspayFacts, wspayRefusalMessage, wspayResponseOutcome, wspayStoredCredentialRequested };
380
+ export { WSPAY_CONFIRM_HOSTS, WSPAY_GUARANTEE, WSPAY_TEST_HOST, classifyWspayRequest, injectWspayCard, isWspayConfirmRequest, isWspayConfirmUrl, isWspayNewCardBody, sanitizeWspayResponse, wspayConfirmClientSecret, wspayConfirmRefusal, wspayFacts, wspayKeepOnFileRefusal, wspayRefusalMessage, wspayResponseOutcome, wspayStoredCredentialRequested };
package/dist/wspay.js CHANGED
@@ -28,19 +28,17 @@ export function isWspayHost(url) {
28
28
  }
29
29
  /**
30
30
  * The pause decision for a POST the registry recognized on a WSPay host. Null when it is the
31
- * card frame's confirm of a new card for a one-time payment, which pauses for approval;
32
- * otherwise the reason it is refused. It judges the whole request (the exact URL, the frame's
33
- * content type, Accept and WP3-v2-Client authorization, and a body of the frame's own keys
34
- * bound to the URL's client_secret) with payment-core's wspayConfirmRefusal, the check the API
35
- * runs at create. It never passes keep-on-file consent: that waits for an approval that asks
36
- * the cardholder.
31
+ * card frame's confirm of a new card, which pauses for approval; otherwise the reason it is
32
+ * refused. It judges the whole request (the exact URL, the frame's content type, Accept and
33
+ * WP3-v2-Client authorization, and a body of the frame's own keys bound to the URL's
34
+ * client_secret) with payment-core's wspayConfirmRefusal, the check the API runs at create.
35
+ * It passes keep-on-file consent because the approval that follows asks the cardholder for it.
37
36
  */
38
37
  export function wspayRefusal(request) {
39
- // No keep-on-file consent (the second argument): no approval asks for it yet.
40
- const refusal = wspayConfirmRefusal({ url: request.url, method: 'POST', headers: request.headers ?? {}, body: request.body ?? '' }, undefined);
41
- if (refusal === null)
42
- return null;
43
- return refusal === 'keep_on_file' ? 'wspay_keep_on_file_unsupported' : 'unsupported_wspay_payment_method';
38
+ // Keep-on-file consent (the second argument): the Vault's approval asks the cardholder whether
39
+ // the merchant may keep the card, and the API admits such a confirm only as a card guarantee.
40
+ const refusal = wspayConfirmRefusal({ url: request.url, method: 'POST', headers: request.headers ?? {}, body: request.body ?? '' }, { keepOnFile: true });
41
+ return refusal === null ? null : 'unsupported_wspay_payment_method';
44
42
  }
45
43
  /**
46
44
  * Whether an approved answer handed to a WSPay confirm is WSPay's 3-D Secure step: HTTP 200 with
@@ -2,7 +2,7 @@
2
2
 
3
3
  You can detect a checkout's PSP before entering card details with `@agent-cards/sdk`. Kernel native support needs a separate profile for the native adapter that actually runs the checkout. The direct SDK's coverage does not establish Kernel's native coverage.
4
4
 
5
- No qualified native profile accompanies this kit. The inventory covers all 25 PSPs in the checkout catalog and reports `unknown` for every native runtime status. Kernel's pinned documentation lists Stripe, Shopify, Square, Recurly, and Razorpay. The other processors are unestablished, rather than explicitly unsupported. See the endpoint constraints and source hashes in [documented-adapters.json](./documented-adapters.json).
5
+ No qualified native profile accompanies this kit. The inventory covers all 24 PSPs in the checkout catalog and reports `unknown` for every native runtime status. Kernel's pinned documentation lists Stripe, Shopify, Square, Recurly, and Razorpay. The other processors are unestablished, rather than explicitly unsupported. See the endpoint constraints and source hashes in [documented-adapters.json](./documented-adapters.json).
6
6
 
7
7
  ## Use the detector now
8
8
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": 1,
3
3
  "catalog_version": "2026-09-15.1",
4
- "catalog_sha256": "a1bfa3f9da2942711afca14cfc7158a49274869c66e685abc0d36839d2e96454",
4
+ "catalog_sha256": "47dd1bc5a749224f66980b42b758f9a19d9ac849e3a8d5230075226a4870fde5",
5
5
  "baseline_version": "kernel-native-docs-2026-09-11.1",
6
6
  "baseline_sha256": "d86722f49633d8223fccae54cd3d485df4375aee06aa156a273cc689b7c8507c",
7
7
  "native_build": null,
@@ -202,15 +202,6 @@
202
202
  "Native runtime evidence is missing."
203
203
  ]
204
204
  },
205
- {
206
- "psp": "moneris",
207
- "mode": "token",
208
- "documentation": "not_documented",
209
- "status": "unknown",
210
- "limitations": [
211
- "No adapter declaration was found in the pinned documentation. Native runtime evidence is missing."
212
- ]
213
- },
214
205
  {
215
206
  "psp": "bambora",
216
207
  "mode": "token",
@@ -228,15 +219,6 @@
228
219
  "limitations": [
229
220
  "No adapter declaration was found in the pinned documentation. Native runtime evidence is missing."
230
221
  ]
231
- },
232
- {
233
- "psp": "gravy",
234
- "mode": "token",
235
- "documentation": "not_documented",
236
- "status": "unknown",
237
- "limitations": [
238
- "No adapter declaration was found in the pinned documentation. Native runtime evidence is missing."
239
- ]
240
222
  }
241
223
  ]
242
224
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-cards/sdk",
3
- "version": "0.23.9",
3
+ "version": "0.23.10",
4
4
  "description": "Agentcard's SDK: one client, Agentcard, with two modules. checkout pauses a browser purchase and pays it with the person's own card; voice connects a person to your product once and approves a purchase by voice inside it. Card data never enters your process.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -27,8 +27,8 @@
27
27
  "_comment_build": "The public SDK keeps zero runtime dependencies. Its build vendors deterministic metadata and substitution artifacts from the internal payment core, and the voice streaming door from packages/voice/src/core (the phone package, @agent-cards/voice, ships it to phones); TypeScript remains pinned.",
28
28
  "build": "node ../payment-core/scripts/build.mjs && node scripts/generate-payment-core.mjs && node scripts/generate-voice-stream.mjs && npx -y -p typescript@5.9.3 tsc && node scripts/generate-preflight-contract.mjs",
29
29
  "prepublishOnly": "pnpm build",
30
- "test": "node test.mjs && node --test lifecycle.test.mjs merchant-abort.test.mjs preparation.test.mjs braintree.test.mjs pre-claim.test.mjs ekashu.test.mjs paypal-hosted-fields.test.mjs cybersource-secure-acceptance.test.mjs autopilot.test.mjs stripe-checkout.test.mjs stripe-setup.test.mjs stripe-legacy-checkout.test.mjs payment-core.test.mjs prepared-processor.test.mjs merchant-hosted.test.mjs merchant-total.test.mjs sandbox-declaration.test.mjs minimum-delay.test.mjs paysafe.test.mjs payu.test.mjs attachment.test.mjs worker-targets.test.mjs mercado-checkout.test.mjs mercado-polling.test.mjs amadeus.test.mjs pelecard.test.mjs aci.test.mjs primer.test.mjs payrails.test.mjs elavon-converge.test.mjs voice.test.mjs agentcard.test.mjs buy.test.mjs readme.test.mjs gmo.test.mjs cybersource-flex.test.mjs juspay-gate.test.mjs card-endpoint.test.mjs card-endpoint-fastspring.test.mjs card-endpoint-navigation.test.mjs merchant-session-probe.test.mjs datatrans.test.mjs authorize-net-dpm.test.mjs creditguard.test.mjs eprotect.test.mjs wspay.test.mjs shiji-sts.test.mjs worldline-mycheckout.test.mjs verifone.test.mjs && node --test preflight-package.test.mjs preflight-collector.test.mjs && node --test kernel-native-qualification.test.mjs && node --test ../vault/scripts/recurly-validation/watch-duty-sdk-result.test.mjs",
31
- "test:browser": "node browser.test.mjs && node stripe-browser.test.mjs && node preparation-browser.test.mjs && node checkout-com-browser.test.mjs && node paysafe-preparation-browser.test.mjs && node owned-shop-browser.test.mjs && node spreedly-browser.test.mjs && node worker-browser.test.mjs && node amadeus-browser.test.mjs && node adyen-merchant-hosted-browser.test.mjs && node adyen-merchant-frames-browser.test.mjs && node fiserv-browser.test.mjs && node gmo-browser.test.mjs && node cybersource-flex-browser.test.mjs && node juspay-browser.test.mjs && node card-endpoint-browser.test.mjs && node datatrans-browser.test.mjs && node authorize-net-dpm-browser.test.mjs && node pelecard-browser.test.mjs && node aci-browser.test.mjs && node creditguard-browser.test.mjs && node primer-browser.test.mjs && node payrails-browser.test.mjs && node elavon-converge-browser.test.mjs && node eprotect-browser.test.mjs && node wspay-browser.test.mjs && node shiji-sts-browser.test.mjs && node worldline-mycheckout-browser.test.mjs && node paypal-hosted-fields-browser.test.mjs && node cybersource-secure-acceptance-browser.test.mjs",
30
+ "test": "node test.mjs && node --test lifecycle.test.mjs merchant-abort.test.mjs preparation.test.mjs braintree.test.mjs pre-claim.test.mjs ekashu.test.mjs paypal-hosted-fields.test.mjs cybersource-secure-acceptance.test.mjs autopilot.test.mjs stripe-checkout.test.mjs stripe-setup.test.mjs stripe-legacy-checkout.test.mjs payment-core.test.mjs prepared-processor.test.mjs merchant-hosted.test.mjs merchant-total.test.mjs sandbox-declaration.test.mjs minimum-delay.test.mjs paysafe.test.mjs payu.test.mjs attachment.test.mjs worker-targets.test.mjs mercado-checkout.test.mjs mercado-polling.test.mjs amadeus.test.mjs pelecard.test.mjs aci.test.mjs primer.test.mjs payrails.test.mjs elavon-converge.test.mjs voice.test.mjs agentcard.test.mjs buy.test.mjs readme.test.mjs gmo.test.mjs cybersource-flex.test.mjs juspay-gate.test.mjs card-endpoint.test.mjs card-endpoint-fastspring.test.mjs card-endpoint-navigation.test.mjs merchant-session-probe.test.mjs datatrans.test.mjs authorize-net-dpm.test.mjs creditguard.test.mjs eprotect.test.mjs wspay.test.mjs shiji-sts.test.mjs worldline-mycheckout.test.mjs verifone.test.mjs gravy.test.mjs && node --test preflight-package.test.mjs preflight-collector.test.mjs && node --test kernel-native-qualification.test.mjs && node --test ../vault/scripts/recurly-validation/watch-duty-sdk-result.test.mjs",
31
+ "test:browser": "node browser.test.mjs && node stripe-browser.test.mjs && node preparation-browser.test.mjs && node checkout-com-browser.test.mjs && node paysafe-preparation-browser.test.mjs && node owned-shop-browser.test.mjs && node spreedly-browser.test.mjs && node worker-browser.test.mjs && node amadeus-browser.test.mjs && node adyen-merchant-hosted-browser.test.mjs && node adyen-merchant-frames-browser.test.mjs && node fiserv-browser.test.mjs && node gmo-browser.test.mjs && node cybersource-flex-browser.test.mjs && node juspay-browser.test.mjs && node card-endpoint-browser.test.mjs && node datatrans-browser.test.mjs && node authorize-net-dpm-browser.test.mjs && node pelecard-browser.test.mjs && node aci-browser.test.mjs && node creditguard-browser.test.mjs && node primer-browser.test.mjs && node payrails-browser.test.mjs && node elavon-converge-browser.test.mjs && node eprotect-browser.test.mjs && node wspay-browser.test.mjs && node shiji-sts-browser.test.mjs && node worldline-mycheckout-browser.test.mjs && node paypal-hosted-fields-browser.test.mjs && node cybersource-secure-acceptance-browser.test.mjs && node gravy-browser.test.mjs",
32
32
  "check:payment-core": "node scripts/generate-payment-core.mjs --check",
33
33
  "check:voice-stream": "node scripts/generate-voice-stream.mjs --check",
34
34
  "test:preflight": "node --test preflight-package.test.mjs preflight-collector.test.mjs kernel-native-qualification.test.mjs",