@agent-cards/sdk 0.20.0 → 0.22.0

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 (111) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/PREFLIGHT.md +34 -0
  3. package/README.md +1217 -15
  4. package/dist/aci.d.ts +173 -0
  5. package/dist/aci.generated.d.ts +87 -0
  6. package/dist/aci.generated.js +306 -0
  7. package/dist/aci.js +371 -0
  8. package/dist/adyen-merchant-hosted.generated.d.ts +3 -3
  9. package/dist/adyen-merchant-hosted.generated.js +283 -49
  10. package/dist/agentcard.d.ts +9 -2
  11. package/dist/agentcard.js +4 -2
  12. package/dist/amadeus-checkout.generated.d.ts +57 -0
  13. package/dist/amadeus-checkout.generated.js +549 -0
  14. package/dist/amadeus.d.ts +41 -0
  15. package/dist/amadeus.js +81 -0
  16. package/dist/builtin-registry.generated.js +1 -1
  17. package/dist/card-endpoint.d.ts +177 -0
  18. package/dist/card-endpoint.js +286 -0
  19. package/dist/card-fields.generated.d.ts +5 -3
  20. package/dist/card-fields.generated.js +284 -7
  21. package/dist/cdp.d.ts +6 -1
  22. package/dist/cdp.js +983 -147
  23. package/dist/client.d.ts +493 -13
  24. package/dist/client.js +1100 -82
  25. package/dist/creditguard.generated.d.ts +79 -0
  26. package/dist/creditguard.generated.js +662 -0
  27. package/dist/cse-body.js +11 -1
  28. package/dist/cybersource-flex.d.ts +183 -0
  29. package/dist/cybersource-flex.generated.d.ts +92 -0
  30. package/dist/cybersource-flex.generated.js +689 -0
  31. package/dist/cybersource-flex.js +275 -0
  32. package/dist/datatrans.d.ts +96 -0
  33. package/dist/datatrans.generated.d.ts +64 -0
  34. package/dist/datatrans.generated.js +426 -0
  35. package/dist/datatrans.js +178 -0
  36. package/dist/elavon-converge.d.ts +37 -0
  37. package/dist/elavon-converge.generated.d.ts +49 -0
  38. package/dist/elavon-converge.generated.js +439 -0
  39. package/dist/elavon-converge.js +84 -0
  40. package/dist/eprotect.d.ts +152 -0
  41. package/dist/eprotect.generated.d.ts +77 -0
  42. package/dist/eprotect.generated.js +498 -0
  43. package/dist/eprotect.js +222 -0
  44. package/dist/gmo.d.ts +63 -0
  45. package/dist/gmo.generated.d.ts +60 -0
  46. package/dist/gmo.generated.js +586 -0
  47. package/dist/gmo.js +119 -0
  48. package/dist/hosted-form.d.ts +3 -2
  49. package/dist/hosted-form.js +3 -2
  50. package/dist/index.d.ts +19 -3
  51. package/dist/index.js +6 -1
  52. package/dist/juspay-gate.d.ts +82 -0
  53. package/dist/juspay-gate.js +126 -0
  54. package/dist/juspay.generated.d.ts +89 -0
  55. package/dist/juspay.generated.js +705 -0
  56. package/dist/lifecycle.d.ts +62 -6
  57. package/dist/lifecycle.js +123 -8
  58. package/dist/merchant-card-endpoints.generated.d.ts +63 -0
  59. package/dist/merchant-card-endpoints.generated.js +1103 -0
  60. package/dist/merchant-hosted.d.ts +4 -1
  61. package/dist/merchant-hosted.js +5 -2
  62. package/dist/merchant-session-probe.d.ts +48 -0
  63. package/dist/merchant-session-probe.js +53 -0
  64. package/dist/merchant-session.generated.d.ts +69 -0
  65. package/dist/merchant-session.generated.js +919 -0
  66. package/dist/payrails.d.ts +133 -0
  67. package/dist/payrails.generated.d.ts +73 -0
  68. package/dist/payrails.generated.js +596 -0
  69. package/dist/payrails.js +305 -0
  70. package/dist/pelecard.d.ts +32 -0
  71. package/dist/pelecard.generated.d.ts +32 -0
  72. package/dist/pelecard.generated.js +283 -0
  73. package/dist/pelecard.js +42 -0
  74. package/dist/pre-claim.d.ts +84 -3
  75. package/dist/pre-claim.js +987 -9
  76. package/dist/preflight-catalog.json +2310 -263
  77. package/dist/preflight-schemas.json +187 -6
  78. package/dist/preflight.generated.d.ts +1 -1
  79. package/dist/preflight.generated.js +247 -4
  80. package/dist/preparation.d.ts +5 -2
  81. package/dist/preparation.js +36 -10
  82. package/dist/prepared-processor.d.ts +86 -5
  83. package/dist/prepared-processor.js +157 -6
  84. package/dist/primer.d.ts +53 -0
  85. package/dist/primer.generated.d.ts +38 -0
  86. package/dist/primer.generated.js +624 -0
  87. package/dist/primer.js +82 -0
  88. package/dist/registry.d.ts +114 -12
  89. package/dist/registry.js +85 -11
  90. package/dist/shiji-sts.d.ts +144 -0
  91. package/dist/shiji-sts.generated.d.ts +72 -0
  92. package/dist/shiji-sts.generated.js +913 -0
  93. package/dist/shiji-sts.js +222 -0
  94. package/dist/stripe-checkout.generated.d.ts +1 -1
  95. package/dist/stripe-checkout.generated.js +735 -10
  96. package/dist/stripe-setup.d.ts +16 -0
  97. package/dist/stripe-setup.js +61 -0
  98. package/dist/substitutions.generated.js +187 -29
  99. package/dist/verifone.d.ts +68 -0
  100. package/dist/verifone.generated.d.ts +51 -0
  101. package/dist/verifone.generated.js +1401 -0
  102. package/dist/verifone.js +119 -0
  103. package/dist/voice-types.d.ts +27 -0
  104. package/dist/voice.d.ts +6 -1
  105. package/dist/voice.js +6 -1
  106. package/dist/wspay.d.ts +41 -0
  107. package/dist/wspay.generated.d.ts +41 -0
  108. package/dist/wspay.generated.js +370 -0
  109. package/dist/wspay.js +65 -0
  110. package/examples/preflight/kernel-native/inventory.json +1 -1
  111. package/package.json +3 -3
