@agent-cards/sdk 0.23.19 → 0.23.21

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 (66) hide show
  1. package/CHANGELOG.md +61 -2
  2. package/PREFLIGHT.md +4 -0
  3. package/README.md +175 -7
  4. package/dist/amadeus.d.ts +4 -2
  5. package/dist/amadeus.js +16 -3
  6. package/dist/buy.d.ts +84 -2
  7. package/dist/buy.js +16 -1
  8. package/dist/card-endpoint-facts.d.ts +2 -1
  9. package/dist/card-endpoint-facts.js +18 -9
  10. package/dist/card-endpoint.d.ts +20 -4
  11. package/dist/card-endpoint.js +38 -4
  12. package/dist/card-fields.generated.js +1444 -51
  13. package/dist/cdp.d.ts +29 -0
  14. package/dist/cdp.js +444 -24
  15. package/dist/client.d.ts +134 -7
  16. package/dist/client.js +350 -25
  17. package/dist/comgate.d.ts +50 -0
  18. package/dist/comgate.js +80 -0
  19. package/dist/cybersource-secure-acceptance.generated.d.ts +1 -1
  20. package/dist/eprotect.generated.d.ts +1 -1
  21. package/dist/gmo.generated.js +7 -3
  22. package/dist/hipay.generated.d.ts +56 -0
  23. package/dist/hipay.generated.js +392 -0
  24. package/dist/hold.d.ts +180 -0
  25. package/dist/hold.js +270 -0
  26. package/dist/hosted-entry.d.ts +4 -0
  27. package/dist/hosted-entry.js +32 -0
  28. package/dist/index.d.ts +4 -1
  29. package/dist/index.js +1 -0
  30. package/dist/lifecycle.d.ts +10 -0
  31. package/dist/lifecycle.js +21 -0
  32. package/dist/merchant-card-endpoints.generated.js +102 -11
  33. package/dist/paddle-tokenex.generated.js +7 -3
  34. package/dist/paddle-vgs.generated.d.ts +3 -2
  35. package/dist/paddle-vgs.generated.js +9 -5
  36. package/dist/payrails.generated.js +7 -3
  37. package/dist/pre-claim.d.ts +20 -0
  38. package/dist/pre-claim.js +202 -22
  39. package/dist/preflight-catalog.json +58 -0
  40. package/dist/preflight-schemas.json +4 -0
  41. package/dist/preflight.generated.js +26 -2
  42. package/dist/prepared-processor.d.ts +6 -2
  43. package/dist/prepared-processor.js +18 -2
  44. package/dist/primer.generated.js +1487 -94
  45. package/dist/przelewy24.d.ts +47 -0
  46. package/dist/przelewy24.js +88 -0
  47. package/dist/quickpay.d.ts +44 -0
  48. package/dist/quickpay.js +65 -0
  49. package/dist/registry.d.ts +6 -5
  50. package/dist/registry.js +27 -7
  51. package/dist/shiji-sts.generated.js +7 -3
  52. package/dist/stripe-checkout.generated.js +744 -13
  53. package/dist/tokenex-iframe.d.ts +93 -0
  54. package/dist/tokenex-iframe.generated.d.ts +81 -0
  55. package/dist/tokenex-iframe.generated.js +403 -0
  56. package/dist/tokenex-iframe.js +195 -0
  57. package/dist/transbank.d.ts +45 -0
  58. package/dist/transbank.js +73 -0
  59. package/dist/twoc2p.d.ts +54 -0
  60. package/dist/twoc2p.generated.d.ts +33 -0
  61. package/dist/twoc2p.generated.js +1149 -0
  62. package/dist/twoc2p.js +85 -0
  63. package/dist/voice.d.ts +16 -13
  64. package/dist/voice.js +13 -10
  65. package/examples/preflight/kernel-native/inventory.json +1 -1
  66. package/package.json +3 -3
package/dist/amadeus.js CHANGED
@@ -19,6 +19,11 @@
19
19
  // handed back in time. A token Wix mints after that point is one no charge ever names, so a paused
20
20
  // tokenize request gets the same 20 second window.
21
21
  //
