@agent-cards/checkout 0.19.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.
- package/README.md +6 -753
- package/cdp.d.ts +1 -0
- package/cdp.js +2 -0
- package/index.d.ts +1 -0
- package/index.js +2 -0
- package/package.json +33 -33
- package/playwright.d.ts +1 -0
- package/playwright.js +2 -0
- package/preflight.d.ts +1 -0
- package/preflight.js +2 -0
- package/CHANGELOG.md +0 -132
- package/PREFLIGHT.md +0 -312
- package/dist/adyen-merchant-hosted.generated.d.ts +0 -277
- package/dist/adyen-merchant-hosted.generated.js +0 -1902
- package/dist/adyen.generated.d.ts +0 -24
- package/dist/adyen.generated.js +0 -64
- package/dist/attachment.d.ts +0 -11
- package/dist/attachment.js +0 -50
- package/dist/braintree.d.ts +0 -2
- package/dist/braintree.generated.d.ts +0 -10
- package/dist/braintree.generated.js +0 -302
- package/dist/braintree.js +0 -2
- package/dist/builtin-registry.generated.d.ts +0 -2
- package/dist/builtin-registry.generated.js +0 -1
- package/dist/card-fields.generated.d.ts +0 -3
- package/dist/card-fields.generated.js +0 -46
- package/dist/cdp.d.ts +0 -192
- package/dist/cdp.js +0 -2393
- package/dist/checkout-com.generated.d.ts +0 -4
- package/dist/checkout-com.generated.js +0 -183
- package/dist/client.d.ts +0 -849
- package/dist/client.js +0 -1754
- package/dist/cse-body.d.ts +0 -25
- package/dist/cse-body.js +0 -41
- package/dist/fiserv.d.ts +0 -65
- package/dist/fiserv.generated.d.ts +0 -73
- package/dist/fiserv.generated.js +0 -830
- package/dist/fiserv.js +0 -104
- package/dist/hosted-form.d.ts +0 -44
- package/dist/hosted-form.js +0 -78
- package/dist/index.d.ts +0 -18
- package/dist/index.js +0 -10
- package/dist/lifecycle.d.ts +0 -179
- package/dist/lifecycle.js +0 -395
- package/dist/mercado-checkout.d.ts +0 -20
- package/dist/mercado-checkout.generated.d.ts +0 -52
- package/dist/mercado-checkout.generated.js +0 -198
- package/dist/mercado-checkout.js +0 -108
- package/dist/merchant-handoff.d.ts +0 -54
- package/dist/merchant-handoff.js +0 -100
- package/dist/merchant-hosted.d.ts +0 -140
- package/dist/merchant-hosted.js +0 -170
- package/dist/merchant-total-watch.d.ts +0 -115
- package/dist/merchant-total-watch.js +0 -268
- package/dist/merchant-total.d.ts +0 -257
- package/dist/merchant-total.js +0 -383
- package/dist/owned-shop.generated.d.ts +0 -24
- package/dist/owned-shop.generated.js +0 -108
- package/dist/paysafe.generated.d.ts +0 -12
- package/dist/paysafe.generated.js +0 -87
- package/dist/playwright.d.ts +0 -3
- package/dist/playwright.js +0 -3
- package/dist/pre-claim.d.ts +0 -123
- package/dist/pre-claim.js +0 -386
- package/dist/preflight-capabilities.generated.d.ts +0 -1253
- package/dist/preflight-capabilities.generated.js +0 -1929
- package/dist/preflight-catalog.json +0 -4727
- package/dist/preflight-playwright.d.ts +0 -34
- package/dist/preflight-playwright.js +0 -355
- package/dist/preflight-schemas.json +0 -1122
- package/dist/preflight.d.ts +0 -1
- package/dist/preflight.generated.d.ts +0 -1965
- package/dist/preflight.generated.js +0 -570
- package/dist/preflight.js +0 -2
- package/dist/preparation.d.ts +0 -38
- package/dist/preparation.js +0 -191
- package/dist/prepared-processor.d.ts +0 -43
- package/dist/prepared-processor.js +0 -172
- package/dist/recurly.generated.d.ts +0 -1
- package/dist/recurly.generated.js +0 -87
- package/dist/registry.d.ts +0 -121
- package/dist/registry.js +0 -310
- package/dist/spreedly.generated.d.ts +0 -10
- package/dist/spreedly.generated.js +0 -332
- package/dist/stripe-checkout.d.ts +0 -81
- package/dist/stripe-checkout.generated.d.ts +0 -82
- package/dist/stripe-checkout.generated.js +0 -1093
- package/dist/stripe-checkout.js +0 -140
- package/dist/substitute.d.ts +0 -38
- package/dist/substitute.js +0 -23
- package/dist/substitutions.generated.d.ts +0 -11
- package/dist/substitutions.generated.js +0 -818
- package/examples/existing-browser.mjs +0 -63
- package/examples/preflight/classify-direct.mjs +0 -21
- package/examples/preflight/classify-kernel.mjs +0 -30
- package/examples/preflight/inspect-browser.mjs +0 -44
- package/examples/preflight/kernel-native/README.md +0 -112
- package/examples/preflight/kernel-native/documented-adapters.json +0 -113
- package/examples/preflight/kernel-native/inventory.json +0 -233
- package/examples/preflight/kernel-native/qualification.mjs +0 -182
- package/examples/preflight/kernel-profile.empty.json +0 -11
- package/examples/preflight/mollie-hosted.observations.json +0 -23
- package/examples/preflight/mollie-hosted.result.json +0 -103
- package/examples/preflight/stripe-script.direct.result.json +0 -92
- package/examples/preflight/stripe-script.observations.json +0 -16
- package/examples/preflight/stripe-script.result.json +0 -87
package/dist/merchant-total.d.ts
DELETED
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
/** One merchant request this browser saw answered, as payment-core reads it. */
|
|
2
|
-
export interface MerchantExchange {
|
|
3
|
-
method: string;
|
|
4
|
-
/** The request's full URL, serialized. */
|
|
5
|
-
url: string;
|
|
6
|
-
/** The response's HTTP status. */
|
|
7
|
-
status: number;
|
|
8
|
-
/** When the response finished: ms since the epoch, this browser's clock. */
|
|
9
|
-
completedAt: number;
|
|
10
|
-
/** When the request left, same clock. */
|
|
11
|
-
sentAt?: number;
|
|
12
|
-
/** An opaque id of the tab and document the request came from. */
|
|
13
|
-
page?: string;
|
|
14
|
-
/** The request's JSON text. */
|
|
15
|
-
requestBody?: string;
|
|
16
|
-
/** The response text. */
|
|
17
|
-
body: string;
|
|
18
|
-
}
|
|
19
|
-
/** The paused card request the total is read for (payment-core MerchantAmountCard). */
|
|
20
|
-
export interface MerchantAmountCard {
|
|
21
|
-
url: string;
|
|
22
|
-
method: string;
|
|
23
|
-
body: string;
|
|
24
|
-
pausedAt: number;
|
|
25
|
-
checkoutOrigin?: string;
|
|
26
|
-
page?: string;
|
|
27
|
-
/** A sandbox declaration's endpoint: its own responses price it. */
|
|
28
|
-
declaredEndpoint?: string;
|
|
29
|
-
}
|
|
30
|
-
export type MerchantAmountStage = 'source' | 'charged';
|
|
31
|
-
type Refusal = {
|
|
32
|
-
ok: false;
|
|
33
|
-
code: string;
|
|
34
|
-
reason: string;
|
|
35
|
-
};
|
|
36
|
-
type Reading = {
|
|
37
|
-
ok: true;
|
|
38
|
-
amount: {
|
|
39
|
-
amount: number;
|
|
40
|
-
currency: string;
|
|
41
|
-
};
|
|
42
|
-
source: Record<string, unknown>;
|
|
43
|
-
} | Refusal;
|
|
44
|
-
/**
|
|
45
|
-
* A priced profile whose requests the log records: its id alone reads them at the profile's
|
|
46
|
-
* reviewed origins; with `declaredEndpoint` (this client's sandbox declaration for the
|
|
47
|
-
* profile, VaultClientOptions.sandboxMerchants) at that endpoint's origin instead.
|
|
48
|
-
*/
|
|
49
|
-
export type MerchantTotalScope = string | {
|
|
50
|
-
id: string;
|
|
51
|
-
declaredEndpoint?: string;
|
|
52
|
-
};
|
|
53
|
-
/** How many exchanges one read takes: the API's limit on merchant_total and merchant_charge. */
|
|
54
|
-
export declare const MERCHANT_EXCHANGES_MAX: number;
|
|
55
|
-
/** The exchanges one read of a stage sends: its shapes at its origin, projected, the newest MERCHANT_EXCHANGES_MAX. */
|
|
56
|
-
export declare const merchantAmountExchanges: (profileId: string, stage: MerchantAmountStage, input: {
|
|
57
|
-
card: MerchantAmountCard;
|
|
58
|
-
responses: MerchantExchange[];
|
|
59
|
-
}) => {
|
|
60
|
-
ok: true;
|
|
61
|
-
responses: MerchantExchange[];
|
|
62
|
-
} | Refusal;
|
|
63
|
-
/** The reading the API makes of the merchant's total for a paused card request, its total as `{amount, currency}`. */
|
|
64
|
-
export declare const merchantAmountFromResponses: (profileId: string, input: {
|
|
65
|
-
card: MerchantAmountCard;
|
|
66
|
-
responses: MerchantExchange[];
|
|
67
|
-
}) => Reading;
|
|
68
|
-
/** Whether a reviewed profile is priced by the merchant's own responses (it names an amount source). */
|
|
69
|
-
export declare function merchantProfilePricedBySource(profileId: string): boolean;
|
|
70
|
-
/** What the adapter knows of a request when it leaves. */
|
|
71
|
-
export interface MerchantRequestSeen {
|
|
72
|
-
method: string;
|
|
73
|
-
url: string;
|
|
74
|
-
/** The request's JSON text, when it has one. */
|
|
75
|
-
requestBody?: string;
|
|
76
|
-
/** An opaque id of the tab and document the request came from. */
|
|
77
|
-
page?: string;
|
|
78
|
-
}
|
|
79
|
-
/** A response body past this many characters is recorded as unreadable (''). */
|
|
80
|
-
export declare const MERCHANT_BODY_READ_MAX: number;
|
|
81
|
-
/**
|
|
82
|
-
* The most request and response text the log holds at once, in characters: room for the
|
|
83
|
-
* MERCHANT_EXCHANGES_MAX newest exchanges one read sends at MERCHANT_BODY_READ_MAX each.
|
|
84
|
-
* Past it the oldest answered exchanges go first; a request still waiting for its answer
|
|
85
|
-
* goes only when waiting ones alone fill it, and reads refuse as stale until it ends.
|
|
86
|
-
*/
|
|
87
|
-
export declare const MERCHANT_LOG_TEXT_MAX: number;
|
|
88
|
-
/**
|
|
89
|
-
* Every request this browser sent that a reviewed profile's amount rules could read, and
|
|
90
|
-
* its answer. The adapters feed it from their network events: sent() when a request
|
|
91
|
-
* leaves (recorded only when some profile's amount stage reads its shape at an origin that
|
|
92
|
-
* stage reads, payment-core merchantAmountRequestStages, so another shop's /checkout is
|
|
93
|
-
* never read or held), answered() with the status and the time the response finished,
|
|
94
|
-
* then read() with the response text (null when it could not be read), or failed(). It
|
|
95
|
-
* holds at most LOG_MAX requests and MERCHANT_LOG_TEXT_MAX characters of their text.
|
|
96
|
-
* Read-only: nothing here pauses, changes or answers a request.
|
|
97
|
-
*/
|
|
98
|
-
export declare class MerchantExchangeLog {
|
|
99
|
-
/** The reviewed profiles whose amount rules decide what is recorded, and where (MerchantTotalScope). */
|
|
100
|
-
private readonly profiles;
|
|
101
|
-
private readonly now;
|
|
102
|
-
private readonly textMax;
|
|
103
|
-
/** Told after each answer the log reads (MerchantTotalWatch reports a confirmation that came late). */
|
|
104
|
-
private readonly onRead?;
|
|
105
|
-
private readonly entries;
|
|
106
|
-
private readonly waiters;
|
|
107
|
-
private text;
|
|
108
|
-
/**
|
|
109
|
-
* The requests the log dropped while they still waited for their answer, by key, with when
|
|
110
|
-
* they left and the stages they are read for (the newest LOST_MAX). Each one can still move
|
|
111
|
-
* what its stage reads, so every wait for that stage counts it as unanswered until the
|
|
112
|
-
* adapter reports it failed or answered.
|
|
113
|
-
*/
|
|
114
|
-
private readonly lostWaiting;
|
|
115
|
-
/** More requests were lost at once than lostWaiting holds: no read of a total is trusted again. */
|
|
116
|
-
private lostOverflow;
|
|
117
|
-
/**
|
|
118
|
-
* When the newest answer came to a dropped request that is read for the total (a 'source'
|
|
119
|
-
* stage request), or null. The log never read that answer, which could carry another total,
|
|
120
|
-
* so every read of the total refuses as stale until a total request the page sent after it
|
|
121
|
-
* answers: the merchant's later word on its total, read in full. An answer of any other
|
|
122
|
-
* stage (a payment's) says nothing about the total and never clears it.
|
|
123
|
-
*/
|
|
124
|
-
private unreadSourceAnswerAt;
|
|
125
|
-
constructor(
|
|
126
|
-
/** The reviewed profiles whose amount rules decide what is recorded, and where (MerchantTotalScope). */
|
|
127
|
-
profiles: () => readonly MerchantTotalScope[], now?: () => number, textMax?: number,
|
|
128
|
-
/** Told after each answer the log reads (MerchantTotalWatch reports a confirmation that came late). */
|
|
129
|
-
onRead?: (() => void) | undefined);
|
|
130
|
-
/** A request left; true when it is recorded. */
|
|
131
|
-
sent(key: string, request: MerchantRequestSeen, sentAt?: number): boolean;
|
|
132
|
-
/** Forgets one request and its text. */
|
|
133
|
-
private drop;
|
|
134
|
-
/**
|
|
135
|
-
* Past LOG_MAX requests or MERCHANT_LOG_TEXT_MAX characters, drops the oldest answered
|
|
136
|
-
* exchanges first. Only when the requests still waiting fill the log on their own do the
|
|
137
|
-
* oldest of them go too: the log keeps only their keys (lostWaiting), so it stays bounded
|
|
138
|
-
* while every read still waits for them and refuses the total as unknown.
|
|
139
|
-
*/
|
|
140
|
-
private trim;
|
|
141
|
-
/** Whether a request is recorded (its answer is then worth reading). */
|
|
142
|
-
tracks(key: string): boolean;
|
|
143
|
-
/** The response's status arrived. */
|
|
144
|
-
answered(key: string, status: number): void;
|
|
145
|
-
/** The page a request's answer belongs to, when only its answer says (a document the frame then shows). */
|
|
146
|
-
repage(key: string, page: string): void;
|
|
147
|
-
/**
|
|
148
|
-
* The response finished at `completedAt` (taken when it finished, before its text was
|
|
149
|
-
* read), with this text, or null when it could not be read (it then reads as empty).
|
|
150
|
-
*/
|
|
151
|
-
read(key: string, body: string | null, completedAt?: number): void;
|
|
152
|
-
/** The request failed or was cancelled: it has no answer to read. */
|
|
153
|
-
failed(key: string): void;
|
|
154
|
-
/** Every answered exchange, in the order the requests left. */
|
|
155
|
-
exchanges(): MerchantExchange[];
|
|
156
|
-
/** The unanswered requests, sent at or before `before`, that can change what a stage reads for this card request. */
|
|
157
|
-
pending(profileId: string, stage: MerchantAmountStage, card: MerchantAmountCard, before: number): number;
|
|
158
|
-
/**
|
|
159
|
-
* Resolves once no request sent at or before `before` that can change what the stage
|
|
160
|
-
* reads is unanswered, with every answered exchange; rejects with a MerchantTotalWait
|
|
161
|
-
* when one is still unanswered after `timeoutMs`, or when `signal` aborts first.
|
|
162
|
-
*/
|
|
163
|
-
settle(profileId: string, stage: MerchantAmountStage, card: MerchantAmountCard, before: number, timeoutMs: number, signal?: AbortSignal): Promise<MerchantExchange[]>;
|
|
164
|
-
/** The capture the client reads for a card request that paused now, on `page`. */
|
|
165
|
-
capture(pausedAt: number, page: string | undefined, waitMs?: number): MerchantTotalCapture;
|
|
166
|
-
private changed;
|
|
167
|
-
}
|
|
168
|
-
/** How long the SDK waits for the merchant's own requests to answer before it reads the total. */
|
|
169
|
-
export declare const MERCHANT_TOTAL_WAIT_MS = 5000;
|
|
170
|
-
/** A request that can change the total was still unanswered when the SDK had to read it. */
|
|
171
|
-
export declare class MerchantTotalWait extends Error {
|
|
172
|
-
readonly open: number;
|
|
173
|
-
constructor(open: number);
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* What an adapter hands the client for a card request priced by the merchant's total:
|
|
177
|
-
* when it paused, on which page, and the exchanges it recorded once the requests that can
|
|
178
|
-
* change the total have answered (bounded; see MerchantExchangeLog.settle). A raw CDP
|
|
179
|
-
* runtime that calls authorize() itself builds one from its own network events.
|
|
180
|
-
*/
|
|
181
|
-
export interface MerchantTotalCapture {
|
|
182
|
-
/** When the card request paused: ms since the epoch, this browser's clock. */
|
|
183
|
-
pausedAt: number;
|
|
184
|
-
/** An opaque id of the tab and document the card request came from. */
|
|
185
|
-
page?: string;
|
|
186
|
-
exchanges(profileId: string, stage: MerchantAmountStage, card: MerchantAmountCard, signal?: AbortSignal): Promise<MerchantExchange[]>;
|
|
187
|
-
}
|
|
188
|
-
/** The merchant's total a merchant-hosted approval was created at, for the check at release. */
|
|
189
|
-
export interface MerchantTotalApproved {
|
|
190
|
-
card: MerchantAmountCard;
|
|
191
|
-
/** The approved total: `amount` in the currency's smallest unit. */
|
|
192
|
-
amount: {
|
|
193
|
-
amount: number;
|
|
194
|
-
currency: string;
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
/** merchant_total on the create's wire: snake_case, as the API takes it. */
|
|
198
|
-
export declare function merchantTotalWire(pausedAt: number, page: string | undefined, responses: readonly MerchantExchange[]): {
|
|
199
|
-
responses: {
|
|
200
|
-
body: string;
|
|
201
|
-
request_body?: string | undefined;
|
|
202
|
-
page?: string | undefined;
|
|
203
|
-
sent_at?: number | undefined;
|
|
204
|
-
method: string;
|
|
205
|
-
url: string;
|
|
206
|
-
status: number;
|
|
207
|
-
completed_at: number;
|
|
208
|
-
}[];
|
|
209
|
-
page?: string | undefined;
|
|
210
|
-
paused_at: number;
|
|
211
|
-
};
|
|
212
|
-
/** One exchange on the wire. */
|
|
213
|
-
export declare function exchangeWire(exchange: MerchantExchange): {
|
|
214
|
-
body: string;
|
|
215
|
-
request_body?: string | undefined;
|
|
216
|
-
page?: string | undefined;
|
|
217
|
-
sent_at?: number | undefined;
|
|
218
|
-
method: string;
|
|
219
|
-
url: string;
|
|
220
|
-
status: number;
|
|
221
|
-
completed_at: number;
|
|
222
|
-
};
|
|
223
|
-
/**
|
|
224
|
-
* The read at release: the same reading as at the create, with the release moment as the
|
|
225
|
-
* pause, over what the log holds once the requests that can move the total have answered.
|
|
226
|
-
* null when the card request may go out; otherwise why it is held: a request still
|
|
227
|
-
* unanswered ('stale'), a reading that fails, or a total that is not the approved amount
|
|
228
|
-
* ('changed'). The API bounds the total's age when it serves the card, but never sees the
|
|
229
|
-
* responses between the pause and the release: this read does.
|
|
230
|
-
*/
|
|
231
|
-
export declare function merchantTotalAtRelease(log: MerchantExchangeLog, profileId: string, approved: MerchantTotalApproved, releasedAt: number, waitMs?: number, signal?: AbortSignal): Promise<Refusal | null>;
|
|
232
|
-
/** How long after the merchant answers the card request the SDK waits for the rest of its confirmation. */
|
|
233
|
-
export declare const MERCHANT_CHARGE_SETTLE_MS = 500;
|
|
234
|
-
/**
|
|
235
|
-
* The after-payment report (POST /v2/checkout/authorizations/:id/merchant_charge): the
|
|
236
|
-
* paused card request the create read and the merchant's answers after it that the
|
|
237
|
-
* profile's after-payment rules read, projected; null when the profile reads none, none
|
|
238
|
-
* came, or none the API's reading ties to this card request.
|
|
239
|
-
*/
|
|
240
|
-
export declare function merchantChargeReport(log: MerchantExchangeLog, profileId: string, approved: MerchantTotalApproved): {
|
|
241
|
-
request: {
|
|
242
|
-
url: string;
|
|
243
|
-
method: string;
|
|
244
|
-
body: string;
|
|
245
|
-
};
|
|
246
|
-
responses: {
|
|
247
|
-
body: string;
|
|
248
|
-
request_body?: string | undefined;
|
|
249
|
-
page?: string | undefined;
|
|
250
|
-
sent_at?: number | undefined;
|
|
251
|
-
method: string;
|
|
252
|
-
url: string;
|
|
253
|
-
status: number;
|
|
254
|
-
completed_at: number;
|
|
255
|
-
}[];
|
|
256
|
-
} | null;
|
|
257
|
-
export {};
|
package/dist/merchant-total.js
DELETED
|
@@ -1,383 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The merchant's own total, as this browser saw it (payment-core merchant-amount.js).
|
|
3
|
-
*
|
|
4
|
-
* A reviewed Adyen merchant-hosted profile's card request goes to the merchant's own
|
|
5
|
-
* server, which picks what it charges, and several merchants' card bodies name no amount
|
|
6
|
-
* at all. So a profile can name an amount source: the merchant's own checkout responses
|
|
7
|
-
* that carry the grand total. The adapters record those responses read-only from the
|
|
8
|
-
* moment they attach (they never pause, change or answer one), the client sends the ones
|
|
9
|
-
* the profile's rules read with the create, and the API refuses a payment whose agent
|
|
10
|
-
* amount is not that total. After payment the adapters report what the merchant's
|
|
11
|
-
* confirmation says it charged, and the API alerts on a higher charge.
|
|
12
|
-
*
|
|
13
|
-
* What this proves: the total is what the agent's browser session saw. It catches a
|
|
14
|
-
* mistyped amount and a cart that changed under the agent; it cannot tell a merchant's
|
|
15
|
-
* response from one the agent wrote, so every profile priced this way stays in observe.
|
|
16
|
-
*
|
|
17
|
-
* What the SDK owes payment-core (the header of merchant-amount.js): every exchange of
|
|
18
|
-
* the profile's shapes, projected, with the time it left; a wait for any such request
|
|
19
|
-
* still unanswered before it reads the total; the newest MERCHANT_EXCHANGES_MAX; and at
|
|
20
|
-
* release the same read with the release moment as the pause, the card request held
|
|
21
|
-
* unless the total is still the approved amount.
|
|
22
|
-
*/
|
|
23
|
-
import { MERCHANT_EXCHANGES_MAX as SHARED_MAX, MERCHANT_PROFILE_RECOGNIZERS as SHARED_PROFILES, merchantAmountExchanges as sharedExchanges, merchantChargedFromResponses as sharedCharged, merchantTotalFromResponses as sharedRead, merchantAmountPending as sharedPending, merchantAmountStagesOf as sharedStages, } from './adyen-merchant-hosted.generated.js';
|
|
24
|
-
/** How many exchanges one read takes: the API's limit on merchant_total and merchant_charge. */
|
|
25
|
-
export const MERCHANT_EXCHANGES_MAX = SHARED_MAX;
|
|
26
|
-
const stagesOf = sharedStages;
|
|
27
|
-
const pendingFor = sharedPending;
|
|
28
|
-
/** The exchanges one read of a stage sends: its shapes at its origin, projected, the newest MERCHANT_EXCHANGES_MAX. */
|
|
29
|
-
export const merchantAmountExchanges = sharedExchanges;
|
|
30
|
-
/** The reading the API makes of the merchant's total for a paused card request, its total as `{amount, currency}`. */
|
|
31
|
-
export const merchantAmountFromResponses = sharedRead;
|
|
32
|
-
/** Whether the API's own after-payment reading reads a charge from these exchanges for this card request. */
|
|
33
|
-
const chargeReads = (profileId, input) => sharedCharged(profileId, input).ok === true;
|
|
34
|
-
const PRICED = new Set(SHARED_PROFILES
|
|
35
|
-
.filter((profile) => profile.amountSource !== undefined && profile.amountSource !== null).map((profile) => profile.id));
|
|
36
|
-
/** Whether a reviewed profile is priced by the merchant's own responses (it names an amount source). */
|
|
37
|
-
export function merchantProfilePricedBySource(profileId) {
|
|
38
|
-
return PRICED.has(profileId);
|
|
39
|
-
}
|
|
40
|
-
/** How many dropped, still unanswered requests the log keeps the keys of; past it, every read refuses. */
|
|
41
|
-
const LOST_MAX = 4096;
|
|
42
|
-
/**
|
|
43
|
-
* The most requests the log keeps, the oldest answered one dropped first: far more than any
|
|
44
|
-
* profile's rules read. A request still waiting for its answer goes only when waiting ones
|
|
45
|
-
* alone fill the log, and the log then counts it as unanswered until it ends (see trim).
|
|
46
|
-
*/
|
|
47
|
-
const LOG_MAX = 256;
|
|
48
|
-
/** A response body past this many characters is recorded as unreadable (''). */
|
|
49
|
-
export const MERCHANT_BODY_READ_MAX = 1024 * 1024;
|
|
50
|
-
/**
|
|
51
|
-
* The most request and response text the log holds at once, in characters: room for the
|
|
52
|
-
* MERCHANT_EXCHANGES_MAX newest exchanges one read sends at MERCHANT_BODY_READ_MAX each.
|
|
53
|
-
* Past it the oldest answered exchanges go first; a request still waiting for its answer
|
|
54
|
-
* goes only when waiting ones alone fill it, and reads refuse as stale until it ends.
|
|
55
|
-
*/
|
|
56
|
-
export const MERCHANT_LOG_TEXT_MAX = MERCHANT_EXCHANGES_MAX * MERCHANT_BODY_READ_MAX;
|
|
57
|
-
const scopeId = (scope) => (typeof scope === 'string' ? scope : scope.id);
|
|
58
|
-
const entryText = (entry) => (entry.body?.length ?? 0) + (entry.requestBody?.length ?? 0);
|
|
59
|
-
/**
|
|
60
|
-
* Every request this browser sent that a reviewed profile's amount rules could read, and
|
|
61
|
-
* its answer. The adapters feed it from their network events: sent() when a request
|
|
62
|
-
* leaves (recorded only when some profile's amount stage reads its shape at an origin that
|
|
63
|
-
* stage reads, payment-core merchantAmountRequestStages, so another shop's /checkout is
|
|
64
|
-
* never read or held), answered() with the status and the time the response finished,
|
|
65
|
-
* then read() with the response text (null when it could not be read), or failed(). It
|
|
66
|
-
* holds at most LOG_MAX requests and MERCHANT_LOG_TEXT_MAX characters of their text.
|
|
67
|
-
* Read-only: nothing here pauses, changes or answers a request.
|
|
68
|
-
*/
|
|
69
|
-
export class MerchantExchangeLog {
|
|
70
|
-
profiles;
|
|
71
|
-
now;
|
|
72
|
-
textMax;
|
|
73
|
-
onRead;
|
|
74
|
-
entries = new Map();
|
|
75
|
-
waiters = new Set();
|
|
76
|
-
text = 0;
|
|
77
|
-
/**
|
|
78
|
-
* The requests the log dropped while they still waited for their answer, by key, with when
|
|
79
|
-
* they left and the stages they are read for (the newest LOST_MAX). Each one can still move
|
|
80
|
-
* what its stage reads, so every wait for that stage counts it as unanswered until the
|
|
81
|
-
* adapter reports it failed or answered.
|
|
82
|
-
*/
|
|
83
|
-
lostWaiting = new Map();
|
|
84
|
-
/** More requests were lost at once than lostWaiting holds: no read of a total is trusted again. */
|
|
85
|
-
lostOverflow = false;
|
|
86
|
-
/**
|
|
87
|
-
* When the newest answer came to a dropped request that is read for the total (a 'source'
|
|
88
|
-
* stage request), or null. The log never read that answer, which could carry another total,
|
|
89
|
-
* so every read of the total refuses as stale until a total request the page sent after it
|
|
90
|
-
* answers: the merchant's later word on its total, read in full. An answer of any other
|
|
91
|
-
* stage (a payment's) says nothing about the total and never clears it.
|
|
92
|
-
*/
|
|
93
|
-
unreadSourceAnswerAt = null;
|
|
94
|
-
constructor(
|
|
95
|
-
/** The reviewed profiles whose amount rules decide what is recorded, and where (MerchantTotalScope). */
|
|
96
|
-
profiles, now = Date.now, textMax = MERCHANT_LOG_TEXT_MAX,
|
|
97
|
-
/** Told after each answer the log reads (MerchantTotalWatch reports a confirmation that came late). */
|
|
98
|
-
onRead) {
|
|
99
|
-
this.profiles = profiles;
|
|
100
|
-
this.now = now;
|
|
101
|
-
this.textMax = textMax;
|
|
102
|
-
this.onRead = onRead;
|
|
103
|
-
}
|
|
104
|
-
/** A request left; true when it is recorded. */
|
|
105
|
-
sent(key, request, sentAt = this.now()) {
|
|
106
|
-
if (typeof request?.url !== 'string' || typeof request.method !== 'string')
|
|
107
|
-
return false;
|
|
108
|
-
const probe = { method: request.method.toUpperCase(), url: request.url,
|
|
109
|
-
...(typeof request.requestBody === 'string' ? { requestBody: request.requestBody } : {}) };
|
|
110
|
-
const stages = new Set();
|
|
111
|
-
for (const scope of this.profiles()) {
|
|
112
|
-
try {
|
|
113
|
-
for (const stage of stagesOf(scopeId(scope), probe, typeof scope === 'string' || scope.declaredEndpoint === undefined
|
|
114
|
-
? undefined : { declaredEndpoint: scope.declaredEndpoint }))
|
|
115
|
-
stages.add(stage);
|
|
116
|
-
}
|
|
117
|
-
catch { /* not a request this profile reads */ }
|
|
118
|
-
}
|
|
119
|
-
if (stages.size === 0)
|
|
120
|
-
return false;
|
|
121
|
-
this.drop(key);
|
|
122
|
-
const entry = { ...probe, ...(request.page ? { page: request.page } : {}), sentAt, stages: [...stages], settled: false, failed: false };
|
|
123
|
-
this.entries.set(key, entry);
|
|
124
|
-
this.text += entryText(entry);
|
|
125
|
-
this.trim();
|
|
126
|
-
this.changed();
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
/** Forgets one request and its text. */
|
|
130
|
-
drop(key) {
|
|
131
|
-
const entry = this.entries.get(key);
|
|
132
|
-
if (!entry)
|
|
133
|
-
return;
|
|
134
|
-
this.text -= entryText(entry);
|
|
135
|
-
this.entries.delete(key);
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Past LOG_MAX requests or MERCHANT_LOG_TEXT_MAX characters, drops the oldest answered
|
|
139
|
-
* exchanges first. Only when the requests still waiting fill the log on their own do the
|
|
140
|
-
* oldest of them go too: the log keeps only their keys (lostWaiting), so it stays bounded
|
|
141
|
-
* while every read still waits for them and refuses the total as unknown.
|
|
142
|
-
*/
|
|
143
|
-
trim() {
|
|
144
|
-
const over = () => this.entries.size > LOG_MAX || this.text > this.textMax;
|
|
145
|
-
if (!over())
|
|
146
|
-
return;
|
|
147
|
-
for (const [key, entry] of this.entries) {
|
|
148
|
-
if (!over())
|
|
149
|
-
return;
|
|
150
|
-
if (entry.settled)
|
|
151
|
-
this.drop(key);
|
|
152
|
-
}
|
|
153
|
-
for (const [key, entry] of this.entries) {
|
|
154
|
-
if (!over())
|
|
155
|
-
return;
|
|
156
|
-
this.lostWaiting.set(key, { sentAt: entry.sentAt, stages: entry.stages });
|
|
157
|
-
if (this.lostWaiting.size > LOST_MAX) {
|
|
158
|
-
this.lostWaiting.delete(this.lostWaiting.keys().next().value);
|
|
159
|
-
this.lostOverflow = true;
|
|
160
|
-
}
|
|
161
|
-
this.drop(key);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
/** Whether a request is recorded (its answer is then worth reading). */
|
|
165
|
-
tracks(key) {
|
|
166
|
-
return this.entries.has(key);
|
|
167
|
-
}
|
|
168
|
-
/** The response's status arrived. */
|
|
169
|
-
answered(key, status) {
|
|
170
|
-
const entry = this.entries.get(key);
|
|
171
|
-
if (!entry) {
|
|
172
|
-
// A request the log dropped got its answer, which the log will never read.
|
|
173
|
-
const lost = this.lostWaiting.get(key);
|
|
174
|
-
if (lost) {
|
|
175
|
-
this.lostWaiting.delete(key);
|
|
176
|
-
if (lost.stages.includes('source'))
|
|
177
|
-
this.unreadSourceAnswerAt = Math.max(this.unreadSourceAnswerAt ?? 0, this.now());
|
|
178
|
-
this.changed();
|
|
179
|
-
}
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (entry.settled || !Number.isInteger(status))
|
|
183
|
-
return;
|
|
184
|
-
entry.status = status;
|
|
185
|
-
}
|
|
186
|
-
/** The page a request's answer belongs to, when only its answer says (a document the frame then shows). */
|
|
187
|
-
repage(key, page) {
|
|
188
|
-
const entry = this.entries.get(key);
|
|
189
|
-
if (entry && !entry.settled)
|
|
190
|
-
entry.page = page;
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* The response finished at `completedAt` (taken when it finished, before its text was
|
|
194
|
-
* read), with this text, or null when it could not be read (it then reads as empty).
|
|
195
|
-
*/
|
|
196
|
-
read(key, body, completedAt = this.now()) {
|
|
197
|
-
const entry = this.entries.get(key);
|
|
198
|
-
if (!entry || entry.settled)
|
|
199
|
-
return;
|
|
200
|
-
entry.completedAt = completedAt;
|
|
201
|
-
entry.body = typeof body === 'string' && body.length <= MERCHANT_BODY_READ_MAX ? body : '';
|
|
202
|
-
this.text += entry.body.length;
|
|
203
|
-
entry.settled = true;
|
|
204
|
-
if (this.unreadSourceAnswerAt !== null && entry.stages.includes('source') && entry.sentAt > this.unreadSourceAnswerAt) {
|
|
205
|
-
this.unreadSourceAnswerAt = null;
|
|
206
|
-
}
|
|
207
|
-
this.trim();
|
|
208
|
-
this.changed();
|
|
209
|
-
try {
|
|
210
|
-
this.onRead?.();
|
|
211
|
-
}
|
|
212
|
-
catch { /* a listener never breaks the log */ }
|
|
213
|
-
}
|
|
214
|
-
/** The request failed or was cancelled: it has no answer to read. */
|
|
215
|
-
failed(key) {
|
|
216
|
-
const entry = this.entries.get(key);
|
|
217
|
-
if (!entry) {
|
|
218
|
-
// A request the log dropped failed: no answer came, so it moved nothing.
|
|
219
|
-
if (this.lostWaiting.delete(key))
|
|
220
|
-
this.changed();
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
if (entry.settled)
|
|
224
|
-
return;
|
|
225
|
-
entry.settled = true;
|
|
226
|
-
entry.failed = true;
|
|
227
|
-
this.trim();
|
|
228
|
-
this.changed();
|
|
229
|
-
}
|
|
230
|
-
/** Every answered exchange, in the order the requests left. */
|
|
231
|
-
exchanges() {
|
|
232
|
-
const out = [];
|
|
233
|
-
for (const entry of this.entries.values()) {
|
|
234
|
-
if (!entry.settled || entry.failed || entry.status === undefined || entry.completedAt === undefined)
|
|
235
|
-
continue;
|
|
236
|
-
out.push({ method: entry.method, url: entry.url, status: entry.status, completedAt: entry.completedAt, sentAt: entry.sentAt,
|
|
237
|
-
...(entry.page ? { page: entry.page } : {}), ...(entry.requestBody !== undefined ? { requestBody: entry.requestBody } : {}),
|
|
238
|
-
body: entry.body ?? '' });
|
|
239
|
-
}
|
|
240
|
-
return out;
|
|
241
|
-
}
|
|
242
|
-
/** The unanswered requests, sent at or before `before`, that can change what a stage reads for this card request. */
|
|
243
|
-
pending(profileId, stage, card, before) {
|
|
244
|
-
// What the log dropped still counts: a request of this stage still waiting, and a total it never read.
|
|
245
|
-
let count = this.lostOverflow ? 1 : 0;
|
|
246
|
-
for (const lost of this.lostWaiting.values())
|
|
247
|
-
if (lost.sentAt <= before && lost.stages.includes(stage))
|
|
248
|
-
count += 1;
|
|
249
|
-
if (stage === 'source' && this.unreadSourceAnswerAt !== null && this.unreadSourceAnswerAt <= before)
|
|
250
|
-
count += 1;
|
|
251
|
-
for (const entry of this.entries.values()) {
|
|
252
|
-
if (entry.settled || entry.sentAt > before)
|
|
253
|
-
continue;
|
|
254
|
-
try {
|
|
255
|
-
if (pendingFor(profileId, stage, card, entry))
|
|
256
|
-
count += 1;
|
|
257
|
-
}
|
|
258
|
-
catch {
|
|
259
|
-
count += 1;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
return count;
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Resolves once no request sent at or before `before` that can change what the stage
|
|
266
|
-
* reads is unanswered, with every answered exchange; rejects with a MerchantTotalWait
|
|
267
|
-
* when one is still unanswered after `timeoutMs`, or when `signal` aborts first.
|
|
268
|
-
*/
|
|
269
|
-
async settle(profileId, stage, card, before, timeoutMs, signal) {
|
|
270
|
-
const deadline = this.now() + timeoutMs;
|
|
271
|
-
for (;;) {
|
|
272
|
-
if (signal?.aborted)
|
|
273
|
-
throw signal.reason ?? new Error('aborted');
|
|
274
|
-
const open = this.pending(profileId, stage, card, before);
|
|
275
|
-
if (open === 0)
|
|
276
|
-
return this.exchanges();
|
|
277
|
-
const left = deadline - this.now();
|
|
278
|
-
if (left <= 0)
|
|
279
|
-
throw new MerchantTotalWait(open);
|
|
280
|
-
await new Promise((resolve) => {
|
|
281
|
-
const done = () => { clearTimeout(timer); this.waiters.delete(done); signal?.removeEventListener('abort', done); resolve(); };
|
|
282
|
-
const timer = setTimeout(done, Math.min(left, 250));
|
|
283
|
-
this.waiters.add(done);
|
|
284
|
-
signal?.addEventListener('abort', done, { once: true });
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
/** The capture the client reads for a card request that paused now, on `page`. */
|
|
289
|
-
capture(pausedAt, page, waitMs = MERCHANT_TOTAL_WAIT_MS) {
|
|
290
|
-
return {
|
|
291
|
-
pausedAt,
|
|
292
|
-
...(page ? { page } : {}),
|
|
293
|
-
exchanges: (profileId, stage, card, signal) => this.settle(profileId, stage, card, card.pausedAt, waitMs, signal),
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
changed() {
|
|
297
|
-
for (const waiter of [...this.waiters])
|
|
298
|
-
waiter();
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
/** How long the SDK waits for the merchant's own requests to answer before it reads the total. */
|
|
302
|
-
export const MERCHANT_TOTAL_WAIT_MS = 5_000;
|
|
303
|
-
/** A request that can change the total was still unanswered when the SDK had to read it. */
|
|
304
|
-
export class MerchantTotalWait extends Error {
|
|
305
|
-
open;
|
|
306
|
-
constructor(open) {
|
|
307
|
-
super(`${open} of the merchant's own checkout requests had not answered, so its total was not known`);
|
|
308
|
-
this.open = open;
|
|
309
|
-
this.name = 'MerchantTotalWait';
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
/** merchant_total on the create's wire: snake_case, as the API takes it. */
|
|
313
|
-
export function merchantTotalWire(pausedAt, page, responses) {
|
|
314
|
-
return {
|
|
315
|
-
paused_at: pausedAt,
|
|
316
|
-
...(page ? { page } : {}),
|
|
317
|
-
responses: responses.map(exchangeWire),
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
/** One exchange on the wire. */
|
|
321
|
-
export function exchangeWire(exchange) {
|
|
322
|
-
return {
|
|
323
|
-
method: exchange.method, url: exchange.url, status: exchange.status, completed_at: exchange.completedAt,
|
|
324
|
-
...(exchange.sentAt !== undefined ? { sent_at: exchange.sentAt } : {}),
|
|
325
|
-
...(exchange.page !== undefined ? { page: exchange.page } : {}),
|
|
326
|
-
...(exchange.requestBody !== undefined ? { request_body: exchange.requestBody } : {}),
|
|
327
|
-
body: exchange.body,
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* The read at release: the same reading as at the create, with the release moment as the
|
|
332
|
-
* pause, over what the log holds once the requests that can move the total have answered.
|
|
333
|
-
* null when the card request may go out; otherwise why it is held: a request still
|
|
334
|
-
* unanswered ('stale'), a reading that fails, or a total that is not the approved amount
|
|
335
|
-
* ('changed'). The API bounds the total's age when it serves the card, but never sees the
|
|
336
|
-
* responses between the pause and the release: this read does.
|
|
337
|
-
*/
|
|
338
|
-
export async function merchantTotalAtRelease(log, profileId, approved, releasedAt, waitMs = MERCHANT_TOTAL_WAIT_MS, signal) {
|
|
339
|
-
const card = { ...approved.card, pausedAt: releasedAt };
|
|
340
|
-
let seen;
|
|
341
|
-
try {
|
|
342
|
-
seen = await log.settle(profileId, 'source', card, releasedAt, waitMs, signal);
|
|
343
|
-
}
|
|
344
|
-
catch (error) {
|
|
345
|
-
if (error instanceof MerchantTotalWait)
|
|
346
|
-
return { ok: false, code: 'stale', reason: error.message };
|
|
347
|
-
throw error;
|
|
348
|
-
}
|
|
349
|
-
const sent = merchantAmountExchanges(profileId, 'source', { card, responses: seen });
|
|
350
|
-
if (!sent.ok)
|
|
351
|
-
return sent;
|
|
352
|
-
const read = merchantAmountFromResponses(profileId, { card, responses: sent.responses });
|
|
353
|
-
if (!read.ok)
|
|
354
|
-
return read;
|
|
355
|
-
const want = approved.amount;
|
|
356
|
-
if (read.amount.amount !== want.amount || read.amount.currency.toLowerCase() !== want.currency.toLowerCase()) {
|
|
357
|
-
return { ok: false, code: 'changed',
|
|
358
|
-
reason: `the merchant's total is now ${read.amount.amount} ${read.amount.currency}, not the ${want.amount} ${want.currency.toLowerCase()} the cardholder approved` };
|
|
359
|
-
}
|
|
360
|
-
return null;
|
|
361
|
-
}
|
|
362
|
-
/** How long after the merchant answers the card request the SDK waits for the rest of its confirmation. */
|
|
363
|
-
export const MERCHANT_CHARGE_SETTLE_MS = 500;
|
|
364
|
-
/**
|
|
365
|
-
* The after-payment report (POST /v2/checkout/authorizations/:id/merchant_charge): the
|
|
366
|
-
* paused card request the create read and the merchant's answers after it that the
|
|
367
|
-
* profile's after-payment rules read, projected; null when the profile reads none, none
|
|
368
|
-
* came, or none the API's reading ties to this card request.
|
|
369
|
-
*/
|
|
370
|
-
export function merchantChargeReport(log, profileId, approved) {
|
|
371
|
-
const after = log.exchanges().filter((exchange) => exchange.completedAt >= approved.card.pausedAt);
|
|
372
|
-
const sent = merchantAmountExchanges(profileId, 'charged', { card: approved.card, responses: after });
|
|
373
|
-
if (!sent.ok || sent.responses.length === 0)
|
|
374
|
-
return null;
|
|
375
|
-
// Only a confirmation the API's own reading ties to this card request counts: another
|
|
376
|
-
// payment's on the same page (another order) is never sent under this approval.
|
|
377
|
-
if (!chargeReads(profileId, { card: approved.card, responses: sent.responses }))
|
|
378
|
-
return null;
|
|
379
|
-
return {
|
|
380
|
-
request: { url: approved.card.url, method: approved.card.method, body: approved.card.body },
|
|
381
|
-
responses: sent.responses.map(exchangeWire),
|
|
382
|
-
};
|
|
383
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
declare var OWNED_SHOP_OPERATION: string;
|
|
2
|
-
declare var OWNED_SHOP_SKU: string;
|
|
3
|
-
declare var OWNED_SHOP_AMOUNT_CENTS: number;
|
|
4
|
-
declare var OWNED_SHOP_CURRENCY: string;
|
|
5
|
-
declare var OWNED_SHOP_ORDER_FIELD: string;
|
|
6
|
-
declare function isOwnedShopPaymentIntentId(value: any): boolean;
|
|
7
|
-
declare function serializeOwnedShopOrder(value: any): string;
|
|
8
|
-
declare function hasOwnedShopMarker(request: any): any;
|
|
9
|
-
declare function parseOwnedShopOrder(request: any): {
|
|
10
|
-
order_id: any;
|
|
11
|
-
sku: string;
|
|
12
|
-
amount: number;
|
|
13
|
-
currency: string;
|
|
14
|
-
} | null;
|
|
15
|
-
declare function parseOwnedShopReceipt(value: any, expectedOrder: any): {
|
|
16
|
-
object: string;
|
|
17
|
-
order_id: any;
|
|
18
|
-
payment_intent_id: any;
|
|
19
|
-
status: string;
|
|
20
|
-
amount: number;
|
|
21
|
-
currency: string;
|
|
22
|
-
livemode: boolean;
|
|
23
|
-
} | null;
|
|
24
|
-
export { OWNED_SHOP_AMOUNT_CENTS, OWNED_SHOP_CURRENCY, OWNED_SHOP_OPERATION, OWNED_SHOP_ORDER_FIELD, OWNED_SHOP_SKU, hasOwnedShopMarker, isOwnedShopPaymentIntentId, parseOwnedShopOrder, parseOwnedShopReceipt, serializeOwnedShopOrder };
|