@agent-cards/checkout 0.18.0 → 0.21.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 (88) hide show
  1. package/README.md +6 -669
  2. package/cdp.d.ts +1 -0
  3. package/cdp.js +2 -0
  4. package/index.d.ts +1 -0
  5. package/index.js +2 -0
  6. package/package.json +33 -33
  7. package/playwright.d.ts +1 -0
  8. package/playwright.js +2 -0
  9. package/preflight.d.ts +1 -0
  10. package/preflight.js +2 -0
  11. package/CHANGELOG.md +0 -124
  12. package/PREFLIGHT.md +0 -308
  13. package/dist/adyen.generated.d.ts +0 -24
  14. package/dist/adyen.generated.js +0 -64
  15. package/dist/attachment.d.ts +0 -11
  16. package/dist/attachment.js +0 -50
  17. package/dist/braintree.d.ts +0 -2
  18. package/dist/braintree.generated.d.ts +0 -10
  19. package/dist/braintree.generated.js +0 -302
  20. package/dist/braintree.js +0 -2
  21. package/dist/builtin-registry.generated.d.ts +0 -2
  22. package/dist/builtin-registry.generated.js +0 -1
  23. package/dist/card-fields.generated.d.ts +0 -3
  24. package/dist/card-fields.generated.js +0 -46
  25. package/dist/cdp.d.ts +0 -189
  26. package/dist/cdp.js +0 -2194
  27. package/dist/checkout-com.generated.d.ts +0 -4
  28. package/dist/checkout-com.generated.js +0 -183
  29. package/dist/client.d.ts +0 -618
  30. package/dist/client.js +0 -1251
  31. package/dist/hosted-form.d.ts +0 -44
  32. package/dist/hosted-form.js +0 -78
  33. package/dist/index.d.ts +0 -13
  34. package/dist/index.js +0 -6
  35. package/dist/lifecycle.d.ts +0 -165
  36. package/dist/lifecycle.js +0 -370
  37. package/dist/mercado-checkout.d.ts +0 -20
  38. package/dist/mercado-checkout.generated.d.ts +0 -52
  39. package/dist/mercado-checkout.generated.js +0 -198
  40. package/dist/mercado-checkout.js +0 -108
  41. package/dist/owned-shop.generated.d.ts +0 -24
  42. package/dist/owned-shop.generated.js +0 -108
  43. package/dist/paysafe.generated.d.ts +0 -12
  44. package/dist/paysafe.generated.js +0 -87
  45. package/dist/playwright.d.ts +0 -3
  46. package/dist/playwright.js +0 -3
  47. package/dist/preflight-capabilities.generated.d.ts +0 -1253
  48. package/dist/preflight-capabilities.generated.js +0 -1929
  49. package/dist/preflight-catalog.json +0 -4595
  50. package/dist/preflight-playwright.d.ts +0 -34
  51. package/dist/preflight-playwright.js +0 -355
  52. package/dist/preflight-schemas.json +0 -1110
  53. package/dist/preflight.d.ts +0 -1
  54. package/dist/preflight.generated.d.ts +0 -1965
  55. package/dist/preflight.generated.js +0 -556
  56. package/dist/preflight.js +0 -2
  57. package/dist/preparation.d.ts +0 -31
  58. package/dist/preparation.js +0 -164
  59. package/dist/prepared-processor.d.ts +0 -10
  60. package/dist/prepared-processor.js +0 -122
  61. package/dist/recurly.generated.d.ts +0 -1
  62. package/dist/recurly.generated.js +0 -87
  63. package/dist/registry.d.ts +0 -76
  64. package/dist/registry.js +0 -296
  65. package/dist/spreedly.generated.d.ts +0 -10
  66. package/dist/spreedly.generated.js +0 -332
  67. package/dist/stripe-checkout.d.ts +0 -81
  68. package/dist/stripe-checkout.generated.d.ts +0 -82
  69. package/dist/stripe-checkout.generated.js +0 -1004
  70. package/dist/stripe-checkout.js +0 -140
  71. package/dist/substitute.d.ts +0 -38
  72. package/dist/substitute.js +0 -23
  73. package/dist/substitutions.generated.d.ts +0 -10
  74. package/dist/substitutions.generated.js +0 -66
  75. package/examples/existing-browser.mjs +0 -63
  76. package/examples/preflight/classify-direct.mjs +0 -21
  77. package/examples/preflight/classify-kernel.mjs +0 -30
  78. package/examples/preflight/inspect-browser.mjs +0 -44
  79. package/examples/preflight/kernel-native/README.md +0 -112
  80. package/examples/preflight/kernel-native/documented-adapters.json +0 -113
  81. package/examples/preflight/kernel-native/inventory.json +0 -233
  82. package/examples/preflight/kernel-native/qualification.mjs +0 -182
  83. package/examples/preflight/kernel-profile.empty.json +0 -11
  84. package/examples/preflight/mollie-hosted.observations.json +0 -23
  85. package/examples/preflight/mollie-hosted.result.json +0 -103
  86. package/examples/preflight/stripe-script.direct.result.json +0 -92
  87. package/examples/preflight/stripe-script.observations.json +0 -16
  88. package/examples/preflight/stripe-script.result.json +0 -87