22
+ // HiPay's merchant page gives its hosted fields 20 seconds after Pay (getPaymentData answers TIMEOUT_ERROR), and
23
+ // the merchant's server charges only a token the page read in time. A token HiPay mints after that point stays in
24
+ // the frame unused (the frame's next Pay would send /update, which the SDK refuses), so a paused token create gets a
25
+ // 12 second window: HiPay answers a create in about a second, so a card sent inside it reaches the page in time.
26
+ //
22
27
  // So when the window closes and no answer is being handed to the page yet, the adapter asks for that
23
28
  // cancel. Confirmed: nothing was sent and nothing can be, so the attempt stops, the page's request
24
29
  // fails (a Secure Acceptance frame gets the stopped page), and the checkout reads failed. Not
@@ -30,16 +35,20 @@
30
35
  // the same cancel and the same rule, and when the API confirms neither, the attempt keeps waiting. The
31
36
  // Data Collection Page's store has no client timeout and gets no window. The window's events name the
32
37
  // processor: `amadeus_send_window_closed` and `amadeus_send_window_unconfirmed`,
33
- // `secure_acceptance_send_window_closed` and `secure_acceptance_send_window_unconfirmed`, or
34
- // `wix_payments_send_window_closed` and `wix_payments_send_window_unconfirmed`.
38
+ // `secure_acceptance_send_window_closed` and `secure_acceptance_send_window_unconfirmed`,
39
+ // `wix_payments_send_window_closed` and `wix_payments_send_window_unconfirmed`, or
40
+ // `hipay_send_window_closed` and `hipay_send_window_unconfirmed`.
35
41
  import { amadeusCardUrl } from './amadeus-checkout.generated.js';
36
42
  import { isSecureAcceptanceTokenRequest } from './cybersource-secure-acceptance.generated.js';
37
43
  import { SECURE_ACCEPTANCE_PSP, SECURE_ACCEPTANCE_SEND_WINDOW_MS } from './cybersource-secure-acceptance.js';
38
44
  import { WIX_PAYMENTS_PSP, isWixPaymentsTokenizeUrl } from './wix-payments.generated.js';
45
+ import { HIPAY_PSP, isHipayTokenUrl } from './hipay.generated.js';
39
46
  /** How long after the pause a paused Amadeus SDK add may still be sent. */
40
47
  export const AMADEUS_SEND_WINDOW_MS = 20_000;
41
48
  /** How long after the pause a paused Wix tokenize request may still be sent, of the 30 seconds Wix's widget waits. */
42
49
  export const WIX_PAYMENTS_SEND_WINDOW_MS = 20_000;
50
+ /** How long after the pause a paused HiPay token create may still be sent, of the 20 seconds the merchant's page waits. */
51
+ export const HIPAY_SEND_WINDOW_MS = 12_000;
43
52
  /**
44
53
  * True when a request the adapters refuse must fail as a network error (ERR_FAILED) rather than as
45
54
  * the cancellation (ERR_ABORTED) they use elsewhere. An XMLHttpRequest reports a cancellation only
@@ -53,7 +62,7 @@ export function failsAsNetworkError(url) {
53
62
  /**
54
63
  * The send window for a request paused under a preparation for `psp`, or null when it has none: a
55
64
  * prepared Amadeus SDK add, a prepared CyberSource Secure Acceptance token create and a prepared Wix
56
- * tokenize request, each 20 seconds.
65
+ * tokenize request, each 20 seconds, and a prepared HiPay token create, 12 seconds.
57
66
  */
58
67
  export function preparedSendWindowMs(psp, url) {
59
68
  if (psp === 'amadeus_checkout')
@@ -70,12 +79,16 @@ export function preparedSendWindowMs(psp, url) {
70
79
  }
71
80
  if (psp === WIX_PAYMENTS_PSP)
72
81
  return isWixPaymentsTokenizeUrl(url) ? WIX_PAYMENTS_SEND_WINDOW_MS : null;
82
+ if (psp === HIPAY_PSP)
83
+ return isHipayTokenUrl(url) ? HIPAY_SEND_WINDOW_MS : null;
73
84
  return null;
74
85
  }
75
86
  /** The processor a prepared request's send window names in its events, or null when it has none. */
76
87
  export function sendWindowProcessor(psp) {
77
88
  if (psp === WIX_PAYMENTS_PSP)
78
89
  return 'wix_payments';
90
+ if (psp === HIPAY_PSP)
91
+ return 'hipay';
79
92
  return psp === 'amadeus_checkout' ? 'amadeus' : psp === SECURE_ACCEPTANCE_PSP ? 'secure_acceptance' : null;
80
93
  }