@@ -1,6 +1,9 @@
1
1
  import { CheckoutPreparationError, type PrepareCheckoutOptions, type PreparedCheckout, type ReplayResponse } from './client.js';
2
2
  import type { CheckoutMode } from './registry.js';
3
+ import type { PayrailsUnsupportedReason } from './payrails.js';
3
4
  import type { Substitutions } from './substitute.js';
5
+ import type { GmoUnsupportedReason } from './gmo.js';
6
+ import type { ShijiStsUnsupportedReason } from './shiji-sts.js';
4
7
  /** The application's resolver confirmed the payment for this checkout authorization. */
5
8
  export interface MerchantPaymentConfirmation {
6
9
  kind: 'merchant_payment';
@@ -27,7 +30,8 @@ export interface CheckoutState {
27
30
  status: 'idle' | 'awaiting_approval' | 'ready_to_submit' | 'awaiting_merchant' | 'requires_user_action' | 'completed' | 'declined' | 'timed_out' | 'cancelled' | 'unsupported' | 'outcome_unknown' | 'failed';
28
31
  authorizationId: string | null;
29
32
  preparationId?: string;
30
- mode?: CheckoutMode;
33
+ /** How the card reached the merchant: a processor mode, or `card_endpoint` for a reviewed merchant's own card endpoint. */
34
+ mode?: CheckoutMode | 'card_endpoint';
31
35
  orderId?: string;
32
36
  confirmation?: MerchantPaymentConfirmation;
33
37
  /** Stable SDK category; never includes a request body, processor response, or approval link. */
@@ -115,6 +119,12 @@ export interface CheckoutController {
115
119
  }
116
120
  /** How many times one approval's card-free Stripe confirmation may be checked before it is simply held. */
117
121
  export declare const MAX_STRIPE_CONTINUATION_CHECKS = 3;
122
+ /**
123
+ * How long a Datatrans re-submit waits for the merchant resolver (merchantOrderPending) while the
124
+ * page's card post stays paused. A resolver that has not answered by then reads as not pending, so
125
+ * the post becomes a new approval instead of hanging the checkout.
126
+ */
127
+ export declare const MERCHANT_ORDER_READ_MS = 5000;
118
128
  /** Shared by the raw CDP and Playwright transports. No browser ownership or payment execution lives here. */
119
129
  export declare class CheckoutLifecycle implements CheckoutController {
120
130
  private readonly options;
@@ -127,7 +137,12 @@ export declare class CheckoutLifecycle implements CheckoutController {
127
137
  private stripeTokenPrepared;
128
138
  private stripeContinuation;
129
139
  private stripeContinuationChecks;
140
+ /** The handoff in progress is WSPay's 3-D Secure step; see handedOff. */
141
+ private wspayChallenge;
142
+ private stripeContinuedReason;
143
+ private approvedPaymentMethod;
130
144
  private ownedShopOrderId;
145
+ private readonly handoffs;
131
146
  private reconciliation;
132
147
  private preparationHandler?;
133
148
  private preparationUsed;
@@ -146,6 +161,17 @@ export declare class CheckoutLifecycle implements CheckoutController {
146
161
  preparationFailed(error: CheckoutPreparationError): void;
147
162
  isBlocked(): boolean;
148
163
  isCancelled(): boolean;
164
+ /** Whether the application gave a merchant resolver, so reconcile() reads the merchant's own record. */
165
+ hasMerchantResolver(): boolean;
166
+ /**
167
+ * For a Datatrans re-submit once the approved answer reached the page (pre-claim.ts): whether the
168
+ * application's merchant resolver still reports this approval's order pending. A read only: no
169
+ * answer changes the state or the hold, so a pending order leaves the checkout exactly as it was.
170
+ * False with no resolver, while a request is in flight, once the checkout no longer awaits the
171
+ * merchant, when the resolver throws or answers anything but pending, and when it has not answered
172
+ * within `deadlineMs` (MERCHANT_ORDER_READ_MS): the paused card post never waits on it longer.
173
+ */
174
+ merchantOrderPending(deadlineMs?: number): Promise<boolean>;
149
175
  begin(): void;
150
176
  end(): void;
151
177
  private set;
@@ -183,10 +209,23 @@ export declare class CheckoutLifecycle implements CheckoutController {
183
209
  /**
184
210
  * A card request this SDK cannot pay was aborted. `reason`: an unrecognized
185
211
  * declared payment endpoint (the default), a merchant profile Agentcard has not
186
- * turned on (merchant_profile_observe), or a card request the profile cannot
187
- * finish (merchant_request_unsupported).
212
+ * turned on (merchant_profile_observe), a card request the profile cannot
213
+ * finish (merchant_request_unsupported), a recognized processor's request in
214
+ * a form its gate refuses (GMO's unsupported_gmo_request and
215
+ * unsupported_gmo_token_v1), a CyberSource Flex request that is not a
216
+ * reviewed Microform v2 card field's (microform_unsupported,
217
+ * cybersource-flex.ts), a Juspay card request that is not the reviewed
218
+ * payment (juspay_request_unsupported, juspay-gate.ts), such as save card,
219
+ * EMI, a direct card or a sibling host, a reviewed merchant card endpoint
220
+ * this build does not pay (merchant_card_endpoint_not_enabled), a CreditGuard
221
+ * page that runs its own 3-D Secure (creditguard_three_d_secure), a
222
+ * recognized Primer request in a form its gate refuses (unsupported_primer_sdk,
223
+ * unsupported_primer_request), a Payrails request its gate refuses
224
+ * (`unsupported_payrails_card_payment` and the card-save refusal codes), or a
225
+ * Shiji STS request its gate refuses (shiji_sts_keep_on_file and the other
226
+ * shiji-sts.ts reasons).
188
227
  */
189
- unsupported(reason?: 'unrecognized_payment_endpoint' | 'merchant_profile_observe' | 'merchant_request_unsupported'): void;
228
+ unsupported(reason?: 'unrecognized_payment_endpoint' | 'merchant_profile_observe' | 'merchant_request_unsupported' | GmoUnsupportedReason | 'microform_unsupported' | 'juspay_request_unsupported' | 'merchant_card_endpoint_not_enabled' | 'creditguard_three_d_secure' | 'unsupported_primer_sdk' | 'unsupported_primer_request' | PayrailsUnsupportedReason | ShijiStsUnsupportedReason): void;
190
229
  /**
191
230
  * A card request that only a prepared checkout pays (an Adyen merchant
192
231
  * profile's, or a Fiserv card capture) arrived with no preparation on this
@@ -196,6 +235,22 @@ export declare class CheckoutLifecycle implements CheckoutController {
196
235
  */
197
236
  preparationRequired(): void;
198
237
  prepareHandoff(replay: ReplayResponse, requestUrl: string): void;
238
+ /**
239
+ * The URLs of the requests this attachment has handed the card to, oldest
240
+ * first. Each is recorded before delivery, since the processor may already
241
+ * have minted a token from the card even if the browser never acknowledges
242
+ * it, and none is forgotten on a retry. The adapters pass them to
243
+ * VaultClient.withoutCard, which then refuses a Nuvei payment without a card.
244
+ */
245
+ cardHandoffs(): readonly string[];
246
+ /**
247
+ * The payment method a Stripe card save may pay with in place of the card
248
+ * it sends again (cdp.ts claimStripeContinuation): the one this
249
+ * attachment's approved `/v1/payment_methods` request returned, while its
250
+ * continuation is still open. Null for any other tokenization, and once the
251
+ * checkout was cancelled, aborted or reconciled.
252
+ */
253
+ approvedStripePaymentMethod(): string | null;
199
254
  /**
200
255
  * The approval a page's card-free Stripe confirmation may continue, or null.
201
256
  * Only once a Stripe tokenization is being handed to the page, and while
@@ -210,9 +265,10 @@ export declare class CheckoutLifecycle implements CheckoutController {
210
265
  * Settle a claimed check. True only when the API allowed it and the checkout
211
266
  * still awaits the merchant (nothing cancelled, aborted or reconciled it
212
267
  * while the API answered): the page's confirmation continues, and no later
213
- * one will.
268
+ * one will. `kind` names what it confirms: a payment (a PaymentIntent) or a
269
+ * card save (a SetupIntent), which the reason then says.
214
270
  */
215
- stripeContinuationChecked(allowed: boolean): boolean;
271
+ stripeContinuationChecked(allowed: boolean, kind?: 'payment' | 'setup'): boolean;
216
272
  private stripeContinuationOpen;
217
273
  handedOff(replay: ReplayResponse): void;
218
274
  failed(error: unknown, handoffStarted?: boolean): void;
package/dist/lifecycle.js CHANGED
@@ -1,7 +1,28 @@
1
1
  import { AdyenTestPlatformRefusedError, ApprovalDeclinedError, ApprovalTimeoutError, CheckoutCancelledError, CheckoutPreparationError, IntentNotConfirmableError, PaymentOutcomeUnknownError, ProcessorRefusedError } from './client.js';
2
+ import { CardEndpointRefusedError } from './card-endpoint.js';
2
3
  import { parseOwnedShopReceipt, OWNED_SHOP_SKU, OWNED_SHOP_AMOUNT_CENTS, OWNED_SHOP_CURRENCY } from './owned-shop.generated.js';
4
+ import { isWspayChallenge } from './wspay.js';
3
5
  /** How many times one approval's card-free Stripe confirmation may be checked before it is simply held. */
4
6
  export const MAX_STRIPE_CONTINUATION_CHECKS = 3;
7
+ /**
8
+ * How long a Datatrans re-submit waits for the merchant resolver (merchantOrderPending) while the
9
+ * page's card post stays paused. A resolver that has not answered by then reads as not pending, so
10
+ * the post becomes a new approval instead of hanging the checkout.
11
+ */
12
+ export const MERCHANT_ORDER_READ_MS = 5_000;
13
+ const STRIPE_PAYMENT_METHOD_ID = /^pm_[A-Za-z0-9]{1,250}$/;
14
+ /** The `pm_` id in a Stripe `/v1/payment_methods` answer, or null for any other body. */
15
+ function stripePaymentMethodId(body) {
16
+ if (typeof body !== 'string')
17
+ return null;
18
+ try {
19
+ const id = JSON.parse(body)?.id;
20
+ return typeof id === 'string' && STRIPE_PAYMENT_METHOD_ID.test(id) ? id : null;
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
5
26
  /** Shared by the raw CDP and Playwright transports. No browser ownership or payment execution lives here. */
6
27
  export class CheckoutLifecycle {
7
28
  options;
@@ -15,7 +36,15 @@ export class CheckoutLifecycle {
15
36
  stripeTokenPrepared = false;
16
37
  stripeContinuation = 'none';
17
38
  stripeContinuationChecks = 0;
39
+ /** The handoff in progress is WSPay's 3-D Secure step; see handedOff. */
40
+ wspayChallenge = false;
41
+ // What the continuation that went through confirmed: a payment, or a card save.
42
+ stripeContinuedReason = 'stripe_payment_continued';
43
+ // The payment method a Stripe /v1/payment_methods approval returned: see approvedStripePaymentMethod.
44
+ approvedPaymentMethod = null;
18
45
  ownedShopOrderId = null;
46
+ // Every request this attachment handed the card to: see cardHandoffs().
47
+ handoffs = [];
19
48
  reconciliation = null;
20
49
  preparationHandler;
21
50
  preparationUsed = false;
@@ -58,6 +87,30 @@ export class CheckoutLifecycle {
58
87
  }
59
88
  isBlocked() { return this.held || this.cancelled; }
60
89
  isCancelled() { return this.cancelled; }
90
+ /** Whether the application gave a merchant resolver, so reconcile() reads the merchant's own record. */
91
+ hasMerchantResolver() { return typeof this.options.resolveMerchantResult === 'function'; }
92
+ /**
93
+ * For a Datatrans re-submit once the approved answer reached the page (pre-claim.ts): whether the
94
+ * application's merchant resolver still reports this approval's order pending. A read only: no
95
+ * answer changes the state or the hold, so a pending order leaves the checkout exactly as it was.
96
+ * False with no resolver, while a request is in flight, once the checkout no longer awaits the
97
+ * merchant, when the resolver throws or answers anything but pending, and when it has not answered
98
+ * within `deadlineMs` (MERCHANT_ORDER_READ_MS): the paused card post never waits on it longer.
99
+ */
100
+ async merchantOrderPending(deadlineMs = MERCHANT_ORDER_READ_MS) {
101
+ const resolver = this.options.resolveMerchantResult;
102
+ if (!resolver || this.active || this.state.status !== 'awaiting_merchant')
103
+ return false;
104
+ let timer;
105
+ const expired = new Promise((resolve) => { timer = setTimeout(() => resolve(false), deadlineMs); });
106
+ const read = Promise.resolve().then(() => resolver(this.getState())).then((result) => !!result && typeof result === 'object' && !Array.isArray(result) && result.status === 'pending', () => false);
107
+ try {
108
+ return await Promise.race([read, expired]);
109
+ }
110
+ finally {
111
+ clearTimeout(timer);
112
+ }
113
+ }
61
114
  begin() {
62
115
  this.active = true;
63
116
  this.set({ status: 'awaiting_approval', authorizationId: null, ...(this.state.preparationId ? { preparationId: this.state.preparationId } : {}) });
@@ -132,8 +185,21 @@ export class CheckoutLifecycle {
132
185
  /**
133
186
  * A card request this SDK cannot pay was aborted. `reason`: an unrecognized
134
187
  * declared payment endpoint (the default), a merchant profile Agentcard has not
135
- * turned on (merchant_profile_observe), or a card request the profile cannot
136
- * finish (merchant_request_unsupported).
188
+ * turned on (merchant_profile_observe), a card request the profile cannot
189
+ * finish (merchant_request_unsupported), a recognized processor's request in
190
+ * a form its gate refuses (GMO's unsupported_gmo_request and
191
+ * unsupported_gmo_token_v1), a CyberSource Flex request that is not a
192
+ * reviewed Microform v2 card field's (microform_unsupported,
193
+ * cybersource-flex.ts), a Juspay card request that is not the reviewed
194
+ * payment (juspay_request_unsupported, juspay-gate.ts), such as save card,
195
+ * EMI, a direct card or a sibling host, a reviewed merchant card endpoint
196
+ * this build does not pay (merchant_card_endpoint_not_enabled), a CreditGuard
197
+ * page that runs its own 3-D Secure (creditguard_three_d_secure), a
198
+ * recognized Primer request in a form its gate refuses (unsupported_primer_sdk,
199
+ * unsupported_primer_request), a Payrails request its gate refuses
200
+ * (`unsupported_payrails_card_payment` and the card-save refusal codes), or a
201
+ * Shiji STS request its gate refuses (shiji_sts_keep_on_file and the other
202
+ * shiji-sts.ts reasons).
137
203
  */
138
204
  unsupported(reason = 'unrecognized_payment_endpoint') {
139
205
  this.held = true;
@@ -154,6 +220,8 @@ export class CheckoutLifecycle {
154
220
  this.set({ ...this.state, status: 'failed', reason: 'preparation_required' });
155
221
  }
156
222
  prepareHandoff(replay, requestUrl) {
223
+ this.wspayChallenge = isWspayChallenge(requestUrl, replay);
224
+ this.handoffs.push(requestUrl);
157
225
  if (replay.mode === 'token' && replay.executionMode === 'autopilot' && replay.shopOrderId) {
158
226
  let receipt;
159
227
  try {
@@ -181,11 +249,31 @@ export class CheckoutLifecycle {
181
249
  this.unboundStripeToken = true;
182
250
  this.stripeTokenPrepared = true;
183
251
  this.held = true;
252
+ if (url.pathname === '/v1/payment_methods')
253
+ this.approvedPaymentMethod = stripePaymentMethodId(replay.body);
184
254
  }
185
255
  }
186
256
  catch { /* unknown URL cannot establish a token binding */ }
187
257
  }
188
258
  }
259
+ /**
260
+ * The URLs of the requests this attachment has handed the card to, oldest
261
+ * first. Each is recorded before delivery, since the processor may already
262
+ * have minted a token from the card even if the browser never acknowledges
263
+ * it, and none is forgotten on a retry. The adapters pass them to
264
+ * VaultClient.withoutCard, which then refuses a Nuvei payment without a card.
265
+ */
266
+ cardHandoffs() { return [...this.handoffs]; }
267
+ /**
268
+ * The payment method a Stripe card save may pay with in place of the card
269
+ * it sends again (cdp.ts claimStripeContinuation): the one this
270
+ * attachment's approved `/v1/payment_methods` request returned, while its
271
+ * continuation is still open. Null for any other tokenization, and once the
272
+ * checkout was cancelled, aborted or reconciled.
273
+ */
274
+ approvedStripePaymentMethod() {
275
+ return this.stripeContinuationOpen() ? this.approvedPaymentMethod : null;
276
+ }
189
277
  /**
190
278
  * The approval a page's card-free Stripe confirmation may continue, or null.
191
279
  * Only once a Stripe tokenization is being handed to the page, and while
@@ -207,15 +295,18 @@ export class CheckoutLifecycle {
207
295
  * Settle a claimed check. True only when the API allowed it and the checkout
208
296
  * still awaits the merchant (nothing cancelled, aborted or reconciled it
209
297
  * while the API answered): the page's confirmation continues, and no later
210
- * one will.
298
+ * one will. `kind` names what it confirms: a payment (a PaymentIntent) or a
299
+ * card save (a SetupIntent), which the reason then says.
211
300
  */
212
- stripeContinuationChecked(allowed) {
301
+ stripeContinuationChecked(allowed, kind = 'payment') {
213
302
  if (this.stripeContinuation !== 'checking')
214
303
  return false;
215
304
  const proceed = allowed && this.stripeContinuationOpen();
216
305
  this.stripeContinuation = proceed ? 'continued' : 'none';
217
- if (proceed)
218
- this.set({ ...this.state, reason: 'stripe_payment_continued' });
306
+ if (proceed) {
307
+ this.stripeContinuedReason = kind === 'setup' ? 'stripe_setup_continued' : 'stripe_payment_continued';
308
+ this.set({ ...this.state, reason: this.stripeContinuedReason });
309
+ }
219
310
  return proceed;
220
311
  }
221
312
  stripeContinuationOpen() {
@@ -230,14 +321,31 @@ export class CheckoutLifecycle {
230
321
  this.set({ status: 'completed', authorizationId: replay.authorizationId, mode: 'token', orderId: this.ownedShopOrderId });
231
322
  return;
232
323
  }
324
+ if (this.wspayChallenge) {
325
+ // WSPay answered with its 3-D Secure step, and the card frame now runs the
326
+ // bank's challenge in this browser. Nothing is charged until the cardholder
327
+ // completes it there, which the SDK cannot see, so this reads as
328
+ // requestUserAction('3ds') would, and holds until the merchant is reconciled.
329
+ // The step names no amount (amountVerified is null): WSPay then charges the
330
+ // amount the merchant's server set, and no answer Agentcard receives
331
+ // confirms it, so only the merchant's order can.
332
+ this.held = true;
333
+ this.set({ status: 'requires_user_action', authorizationId: replay.authorizationId, mode: 'token', reason: '3ds',
334
+ ...(this.state.preparationId ? { preparationId: this.state.preparationId } : {}) });
335
+ this.notify({ reason: '3ds', authorizationId: replay.authorizationId });
336
+ return;
337
+ }
233
338
  const mismatch = (!replay.mode || replay.mode === 'token') && replay.amountVerified === false;
234
- this.held = !!this.options.requireMerchantResult || replay.mode === 'hosted_form' || mismatch || this.unboundStripeToken;
339
+ // A card endpoint answer means the merchant already holds the card: its page's own
340
+ // retry, or a step-up that sends the card again, is refused until the application
341
+ // reconciles the order, exactly as for a hosted form the device submitted.
342
+ this.held = !!this.options.requireMerchantResult || replay.mode === 'hosted_form' || replay.mode === 'card_endpoint' || mismatch || this.unboundStripeToken;
235
343
  if (mismatch)
236
344
  this.stripeTokenPrepared = false;
237
345
  this.set({ status: 'awaiting_merchant', authorizationId: replay.authorizationId, mode: replay.mode ?? 'token',
238
346
  ...(this.state.preparationId ? { preparationId: this.state.preparationId } : {}),
239
347
  ...(mismatch ? { reason: 'charged_amount_mismatch' }
240
- : this.stripeContinuation === 'continued' ? { reason: 'stripe_payment_continued' }
348
+ : this.stripeContinuation === 'continued' ? { reason: this.stripeContinuedReason }
241
349
  : this.unboundStripeToken ? { reason: 'stripe_tokenization_unbound' } : {}) });
242
350
  }
243
351
  failed(error, handoffStarted = false) {
@@ -257,6 +365,13 @@ export class CheckoutLifecycle {
257
365
  this.held = true;
258
366
  this.set({ ...this.state, authorizationId, status: 'outcome_unknown', reason: error instanceof PaymentOutcomeUnknownError ? error.reason : error instanceof IntentNotConfirmableError ? 'intent_not_confirmable' : 'browser_handoff_failed' });
259
367
  }
368
+ else if (error instanceof CardEndpointRefusedError) {
369
+ // Refused before anything was created or sent. A refusal that every later request
370
+ // would meet holds the attachment; one about this request alone does not.
371
+ if (error.permanent)
372
+ this.held = true;
373
+ this.set({ ...this.state, status: error.reason === 'not_enabled' ? 'unsupported' : 'failed', reason: `merchant_card_endpoint_${error.reason}` });
374
+ }
260
375
  else if (error instanceof CheckoutCancelledError) {
261
376
  this.cancel();
262
377
  }
@@ -0,0 +1,63 @@
1
+ declare var SubstitutionError: {
2
+ new (message: any): {
3
+ name: string;
4
+ message: string;
5
+ stack?: string;
6
+ cause?: unknown;
7
+ };
8
+ };
9
+ declare var MERCHANT_CARD_ENDPOINT_PSP: string;
10
+ declare var MERCHANT_CARD_ENDPOINT_MODE: string;
11
+ declare var MERCHANT_CARD_ENDPOINT_PROFILES: any;
12
+ declare var MERCHANT_CARD_ENDPOINT_RECOGNIZERS: any;
13
+ declare var MERCHANT_CARD_ENDPOINT_CLIENT_TABLE: any;
14
+ declare function merchantCardEndpointById(id: any): any;
15
+ declare function trustedMerchantCardEndpoint(idOrEntry: any): any;
16
+ declare function merchantCardEndpointRecognizerById(id: any): any;
17
+ declare function merchantCardEndpointLive(idOrEntry: any): boolean;
18
+ declare var MERCHANT_CARD_ENDPOINT_LIMITS: Readonly<{
19
+ bodyBytes: number;
20
+ answerBytes: number;
21
+ urlLength: 8192;
22
+ headerValueBytes: number;
23
+ headerBytes: number;
24
+ cookies: 64;
25
+ }>;
26
+ declare function merchantCardEndpointBodyRefusal(profile: any, body: any, contentType: any): string | null;
27
+ declare function merchantCardEndpointAmount(profile: any, body: any, contentType: any): {
28
+ amountCents: null;
29
+ currency: any;
30
+ } | {
31
+ amountCents: number;
32
+ currency: any;
33
+ };
34
+ declare function merchantCardEndpointMatches(profile: any, rawUrl: any, method: any, opts: any): boolean;
35
+ declare function matchMerchantCardEndpoint(rawUrl: any, method: any, opts: any): any;
36
+ declare function assertMerchantCardEndpoint(authorization: any): URL & {
37
+ profile: any;
38
+ };
39
+ declare function merchantCardEndpointSessionRefusal(profile: any, bundle: any): string | null;
40
+ declare function substituteMerchantCardEndpoint(body: any, contentType: any, profile: any, card: any): any;
41
+ declare function merchantEndpointRequest(profile: any, bundle: any, card: any): {
42
+ url: any;
43
+ method: any;
44
+ headers: {
45
+ "content-type": any;
46
+ };
47
+ body: any;
48
+ };
49
+ declare function responseCardLeak(card: any, response: any): "card_in_response" | "unreadable_in_response" | null;
50
+ declare function merchantCardEndpointAnswer(profile: any, response: any, card: any): {
51
+ status: any;
52
+ withheld: string;
53
+ headers: {};
54
+ set_cookies: never[];
55
+ bodyText: null;
56
+ } | {
57
+ status: any;
58
+ headers: {};
59
+ set_cookies: any[];
60
+ bodyText: any;
61
+ withheld?: undefined;
62
+ };
63
+ export { MERCHANT_CARD_ENDPOINT_CLIENT_TABLE, MERCHANT_CARD_ENDPOINT_LIMITS, MERCHANT_CARD_ENDPOINT_MODE, MERCHANT_CARD_ENDPOINT_PROFILES, MERCHANT_CARD_ENDPOINT_PSP, MERCHANT_CARD_ENDPOINT_RECOGNIZERS, SubstitutionError, assertMerchantCardEndpoint, matchMerchantCardEndpoint, merchantCardEndpointAmount, merchantCardEndpointAnswer, merchantCardEndpointBodyRefusal, merchantCardEndpointById, merchantCardEndpointLive, merchantCardEndpointMatches, merchantCardEndpointRecognizerById, merchantCardEndpointSessionRefusal, merchantEndpointRequest, responseCardLeak, substituteMerchantCardEndpoint, trustedMerchantCardEndpoint };