@agent-cards/sdk 0.21.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 (103) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/PREFLIGHT.md +30 -0
  3. package/README.md +1070 -21
  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 +5 -1
  22. package/dist/cdp.js +841 -135
  23. package/dist/client.d.ts +403 -19
  24. package/dist/client.js +949 -98
  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/datatrans.d.ts +96 -0
  29. package/dist/datatrans.generated.d.ts +64 -0
  30. package/dist/datatrans.generated.js +426 -0
  31. package/dist/datatrans.js +178 -0
  32. package/dist/elavon-converge.d.ts +37 -0
  33. package/dist/elavon-converge.generated.d.ts +49 -0
  34. package/dist/elavon-converge.generated.js +439 -0
  35. package/dist/elavon-converge.js +84 -0
  36. package/dist/eprotect.d.ts +152 -0
  37. package/dist/eprotect.generated.d.ts +77 -0
  38. package/dist/eprotect.generated.js +498 -0
  39. package/dist/eprotect.js +222 -0
  40. package/dist/hosted-form.d.ts +3 -2
  41. package/dist/hosted-form.js +3 -2
  42. package/dist/index.d.ts +17 -3
  43. package/dist/index.js +6 -1
  44. package/dist/juspay-gate.d.ts +82 -0
  45. package/dist/juspay-gate.js +126 -0
  46. package/dist/juspay.generated.d.ts +89 -0
  47. package/dist/juspay.generated.js +705 -0
  48. package/dist/lifecycle.d.ts +57 -6
  49. package/dist/lifecycle.js +119 -8
  50. package/dist/merchant-card-endpoints.generated.d.ts +63 -0
  51. package/dist/merchant-card-endpoints.generated.js +1103 -0
  52. package/dist/merchant-hosted.d.ts +4 -1
  53. package/dist/merchant-hosted.js +5 -2
  54. package/dist/merchant-session-probe.d.ts +48 -0
  55. package/dist/merchant-session-probe.js +53 -0
  56. package/dist/merchant-session.generated.d.ts +69 -0
  57. package/dist/merchant-session.generated.js +919 -0
  58. package/dist/payrails.d.ts +133 -0
  59. package/dist/payrails.generated.d.ts +73 -0
  60. package/dist/payrails.generated.js +596 -0
  61. package/dist/payrails.js +305 -0
  62. package/dist/pelecard.d.ts +32 -0
  63. package/dist/pelecard.generated.d.ts +32 -0
  64. package/dist/pelecard.generated.js +283 -0
  65. package/dist/pelecard.js +42 -0
  66. package/dist/pre-claim.d.ts +72 -2
  67. package/dist/pre-claim.js +857 -5
  68. package/dist/preflight-catalog.json +2218 -287
  69. package/dist/preflight-schemas.json +179 -6
  70. package/dist/preflight.generated.d.ts +1 -1
  71. package/dist/preflight.generated.js +233 -4
  72. package/dist/preparation.d.ts +5 -2
  73. package/dist/preparation.js +36 -10
  74. package/dist/prepared-processor.d.ts +84 -5
  75. package/dist/prepared-processor.js +147 -6
  76. package/dist/primer.d.ts +53 -0
  77. package/dist/primer.generated.d.ts +38 -0
  78. package/dist/primer.generated.js +624 -0
  79. package/dist/primer.js +82 -0
  80. package/dist/registry.d.ts +100 -8
  81. package/dist/registry.js +85 -11
  82. package/dist/shiji-sts.d.ts +144 -0
  83. package/dist/shiji-sts.generated.d.ts +72 -0
  84. package/dist/shiji-sts.generated.js +913 -0
  85. package/dist/shiji-sts.js +222 -0
  86. package/dist/stripe-checkout.generated.d.ts +1 -1
  87. package/dist/stripe-checkout.generated.js +699 -9
  88. package/dist/stripe-setup.d.ts +16 -0
  89. package/dist/stripe-setup.js +61 -0
  90. package/dist/substitutions.generated.js +187 -29
  91. package/dist/verifone.d.ts +68 -0
  92. package/dist/verifone.generated.d.ts +51 -0
  93. package/dist/verifone.generated.js +1401 -0
  94. package/dist/verifone.js +119 -0
  95. package/dist/voice-types.d.ts +27 -0
  96. package/dist/voice.d.ts +6 -1
  97. package/dist/voice.js +6 -1
  98. package/dist/wspay.d.ts +41 -0
  99. package/dist/wspay.generated.d.ts +41 -0
  100. package/dist/wspay.generated.js +370 -0
  101. package/dist/wspay.js +65 -0
  102. package/examples/preflight/kernel-native/inventory.json +1 -1
  103. package/package.json +3 -3