81
94
  /** The send window for a prepared Amadeus SDK add, or null: preparedSendWindowMs for Amadeus alone. */
package/dist/buy.d.ts CHANGED
@@ -16,8 +16,8 @@
16
16
  */
17
17
  import { ApiModule, type ApiTransport } from './api-module.js';
18
18
  import { AgentcardError } from './voice-types.js';
19
- /** Where a turn stands. `needs_input` is the common case: a question, or a cart waiting for its confirm; `needs_approval` is a confirm under `approval: 'voice'` waiting for the person's verdict on `authorization_id`. */
20
- export type BuyStatus = 'needs_input' | 'needs_approval' | 'order_placed' | 'partially_placed' | 'declined';
19
+ /** Where a turn stands. `needs_input` is the common case: a question, or a cart waiting for its confirm; `needs_approval` is a confirm under `approval: 'voice'` waiting for the person's verdict on `authorization_id`; `approved_not_charged` is a sandbox browser checkout the person approved, where the card was never sent to the store. */
20
+ export type BuyStatus = 'needs_input' | 'needs_approval' | 'order_placed' | 'partially_placed' | 'declined' | 'approved_not_charged';
21
21
  /** How a confirm that pays with the person's own card asks them: `page`, Agentcard's approval page on `approval_url` (the default); `voice`, the key word inside your app on the purchase's own `authorization_id`. */
22
22
  export type BuyApproval = 'voice' | 'page';
23
23
  /** The two ways a purchase approved by voice ends without an order: the person declined it, or the approval ran out before a confirm placed against it. */
@@ -129,6 +129,75 @@ export interface BuyUnmatched {
129
129
  detail: string | null;
130
130
  at: string;
131
131
  }
132
+ /**
133
+ * The step your own agent takes next when the purchase agent hands it a matched offer's checkout
134
+ * (your client's browser mode is `agent`): open `buy_url` itself, in the browser that will check out,
135
+ * attach this SDK before the payment form, and create the checkout authorization the hint describes
136
+ * before `expires_at`. `reply` says the same in words.
137
+ */
138
+ export interface BuyCheckoutHandoff {
139
+ type: 'open_offer';
140
+ buy_url: string;
141
+ store: {
142
+ domain: string;
143
+ name?: string;
144
+ };
145
+ product: {
146
+ id: string;
147
+ title: string;
148
+ };
149
+ quantity: number;
150
+ /** The catalog's price for the product and its `currency`, as the API sends it (see the Purchase API reference). */
151
+ listed_price: BuyApiAmount;
152
+ checkout: {
153
+ /** The `merchant`, `currency` and `checkout_origin` the checkout authorization names, and the most it may authorize, as the API sends them. */
154
+ authorization_hint: BuyApiAmount & {
155
+ merchant: string;
156
+ checkout_origin: string;
157
+ };
158
+ sdk: string;
159
+ };
160
+ rules: string[];
161
+ expires_at: string;
162
+ }
163
+ /**
164
+ * An amount object as the Purchase API sends it inside a browser checkout step: its `currency`, and its
165
+ * amount fields under the API's own names, passed through untyped. The SDK's one word for an amount is
166
+ * `amount`, and these objects reach a caller exactly as the API wrote them.
167
+ */
168
+ export type BuyApiAmount = {
169
+ currency: string;
170
+ } & Record<string, unknown>;
171
+ /** An offer handed to your own agent, and how far it got (`link`, `product`, `cart`, `payment`, `approved`), read from the checkout authorization your SDK created. */
172
+ export interface BuyOpenOffer {
173
+ type: 'open_offer';
174
+ buy_url: string;
175
+ store_domain: string;
176
+ product_id: string;
177
+ /** The catalog's price for the product, as the API sends it. */
178
+ listed_price: BuyApiAmount;
179
+ expires_at: string;
180
+ stage: string;
181
+ authorization_id: string | null;
182
+ authorization_status: string | null;
183
+ /** The store's charge on that authorization: `authorized` (a hold), `captured`, or null. */
184
+ charged_kind: string | null;
185
+ }
186
+ /** A checkout Agentcard ran in its own browser at a store, and where it stands (`at_payment`, `awaiting_approval`, `approved_not_charged`, `placed`, `declined`, `expired`, `failed`, `released`). */
187
+ export interface BuyBrowserCheckout {
188
+ checkout_id: string;
189
+ status: string;
190
+ store: string;
191
+ store_url: string | null;
192
+ total_cents: number | null;
193
+ currency: string;
194
+ cart_hash: string;
195
+ approval_url: string | null;
196
+ authorization_id: string | null;
197
+ order_id: string | null;
198
+ failure_reason: string | null;
199
+ updated_at: string;
200
+ }
132
201
  /**
133
202
  * One turn's answer. Branch on `status`; read `cart.hash` for the confirm
134
203
  * that follows; never parse `reply`. `error_code: 'turn_timeout'` means the
@@ -149,6 +218,8 @@ export interface BuyTurn extends BuyOutcome {
149
218
  catalog: BuyCatalog | null;
150
219
  error_code: string | null;
151
220
  unmatched: BuyUnmatched[];
221
+ /** The step your own agent takes next, when the purchase agent hands it the checkout; null otherwise. */
222
+ checkout_handoff: BuyCheckoutHandoff | null;
152
223
  }