package/dist/lifecycle.js DELETED
@@ -1,370 +0,0 @@
1
- import { ApprovalDeclinedError, ApprovalTimeoutError, CheckoutCancelledError, CheckoutPreparationError, IntentNotConfirmableError, PaymentOutcomeUnknownError, ProcessorRefusedError } from './client.js';
2
- import { parseOwnedShopReceipt, OWNED_SHOP_SKU, OWNED_SHOP_AMOUNT_CENTS, OWNED_SHOP_CURRENCY } from './owned-shop.generated.js';
3
- /** How many times one approval's card-free Stripe confirmation may be checked before it is simply held. */
4
- export const MAX_STRIPE_CONTINUATION_CHECKS = 3;
5
- /** Shared by the raw CDP and Playwright transports. No browser ownership or payment execution lives here. */
6
- export class CheckoutLifecycle {
7
- options;
8
- state = { status: 'idle', authorizationId: null };
9
- held = false;
10
- active = false;
11
- cancelled = false;
12
- merchantAborted = false;
13
- unboundStripeToken = false;
14
- // The page's own confirmation paying with that token: see claimStripeContinuation.
15
- stripeTokenPrepared = false;
16
- stripeContinuation = 'none';
17
- stripeContinuationChecks = 0;
18
- ownedShopOrderId = null;
19
- reconciliation = null;
20
- preparationHandler;
21
- preparationUsed = false;
22
- abort = new AbortController();
23
- constructor(options) {
24
- this.options = options;
25
- }
26
- getState() {
27
- return { ...this.state, ...(this.state.confirmation ? { confirmation: { ...this.state.confirmation } } : {}) };
28
- }
29
- setPreparationHandler(handler) { this.preparationHandler = handler; }
30
- prepare(options) {
31
- if (!this.preparationHandler)
32
- return Promise.reject(new CheckoutPreparationError(null, 'transport_unavailable'));
33
- return this.preparationHandler(options);
34
- }
35
- preparing() {
36
- this.preparationUsed = true;
37
- this.set({ status: 'awaiting_approval', authorizationId: null });
38
- }
39
- preparationCreated(preparationId) { this.set({ ...this.state, preparationId }); }
40
- prepared(preparation) {
41
- this.set({ status: 'ready_to_submit', authorizationId: null, preparationId: preparation.id });
42
- }
43
- preparationFailed(error) {
44
- this.held = true;
45
- if (this.cancelled)
46
- return;
47
- this.set({ ...this.state, status: error.reason === 'expired' ? 'timed_out' : error.reason === 'cancelled' ? 'cancelled' : 'failed', reason: error.reason });
48
- }
49
- isBlocked() { return this.held || this.cancelled; }
50
- isCancelled() { return this.cancelled; }
51
- begin() {
52
- this.active = true;
53
- this.set({ status: 'awaiting_approval', authorizationId: null, ...(this.state.preparationId ? { preparationId: this.state.preparationId } : {}) });
54
- }
55
- end() { this.active = false; }
56
- set(state) {
57
- this.state = state;
58
- // Application telemetry must never interrupt a paused payment after it reaches the processor.
59
- try {
60
- Promise.resolve(this.options.onStateChange?.(this.getState())).catch(() => { });
61
- }
62
- catch { /* observer only */ }
63
- }
64
- approvalCreated(authorizationId) {
65
- this.set({ ...this.state, authorizationId });
66
- }
67
- approvalUrl(approvalUrl) {
68
- this.notify({ reason: 'approval', authorizationId: this.state.authorizationId, approvalUrl });
69
- }
70
- notify(action) {
71
- try {
72
- Promise.resolve(this.options.onUserAction?.(action)).catch(() => { });
73
- }
74
- catch { /* observer only */ }
75
- }
76
- cancel() {
77
- this.cancelled = true;
78
- this.held = true;
79
- this.abort.abort();
80
- this.set({ ...this.state, status: this.active || this.state.authorizationId ? 'outcome_unknown' : 'cancelled', reason: 'local_cancel' });
81
- }
82
- /** The exact browser request is gone; a late approval cannot reopen it. */
83
- merchantRequestAborted() {
84
- this.merchantAborted = true;
85
- this.held = true;
86
- this.set({ ...this.state, status: 'outcome_unknown', reason: 'merchant_request_aborted' });
87
- }
88
- /**
89
- * The page abandoned its card request (its own script timed out) while
90
- * the cardholder is still deciding. The approval stays pending and the
91
- * page's next matching request will be answered from it, so the status
92
- * stays `awaiting_approval`; the reason says the page has to ask again.
93
- * Not a hold: nothing is refused, and no card or token has moved.
94
- */
95
- merchantRequestLost() {
96
- this.set({ ...this.state, reason: 'merchant_request_lost' });
97
- }
98
- /**
99
- * The cardholder approved, and there is no live page request to hand the
100
- * answer to: the page gave up on its own request while they decided.
101
- * `ready_to_submit` is the state a prepared checkout uses for "consent in
102
- * hand, click Pay": the application's next Pay action produces the request
103
- * this approval answers, with no second prompt on the phone.
104
- */
105
- awaitingMerchantRetry(authorizationId) {
106
- this.set({ status: 'ready_to_submit', authorizationId, reason: 'awaiting_merchant_retry',
107
- ...(this.state.preparationId ? { preparationId: this.state.preparationId } : {}) });
108
- }
109
- /**
110
- * The page never asked again inside the retry wait and the API confirmed
111
- * the approval is retired: `declined`, like any other approval that ended
112
- * with nothing charged, and not held, because the next card request is a
113
- * new question (the cardholder is told to ask their agent to try again).
114
- * A retirement the API refused or did not confirm is an unknown outcome and
115
- * is reported through failed() instead, which holds.
116
- */
117
- merchantNeverRetried(authorizationId) {
118
- if (this.cancelled)
119
- return;
120
- this.set({ ...this.state, authorizationId, status: 'declined', reason: 'merchant_never_retried' });
121
- }
122
- unsupported() {
123
- this.held = true;
124
- if (this.state.authorizationId)
125
- return; // do not hide an outstanding/completed payment behind a later unsupported request
126
- this.set({ ...this.state, status: 'unsupported', reason: 'unrecognized_payment_endpoint' });
127
- }
128
- prepareHandoff(replay, requestUrl) {
129
- if (replay.mode === 'token' && replay.executionMode === 'autopilot' && replay.shopOrderId) {
130
- let receipt;
131
- try {
132
- receipt = parseOwnedShopReceipt(JSON.parse(replay.body), {
133
- order_id: replay.shopOrderId, sku: OWNED_SHOP_SKU, amount: OWNED_SHOP_AMOUNT_CENTS, currency: OWNED_SHOP_CURRENCY,
134
- });
135
- }
136
- catch { /* An unrecognized response remains subject to the token continuation guard. */ }
137
- if (receipt) {
138
- this.ownedShopOrderId = receipt.order_id;
139
- this.held = true;
140
- return;
141
- }
142
- }
143
- // A tokenization approval has no authoritative PaymentIntent or amount
144
- // binding. Neither the first observed confirm nor the returned opaque
145
- // token can supply one. Hold even in compatibility mode: this attachment
146
- // must never turn that token into an unreviewed payment continuation.
147
- if (!replay.mode || replay.mode === 'token') {
148
- try {
149
- const url = new URL(requestUrl);
150
- if (url.origin === 'https://api.stripe.com' && ['/v1/payment_methods', '/v1/tokens', '/v1/sources', '/v1/confirmation_tokens'].includes(url.pathname)) {
151
- // Set before delivery: a lost browser acknowledgement must not reset
152
- // an already-issued token into a retryable authorization.
153
- this.unboundStripeToken = true;
154
- this.stripeTokenPrepared = true;
155
- this.held = true;
156
- }
157
- }
158
- catch { /* unknown URL cannot establish a token binding */ }
159
- }
160
- }
161
- /**
162
- * The approval a page's card-free Stripe confirmation may continue, or null.
163
- * Only once a Stripe tokenization is being handed to the page, and while
164
- * the checkout still awaits it (the page can send its confirmation before
165
- * the browser acknowledges the delivery) or the merchant; for one check at
166
- * a time and at most MAX_STRIPE_CONTINUATION_CHECKS checks. Reserves the check synchronously,
167
- * so two confirmations can never both continue: the caller settles it with
168
- * stripeContinuationChecked(). The hold stays on throughout.
169
- */
170
- claimStripeContinuation() {
171
- if (!this.stripeContinuationOpen() || this.stripeContinuation !== 'none'
172
- || this.stripeContinuationChecks >= MAX_STRIPE_CONTINUATION_CHECKS)
173
- return null;
174
- this.stripeContinuation = 'checking';
175
- this.stripeContinuationChecks++;
176
- return this.state.authorizationId;
177
- }
178
- /**
179
- * Settle a claimed check. True only when the API allowed it and the checkout
180
- * still awaits the merchant (nothing cancelled, aborted or reconciled it
181
- * while the API answered): the page's confirmation continues, and no later
182
- * one will.
183
- */
184
- stripeContinuationChecked(allowed) {
185
- if (this.stripeContinuation !== 'checking')
186
- return false;
187
- const proceed = allowed && this.stripeContinuationOpen();
188
- this.stripeContinuation = proceed ? 'continued' : 'none';
189
- if (proceed)
190
- this.set({ ...this.state, reason: 'stripe_payment_continued' });
191
- return proceed;
192
- }
193
- stripeContinuationOpen() {
194
- return this.stripeTokenPrepared && !this.cancelled && !this.merchantAborted && !!this.state.authorizationId
195
- && (this.state.status === 'awaiting_approval' || this.state.status === 'awaiting_merchant');
196
- }
197
- handedOff(replay) {
198
- if (this.merchantAborted)
199
- return;
200
- if (this.ownedShopOrderId) {
201
- this.held = true;
202
- this.set({ status: 'completed', authorizationId: replay.authorizationId, mode: 'token', orderId: this.ownedShopOrderId });
203
- return;
204
- }
205
- const mismatch = (!replay.mode || replay.mode === 'token') && replay.amountVerified === false;
206
- this.held = !!this.options.requireMerchantResult || replay.mode === 'hosted_form' || mismatch || this.unboundStripeToken;
207
- if (mismatch)
208
- this.stripeTokenPrepared = false;
209
- this.set({ status: 'awaiting_merchant', authorizationId: replay.authorizationId, mode: replay.mode ?? 'token',
210
- ...(this.state.preparationId ? { preparationId: this.state.preparationId } : {}),
211
- ...(mismatch ? { reason: 'charged_amount_mismatch' }
212
- : this.stripeContinuation === 'continued' ? { reason: 'stripe_payment_continued' }
213
- : this.unboundStripeToken ? { reason: 'stripe_tokenization_unbound' } : {}) });
214
- }
215
- failed(error, handoffStarted = false) {
216
- if (this.cancelled)
217
- return;
218
- if (this.merchantAborted) {
219
- this.merchantRequestAborted();
220
- return;
221
- }
222
- const authorizationId = error instanceof PaymentOutcomeUnknownError || error instanceof ProcessorRefusedError ? error.authorizationId : this.state.authorizationId;
223
- if (!handoffStarted && error instanceof PaymentOutcomeUnknownError && error.reason === 'autopilot_action_required') {
224
- this.held = true;
225
- this.set({ ...this.state, authorizationId, status: 'requires_user_action', reason: 'other' });
226
- this.notify({ reason: 'other', authorizationId });
227
- }
228
- else if (handoffStarted || error instanceof PaymentOutcomeUnknownError || error instanceof IntentNotConfirmableError) {
229
- this.held = true;
230
- this.set({ ...this.state, authorizationId, status: 'outcome_unknown', reason: error instanceof PaymentOutcomeUnknownError ? error.reason : error instanceof IntentNotConfirmableError ? 'intent_not_confirmable' : 'browser_handoff_failed' });
231
- }
232
- else if (error instanceof CheckoutCancelledError) {
233
- this.cancel();
234
- }
235
- else if (error instanceof CheckoutPreparationError) {
236
- this.preparationFailed(error);
237
- }
238
- else if (error instanceof ApprovalTimeoutError) {
239
- this.set({ ...this.state, status: 'timed_out', reason: 'approval_expired' });
240
- }
241
- else if (error instanceof ProcessorRefusedError) {
242
- // A rejected processor request is not proof that the merchant cannot
243
- // collect this payment. Keep native retries held until the application
244
- // checks the merchant and explicitly starts another attempt.
245
- this.held = true;
246
- this.set({ ...this.state, authorizationId, status: 'declined', reason: 'processor_refused' });
247
- }
248
- else if (error instanceof ApprovalDeclinedError) {
249
- this.set({ ...this.state, status: 'declined', reason: 'approval_declined' });
250
- }
251
- else {
252
- this.set({ ...this.state, status: 'failed', reason: 'checkout_failed' });
253
- }
254
- }
255
- async requestUserAction(reason) {
256
- if (this.state.status === 'completed')
257
- return;
258
- this.held = true;
259
- this.set({ ...this.state, status: 'requires_user_action', reason });
260
- // Hooks deliver a link/live view owned by the integrator; the SDK does not invent challenge URLs.
261
- try {
262
- await this.options.onUserAction?.({ reason, authorizationId: this.state.authorizationId });
263
- }
264
- catch { /* observer only */ }
265
- }
266
- reconcile() {
267
- if (this.state.status === 'completed')
268
- return Promise.resolve(this.getState());
269
- if (this.reconciliation)
270
- return this.reconciliation;
271
- if (!this.options.resolveMerchantResult)
272
- return Promise.resolve(this.getState());
273
- if (this.active)
274
- throw new Error('Wait for the paused request to finish before reconciling the merchant order.');
275
- this.reconciliation = this.resolve().finally(() => { this.reconciliation = null; });
276
- return this.reconciliation;
277
- }
278
- async resolve() {
279
- let result;
280
- try {
281
- result = await this.options.resolveMerchantResult(this.getState());
282
- }
283
- catch {
284
- result = { status: 'unknown' };
285
- }
286
- if (!result || typeof result !== 'object' || Array.isArray(result))
287
- result = { status: 'unknown' };
288
- // The resolver is trusted application code: it must check the original
289
- // request, amount, currency, selected card and authoritative merchant
290
- // result. The SDK binds its explicit confirmation to this authorization;
291
- // it does not turn an HTTP response or a success URL into payment proof.
292
- const orderConfirmed = result.status === 'completed' && result.confirmation === undefined
293
- && typeof result.orderId === 'string' && result.orderId.length > 0;
294
- const paymentConfirmed = result.status === 'completed' && result.orderId === undefined
295
- && result.confirmation && typeof result.confirmation === 'object' && !Array.isArray(result.confirmation)
296
- && result.confirmation.kind === 'merchant_payment'
297
- && typeof this.state.authorizationId === 'string' && this.state.authorizationId.length > 0
298
- && result.confirmation.authorizationId === this.state.authorizationId;
299
- if (result.status === 'completed' && (orderConfirmed || paymentConfirmed)) {
300
- this.held = true;
301
- // Completion supersedes an earlier failure/challenge, while retaining
302
- // preparation and authorization context. Never copy arbitrary evidence
303
- // into public state or release the hold on additional payment requests.
304
- const { reason: _previousReason, orderId: _previousOrder, confirmation: _previousConfirmation, ...context } = this.state;
305
- this.set({ ...context, status: 'completed', ...(orderConfirmed
306
- ? { orderId: result.orderId }
307
- : { confirmation: { kind: 'merchant_payment', authorizationId: this.state.authorizationId } }) });
308
- }
309
- else if (result.status === 'failed') {
310
- // Keep the guard armed until the application deliberately starts another attempt.
311
- this.held = true;
312
- this.set({ ...this.state, status: 'failed', reason: 'merchant_confirmed_failure' });
313
- }
314
- else if (result.status === 'requires_user_action') {
315
- await this.requestUserAction(result.reason);
316
- }
317
- else {
318
- this.held = true;
319
- this.set({ ...this.state, status: result.status === 'pending' ? 'awaiting_merchant' : 'outcome_unknown', reason: 'merchant_not_confirmed' });
320
- }
321
- return this.getState();
322
- }
323
- retryAfterMerchantFailure(result) {
324
- if (this.ownedShopOrderId)
325
- throw new Error('This shop payment already has a receipt; another payment is not permitted.');
326
- if (this.preparationUsed)
327
- throw new Error('Prepared checkouts are single use. Reconcile this attempt and create a new attachment.');
328
- if (this.active || this.reconciliation)
329
- throw new Error('Cannot start another attempt while payment or reconciliation is in progress.');
330
- if (this.cancelled)
331
- throw new Error('This attachment was cancelled; attach again after reconciling the merchant order.');
332
- if (result?.status !== 'failed')
333
- throw new Error('A merchant-confirmed failure is required before retrying.');
334
- if (this.state.status === 'completed')
335
- throw new Error('This order is already complete. Use a new attachment for another order.');
336
- if (this.unboundStripeToken)
337
- throw new Error('This attachment delivered an unbound Stripe token; reconcile the merchant order and use a separately validated checkout flow.');
338
- this.held = false;
339
- this.merchantAborted = false;
340
- this.set({ status: 'idle', authorizationId: null });
341
- }
342
- }
343
- export function paymentEndpointGuards(input = []) {
344
- const guards = input.map((guard) => {
345
- const origin = new URL(guard.origin);
346
- if (!['http:', 'https:'].includes(origin.protocol) || origin.origin !== guard.origin
347
- || !guard.pathname.startsWith('/') || /[?#*]/.test(guard.pathname)) {
348
- throw new Error('Payment endpoint guards require a canonical http(s) origin and an exact path without wildcards or query strings.');
349
- }
350
- const methods = (guard.methods ?? ['POST', 'PUT', 'PATCH']).map((method) => method.toUpperCase());
351
- if (!methods.length || methods.some((method) => !['POST', 'PUT', 'PATCH', 'DELETE'].includes(method))) {
352
- throw new Error('Payment endpoint guards can only block explicit mutation methods.');
353
- }
354
- return { ...guard, methods };
355
- });
356
- return {
357
- patterns: guards.map((guard) => `${guard.origin}${guard.pathname}*`),
358
- matches(raw, method) {
359
- let url;
360
- try {
361
- url = new URL(raw);
362
- }
363
- catch {
364
- return false;
365
- }
366
- return guards.some((guard) => url.origin === guard.origin && url.pathname === guard.pathname
367
- && (method === undefined || guard.methods.includes(method.toUpperCase())));
368
- },
369
- };
370
- }
@@ -1,20 +0,0 @@
1
- import type { ReplayResponse } from './client.js';
2
- /** Capture only the native Checkout Pro configuration. A late configuration,
3
- * navigation or repeated token cannot replace the context of an issued token.
4
- * Final payment requests retain the attachment's existing payment guards.
5
- */
6
- export declare class MercadoCheckoutGate {
7
- private context;
8
- private started;
9
- private stopped;
10
- private sequence;
11
- private associatedFlow;
12
- private association;
13
- matches(url: string): boolean;
14
- requiresDocument(url: string, method: string): boolean;
15
- invalidate(): void;
16
- configuration(url: string, method: string, body: string, readDocument: () => Promise<string>): Promise<boolean>;
17
- claimToken(url: string, method: string, documentUrl: string): unknown;
18
- arm(replay: ReplayResponse, tokenUrl: string): Promise<void>;
19
- associationBody(url: string, method: string, body: string, documentUrl: string): string | undefined;
20
- }
@@ -1,52 +0,0 @@
1
- declare var MERCADO_CHECKOUT_ORIGIN: string;
2
- declare var MERCADO_CONFIGURATION_URL: string;
3
- declare var MERCADO_CHECKOUT_PATTERN: string;
4
- declare function mercadoCheckoutDocument(raw: any): {
5
- flowId: string;
6
- preferenceId: string | null;
7
- query: string;
8
- };
9
- declare function parseMercadoCheckoutContext(value: any): any;
10
- declare function captureMercadoCheckoutContext(url: any, method: any, body: any, documentUrl: any): any;
11
- declare function isMercadoTokenRequest(url: any, method: any): boolean;
12
- declare function mercadoDigest(value: any): Promise<string>;
13
- declare function mercadoContextDigest(context: any, tokenUrl: any): Promise<string>;
14
- declare function prepareMercadoConfiguration(context: any, card: any): {
15
- url: string;
16
- body: string;
17
- };
18
- declare function parseMercadoConfiguration(value: any): {
19
- paymentMethodId: any;
20
- paymentTypeId: any;
21
- paymentMethodName: any;
22
- issuerId: string;
23
- issuerName: any;
24
- };
25
- declare function createMercadoProcessorContext(configuration: any, context: any, tokenUrl: any, tokenBody: any): Promise<{
26
- paymentMethodId: any;
27
- paymentTypeId: any;
28
- paymentMethodName: any;
29
- issuerId: string;
30
- issuerName: any;
31
- version: number;
32
- psp: string;
33
- source: string;
34
- contextDigest: string;
35
- tokenDigest: string;
36
- }>;
37
- declare function validateMercadoProcessorContext(value: any, context: any, tokenUrl: any, tokenBody: any): Promise<any>;
38
- declare function createMercadoAssociationGate({ context, tokenUrl, tokenBody, processorContext }: {
39
- context: any;
40
- tokenUrl: any;
41
- tokenBody: any;
42
- processorContext: any;
43
- }): Promise<{
44
- take({ url, method, body, documentUrl }: {
45
- url: any;
46
- method: any;
47
- body: any;
48
- documentUrl: any;
49
- }): string;
50
- readonly claimed: boolean;
51
- }>;
52
- export { MERCADO_CHECKOUT_ORIGIN, MERCADO_CHECKOUT_PATTERN, MERCADO_CONFIGURATION_URL, captureMercadoCheckoutContext, createMercadoAssociationGate, createMercadoProcessorContext, isMercadoTokenRequest, mercadoCheckoutDocument, mercadoContextDigest, mercadoDigest, parseMercadoCheckoutContext, parseMercadoConfiguration, prepareMercadoConfiguration, validateMercadoProcessorContext };
@@ -1,198 +0,0 @@
1
- // @ts-nocheck
2
- // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: 0ea817a75d86052699eeaa7546950e6c046bb0d7a96e4a18dfcd3a0fdd2afc99
4
- // src/mercado-checkout.js
5
- var MERCADO_CHECKOUT_ORIGIN = "https://www.mercadopago.com.mx";
6
- var MERCADO_CONFIGURATION_URL = MERCADO_CHECKOUT_ORIGIN + "/checkout/v1/payment/api/bricks/card-form/tokenization";
7
- var MERCADO_CHECKOUT_PATTERN = MERCADO_CHECKOUT_ORIGIN + "/checkout/v1/payment/api/*";
8
- var reject = () => {
9
- throw new Error("mercado_checkout_context_invalid");
10
- };
11
- var object = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
12
- var only = (v, keys) => object(v) && Object.keys(v).every((k) => keys.includes(k));
13
- var exact = (v, keys) => only(v, keys) && keys.every((k) => Object.hasOwn(v, k));
14
- var text = (v, max = 200) => typeof v === "string" && v.length > 0 && v.length <= max && !/[\u0000-\u001f\u007f]/.test(v);
15
- var id = (v) => typeof v === "string" && /^[A-Za-z0-9_-]{1,128}$/.test(v);
16
- var numberId = (v) => typeof v === "string" && /^\d{1,20}$/.test(v);
17
- var label = (v) => text(v, 120) && /\p{L}/u.test(v) && !/[<>]|\d(?:[ -]*\d){5}|https?:|javascript:|data:/i.test(v);
18
- var digest = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
19
- var METHODS = ["visa", "master", "amex"];
20
- var TYPES = ["credit_card", "debit_card", "prepaid_card"];
21
- function checkedUrl(raw) {
22
- let url;
23
- try {
24
- url = new URL(raw);
25
- }
26
- catch {
27
- reject();
28
- }
29
- if (!text(raw, 4096) || url.href !== raw || url.origin !== MERCADO_CHECKOUT_ORIGIN || url.username || url.password || url.hash)
30
- reject();
31
- return url;
32
- }
33
- function mercadoCheckoutDocument(raw) {
34
- const url = checkedUrl(raw);
35
- const match = /^\/checkout\/v1\/payment\/redirect\/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})\/card-form\/$/.exec(url.pathname);
36
- const keys = [...url.searchParams.keys()];
37
- if (!match || keys.length !== 3 || new Set(keys).size !== 3 || keys.some((k) => !["preference-id", "router-request-id", "p"].includes(k)) || !id(url.searchParams.get("preference-id")) || !id(url.searchParams.get("router-request-id")) || !text(url.searchParams.get("p"), 2048))
38
- reject();
39
- return { flowId: match[1], preferenceId: url.searchParams.get("preference-id"), query: url.search.slice(1) };
40
- }
41
- function initialization(value) {
42
- const keys = [
43
- "callerId",
44
- "collectorId",
45
- "allowThirdPartyCard",
46
- "clientId",
47
- "flowId",
48
- "checkoutId",
49
- "scope",
50
- "excludedPaymentTypesAndIds",
51
- "locale",
52
- "siteId",
53
- "serviceRoutePrefix",
54
- "productId",
55
- "marketplace",
56
- "platform",
57
- "cardDetails"
58
- ];
59
- if (!exact(value, keys) || !numberId(value.callerId) || !numberId(value.collectorId) || !numberId(value.clientId) || value.allowThirdPartyCard !== true || value.flowId !== "checkout-pro-payment" || value.checkoutId !== "cow" || value.scope !== "prod" || value.locale !== "es-MX" || value.siteId !== "MLM" || value.serviceRoutePrefix !== "/checkout/v1/payment/api" || !id(value.productId) || value.marketplace !== "NONE" || value.platform !== "MP" || !Array.isArray(value.excludedPaymentTypesAndIds) || value.excludedPaymentTypesAndIds.length !== 0 || !exact(value.cardDetails, ["fieldsSetting", "identificationTypes", "additionalSteps"]) || !Array.isArray(value.cardDetails.identificationTypes) || value.cardDetails.identificationTypes.length !== 0 || JSON.stringify(value.cardDetails.additionalSteps) !== '["name"]')
60
- reject();
61
- const fields = value.cardDetails.fieldsSetting;
62
- if (!Array.isArray(fields) || fields.length !== 5 || new Set(fields.map((f) => f?.name)).size !== 5)
63
- reject();
64
- for (const field of fields) {
65
- if (!only(field, ["name", "type", "title", "placeholder", "length", "validationMessages", "mask", "hintMessage", "validationPattern", "autocomplete", "tooltipMessage"]) || !["card_number", "name", "expiration", "security_code", "identification_types"].includes(field.name) || field.type !== (field.name === "name" ? "text" : "number") || !text(field.title) || field.placeholder !== void 0 && !text(field.placeholder) || ["hintMessage", "validationPattern", "tooltipMessage"].some((k) => field[k] !== void 0 && !text(field[k], 1024)) || field.autocomplete !== void 0 && typeof field.autocomplete !== "boolean" || field.length !== void 0 && (!Number.isInteger(field.length) || field.length < 1 || field.length > 40) || field.mask !== void 0 && (!text(field.mask, 30) || !/^[ $/]+$/.test(field.mask)) || !only(field.validationMessages, ["emptyField", "incompleteField", "invalidField"]) || Object.values(field.validationMessages).some((v) => !text(v)))
66
- reject();
67
- }
68
- if (JSON.stringify(value).length > 12e3)
69
- reject();
70
- return value;
71
- }
72
- function parseMercadoCheckoutContext(value) {
73
- if (!exact(value, ["version", "documentUrl", "initializationData"]) || value.version !== 1)
74
- reject();
75
- mercadoCheckoutDocument(value.documentUrl);
76
- initialization(value.initializationData);
77
- return JSON.parse(JSON.stringify(value));
78
- }
79
- function captureMercadoCheckoutContext(url, method, body, documentUrl) {
80
- if (url !== MERCADO_CONFIGURATION_URL || method !== "POST" || typeof body !== "string" || body.length > 16e3)
81
- reject();
82
- let parsed;
83
- try {
84
- parsed = JSON.parse(body);
85
- }
86
- catch {
87
- reject();
88
- }
89
- if (!exact(parsed, ["cardBin", "initializationData"]) || typeof parsed.cardBin !== "string" || !/^\d{8}$/.test(parsed.cardBin))
90
- reject();
91
- return parseMercadoCheckoutContext({ version: 1, documentUrl, initializationData: parsed.initializationData });
92
- }
93
- function isMercadoTokenRequest(url, method) {
94
- try {
95
- const value = new URL(url), names = [...value.searchParams.keys()];
96
- return method === "POST" && value.href === url && value.origin === "https://api.mercadopago.com" && value.pathname === "/v1/card_tokens" && !value.username && !value.password && !value.hash && new Set(names).size === names.length && names.every((n) => ["public_key", "locale", "js_version", "referer"].includes(n)) && id(value.searchParams.get("public_key"));
97
- }
98
- catch {
99
- return false;
100
- }
101
- }
102
- async function mercadoDigest(value) {
103
- const bytes = new TextEncoder().encode(value);
104
- return Array.from(new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", bytes)), (byte) => byte.toString(16).padStart(2, "0")).join("");
105
- }
106
- function canonical(value) {
107
- return Array.isArray(value) ? value.map(canonical) : object(value) ? Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical(value[key])])) : value;
108
- }
109
- async function mercadoContextDigest(context, tokenUrl) {
110
- if (!isMercadoTokenRequest(tokenUrl, "POST"))
111
- reject();
112
- return mercadoDigest(JSON.stringify(canonical({ context: parseMercadoCheckoutContext(context), tokenUrl })));
113
- }
114
- function prepareMercadoConfiguration(context, card) {
115
- const parsed = parseMercadoCheckoutContext(context);
116
- const number = card?.number;
117
- if (typeof number !== "string" || !/^\d{12,19}$/.test(number))
118
- reject();
119
- return { url: MERCADO_CONFIGURATION_URL, body: JSON.stringify({ cardBin: number.slice(0, 8), initializationData: parsed.initializationData }) };
120
- }
121
- function parseMercadoConfiguration(value) {
122
- const method = value?.paymentMethod, issuers = value?.issuersSettings;
123
- if (!object(value) || !exact(method, ["paymentMethodId", "paymentTypeId", "name"]) || !METHODS.includes(method.paymentMethodId) || !TYPES.includes(method.paymentTypeId) || !label(method.name) || !Array.isArray(issuers) || issuers.length !== 1 || !object(issuers[0]) || !label(issuers[0].name))
124
- reject();
125
- const issuerId = String(issuers[0].id);
126
- if (!/^\d{1,11}$/.test(issuerId) || typeof issuers[0].id !== "string" && !Number.isSafeInteger(issuers[0].id))
127
- reject();
128
- const cvc = Array.isArray(value.fieldsSetting) ? value.fieldsSetting.filter((f) => f?.name === "security_code") : [];
129
- const amex = method.paymentMethodId === "amex";
130
- if (cvc.length !== 1 || cvc[0].type !== "number" || cvc[0].length !== (amex ? 4 : 3) || value.securityCodeSettings?.mode !== "mandatory" || value.securityCodeSettings?.location !== (amex ? "front" : "back"))
131
- reject();
132
- return {
133
- paymentMethodId: method.paymentMethodId,
134
- paymentTypeId: method.paymentTypeId,
135
- paymentMethodName: method.name,
136
- issuerId,
137
- issuerName: issuers[0].name
138
- };
139
- }
140
- function tokenIdentity(body) {
141
- let value;
142
- try {
143
- value = JSON.parse(body);
144
- }
145
- catch {
146
- reject();
147
- }
148
- if (!object(value) || value.live_mode === false || typeof value.id !== "string" || !/^[A-Za-z0-9_-]{8,256}$/.test(value.id))
149
- reject();
150
- return value.id;
151
- }
152
- async function createMercadoProcessorContext(configuration, context, tokenUrl, tokenBody) {
153
- return {
154
- version: 1,
155
- psp: "mercado_pago",
156
- source: "checkout_pro_bin8",
157
- contextDigest: await mercadoContextDigest(context, tokenUrl),
158
- tokenDigest: await mercadoDigest(tokenIdentity(tokenBody)),
159
- ...parseMercadoConfiguration(configuration)
160
- };
161
- }
162
- async function validateMercadoProcessorContext(value, context, tokenUrl, tokenBody) {
163
- if (!exact(value, ["version", "psp", "source", "contextDigest", "tokenDigest", "paymentMethodId", "paymentTypeId", "paymentMethodName", "issuerId", "issuerName"]) || value.version !== 1 || value.psp !== "mercado_pago" || value.source !== "checkout_pro_bin8" || !digest(value.contextDigest) || !digest(value.tokenDigest) || !METHODS.includes(value.paymentMethodId) || !TYPES.includes(value.paymentTypeId) || !label(value.paymentMethodName) || !numberId(value.issuerId) || value.issuerId.length > 11 || !label(value.issuerName) || value.contextDigest !== await mercadoContextDigest(context, tokenUrl) || value.tokenDigest !== await mercadoDigest(tokenIdentity(tokenBody)))
164
- reject();
165
- return { ...value };
166
- }
167
- async function createMercadoAssociationGate({ context, tokenUrl, tokenBody, processorContext }) {
168
- const checked = parseMercadoCheckoutContext(context), metadata = await validateMercadoProcessorContext(processorContext, checked, tokenUrl, tokenBody);
169
- const document = mercadoCheckoutDocument(checked.documentUrl), token = tokenIdentity(tokenBody);
170
- let claimed = false;
171
- return { take({ url, method, body, documentUrl }) {
172
- if (claimed)
173
- reject();
174
- const target = checkedUrl(url);
175
- if (method !== "PUT" || documentUrl !== checked.documentUrl || target.pathname !== "/checkout/v1/payment/api/flow/" + document.flowId || target.search || typeof body !== "string" || body.length > 64e3)
176
- reject();
177
- let parsed;
178
- try {
179
- parsed = JSON.parse(body);
180
- }
181
- catch {
182
- reject();
183
- }
184
- if (!only(parsed, ["id", "values", "type", "featureFlags", "version", "prev_step", "urlParams"]) || parsed.id !== "next_step" || parsed.type !== "redirect" || parsed.version !== "checkout" || parsed.prev_step !== "card_form" || parsed.urlParams !== document.query || !only(parsed.values, ["payment_method", "card_token", "card_ui_settings", "captchaToken"]) || parsed.values.card_token !== token || !object(parsed.values.card_ui_settings) || !only(parsed.values.payment_method, ["id", "type", "name", "issuer"]) || parsed.values.payment_method.id !== metadata.paymentMethodId || parsed.values.payment_method.type !== metadata.paymentTypeId)
185
- reject();
186
- claimed = true;
187
- parsed.values.payment_method = {
188
- id: metadata.paymentMethodId,
189
- type: metadata.paymentTypeId,
190
- name: metadata.paymentMethodName,
191
- issuer: { id: Number(metadata.issuerId), name: metadata.issuerName }
192
- };
193
- return JSON.stringify(parsed);
194
- }, get claimed() {
195
- return claimed;
196
- } };
197
- }
198
- export { MERCADO_CHECKOUT_ORIGIN, MERCADO_CHECKOUT_PATTERN, MERCADO_CONFIGURATION_URL, captureMercadoCheckoutContext, createMercadoAssociationGate, createMercadoProcessorContext, isMercadoTokenRequest, mercadoCheckoutDocument, mercadoContextDigest, mercadoDigest, parseMercadoCheckoutContext, parseMercadoConfiguration, prepareMercadoConfiguration, validateMercadoProcessorContext };