@@ -1,7 +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';
4
5
  import type { GmoUnsupportedReason } from './gmo.js';
6
+ import type { ShijiStsUnsupportedReason } from './shiji-sts.js';
5
7
  /** The application's resolver confirmed the payment for this checkout authorization. */
6
8
  export interface MerchantPaymentConfirmation {
7
9
  kind: 'merchant_payment';
@@ -28,7 +30,8 @@ export interface CheckoutState {
28
30
  status: 'idle' | 'awaiting_approval' | 'ready_to_submit' | 'awaiting_merchant' | 'requires_user_action' | 'completed' | 'declined' | 'timed_out' | 'cancelled' | 'unsupported' | 'outcome_unknown' | 'failed';
29
31
  authorizationId: string | null;
30
32
  preparationId?: string;
31
- 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';
32
35
  orderId?: string;
33
36
  confirmation?: MerchantPaymentConfirmation;
34
37
  /** Stable SDK category; never includes a request body, processor response, or approval link. */
@@ -116,6 +119,12 @@ export interface CheckoutController {
116
119
  }
117
120
  /** How many times one approval's card-free Stripe confirmation may be checked before it is simply held. */
118
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;
119
128
  /** Shared by the raw CDP and Playwright transports. No browser ownership or payment execution lives here. */
120
129
  export declare class CheckoutLifecycle implements CheckoutController {
121
130
  private readonly options;
@@ -128,7 +137,12 @@ export declare class CheckoutLifecycle implements CheckoutController {
128
137
  private stripeTokenPrepared;
129
138
  private stripeContinuation;
130
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;
131
144
  private ownedShopOrderId;
145
+ private readonly handoffs;
132
146
  private reconciliation;
133
147
  private preparationHandler?;
134
148
  private preparationUsed;
@@ -147,6 +161,17 @@ export declare class CheckoutLifecycle implements CheckoutController {
147
161
  preparationFailed(error: CheckoutPreparationError): void;
148
162
  isBlocked(): boolean;
149
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>;
150
175
  begin(): void;
151
176
  end(): void;
152
177
  private set;
@@ -187,11 +212,20 @@ export declare class CheckoutLifecycle implements CheckoutController {
187
212
  * turned on (merchant_profile_observe), a card request the profile cannot
188
213
  * finish (merchant_request_unsupported), a recognized processor's request in
189
214
  * a form its gate refuses (GMO's unsupported_gmo_request and
190
- * unsupported_gmo_token_v1), or a CyberSource Flex request that is not a
215
+ * unsupported_gmo_token_v1), a CyberSource Flex request that is not a
191
216
  * reviewed Microform v2 card field's (microform_unsupported,
192
- * cybersource-flex.ts).
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).
193
227
  */
194
- unsupported(reason?: 'unrecognized_payment_endpoint' | 'merchant_profile_observe' | 'merchant_request_unsupported' | GmoUnsupportedReason | 'microform_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;
195
229
  /**
196
230
  * A card request that only a prepared checkout pays (an Adyen merchant
197
231
  * profile's, or a Fiserv card capture) arrived with no preparation on this
@@ -201,6 +235,22 @@ export declare class CheckoutLifecycle implements CheckoutController {
201
235
  */
202
236
  preparationRequired(): void;
203
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;
204
254
  /**
205
255
  * The approval a page's card-free Stripe confirmation may continue, or null.
206
256
  * Only once a Stripe tokenization is being handed to the page, and while
@@ -215,9 +265,10 @@ export declare class CheckoutLifecycle implements CheckoutController {
215
265
  * Settle a claimed check. True only when the API allowed it and the checkout
216
266
  * still awaits the merchant (nothing cancelled, aborted or reconciled it
217
267
  * while the API answered): the page's confirmation continues, and no later
218
- * 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.
219
270
  */
220
- stripeContinuationChecked(allowed: boolean): boolean;
271
+ stripeContinuationChecked(allowed: boolean, kind?: 'payment' | 'setup'): boolean;
221
272
  private stripeContinuationOpen;
222
273
  handedOff(replay: ReplayResponse): void;
223
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 } : {}) });
@@ -135,9 +188,18 @@ export class CheckoutLifecycle {
135
188
  * turned on (merchant_profile_observe), a card request the profile cannot
136
189
  * finish (merchant_request_unsupported), a recognized processor's request in
137
190
  * a form its gate refuses (GMO's unsupported_gmo_request and
138
- * unsupported_gmo_token_v1), or a CyberSource Flex request that is not a
191
+ * unsupported_gmo_token_v1), a CyberSource Flex request that is not a
139
192
  * reviewed Microform v2 card field's (microform_unsupported,