153
224
  /** A turn the API declined: `status: 'declined'` with the code that says why. `isBuyDeclined` narrows to it. */
154
225
  export type BuyDeclined = BuyTurn & {
@@ -218,6 +289,10 @@ export interface BuyConversation {
218
289
  orders: BuyOrder[];
219
290
  carts: BuyCart[];
220
291
  unmatched: BuyUnmatched[];
292
+ /** The offers handed to your own agent on this conversation, and how far each got. */
293
+ open_offers: BuyOpenOffer[];
294
+ /** Every checkout Agentcard ran in its own browser on this conversation, and where each stands. */
295
+ browser_checkouts: BuyBrowserCheckout[];
221
296
  created_at: string;
222
297
  updated_at: string;
223
298
  }
@@ -263,6 +338,13 @@ export interface BuyConfirmInput {
263
338
  * `voice_declined` or `approval_expired`. Sent only when named.
264
339
  */
265
340
  approval?: BuyApproval;
341
+ /**
342
+ * On a repeat confirm of a browser cart waiting for the person's approval,
343
+ * hold up to 25 seconds and answer the moment they decide, instead of
344
+ * answering "still waiting" at once. The first confirm never holds: it
345
+ * answers with the `approval_url` straight away. Sent only when true.
346
+ */
347
+ wait?: boolean;
266
348
  /** How long this call waits for the turn. Default 130 seconds. */
267
349
  timeout_ms?: number;
268
350
  }
package/dist/buy.js CHANGED
@@ -89,6 +89,17 @@ function cartsOf(v) {
89
89
  }
90
90
  const str = (v) => (typeof v === 'string' ? v : null);
91
91
  const num = (v) => (typeof v === 'number' ? v : null);
92
+ const isRecord = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
93
+ /** The step this version knows, `open_offer`; an answer with none, or with a kind of step a later API adds, reads as null. */
94
+ function checkoutHandoffOf(v) {
95
+ return isRecord(v) && v.type === 'open_offer' && typeof v.buy_url === 'string' ? v : null;
96
+ }
97
+ function openOffersOf(v) {
98
+ return Array.isArray(v) ? v.filter((o) => isRecord(o) && o.type === 'open_offer' && typeof o.buy_url === 'string') : [];
99
+ }
100
+ function browserCheckoutsOf(v) {
101
+ return Array.isArray(v) ? v.filter((b) => isRecord(b) && typeof b.checkout_id === 'string') : [];
102
+ }
92
103
  function paymentSourceOf(v) {
93
104
  if (!v || typeof v !== 'object')
94
105
  return null;
@@ -102,7 +113,7 @@ function outcomeOf(a) {
102
113
  authorization_id: str(a.authorization_id),
103
114
  };
104
115
  }
105
- const STATUSES = new Set(['needs_input', 'needs_approval', 'order_placed', 'partially_placed', 'declined']);
116
+ const STATUSES = new Set(['needs_input', 'needs_approval', 'order_placed', 'partially_placed', 'declined', 'approved_not_charged']);
106
117
  function statusOf(v) { return typeof v === 'string' && STATUSES.has(v) ? v : 'needs_input'; }
107
118
  function placementsOf(v) {
108
119
  if (!Array.isArray(v))
@@ -125,6 +136,7 @@ export function turnOf(body) {
125
136
  catalog: body.catalog && typeof body.catalog === 'object' ? body.catalog : null,
126
137
  error_code: str(body.error_code),
127
138
  unmatched: Array.isArray(body.unmatched) ? body.unmatched : [],
139
+ checkout_handoff: checkoutHandoffOf(body.checkout_handoff),
128
140
  ...outcomeOf(body),
129
141
  };
130
142
  }
@@ -138,6 +150,8 @@ function conversationOf(body) {
138
150
  orders: Array.isArray(body.orders) ? body.orders : [],
139
151
  carts: cartsOf(body.carts),
140
152
  unmatched: Array.isArray(body.unmatched) ? body.unmatched : [],
153
+ open_offers: openOffersOf(body.open_offers),
154
+ browser_checkouts: browserCheckoutsOf(body.browser_checkouts),
141
155
  created_at: str(body.created_at) ?? '',
142
156
  updated_at: str(body.updated_at) ?? '',
143
157
  };
@@ -231,6 +245,7 @@ export class BuyModule extends ApiModule {
231
245
  ...(input.delivery_address ? { delivery_address: input.delivery_address } : {}),
232
246
  ...(input.payment_source ? { payment_source: input.payment_source } : {}),
233
247
  ...(input.approval ? { approval: input.approval } : {}),
248
+ ...(input.wait === true ? { wait: true } : {}),
234
249
  };
235
250
  return this.turn(body, input.timeout_ms);
236
251
  }