140
- * cybersource-flex.ts).
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).
141
203
  */
142
204
  unsupported(reason = 'unrecognized_payment_endpoint') {
143
205
  this.held = true;
@@ -158,6 +220,8 @@ export class CheckoutLifecycle {
158
220
  this.set({ ...this.state, status: 'failed', reason: 'preparation_required' });
159
221
  }
160
222
  prepareHandoff(replay, requestUrl) {
223
+ this.wspayChallenge = isWspayChallenge(requestUrl, replay);
224
+ this.handoffs.push(requestUrl);
161
225
  if (replay.mode === 'token' && replay.executionMode === 'autopilot' && replay.shopOrderId) {
162
226
  let receipt;
163
227
  try {
@@ -185,11 +249,31 @@ export class CheckoutLifecycle {
185
249
  this.unboundStripeToken = true;
186
250
  this.stripeTokenPrepared = true;
187
251
  this.held = true;
252
+ if (url.pathname === '/v1/payment_methods')
253
+ this.approvedPaymentMethod = stripePaymentMethodId(replay.body);
188
254
  }
189
255
  }
190
256
  catch { /* unknown URL cannot establish a token binding */ }
191
257
  }
192
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
+ }
193
277
  /**
194
278
  * The approval a page's card-free Stripe confirmation may continue, or null.
195
279
  * Only once a Stripe tokenization is being handed to the page, and while
@@ -211,15 +295,18 @@ export class CheckoutLifecycle {
211
295
  * Settle a claimed check. True only when the API allowed it and the checkout
212
296
  * still awaits the merchant (nothing cancelled, aborted or reconciled it
213
297
  * while the API answered): the page's confirmation continues, and no later
214
- * 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.
215
300
  */
216
- stripeContinuationChecked(allowed) {
301
+ stripeContinuationChecked(allowed, kind = 'payment') {
217
302
  if (this.stripeContinuation !== 'checking')
218
303
  return false;
219
304
  const proceed = allowed && this.stripeContinuationOpen();
220
305
  this.stripeContinuation = proceed ? 'continued' : 'none';
221
- if (proceed)
222
- 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
+ }
223
310
  return proceed;
224
311
  }
225
312
  stripeContinuationOpen() {
@@ -234,14 +321,31 @@ export class CheckoutLifecycle {
234
321
  this.set({ status: 'completed', authorizationId: replay.authorizationId, mode: 'token', orderId: this.ownedShopOrderId });
235
322
  return;
236
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
+ }
237
338
  const mismatch = (!replay.mode || replay.mode === 'token') && replay.amountVerified === false;
238
- 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;
239
343
  if (mismatch)
240
344
  this.stripeTokenPrepared = false;
241
345
  this.set({ status: 'awaiting_merchant', authorizationId: replay.authorizationId, mode: replay.mode ?? 'token',
242
346
  ...(this.state.preparationId ? { preparationId: this.state.preparationId } : {}),
243
347
  ...(mismatch ? { reason: 'charged_amount_mismatch' }
244
- : this.stripeContinuation === 'continued' ? { reason: 'stripe_payment_continued' }
348
+ : this.stripeContinuation === 'continued' ? { reason: this.stripeContinuedReason }
245
349
  : this.unboundStripeToken ? { reason: 'stripe_tokenization_unbound' } : {}) });
246
350
  }
247
351
  failed(error, handoffStarted = false) {
@@ -261,6 +365,13 @@ export class CheckoutLifecycle {
261
365
  this.held = true;
262
366
  this.set({ ...this.state, authorizationId, status: 'outcome_unknown', reason: error instanceof PaymentOutcomeUnknownError ? error.reason : error instanceof IntentNotConfirmableError ? 'intent_not_confirmable' : 'browser_handoff_failed' });
263
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
+ }
264
375
  else if (error instanceof CheckoutCancelledError) {
265
376
  this.cancel();
266
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 };