@@ -41,7 +41,8 @@ export declare class CardEndpointFactsWatch {
41
41
  tracks(key: string): boolean;
42
42
  /**
43
43
  * A request left. Recorded only when it is a document, an XHR or a fetch (or of a type
44
- * the adapter could not name) on the endpoint origin of a profile with facts.
44
+ * the adapter could not name) on the origin a profile's record comes from: its facts
45
+ * origin (D-EDGE's payment hub), else its endpoint origin (FastSpring's popup).
45
46
  */
46
47
  sent(key: string, request: {
47
48
  method: string;
@@ -6,14 +6,22 @@
6
6
  // the popup document (script#viewdata) and replaces it with every JSON answer it reads
7
7
  // from the session URL, tax included. From attach on, the adapters tell this watch about
8
8
  // every request that leaves, every answer and every failure; it keeps, read-only, the
9
- // documents, XHRs and fetches on the endpoint origin of a profile with facts, and nothing
10
- // else, a bounded number of them, the busiest checkout session's oldest going first; what a
11
- // checkout lost to that bound can still refuse its capture. When that profile's card request
12
- // pauses, capture() waits briefly for any request on the same checkout session that left
13
- // before the pause, then takes the order record whose answer finished last, the one the page
14
- // shows, cut by payment-core to the values its reader reads (the total with tax, the
15
- // currency, test mode and the renewal marks). The create carries that cut record; the client
16
- // and the API read it with the same rules.
9
+ // documents, XHRs and fetches on the origin a profile's record comes from (its endpoint's,
10
+ // or the one its facts name), and nothing else, a bounded number of them, the busiest
11
+ // checkout session's oldest going first; what a checkout lost to that bound can still refuse
12
+ // its capture. When that profile's card request pauses, capture() waits briefly for any
13
+ // request on the same checkout session that left before the pause, then takes the order
14
+ // record whose answer finished last, the one the page shows, cut by payment-core to the
15
+ // values its reader reads (the total with tax, the currency, test mode and the renewal
16
+ // marks). The create carries that cut record; the client and the API read it with the same
17
+ // rules.
18
+ //
19
+ // D-EDGE's card frame posts the card from card.secure-hotel-booking.com, and the payment hub
20
+ // around it loads the booking from pay.secure-hotel-booking.com (POST /load/<session id>):
21
+ // that answer is the record, and the frame's card post and the hub's own calls are one
22
+ // checkout session by the session id they both carry (cardEndpointFactsSession). Its 3-D
23
+ // Secure browser data (/setDevice) is an inert call; a voucher, points or a bank chosen in
24
+ // the hub after /load leaves the booking unknown, and capture() refuses.
17
25
  //
18
26
  // A record holds the order only if the page changed nothing after it. payment-core's
19
27
  // merchantCardEndpointFactsRole names what each request is to the card request: a call
@@ -64,7 +72,8 @@ export class CardEndpointFactsWatch {
64
72
  tracks(key) { return this.exchanges.has(key); }
65
73
  /**
66
74
  * A request left. Recorded only when it is a document, an XHR or a fetch (or of a type
67
- * the adapter could not name) on the endpoint origin of a profile with facts.
75
+ * the adapter could not name) on the origin a profile's record comes from: its facts
76
+ * origin (D-EDGE's payment hub), else its endpoint origin (FastSpring's popup).
68
77
  */
69
78
  sent(key, request) {
70
79
  if (this.origins.length === 0 || typeof request.url !== 'string' || typeof request.method !== 'string')
@@ -49,6 +49,11 @@ export interface CardEndpointProfile {
49
49
  */
50
50
  readonly facts?: {
51
51
  readonly reader: string;
52
+ /**
53
+ * Null when the record's answers come from the endpoint origin; else the origin they come from, another host of
54
+ * the endpoint's site (D-EDGE's payment hub, beside the card frame that posts the card).
55
+ */
56
+ readonly origin: string | null;
52
57
  readonly responses: readonly {
53
58
  readonly method: string;
54
59
  readonly path: string;
@@ -101,9 +106,11 @@ export declare const CARD_ENDPOINT_MODE: "card_endpoint";
101
106
  * (`detail` says what, naming fields, never values).
102
107
  * - `facts_unavailable`: the merchant keeps its own record of the order (profile.facts),
103
108
  * and this runtime read none for this checkout, or one it cannot place.
104
- * - `facts_refused`: the merchant's own record says the order renews on a schedule, or
105
- * that its checkout is in test mode. A total or currency that differs from the agent's
106
- * is an AmountMismatchError instead.
109
+ * - `facts_refused`: the merchant's own record says the order renews on a schedule, that
110
+ * the merchant charges the card later (a hotel rate that only guarantees the booking, a
111
+ * charge on a later date, part of the room left for the stay), or that its checkout is
112
+ * in test mode. A total or currency that differs from the agent's is an
113
+ * AmountMismatchError instead.
107
114
  *
108
115
  * `not_enabled`, `amount_required`, `approval_required` and `preparation_unsupported`
109
116
  * answer the same way for every request on the attachment, so the adapters stop asking;
@@ -138,7 +145,10 @@ export declare function cardEndpointResendFor(url: string, method: string): Card
138
145
  * CORS preflight to the same URL defers), so routing a URL is never a decision to pause it.
139
146
  */
140
147
  export declare function cardEndpointRouted(url: string): boolean;
141
- /** The origins of the profiles whose merchant keeps its own record of the order: the only responses card-endpoint-facts.ts records. */
148
+ /**
149
+ * The origins the record of each profile whose merchant keeps one comes from (its facts.origin, else its
150
+ * endpoint): the only responses card-endpoint-facts.ts records.
151
+ */
142
152
  export declare function cardEndpointFactsOrigins(): readonly string[];
143
153
  /**
144
154
  * The checkout session a request on the endpoint of a profile with facts belongs to: the
@@ -148,6 +158,12 @@ export declare function cardEndpointFactsOrigins(): readonly string[];
148
158
  * request. card-endpoint-facts.ts counts what it keeps, and what a checkout lost, by it.
149
159
  * When the paths of two profiles on one origin both fit, the shorter session is the one:
150
160
  * every request on a card request's session then gets the card request's own.
161
+ *
162
+ * For a profile whose record comes from its own origin (D-EDGE's payment hub beside the card
163
+ * frame), a request on that origin belongs to the session its last path segment names, the
164
+ * session id every hub URL ends in, and the profile's card request to the session its bind
165
+ * value names, both written on that origin ('https://pay.secure-hotel-booking.com/<id>'): the
166
+ * card post and the hub's own calls are one checkout, as payment-core's role rules read them.
151
167
  */
152
168
  export declare function cardEndpointFactsSession(url: string): string | null;
153
169
  /** Whether this build's copy of the profile takes payments (enabled, nothing pending, a reviewed HAR, not recurring). */
@@ -46,7 +46,9 @@
46
46
  // that sends the typed card again outside the card request is aborted in every status
47
47
  // (cardEndpointResendFor). And the merchant's own record of the order, which its page
48
48
  // reads from the endpoint origin, is recorded read-only (card-endpoint-facts.ts) and sent
49
- // with the create, so the agent's amount is held to the merchant's total.
49
+ // with the create, so the agent's amount is held to the merchant's total. D-EDGE's card
50
+ // frame is read the same way from the payment hub around it, whose booking record says
51
+ // what the card pays now and whether the hotel charges it later.
50
52
  //
51
53
  // The endpoint URL (it carries cart, booking and instance ids), the paused body, the
52
54
  // session and the answer are cardholder data even with no real card in them: no
@@ -121,9 +123,23 @@ const ROUTED_METHODS = [...new Set(PROFILES.flatMap((profile) => [profile.method
121
123
  export function cardEndpointRouted(url) {
122
124
  return ROUTED_METHODS.some((method) => cardEndpointFor(url, method) !== null || cardEndpointResendFor(url, method) !== null);
123
125
  }
124
- /** The origins of the profiles whose merchant keeps its own record of the order: the only responses card-endpoint-facts.ts records. */
126
+ /**
127
+ * The origins the record of each profile whose merchant keeps one comes from (its facts.origin, else its
128
+ * endpoint): the only responses card-endpoint-facts.ts records.
129
+ */
125
130
  export function cardEndpointFactsOrigins() {
126
- return [...new Set(PROFILES.filter((profile) => profile.facts).map((profile) => profile.endpoint))];
131
+ return [...new Set(PROFILES.filter((profile) => profile.facts).map((profile) => profile.facts.origin ?? profile.endpoint))];
132
+ }
133
+ /**
134
+ * The `:name` segment of a card path template a URL path gives, when the path has the template's
135
+ * shape (as many segments, every literal one equal); else null.
136
+ */
137
+ function boundSegment(template, segments, name) {
138
+ const want = template.split('/');
139
+ if (want.length !== segments.length || !want.every((part, i) => (part.startsWith(':') ? segments[i] !== '' : part === segments[i])))
140
+ return null;
141
+ const at = want.indexOf(`:${name}`);
142
+ return at < 1 ? null : segments[at];
127
143
  }
128
144
  /**
129
145
  * The checkout session a request on the endpoint of a profile with facts belongs to: the
@@ -133,6 +149,12 @@ export function cardEndpointFactsOrigins() {
133
149
  * request. card-endpoint-facts.ts counts what it keeps, and what a checkout lost, by it.
134
150
  * When the paths of two profiles on one origin both fit, the shorter session is the one:
135
151
  * every request on a card request's session then gets the card request's own.
152
+ *
153
+ * For a profile whose record comes from its own origin (D-EDGE's payment hub beside the card
154
+ * frame), a request on that origin belongs to the session its last path segment names, the
155
+ * session id every hub URL ends in, and the profile's card request to the session its bind
156
+ * value names, both written on that origin ('https://pay.secure-hotel-booking.com/<id>'): the
157
+ * card post and the hub's own calls are one checkout, as payment-core's role rules read them.
136
158
  */
137
159
  export function cardEndpointFactsSession(url) {
138
160
  let parsed;
@@ -143,9 +165,21 @@ export function cardEndpointFactsSession(url) {
143
165
  return null;
144
166
  }
145
167
  const segments = parsed.pathname.split('/');
168
+ for (const profile of PROFILES) {
169
+ const origin = profile.facts?.origin;
170
+ if (!origin)
171
+ continue;
172
+ if (parsed.origin === origin) {
173
+ const last = segments[segments.length - 1];
174
+ return last ? `${origin}/${last}` : null;
175
+ }
176
+ const bound = parsed.origin === profile.endpoint ? boundSegment(profile.path, segments, profile.facts.bind) : null;
177
+ if (bound)
178
+ return `${origin}/${bound}`;
179
+ }
146
180
  let shortest = null;
147
181
  for (const profile of PROFILES) {
148
- if (!profile.facts || profile.endpoint !== parsed.origin)
182
+ if (!profile.facts || profile.facts.origin || profile.endpoint !== parsed.origin)
149
183
  continue;
150
184
  const template = profile.path.split('/');
151
185
  const at = template.indexOf(`:${profile.facts.bind}`);