@tuwaio/quasar-sdk 0.7.3 → 0.8.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.
@@ -0,0 +1,1417 @@
1
+ import { StoreApi } from 'zustand/vanilla';
2
+ import { SubscriptionPermission, InvoiceStatus, PaymentsEnvironment, PaymentsCurrency, TaxCategory, InvoiceLocale, SubscriptionStatus, SubscriptionInterval, Buyer, QuasarRequestIssue, Refund, RefundStart, RefundParams, RefundPreview, SubmitRefundParams } from './index.cjs';
3
+
4
+ /**
5
+ * @file Types of the checkout API (`/v1/payments/checkout/:token`): what the buyer's page reads and sends.
6
+ */
7
+
8
+ /** A line of the invoice as the checkout shows it; amounts are decimal strings, `netMinor` in cents. */
9
+ interface CheckoutLine {
10
+ /** What is sold. */
11
+ name: string;
12
+ /** Details under the name. */
13
+ description?: string;
14
+ /** Quantity. */
15
+ quantity: string;
16
+ /** Price of one unit. */
17
+ unitPrice: string;
18
+ /** VAT category. */
19
+ taxCategory: TaxCategory;
20
+ /** VAT rate in percent. */
21
+ taxRate: string;
22
+ /** Net amount of the line in minor units (cents). */
23
+ netMinor: string;
24
+ }
25
+ /** The invoice the checkout token opens. */
26
+ interface CheckoutInvoice {
27
+ /** Invoice ID. */
28
+ id: string;
29
+ /** Series number; `null` while its document waits for the buyer details. */
30
+ number: string | null;
31
+ /** State of the invoice. */
32
+ status: InvoiceStatus;
33
+ /** Live or test: a test invoice is paid on test networks. */
34
+ environment: PaymentsEnvironment | null;
35
+ /** Currency. */
36
+ currency: PaymentsCurrency;
37
+ /** Total to pay, as a decimal. */
38
+ total: string | number;
39
+ /** Total in minor units (cents). */
40
+ totalMinor: string | null;
41
+ /** The lines. */
42
+ lineItems: CheckoutLine[];
43
+ /** Language of the page and of the documents still to be issued. */
44
+ locale: InvoiceLocale | null;
45
+ /** When it stops being payable. */
46
+ dueAt: string | null;
47
+ /** Where to send the buyer after paying (http(s) only). */
48
+ successUrl: string | null;
49
+ /** Where to send a buyer who gives up (http(s) only). */
50
+ cancelUrl: string | null;
51
+ /** Base units of the quoted asset received so far: an underpaid invoice asks for the rest. */
52
+ amountPaid: string | null;
53
+ /**
54
+ * Whether the rest of an underpaid invoice can be quoted now ({@link CheckoutQuote.amountDue}): its method is still on,
55
+ * something is left and it is not past the due date. `false` for an invoice in any other state.
56
+ */
57
+ restPayable: boolean;
58
+ /** The seller closed the invoice before its due date, with part of its payment received. */
59
+ closed?: boolean;
60
+ /**
61
+ * The wallet that paid an underpaid invoice first, the only one that pays the rest: an address, or a CAIP-10 account.
62
+ * `null` for an invoice in any other state.
63
+ */
64
+ originWallet: string | null;
65
+ /**
66
+ * Why a payment waits for, or was flagged to, the seller ({@link CheckoutReview}); `null` when none does.
67
+ */
68
+ review: CheckoutReview | null;
69
+ /** What was refunded, in minor units (cents) of the invoice currency. */
70
+ refundedTotalMinor: string | null;
71
+ /** When the invoice was paid; `null` until then, and absent from an engine that does not send it yet. */
72
+ paidAt?: string | null;
73
+ /**
74
+ * Payments of the invoice handed over and still on their way, whoever handed them over: tracked transactions
75
+ * settlement has not verified or rejected yet (recorded within the last 25 minutes: Quasar stops counting a stuck
76
+ * one), gasless relays still sending (within 35 minutes), and claims of this invoice on a transaction another invoice
77
+ * handed over first that Quasar has not decided yet (within 25 minutes). Quasar versions before the payment grace
78
+ * (October 2026) leave it out.
79
+ */
80
+ pendingPayments?: number;
81
+ /**
82
+ * Every key the page may know a failed payment by, latest first: the `txKey` of a rejected transaction, the hash the
83
+ * checkout handed over when a sped-up transaction replaced it, and the `userOpHash` of a gasless relay. Left out like
84
+ * `pendingPayments`; read {@link CheckoutInvoice.rejectedPayments} for why they failed.
85
+ */
86
+ rejectedTxKeys?: string[];
87
+ /**
88
+ * The payments that failed, latest first (at most 10), with the reason and whether the buyer may pay again. Quasar
89
+ * versions before October 28, 2026 leave it out.
90
+ */
91
+ rejectedPayments?: CheckoutRejectedPayment[];
92
+ /**
93
+ * While the invoice is payable and a payment Quasar learned of before `dueAt` is on its way: until when the invoice
94
+ * waits for it (`dueAt` plus 30 minutes), so the page can say that the payment was sent in time. `null` otherwise;
95
+ * left out like `rejectedPayments`.
96
+ */
97
+ paymentCountsUntil?: string | null;
98
+ /**
99
+ * Payments of the invoice that may still arrive, with no time limit: tracked transactions neither confirmed nor
100
+ * rejected for good (also one stuck past the 25 minutes `pendingPayments` counts, and one whose tracker lost sight of
101
+ * it, `not_found`, `timeout` or `replaced`, which Quasar reads again for 24 hours), gasless relays still sending, and
102
+ * claims of this invoice on a transaction another invoice handed over first that Quasar has not decided yet. While it
103
+ * is above `0` the page offers no plain second payment and never says that nothing was charged. Quasar versions
104
+ * before October 29, 2026 leave it out.
105
+ */
106
+ unresolvedPayments?: number;
107
+ /**
108
+ * Every key (`txKey`, hash, the hash a sped-up transaction replaced, a relay's `userOpHash`) of the payments that
109
+ * count as sent in time: Quasar learned of them before `dueAt` (all of them when the invoice has none); at most the
110
+ * latest 20 transactions and 10 relays. Match the page's own keys against it instead of the browser clock. Left out
111
+ * like `unresolvedPayments`.
112
+ */
113
+ onTimeTxKeys?: string[];
114
+ /**
115
+ * The invoice that replaced this one after the seller corrected it: its number and, while it can be paid, its
116
+ * checkout token.
117
+ */
118
+ replacedBy: {
119
+ number: string | null;
120
+ checkoutToken: string | null;
121
+ } | null;
122
+ /** The subscription period this invoice bills; `null` for a one-off invoice. */
123
+ subscription: CheckoutSubscription | null;
124
+ }
125
+ /**
126
+ * Why a payment of a `held` (or flagged) invoice waits for the seller, as {@link CheckoutInvoice.review} says it:
127
+ *
128
+ * - `screening`: the sender did not pass the seller's AML check after paying; no details reach the buyer.
129
+ * - `late`: it arrived after the invoice expired or was cancelled; the seller accepts or returns it.
130
+ * - `overpaid`: more arrived than the invoice asks for, and the seller looks at it.
131
+ * - `quote_expired`: it arrived after its locked quote expired, in an asset whose price moved; the seller accepts or
132
+ * returns it.
133
+ *
134
+ * A transfer Quasar could tie to no invoice never holds the invoice: it is a rejected payment with `held`
135
+ * ({@link CheckoutRejectedPayment.held}), which the seller returns to the wallet that sent it.
136
+ */
137
+ type CheckoutReview = {
138
+ reason: 'screening' | 'late' | 'overpaid' | 'quote_expired';
139
+ };
140
+ /** A payment of the invoice that failed, as the checkout lists it ({@link CheckoutInvoice.rejectedPayments}). */
141
+ interface CheckoutRejectedPayment {
142
+ /** The transaction's `txKey`, or the `userOpHash` of a gasless relay that failed before it had a transaction. */
143
+ txKey: string;
144
+ /**
145
+ * Why: `reverted`, `cancelled`, `expired` (a Solana blockhash), `no_matching_transfer` (a wrong amount, asset or
146
+ * recipient), `tx_already_used` (the transaction pays another invoice of the merchant: the one the wallet that sent
147
+ * it confirmed, or whose mark it carries, also when this invoice's page handed it over first or claimed it; with
148
+ * `held`, another invoice holds it for a refund only and it pays none),
149
+ * `reference_missing`, `too_early`, `not_found` (the network never returned the transaction), `timeout` (no receipt
150
+ * in time), `replaced`, `payer_mismatch` (a transfer from another wallet to an invoice paid or held already; one to a
151
+ * payable invoice is held for a refund only: {@link CheckoutReview}), `payer_unconfirmed` (the same from a wallet that
152
+ * never confirmed the invoice: it pays no invoice; final; a refund only when held, a transfer credited to another
153
+ * invoice is `tx_already_used`), `native_from_contract_unverifiable`, `invoice_not_payable` (a transfer to an invoice paid, held or refunded
154
+ * already), `below_minimum` (worth less than one minor unit of the invoice currency), `too_many_transfers` (the
155
+ * invoice holds 10 transfers for the merchant already), `too_many_parts` (it took 10 parts already) and
156
+ * `quote_changed` (a relay landed after it was quoted another way), both with `held`, `settlement_failed` (the network confirmed the transaction, but Quasar has not recorded it yet: not final,
157
+ * never a second payment, the buyer contacts the merchant; Quasar settles it again daily for 30 days after it learned
158
+ * of it, while `rechecked`), `unresolved_after_24h` (no result seen on chain within 24 hours: only a transaction
159
+ * Quasar read again after that day and the network does not have), a relay's reason (`user_operation_lost`…), or
160
+ * `transaction_failed` when Quasar does not know.
161
+ */
162
+ reason: string;
163
+ /**
164
+ * Whether the transaction can never pay the invoice, so the buyer may pay again. When `false` (`not_found`,
165
+ * `timeout`, `replaced`…) the money may still be on its way or already with the merchant: the buyer checks the wallet
166
+ * before paying again. A final rejection may still have moved money: read `movedMoney`.
167
+ */
168
+ final: boolean;
169
+ /** The relay operation the transaction came from, which the page knows when the relay answered `sending`. */
170
+ userOpHash?: string;
171
+ /** The transaction the checkout handed over, when this one replaced it (a payment sped up in the wallet). */
172
+ original?: string;
173
+ /**
174
+ * `true` when the transaction succeeded on chain without paying the invoice (`no_matching_transfer`,
175
+ * `reference_missing`, `too_early`, `tx_already_used`, `payer_mismatch`, `payer_unconfirmed`,
176
+ * `native_from_contract_unverifiable`, `invoice_not_payable`), or Quasar has not recorded it yet
177
+ * (`settlement_failed`): it may have moved money. Never say that it did not go through or that nothing was charged:
178
+ * say that it does not pay the invoice (for `settlement_failed`: that Quasar has not recorded it yet; for
179
+ * `payer_mismatch` and `payer_unconfirmed`: promise a refund only on a held invoice), and whom to contact if money
180
+ * left the wallet. Quasar versions before October 29, 2026 leave it out.
181
+ */
182
+ movedMoney?: true;
183
+ /**
184
+ * `true` while Quasar still reads the transaction again: a rejection for `not_found`, `timeout` or `replaced` of a
185
+ * payment Quasar learned of within the last 24 hours that nothing has resolved since (a payment still unresolved
186
+ * after the 24 hours is closed with the final reason `unresolved_after_24h`), or a `settlement_failed` one Quasar
187
+ * still settles again daily (for 30 days after it learned of the payment; Quasar versions before the payer proof,
188
+ * October 29, 2026, never mark it). Only then may the page promise that Quasar checks the payment again. Any other
189
+ * reason that is not `final` (a relay operation lost, `transaction_failed`, a transfer from another wallet…) is never
190
+ * read again: if money left the wallet, the buyer contacts the merchant with the transaction hash. Quasar versions
191
+ * before October 29, 2026 leave it out.
192
+ */
193
+ rechecked?: true;
194
+ /**
195
+ * `true` when Quasar records the transfer as a held transfer, which the merchant returns to the wallet that sent it:
196
+ * a `payer_mismatch` or `payer_unconfirmed` rejection (the invoice stays payable from the wallet that confirmed it),
197
+ * or a `tx_already_used` one whose transfer is held at another invoice of the merchant (`otherInvoice`; say that it is
198
+ * tied to another invoice, never that it pays one). Quasar versions before October 29, 2026 leave it out.
199
+ */
200
+ held?: true;
201
+ /** The invoice of the merchant the transaction is credited to or held on, when Quasar names it. */
202
+ otherInvoice?: string;
203
+ }
204
+ /** The subscription period an invoice bills, as the checkout shows it. */
205
+ interface CheckoutSubscription {
206
+ /** State of the subscription. */
207
+ status: SubscriptionStatus;
208
+ /** Unit of a period. */
209
+ interval: SubscriptionInterval;
210
+ /** Units in a period (`3` with `month` is a quarter). */
211
+ intervalCount: number;
212
+ /** The period this invoice bills, from 1. */
213
+ period: number;
214
+ /**
215
+ * When the period starts; `null` for a period before the subscription was last resumed, as resuming counts the periods
216
+ * again from that moment.
217
+ */
218
+ periodStart: string | null;
219
+ /** When it ends, which is when the next one is billed; `null` like {@link CheckoutSubscription.periodStart}. */
220
+ periodEnd: string | null;
221
+ /** How many periods the subscription runs; `null` until it is cancelled. */
222
+ cycles: number | null;
223
+ /** When it ends at the latest, if set. */
224
+ endsAt: string | null;
225
+ /** Whether it ends with the current period. */
226
+ cancelAtPeriodEnd: boolean;
227
+ }
228
+ /** Who is paid and on what terms, as the checkout shows it. */
229
+ interface CheckoutMerchant {
230
+ /** The name buyers know. */
231
+ name: string;
232
+ /** Website, as an http(s) link. */
233
+ website: string | null;
234
+ /** Where buyers ask for help. */
235
+ supportEmail: string | null;
236
+ /** Whether the merchant has a logo: read it from the `logo` route ({@link CheckoutState.logoUrl}). */
237
+ logo: boolean;
238
+ /** The seller as the invoice document names them. */
239
+ seller: {
240
+ legalName: string | null;
241
+ tradingName: string | null;
242
+ address: string | null;
243
+ country: string | null;
244
+ registrationNumber: string | null;
245
+ taxId: string | null;
246
+ } | null;
247
+ /** The merchant's terms, refund policy and privacy notice (http(s) links only). */
248
+ links: {
249
+ terms: string | null;
250
+ refundPolicy: string | null;
251
+ privacy: string | null;
252
+ };
253
+ /** A page where buyers get the token to pay, shown when the wallet has too little. */
254
+ fundingUrl: string | null;
255
+ }
256
+ /**
257
+ * A line the merchant asks the buyer to tick before paying, such as a request to start a service at once. The quote
258
+ * records the tick in the invoice's ledger and the receipt email repeats the text.
259
+ */
260
+ interface CheckoutConsent {
261
+ /** The merchant's words, up to 500 characters, shown as they are. */
262
+ text: string;
263
+ /** Whether the buyer cannot pay without ticking it: Quasar refuses the quote with `consent_required`. */
264
+ required: boolean;
265
+ }
266
+ /** The look of the checkout the merchant chose. */
267
+ interface CheckoutTheme {
268
+ /** `system` follows the buyer's device. */
269
+ mode: 'system' | 'light' | 'dark';
270
+ /** Main button and selection color (`#rrggbb`); `null` for the default. */
271
+ accentColor: string | null;
272
+ }
273
+ /** A payment method the invoice can be paid with. */
274
+ interface CheckoutMethod {
275
+ /** Method ID, for {@link CheckoutState.selectMethod}. */
276
+ id: string;
277
+ /** Its name. */
278
+ name: string;
279
+ /** Token symbol. */
280
+ symbol: string;
281
+ /** CAIP-2 chain (`eip155:8453`, `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp`). */
282
+ chainId: string;
283
+ /** CAIP-19 asset. */
284
+ assetId: string | null;
285
+ /** Decimals of the token. */
286
+ decimals: number;
287
+ /** `auto` when the merchant may sponsor the gas, `off` otherwise. */
288
+ gasless: string | null;
289
+ /** Marked "Popular" by the merchant. */
290
+ featured: boolean;
291
+ /** How much more the method costs than the invoice total, in basis points (`150` is 1.5 %). */
292
+ markupBps: number;
293
+ /** How much less the method costs than the invoice total, in basis points. */
294
+ discountBps: number;
295
+ /**
296
+ * When a payment with the method needs the paying wallet's confirmation of the invoice
297
+ * ({@link CheckoutState.confirmPayer}): `before_quote` (an EVM method: before the price; the quote answers
298
+ * `409 payer_proof_required` without it), `unless_relayed` (an EVM method the gasless relay may pay: the price comes
299
+ * first, the relay's EIP-3009 authorization confirms the wallet, and a transfer or a paymaster payment of it needs
300
+ * the confirmation first), `none` (Solana: the quote's reference ties the transfer to the invoice). Quasar versions
301
+ * before October 29, 2026 leave it out: no confirmation.
302
+ */
303
+ payerProof?: PayerProofNeed;
304
+ }
305
+ /** When a payment with a method needs the paying wallet's confirmation ({@link CheckoutMethod.payerProof}). */
306
+ type PayerProofNeed = 'before_quote' | 'unless_relayed' | 'none';
307
+ /**
308
+ * How a wallet confirmed the invoice: `eoa` (an EIP-191 signature of Quasar's message, which counts on every EVM
309
+ * network), `eip1271` and `eip6492` (a smart wallet's signature its contract accepted, deployed or not yet, which
310
+ * counts on that network only), `authorization` (the EIP-3009 authorization of the invoice's gasless relay, on every
311
+ * EVM network).
312
+ */
313
+ type PayerProofKind = 'eoa' | 'eip1271' | 'eip6492' | 'authorization';
314
+ /** A wallet that confirmed the invoice ({@link CheckoutView.payerProofs}). */
315
+ interface CheckoutPayerProof {
316
+ /** The wallet, as a CAIP-10 account on the network it confirmed on (the EVM address checksummed). */
317
+ payer: string;
318
+ /** How it confirmed. */
319
+ kind: PayerProofKind;
320
+ /** The CAIP-2 network it confirmed on: the only one where an `eip1271` or `eip6492` confirmation counts. */
321
+ chainId: string;
322
+ /** When. */
323
+ confirmedAt: string;
324
+ }
325
+ /**
326
+ * What `POST payer-proof/message` answers: the exact EIP-4361 (Sign-In with Ethereum) message the wallet signs, for
327
+ * free and without moving funds, to confirm that it pays this invoice. Quasar builds it from the page's site (its
328
+ * `Origin`), the wallet, the invoice number, the seller and the total, the network, a nonce derived from the invoice and
329
+ * the wallet, the invoice's creation (`Issued At`) and due date (`Expiration Time`); asked twice, it is the same.
330
+ */
331
+ interface PayerProofMessage {
332
+ /** Always `payer_proof_message`. */
333
+ object: 'payer_proof_message';
334
+ /** The wallet, as a CAIP-10 account (the EVM address checksummed). */
335
+ payer: string;
336
+ /** `true` when the wallet confirmed this invoice already (on this network, for a smart wallet): nothing to sign. */
337
+ proven: boolean;
338
+ /** The message to sign as it is, with `personal_sign` (EIP-191). */
339
+ message: string;
340
+ /** The site the message names: the host of the page that asked. */
341
+ domain: string;
342
+ /** The CAIP-2 network of the wallet. */
343
+ chainId: string;
344
+ /** The nonce of the message. */
345
+ nonce: string;
346
+ /** When the invoice was created (`Issued At`). */
347
+ issuedAt: string;
348
+ /** The invoice's due date (`Expiration Time`), `null` without one. */
349
+ expiresAt: string | null;
350
+ }
351
+ /** What `POST payer-proof` answers: the wallet's confirmation of the invoice, as Quasar recorded it. */
352
+ interface PayerProofConfirmation {
353
+ /** Always `payer_proof`. */
354
+ object: 'payer_proof';
355
+ /** The wallet, as a CAIP-10 account. */
356
+ payer: string;
357
+ /** Always `true`. */
358
+ proven: true;
359
+ /** How it confirmed. */
360
+ kind: PayerProofKind;
361
+ /** The CAIP-2 network it confirmed on. */
362
+ chainId: string;
363
+ /** When. */
364
+ confirmedAt: string;
365
+ }
366
+ /** What a wallet needs to pay a locked quote: the exact amount in base units, never rounded again. */
367
+ type PaymentInstructions = {
368
+ /** An EVM chain. */
369
+ family: 'eip155';
370
+ /** EVM chain ID. */
371
+ chainId: number;
372
+ /** The merchant's wallet. */
373
+ to: string;
374
+ /** The token contract, or `native`. */
375
+ token: string;
376
+ /** Amount to send, in base units. */
377
+ amount: string;
378
+ /** Decimals of the token. */
379
+ decimals: number;
380
+ /** Token symbol. */
381
+ symbol: string;
382
+ } | {
383
+ /** Solana. */
384
+ family: 'solana';
385
+ /** CAIP-2 chain ID. */
386
+ chainId: string;
387
+ /** The merchant's wallet. */
388
+ to: string;
389
+ /** The mint, or `native` for SOL. */
390
+ token: string;
391
+ /** Amount to send, in base units. */
392
+ amount: string;
393
+ /** Decimals of the token. */
394
+ decimals: number;
395
+ /** Token symbol. */
396
+ symbol: string;
397
+ /** The Solana Pay reference key of the quote: the transfer must carry it. */
398
+ reference: string;
399
+ /** A Solana Pay transfer request, for a QR code a mobile wallet scans. */
400
+ solanaPayUrl: string;
401
+ };
402
+ /** EIP-712 typed data of an EIP-3009 transfer authorization, ready for `eth_signTypedData_v4`. */
403
+ interface TransferAuthorizationTypedData {
404
+ /** The token's EIP-712 domain. */
405
+ domain: Record<string, unknown>;
406
+ /** The types, with `EIP712Domain`. */
407
+ types: Record<string, {
408
+ name: string;
409
+ type: string;
410
+ }[]>;
411
+ /** `TransferWithAuthorization`. */
412
+ primaryType: string;
413
+ /** The authorization: from the payer to the merchant, the quoted amount, a validity window and a nonce. */
414
+ message: Record<string, string>;
415
+ }
416
+ /**
417
+ * What the merchant sponsors for a quote: `relay`, an EIP-3009 authorization any wallet signs (Quasar sends the
418
+ * transfer), and `paymaster`, the calls a smart wallet sends with the checkout's ERC-7677 paymaster
419
+ * (`paymasterUrl` of the store).
420
+ */
421
+ interface GaslessOffer {
422
+ /** Sign `typedData` and pass the signature to {@link CheckoutState.relay}. */
423
+ relay?: {
424
+ typedData: TransferAuthorizationTypedData;
425
+ };
426
+ /** Send these calls with `wallet_sendCalls` and the `paymasterService` capability set to the paymaster URL. */
427
+ paymaster?: {
428
+ calls: {
429
+ to: string;
430
+ value: string;
431
+ data: string;
432
+ }[];
433
+ };
434
+ }
435
+ /** A locked quote: the price for this payer, valid until `expiresAt`. */
436
+ interface CheckoutQuote {
437
+ /** Always `checkout_quote`. */
438
+ object: 'checkout_quote';
439
+ /** The payment method. */
440
+ methodId: string;
441
+ /** CAIP-10 account the quote was locked to; `null` for a Solana Pay QR quote. */
442
+ payer: string | null;
443
+ /** Token amount the whole invoice costs at the quoted rate, in base units. */
444
+ cryptoAmountExpected: string;
445
+ /**
446
+ * Token amount that must still arrive, in base units: {@link CheckoutQuote.cryptoAmountExpected}, or on an underpaid
447
+ * invoice what is left of it after what arrived, at the same rate.
448
+ */
449
+ amountDue: string;
450
+ /** Token amount the buyer sends: {@link CheckoutQuote.amountDue} grossed up for a transfer fee, in base units. */
451
+ amountToSend: string;
452
+ /**
453
+ * On the quote of a rest in an asset that moves against the invoice currency: until when its rate holds. The quote may
454
+ * run longer; a payment after this time waits for the merchant to accept or refund it (`review.reason`
455
+ * `quote_expired`). `null` otherwise.
456
+ */
457
+ rateHeldUntil: string | null;
458
+ /** The token's transfer fee, when it takes one. */
459
+ transferFee: {
460
+ bps: number;
461
+ maxFee: string | null;
462
+ source: 'contract' | 'method';
463
+ } | null;
464
+ /** Decimals of the token. */
465
+ decimals: number;
466
+ /** Token symbol. */
467
+ symbol: string;
468
+ /** When it was locked. */
469
+ lockedAt: string | null;
470
+ /** When it expires; ask for a new quote after that. */
471
+ expiresAt: string | null;
472
+ /** The Solana Pay reference key. */
473
+ reference: string | null;
474
+ /** Price rounds used, as evidence. */
475
+ rates: unknown;
476
+ /** Markup of the method in basis points. */
477
+ markupBps: number;
478
+ /** Discount of the method in basis points. */
479
+ discountBps: number;
480
+ /** What the wallet sends. */
481
+ instructions: PaymentInstructions;
482
+ /** What the merchant sponsors, or `null`: the buyer pays the gas. */
483
+ gasless: GaslessOffer | null;
484
+ /**
485
+ * Whether the quoted wallet confirmed the invoice ({@link CheckoutState.confirmPayer}): `required` on an EVM method,
486
+ * `proven` once it did (`kind` says how). An EVM quote without the confirmation exists only for a method the gasless
487
+ * relay may pay: a transfer or a paymaster payment from that wallet needs the confirmation first, or Quasar credits it
488
+ * to no invoice and the merchant returns it. Quasar versions before October 29, 2026 leave it out.
489
+ */
490
+ payerProof?: {
491
+ required: boolean;
492
+ proven: boolean;
493
+ kind: PayerProofKind | null;
494
+ };
495
+ }
496
+ /** The ERC-7715 `wallet_grantPermissions` request of a subscription with automatic charges. */
497
+ interface PermissionRequest {
498
+ /** Chain as hex. */
499
+ chainId: string;
500
+ /** The app's collector account, which redeems the permission. */
501
+ to: string;
502
+ /** A periodic ERC-20 allowance. */
503
+ permission: {
504
+ type: 'erc20-token-periodic';
505
+ isAdjustmentAllowed: boolean;
506
+ data: {
507
+ tokenAddress: string;
508
+ periodAmount: string;
509
+ periodDuration: number;
510
+ startTime: number;
511
+ justification: string;
512
+ };
513
+ };
514
+ /** Expiry, payee and redeemer rules. */
515
+ rules: {
516
+ type: string;
517
+ data: unknown;
518
+ }[];
519
+ }
520
+ /** For a subscription with automatic charges: whether the page should ask the wallet for the permission. */
521
+ type AutoChargeOffer = {
522
+ /** Ask the wallet with `request`, then pass its answer to `grantPermission` of the checkout store. */
523
+ status: 'available';
524
+ /** The method the charges use. */
525
+ methodId: string;
526
+ /** The request for `wallet_grantPermissions`. */
527
+ request: PermissionRequest;
528
+ } | {
529
+ /** Already granted. */
530
+ status: 'active';
531
+ /** The permission. */
532
+ permission: SubscriptionPermission | null;
533
+ /**
534
+ * The transaction that revokes it, for the wallet that granted it to send and pay for; then
535
+ * {@link CheckoutState.permissionRevoked}. `null` when it cannot be built.
536
+ */
537
+ revocation: {
538
+ to: string;
539
+ data: string;
540
+ } | null;
541
+ /**
542
+ * What another wallet asks for to take over, when this permission's wallet cannot pay, nothing under it has moved money and
543
+ * its last charge failed, or it expired: it confirms the invoice and gives its own (`POST permission`). Only on an open
544
+ * invoice of a scheduled subscription.
545
+ */
546
+ replacement?: {
547
+ methodId: string;
548
+ request: PermissionRequest;
549
+ };
550
+ } | {
551
+ /** Not possible now; the buyer pays each invoice. */
552
+ status: 'unavailable';
553
+ /** Why, as a stable code. */
554
+ reason: string;
555
+ };
556
+ /** What the checkout token opens, as {@link CheckoutState.checkout} holds it. */
557
+ interface CheckoutView {
558
+ /** Always `checkout`. */
559
+ object: 'checkout';
560
+ /** Who is paid and on what terms. */
561
+ merchant: CheckoutMerchant;
562
+ /** The look the merchant chose. */
563
+ theme: CheckoutTheme;
564
+ /** The invoice. */
565
+ invoice: CheckoutInvoice;
566
+ /**
567
+ * Whether the page collects the buyer: `collect` is `off`, `email` or `full`; `required` while the invoice document
568
+ * waits for them; `companyInvoice` when the page may offer an invoice for a company.
569
+ */
570
+ buyer: {
571
+ collect: 'off' | 'email' | 'full';
572
+ required: boolean;
573
+ companyInvoice: boolean;
574
+ };
575
+ /** The line the buyer ticks before paying; `null` when the merchant asks none. */
576
+ consent: CheckoutConsent | null;
577
+ /**
578
+ * `true` when the merchant checks paying wallets before payment (its AML mode is `block` or `flag`); the checkout
579
+ * says so to the buyer only then.
580
+ */
581
+ amlScreening: boolean;
582
+ /** The methods that take this invoice. */
583
+ methods: CheckoutMethod[];
584
+ /**
585
+ * The wallets that confirmed this invoice ({@link CheckoutState.confirmPayer}), oldest first: a page reopened with
586
+ * one of them asks for no new signature. Quasar versions before October 29, 2026 leave it out.
587
+ */
588
+ payerProofs?: CheckoutPayerProof[];
589
+ /** How long a quote holds once locked, in seconds (never past the invoice deadline). */
590
+ quoteTtlSeconds: number;
591
+ /** The locked quote, if any. */
592
+ quote: CheckoutQuote | null;
593
+ /** The automatic-charge offer of a subscription invoice; `null` otherwise. */
594
+ autoCharge: AutoChargeOffer | null;
595
+ }
596
+ /**
597
+ * The calls a checkout store makes, one per checkout route. {@link createCheckoutStore} makes them over HTTP; pass
598
+ * your own as `api` to drive the store without a network, as tests and the TUWA docs Playground do. A call rejects with
599
+ * a `QuasarSDKError` that carries the Payments `code` when Quasar refuses it.
600
+ */
601
+ interface CheckoutApi {
602
+ /** Reads the checkout (`GET`). */
603
+ view: () => Promise<CheckoutView>;
604
+ /** Locks a quote for a method and, when connected, the payer, with the buyer's consent when given (`POST quote`). */
605
+ quote: (body: {
606
+ methodId: string;
607
+ payer?: string;
608
+ consentGiven?: boolean;
609
+ }) => Promise<CheckoutQuote>;
610
+ /**
611
+ * The message the wallet signs to confirm the invoice (`POST payer-proof/message`). Optional for an `api` written
612
+ * before payer proofs: without it and {@link CheckoutApi.payerProof}, {@link CheckoutState.confirmPayer} fails.
613
+ */
614
+ payerProofMessage?: (body: {
615
+ payer: string;
616
+ }) => Promise<PayerProofMessage>;
617
+ /** Sends the signed message, which Quasar checks and records (`POST payer-proof`). Optional like `payerProofMessage`. */
618
+ payerProof?: (body: {
619
+ payer: string;
620
+ message: string;
621
+ signature: string;
622
+ }) => Promise<PayerProofConfirmation>;
623
+ /** Sends the buyer details (`POST buyer`) and answers with the checkout. */
624
+ buyer: (body: {
625
+ buyer: Buyer;
626
+ company?: boolean;
627
+ }) => Promise<CheckoutView>;
628
+ /** Sets the language of the page and the documents still to be issued (`POST locale`). */
629
+ locale: (locale: InvoiceLocale) => Promise<{
630
+ locale: InvoiceLocale;
631
+ }>;
632
+ /** Hands over the transaction the wallet sent (`POST submit`). */
633
+ submit: (body: {
634
+ txKey: string;
635
+ from?: string;
636
+ connectorType?: string;
637
+ methodId?: string;
638
+ }) => Promise<{
639
+ txKey: string;
640
+ status: string;
641
+ }>;
642
+ /** Relays the signed EIP-3009 authorization (`POST relay`). */
643
+ relay: (signature: string) => Promise<{
644
+ status: 'submitted' | 'sending';
645
+ userOpHash?: string;
646
+ txHash?: string;
647
+ }>;
648
+ /** Sends the ERC-7715 permission the wallet granted (`POST permission`). */
649
+ permission: (body: GrantPermissionParams) => Promise<{
650
+ permission: unknown;
651
+ autoCharge: AutoChargeOffer | null;
652
+ }>;
653
+ /** `POST permission/revoked`: Quasar asks the chain whether the permission was revoked. */
654
+ permissionRevoked: () => Promise<{
655
+ autoCharge: AutoChargeOffer | null;
656
+ }>;
657
+ /** The URL of a route that is read without the store: `receipt`, `logo`, `paymaster`, `events`. */
658
+ url: (route: string) => string;
659
+ }
660
+ /** The answer of the wallet to `wallet_grantPermissions`, as `grantPermission` of the checkout store takes it. */
661
+ interface GrantPermissionParams {
662
+ /** CAIP-10 account that granted it, on the method's chain. */
663
+ payer: string;
664
+ /** The permission context the wallet returned. */
665
+ context: string;
666
+ /** The delegation manager it names. */
667
+ delegationManager: string;
668
+ /** Account deployments the delegator needs first (ERC-7715 `dependencies`). */
669
+ dependencies?: {
670
+ factory: string;
671
+ factoryData: string;
672
+ }[];
673
+ }
674
+ /** One `status` event of the checkout's event stream. */
675
+ interface CheckoutStatusEvent {
676
+ /** State of the invoice. */
677
+ status: InvoiceStatus;
678
+ /** Number of ledger steps so far. */
679
+ ledgerSeq: number;
680
+ /** The payment transaction, once there is one. */
681
+ txHash: string | null;
682
+ /** Amount received, in base units. */
683
+ amountPaid: string | null;
684
+ /** When the locked quote expires. */
685
+ quoteExpiresAt: string | null;
686
+ /**
687
+ * Payments handed over and still on their way, as {@link CheckoutInvoice.pendingPayments}. Quasar keeps the stream of an
688
+ * `expired` or `cancelled` invoice open while it is above `0`.
689
+ */
690
+ pendingPayments?: number;
691
+ /** The keys of the payments that failed, latest first, as {@link CheckoutInvoice.rejectedTxKeys}. */
692
+ rejectedTxKeys?: string[];
693
+ /** The payments that failed and why, latest first, as {@link CheckoutInvoice.rejectedPayments}. */
694
+ rejectedPayments?: CheckoutRejectedPayment[];
695
+ /** Until when a payment sent before the due date counts, as {@link CheckoutInvoice.paymentCountsUntil}. */
696
+ paymentCountsUntil?: string | null;
697
+ /** Payments that may still arrive, with no time limit, as {@link CheckoutInvoice.unresolvedPayments}. */
698
+ unresolvedPayments?: number;
699
+ /** The keys of the payments that count as sent in time, as {@link CheckoutInvoice.onTimeTxKeys}. */
700
+ onTimeTxKeys?: string[];
701
+ }
702
+
703
+ /**
704
+ * @file The headless checkout store: the state of one invoice's payment in the buyer's browser.
705
+ */
706
+
707
+ /**
708
+ * Where the payment stands:
709
+ *
710
+ * - `loading`: reading the checkout.
711
+ * - `buyer`: the buyer details come first ({@link CheckoutState.setBuyer}).
712
+ * - `selectMethod`: choose a method, connect a wallet, confirm it with {@link CheckoutState.confirmPayer} when
713
+ * {@link CheckoutState.payerProof} is `required` (an EVM payment the buyer's wallet sends), tick the merchant's consent
714
+ * when there is one, then {@link CheckoutState.requestQuote}.
715
+ * - `quoting`: Quasar screens the payer and locks the price.
716
+ * - `awaitingPayment`: send `quote.instructions` from the wallet (or sign `quote.gasless`), then
717
+ * {@link CheckoutState.submit} or {@link CheckoutState.relay}.
718
+ * - `submitting`: handing the transaction to Quasar.
719
+ * - `confirming`: Quasar follows the transaction until it is final. Also while Quasar reports a payment of the
720
+ * invoice on its way that this store did not hand over ({@link CheckoutState.invoicePaymentOnTheWay}: the page was
721
+ * reopened, or Quasar found a Solana Pay transfer): the page does not offer to pay meanwhile.
722
+ * - `unconfirmed`: Quasar could not confirm the payment (the network never returned the transaction, no receipt in
723
+ * time…), but it may still have gone through: the page asks the buyer to check the wallet before paying again, and
724
+ * offers no payment; {@link CheckoutState.payAgain} goes back to it once the buyer did. `error` says why
725
+ * (`payment_unconfirmed`, with the `reason`). Also where a reopened page finds the latest payment of the invoice
726
+ * unconfirmed or still unresolved ({@link CheckoutState.invoicePaymentUnresolved}), while the store retries
727
+ * handing over a transaction the wallet sent (`reason` `submit_retrying`, {@link CheckoutState.submit}), while
728
+ * Quasar decides about a transaction another invoice handed over first (`reason` `tx_already_used`), and for a
729
+ * payment the network confirmed that Quasar has not recorded yet (`reason` `settlement_failed`); for these two
730
+ * {@link CheckoutState.payAgain} does nothing.
731
+ * - `paid`, `underpaid` (the buyer may top it up), `held` (the merchant looks at the payment), `refunded`, `expired`,
732
+ * `cancelled`: the invoice's outcome. An invoice that expires or is cancelled while a payment is on its way (one this
733
+ * store handed over with `submit` or `relay`, or one Quasar reports) is not over: the payment may still arrive, and
734
+ * the invoice then becomes `paid` (Quasar lets a payment it learned of before `dueAt` confirm for up to 30 minutes)
735
+ * or `held` (a late payment, or one to a cancelled invoice, that the merchant accepts or returns). The store keeps
736
+ * following it while {@link CheckoutState.paymentOnTheWay} or {@link CheckoutState.invoicePaymentOnTheWay} is
737
+ * `true`, for a limited time.
738
+ * - `blocked`: AML screening refused the payer's wallet.
739
+ * - `error`: the checkout could not be read (a wrong or retired link, the network).
740
+ */
741
+ type CheckoutPhase = 'loading' | 'buyer' | 'selectMethod' | 'quoting' | 'awaitingPayment' | 'submitting' | 'confirming' | 'unconfirmed' | 'paid' | 'underpaid' | 'held' | 'refunded' | 'expired' | 'cancelled' | 'blocked' | 'error';
742
+ /**
743
+ * Why the last action failed: the Payments `code` (`payer_blocked`, `quote_required`, `invoice_expired`…), or
744
+ * `method_required` (no method chosen), `consent_required` (the merchant's consent is not ticked),
745
+ * `payer_proof_required` (the wallet has not confirmed the invoice yet: {@link CheckoutState.confirmPayer}; also Quasar's
746
+ * `409` to a quote), `payer_required` (no wallet to confirm), `payer_proof_declined` (the wallet did not sign the
747
+ * confirmation; nothing was sent), the refusals of a confirmation (`payer_proof_invalid` with `reason`
748
+ * `message_mismatch` or `signature_invalid`, `payer_proof_unavailable` with `reason` `rpc_failed` when the network of a
749
+ * smart wallet cannot be asked now, `payer_chain_mismatch`, `origin_required`, `rate_limited`, `too_many_payers`),
750
+ * `payment_failed`
751
+ * (the transaction did not go through: it reverted, was cancelled, its Solana blockhash expired…; pay again),
752
+ * `payment_mismatch` (the transaction succeeded on chain but does not pay the invoice: a wrong amount, asset or
753
+ * recipient, no Solana Pay reference, sent before the quote, already used; it may have moved money, so never say that
754
+ * nothing was charged: say that it does not pay the invoice and to contact the merchant if money left the wallet; the
755
+ * buyer may pay again once it can never pay the invoice; one that may still pay it keeps `phase` `unconfirmed`, with no
756
+ * plain second payment), `payment_unconfirmed` (Quasar could not confirm the transaction, or has not received it yet,
757
+ * but it may still have gone through: check the wallet before paying again; `rechecked` says whether Quasar reads it
758
+ * again; `reason` `tx_already_used` and `settlement_failed` say more, see {@link CheckoutError.reason}) and
759
+ * `request_failed` (no answer).
760
+ */
761
+ interface CheckoutError {
762
+ /** Stable code. */
763
+ code: string;
764
+ /** What to show or log. */
765
+ message: string;
766
+ /** HTTP status, when the API answered. */
767
+ status?: number;
768
+ /** What to show in the reader's language, when the app said it: a window prefers it to its own words for the code. */
769
+ text?: string;
770
+ /** The fields at fault of an invalid request. */
771
+ issues?: QuasarRequestIssue[];
772
+ /**
773
+ * Why Quasar refused an action, when it says (`details.reason` of the refusal: `signature_invalid` of
774
+ * `payer_proof_invalid`, `feed_unavailable` of `price_unavailable`…). Why Quasar rejected the payment, for
775
+ * `payment_failed`, `payment_mismatch` and `payment_unconfirmed`: a reason of
776
+ * {@link CheckoutRejectedPayment} (`reverted`, `no_matching_transfer`, `not_found`, `timeout`…), `submit_retrying`
777
+ * while the store retries handing over a transaction Quasar has not received yet ({@link CheckoutState.submit}), or
778
+ * `submit_failed` once it gave up after an hour; left out when Quasar did not say. Two reasons of
779
+ * `payment_unconfirmed` are about a payment that left the wallet, and the page offers no payment for them:
780
+ * - `tx_already_used` (with `status` `409`): Quasar tracks the transaction for another invoice whose page handed it
781
+ * over first (anyone who saw the hash may have done so). Quasar recorded this invoice's claim on it and credits it
782
+ * to the invoice the wallet that sent it confirmed ({@link CheckoutState.confirmPayer}): when that is this invoice,
783
+ * it becomes `processing` or `paid` and the error clears; when it is not, Quasar rejects it here with
784
+ * `tx_already_used` (`payment_mismatch`).
785
+ * - `settlement_failed`: the network confirmed the transaction, but Quasar has not recorded it yet. The money reached
786
+ * the merchant's address: the buyer contacts the merchant with the transaction hash and does not pay again; never
787
+ * say that it does not pay the invoice or that nothing was charged. Quasar settles it again daily for 30 days after
788
+ * it learned of it, while `rechecked` is set; only then may the page say so.
789
+ *
790
+ * `payment_mismatch` with `payer_mismatch` or `payer_unconfirmed`: the transfer came from another wallet than the
791
+ * quoted one, or from one that never confirmed the invoice; it pays no invoice and the merchant returns it to the
792
+ * wallet that sent it.
793
+ */
794
+ reason?: string;
795
+ /**
796
+ * Set on `payment_unconfirmed` while Quasar still reads the payment again (for up to 24 hours after it learned of
797
+ * it; a `settlement_failed` one daily for 30 days): the rejection is `rechecked`
798
+ * ({@link CheckoutRejectedPayment.rechecked}), or there is no rejection and Quasar reports a payment that may still
799
+ * arrive (`unresolvedPayments` above `0`). Only then may the page promise that Quasar checks the payment again. Left
800
+ * out otherwise (`user_operation_lost`, `transaction_failed`, `submit_failed`, a `settlement_failed` past its 30 days,
801
+ * a Quasar that does not say): Quasar never reads that payment again, so the page asks the buyer to contact the
802
+ * merchant with the transaction hash if money left the wallet.
803
+ */
804
+ rechecked?: true;
805
+ /**
806
+ * Set on a `payment_mismatch` with the reason `tx_already_used` when another invoice holds the transfer for a refund
807
+ * only ({@link CheckoutRejectedPayment.held}): say that the transaction is tied to another invoice, never that it pays
808
+ * one.
809
+ */
810
+ held?: true;
811
+ /**
812
+ * The other invoice Quasar names: the one a rejected transaction is credited to or held on, or the one whose hold
813
+ * the merchant refunds instead (`payment_held_elsewhere` of a refund, its `details.invoice`).
814
+ */
815
+ otherInvoice?: string;
816
+ /**
817
+ * The wallet that paid the invoice first, on `payer_mismatch` (`409`) of {@link CheckoutState.requestQuote}: Quasar
818
+ * prices the rest of an underpaid invoice only for that wallet, so the buyer pays the rest from it.
819
+ */
820
+ originWallet?: string;
821
+ /** The transaction whose nonce nothing explains, on `nonce_spent_unexplained`: not always the one the page handed over last. */
822
+ txKey?: string;
823
+ /**
824
+ * Set on `payment_unconfirmed` for a payment past the first day Quasar still reads, as a node still shows it or nothing could
825
+ * read it: neither the promise of `rechecked` nor that Quasar stopped reading it holds.
826
+ */
827
+ stillRead?: true;
828
+ }
829
+ /**
830
+ * Whether the connected wallet must confirm the invoice before it pays (a transfer is
831
+ * credited only to an invoice it is tied to by proof):
832
+ *
833
+ * - `not_needed`: no method chosen, a Solana method (the quote's reference ties the transfer), or a Quasar before payer
834
+ * proofs.
835
+ * - `required`: an EVM payment the buyer's wallet sends (paying the gas, or a smart wallet with or without the
836
+ * merchant's paymaster): {@link CheckoutState.confirmPayer} before {@link CheckoutState.requestQuote}, which refuses
837
+ * with `payer_proof_required` until then. Also when Quasar refused a quote with `409 payer_proof_required`.
838
+ * - `unless_relayed`: an EVM method the gasless relay may pay. The quote comes first; paying with
839
+ * {@link CheckoutState.relay} needs nothing more (the signed EIP-3009 authorization confirms the wallet), while a
840
+ * transfer from the wallet or a paymaster payment (`quote.gasless.paymaster`) needs
841
+ * {@link CheckoutState.confirmPayer} before the wallet sends it: Quasar credits a transfer of an unconfirmed wallet to
842
+ * no invoice and the merchant returns it.
843
+ * - `confirming`: {@link CheckoutState.confirmPayer} is under way for a wallet that confirms before the price. A
844
+ * wallet the relay may confirm stays `unless_relayed` meanwhile, so a prompt that never answers blocks no new price.
845
+ * - `confirmed`: the wallet confirmed the invoice: on any EVM network for an ordinary wallet (or through the relay's
846
+ * authorization), on the method's network for a smart wallet. One confirmation per invoice and wallet: a new quote,
847
+ * another token or network needs none, also on a reopened page (`checkout.payerProofs`).
848
+ */
849
+ type PayerProofStatus = 'not_needed' | 'required' | 'unless_relayed' | 'confirming' | 'confirmed';
850
+ /**
851
+ * Signs the message of a payer proof with the connected wallet: `personal_sign` (EIP-191) of `message` exactly as it
852
+ * is, from `request.payer` (a smart wallet signs it through its own flow, ERC-1271 or ERC-6492). Rejects when the buyer
853
+ * refuses or the wallet cannot sign.
854
+ *
855
+ * @param message - The EIP-4361 message to sign.
856
+ * @param request - What Quasar answered: the message with its site, network, nonce and dates.
857
+ * @returns The signature, as hex.
858
+ */
859
+ type PayerProofSigner = (message: string, request: PayerProofMessage) => Promise<string>;
860
+ /** The state and actions of {@link createCheckoutStore}. */
861
+ interface CheckoutState {
862
+ /** Where the payment stands. */
863
+ phase: CheckoutPhase;
864
+ /** What the checkout token opens; `null` until loaded. */
865
+ checkout: CheckoutView | null;
866
+ /** State of the invoice, as Quasar last reported it. */
867
+ invoiceStatus: InvoiceStatus | null;
868
+ /** The chosen payment method. */
869
+ methodId: string | null;
870
+ /** The connected wallet as a CAIP-10 account on the method's chain. */
871
+ payer: string | null;
872
+ /** Whether the buyer ticked the merchant's consent (`checkout.consent`). */
873
+ consentGiven: boolean;
874
+ /**
875
+ * Whether the connected wallet must confirm the invoice before it pays, for the chosen method ({@link PayerProofStatus}).
876
+ * Read-only: the store sets it from the method, the wallet and the confirmations Quasar and this page know of.
877
+ */
878
+ readonly payerProof: PayerProofStatus;
879
+ /**
880
+ * The wallets known to have confirmed the invoice in a way that counts for the chosen method, as CAIP-10 accounts: from
881
+ * Quasar (`checkout.payerProofs`), this page and the locked quote. Read-only: a buyer who changed wallets can be told
882
+ * which one needs no new signature.
883
+ */
884
+ readonly confirmedWallets: readonly string[];
885
+ /** The locked quote. */
886
+ quote: CheckoutQuote | null;
887
+ /** Language of the page and the documents still to be issued. */
888
+ locale: InvoiceLocale | null;
889
+ /**
890
+ * The transaction handed to Quasar: the hash or signature passed to {@link CheckoutState.submit}, or the hash of a
891
+ * relayed transfer when Quasar returns it. A relay Quasar answers with `sending` has none yet: read
892
+ * {@link CheckoutState.paymentOnTheWay} to know that a payment is on its way.
893
+ */
894
+ txKey: string | null;
895
+ /**
896
+ * Whether a payment this store handed to Quasar ({@link CheckoutState.submit}, {@link CheckoutState.relay}, also a
897
+ * relay still `sending`, without a `txKey`) is on its way: `true` from the hand-over until the payment settles
898
+ * (the invoice becomes `paid`, `underpaid`, `held`, `refunded` or `cancelled`) or ends. It ends when Quasar lists it
899
+ * among the rejected payments (`rejectedPayments` of the status events, by its `txKey`, the hash it replaced or the
900
+ * relay's `userOpHash`), on an `open` invoice too: with a final reason `error` is `payment_failed` (reverted,
901
+ * cancelled…) or `payment_mismatch` (a wrong amount or recipient…: it may have moved money) and the buyer may pay
902
+ * again; otherwise (not found, no receipt in time…) `error` is
903
+ * `payment_unconfirmed` and `phase` `unconfirmed`. The same when the invoice goes back to `open` after `processing`.
904
+ * On an `expired` or `cancelled` invoice it tells the page that the buyer's payment may still arrive: `false` again
905
+ * once Quasar reports no payment of the invoice on its way, or at {@link CheckoutState.followingUntil}, when the store
906
+ * stops following the invoice (`error` `payment_unconfirmed` then, or on a read without a payment on its way, while
907
+ * {@link CheckoutState.invoicePaymentUnresolved} is `true`). Also `true` while Quasar decides about a transaction
908
+ * another invoice handed over first (`error.reason` `tx_already_used`). Read-only: the store sets it.
909
+ */
910
+ readonly paymentOnTheWay: boolean;
911
+ /**
912
+ * Whether Quasar last reported a payment of the invoice on its way (`pendingPayments` above `0`), whoever handed it
913
+ * over: another tab, the page before it was reloaded, a Solana Pay transfer Quasar found, this invoice's claim on a
914
+ * transaction another invoice handed over first. While it is `true` on a payable invoice the phase is `confirming`,
915
+ * not a payment; on an `expired` or `cancelled` invoice the payment may still arrive, and the store follows it as it
916
+ * follows {@link CheckoutState.paymentOnTheWay}. `false` with a Quasar that does not report it, and once the store
917
+ * stops following an expired or cancelled invoice. Read-only: the store sets it.
918
+ */
919
+ readonly invoicePaymentOnTheWay: boolean;
920
+ /**
921
+ * Until when the invoice waits for a payment Quasar learned of before `dueAt` (`dueAt` plus 30 minutes), while one is
922
+ * on its way; `null` otherwise or with a Quasar that does not report it. The page can say from it, not from the
923
+ * browser's clock, that the payment was sent in time. Read-only: the store sets it.
924
+ */
925
+ readonly paymentCountsUntil: string | null;
926
+ /**
927
+ * Whether Quasar last reported a payment of the invoice that may still arrive (`unresolvedPayments` above `0`, with
928
+ * no time limit: also one stuck past the 25 minutes `pendingPayments` counts, and one whose tracker lost sight of it,
929
+ * which Quasar reads again for 24 hours); with a Quasar that does not report it, `pendingPayments` above `0`. While it
930
+ * is `true` the page never says that nothing was charged, and a reopened page that finds no payment on its way starts
931
+ * in `unconfirmed` (or `expired` with `payment_unconfirmed`). Read-only: the store sets it.
932
+ */
933
+ readonly invoicePaymentUnresolved: boolean;
934
+ /**
935
+ * Whether the payment this store handed over counts as sent before `dueAt`, as Quasar says (one of its keys is in
936
+ * `onTimeTxKeys`): `true` once Quasar lists it, `false` while Quasar lists the payments sent in time without it (one
937
+ * handed over after `dueAt`, which Quasar holds as late), `null` before Quasar reported it, with a Quasar that does
938
+ * not report it, or without a payment of this page. Use it, not the browser clock. Reset by the next hand-over.
939
+ * Read-only: the store sets it.
940
+ */
941
+ readonly paymentSentInTime: boolean | null;
942
+ /**
943
+ * Until when this page follows an `expired` or `cancelled` invoice for a payment on its way (ISO 8601), `null` when it
944
+ * does not (an invoice that can still be paid is followed with no limit). An expired invoice is followed until 35
945
+ * minutes past the later of `dueAt` and the latest hand-over of this page (Quasar waits 30 minutes past `dueAt` at
946
+ * most for a payment it learned of before it, and settles a later one in minutes); when the page found the payment on
947
+ * its way instead (a reload, another tab), the time it found it counts as the hand-over; an hour after the store found
948
+ * the invoice expired when it has no `dueAt`. A cancelled invoice is followed until 35 minutes after the later of when
949
+ * the store found it cancelled and the latest hand-over. A later hand-over moves it on. Past it the page shows nothing
950
+ * more without a reload, so say "this page follows the payment until {time}", never that it shows the outcome
951
+ * whenever it comes; a reloaded page reads the outcome. Read-only: the store sets it.
952
+ */
953
+ readonly followingUntil: string | null;
954
+ /** The payment's transaction, once Quasar has one. */
955
+ txHash: string | null;
956
+ /** Why the last action failed; cleared by the next one. */
957
+ error: CheckoutError | null;
958
+ /** The receipt PDF of a paid invoice (a plain `GET`, for a link); `null` before. */
959
+ /**
960
+ * How far this machine's clock is ahead of Quasar's, in milliseconds, as measured on the last price locked here; `0` until
961
+ * then and within a few seconds. Countdowns subtract it: a wrong clock must not end a price at once or keep an ended one live.
962
+ */
963
+ clockSkewMs: number;
964
+ receiptUrl: string | null;
965
+ /** The ERC-7677 paymaster URL for `wallet_sendCalls` when the quote offers sponsored calls; `null` otherwise. */
966
+ paymasterUrl: string | null;
967
+ /** The merchant's logo (PNG or JPEG, a plain `GET` for an `<img>`); `null` when the merchant has none. */
968
+ logoUrl: string | null;
969
+ /**
970
+ * Reads the checkout (`GET`) and, while the invoice can change, follows it (server-sent events, else a read every
971
+ * `pollMs`). An event that brings an outcome (underpaid, held, paid, refunded, expired, cancelled) updates
972
+ * `checkout.invoice.amountPaid` at once and reads the checkout again for the rest (`review`, the refund, the invoice
973
+ * that replaced it, `autoCharge`). A checkout whose invoice has a payment on its way (a reloaded or reopened page)
974
+ * starts in `confirming`, not at the payment, and the store follows that payment as its own. One whose latest
975
+ * payment Quasar could not confirm (its latest rejection is not `final`), or that has a payment that may still arrive
976
+ * ({@link CheckoutState.invoicePaymentUnresolved}) with none on its way, starts in `unconfirmed` (`expired` stays
977
+ * `expired`) with `error` `payment_unconfirmed`; one whose latest transaction does not pay the invoice starts with
978
+ * `error` `payment_mismatch` (in `unconfirmed`, with no plain payment, when the rejection is not `final`). A latest
979
+ * rejection that Quasar reads again (`not_found`, `timeout`, `replaced`) no longer counts once Quasar reports no
980
+ * payment that may still arrive (`unresolvedPayments` `0`): Quasar resolved it. Hand-overs this tab kept
981
+ * ({@link CheckoutState.submit}) that Quasar does not list yet are handed over again at once. It stops at a final
982
+ * state, except `expired` and `cancelled` while {@link CheckoutState.paymentOnTheWay} or
983
+ * {@link CheckoutState.invoicePaymentOnTheWay}: it keeps the event stream while Quasar keeps it open for that payment, and
984
+ * otherwise closes it and reads the checkout every 20 seconds (or every `pollMs`, when longer), until the payment
985
+ * ends the invoice (`paid`, `held`), fails, Quasar reports no payment on its way, {@link CheckoutState.followingUntil}
986
+ * comes, or `destroy` is called. A cancelled invoice with a payment that may still arrive
987
+ * and none on its way opens with `error` `payment_unconfirmed`, like an expired one. It never expires the invoice by
988
+ * the clock: past `dueAt`, a confirming payment stays `confirming` until Quasar reports the outcome. Sets `phase`
989
+ * `error` when the link is wrong or retired.
990
+ *
991
+ * @returns Resolves when the checkout is read.
992
+ */
993
+ load: () => Promise<void>;
994
+ /**
995
+ * Chooses a payment method; a quote for another method is dropped.
996
+ *
997
+ * @param methodId - One of `checkout.methods`.
998
+ * @param options - `keepError`: the page chose it, not the buyer, so what it says about a payment that left the wallet
999
+ * stays.
1000
+ */
1001
+ selectMethod: (methodId: string, options?: {
1002
+ keepError?: boolean;
1003
+ }) => void;
1004
+ /**
1005
+ * Sets the connected wallet; a quote locked to another wallet is dropped.
1006
+ *
1007
+ * @param payer - CAIP-10 account (`eip155:8453:0x…`, `solana:<genesis hash>:<address>`), or `null` for a Solana Pay
1008
+ * QR payment from another device.
1009
+ */
1010
+ setPayer: (payer: string | null) => void;
1011
+ /** Forgets the last error: the buyer left what it was about. */
1012
+ clearError: () => void;
1013
+ /**
1014
+ * Ticks or clears the merchant's consent (`checkout.consent`): a required one must be ticked before
1015
+ * {@link CheckoutState.requestQuote}, which sends it with the quote.
1016
+ *
1017
+ * @param given - Whether the buyer ticked it.
1018
+ */
1019
+ setConsentGiven: (given: boolean) => void;
1020
+ /**
1021
+ * Forgets the locked quote and goes back to choosing a method ("Back to methods"), also for a Solana Pay QR quote
1022
+ * that no other action drops; from the quote of a rest it goes back to the `underpaid` outcome. Nothing changes once
1023
+ * the payment is on its way (`submitting`, `confirming`) or done. Quasar keeps the lock until it ends: a payment of the
1024
+ * old quote that still arrives is credited.
1025
+ */
1026
+ releaseQuote: () => void;
1027
+ /**
1028
+ * Screens the payer and locks the price (`POST quote`).
1029
+ *
1030
+ * @returns The quote, or `null` with `error` set (`phase` `blocked` when AML refuses the wallet, `buyer` when the
1031
+ * details come first; `consent_required`, without a request, when a required consent is not ticked;
1032
+ * `payer_proof_required`, without a request while {@link CheckoutState.payerProof} is `required`, and from Quasar
1033
+ * for a wallet it wants confirmed first, `phase` back at `selectMethod`). On an underpaid invoice it prices the
1034
+ * rest ({@link CheckoutQuote.amountDue}) at the rate of the first quote, with no consent asked again, and refuses
1035
+ * with `409`, back at `underpaid`: `payer_mismatch` and {@link CheckoutError.originWallet} (the rest is paid from the
1036
+ * wallet that paid first) or `rest_method_mismatch` (in the method that paid first).
1037
+ */
1038
+ requestQuote: () => Promise<CheckoutQuote | null>;
1039
+ /**
1040
+ * Confirms that the connected wallet pays this invoice: asks Quasar for the exact message
1041
+ * (`POST payer-proof/message`), has `sign` sign it with the wallet (a free signature that moves no funds), and sends
1042
+ * the signature (`POST payer-proof`), which Quasar checks (an ordinary wallet's by recovery, a smart wallet's through
1043
+ * its contract) and records for the invoice. Call it right after the wallet and the method are chosen, while
1044
+ * {@link CheckoutState.payerProof} is `required`, and before a transfer or a paymaster payment while it is
1045
+ * `unless_relayed`. A wallet confirms an invoice once: a wallet Quasar or this page already knows as confirmed asks
1046
+ * for no signature (also one Quasar answers `proven` for, confirmed from another tab), and a method that needs none
1047
+ * (`not_needed`) answers `true` at once. The page stays in its phase meanwhile ({@link CheckoutState.payerProof} is
1048
+ * `confirming` for the wallet being asked before the price, never for another one or an `unless_relayed` one). A new call replaces a confirmation under way, so a
1049
+ * prompt the buyer closed that never answers blocks nothing: call it again to try again. The replaced attempt's late
1050
+ * answer only records the wallet's proof (no `error`, no phase change). Side effect: a wallet prompt through `sign`.
1051
+ *
1052
+ * @param sign - Signs the message with the wallet ({@link PayerProofSigner}).
1053
+ * @returns Whether the wallet confirmed the invoice; {@link CheckoutState.payerProof} is then `confirmed`. `false`
1054
+ * for a replaced attempt that did not confirm it (nothing changes), and with `error` set: `payer_required` (no wallet),
1055
+ * `request_failed` (an `api` without the payer proof calls), `payer_proof_declined` (`sign` rejected: the buyer
1056
+ * refused, or the wallet cannot sign messages), or Quasar's refusal (`payer_proof_invalid`, `payer_proof_unavailable`,
1057
+ * `payer_chain_mismatch`, `origin_required`, `rate_limited`, `too_many_payers`, `invoice_not_payable`;
1058
+ * `payer_not_expected` sets `phase` `blocked`, `invoice_expired` `expired`).
1059
+ */
1060
+ confirmPayer: (sign: PayerProofSigner) => Promise<boolean>;
1061
+ /**
1062
+ * The buyer closed the wallet's window of the confirmation: the request stays open in the wallet, and a signature it
1063
+ * gives later still counts, while a refusal it gives later changes nothing on the page, wherever the buyer went.
1064
+ */
1065
+ abandonConfirmation: () => void;
1066
+ /**
1067
+ * Sends the buyer details the checkout collects (`POST buyer`); the invoice document is issued with them.
1068
+ *
1069
+ * @param buyer - The details.
1070
+ * @param options - `company: true` for an invoice for a company (name, address line, country and tax ID needed).
1071
+ * @returns Whether they were taken.
1072
+ */
1073
+ setBuyer: (buyer: Buyer, options?: {
1074
+ company?: boolean;
1075
+ }) => Promise<boolean>;
1076
+ /**
1077
+ * Changes the language of the page and of the documents still to be issued (`POST locale`).
1078
+ *
1079
+ * @param locale - The language.
1080
+ * @returns Whether it was saved.
1081
+ */
1082
+ setLocale: (locale: InvoiceLocale) => Promise<boolean>;
1083
+ /**
1084
+ * Hands Quasar the transaction the wallet sent (`POST submit`). For an EIP-5792 batch, pass the hash of its
1085
+ * transaction (from `wallet_getCallsStatus`), not the batch ID.
1086
+ *
1087
+ * The wallet has already sent the transaction, so a hand-over that gets no answer (the network, HTTP 408, 429 or
1088
+ * 5xx, `too_many_pending`) is not a failed payment: `txKey` keeps the transaction, `phase` is `unconfirmed` with
1089
+ * `error` `payment_unconfirmed` (`reason` `submit_retrying`), and the store hands it over again after 2, 4, 8, 16
1090
+ * and then every 30 seconds (Quasar takes a transaction once per `txKey`), for up to an hour or until `destroy`;
1091
+ * after the hour `error.reason` is `submit_failed` (Quasar never received it: the buyer contacts the merchant with
1092
+ * the transaction hash if money left the wallet). Once Quasar takes it, the store follows it as below; taken while
1093
+ * the invoice is already `paid`, `held` or `refunded`, it leaves the phase as it is (Quasar records it for the
1094
+ * merchant) and the store reads the checkout once more; taken while it is `expired` or `cancelled`, the phase stays
1095
+ * and the store follows the payment until Quasar holds it (or pays the expired invoice in its grace).
1096
+ *
1097
+ * Quasar answers `409 tx_already_used` when it tracks the transaction for another invoice whose page handed it over
1098
+ * first (EVM invoices share the merchant's address, so anyone who saw the hash may have done so). That is not a
1099
+ * refusal of the payment: Quasar records this invoice's claim and credits the transaction to the invoice whose quoted
1100
+ * wallet sent it. The store follows it as its payment, with `phase` `unconfirmed` (the outcome's on an expired or
1101
+ * cancelled invoice) and `error` `payment_unconfirmed` with `reason` `tx_already_used`, and offers no payment
1102
+ * ({@link CheckoutState.payAgain} does nothing) until Quasar decides. It is not handed over again. Any other refusal
1103
+ * (4xx) is not retried either.
1104
+ *
1105
+ * Until Quasar takes or refuses it, the hand-over is kept in the tab's `sessionStorage`
1106
+ * ({@link CheckoutStoreOptions.handOverStorage}), so a page closed or reloaded meanwhile hands it over again on the
1107
+ * next {@link CheckoutState.load} of the same checkout.
1108
+ *
1109
+ * @param params - `txKey`: transaction hash or Solana signature; `from`: the sender, for a Solana Pay quote without
1110
+ * a payer; `connectorType`: the wallet connector, for the record.
1111
+ * @returns Whether Quasar took it; `phase` is then `confirming` (`expired` or `cancelled` when the invoice expired or
1112
+ * was cancelled meanwhile: Quasar still takes the transaction, and holds it as a late payment) and
1113
+ * {@link CheckoutState.paymentOnTheWay} `true`. `false` while the hand-over is retried, after a refusal, and for
1114
+ * `tx_already_used` (followed all the same).
1115
+ */
1116
+ submit: (params: {
1117
+ txKey: string;
1118
+ from?: string;
1119
+ connectorType?: string;
1120
+ methodId?: string;
1121
+ }) => Promise<boolean>;
1122
+ /**
1123
+ * Sends the payer's signature of `quote.gasless.relay.typedData` (`POST relay`): Quasar sends the transfer and the
1124
+ * merchant pays the gas.
1125
+ *
1126
+ * @param signature - The 65-byte EIP-712 signature.
1127
+ * @returns Whether it was relayed; `phase` is then `confirming` and {@link CheckoutState.paymentOnTheWay} `true`.
1128
+ * `txKey` is the transfer's hash when Quasar returns it, `null` while the relay is still `sending`.
1129
+ */
1130
+ relay: (signature: string) => Promise<boolean>;
1131
+ /**
1132
+ * Passes the wallet's answer to `wallet_grantPermissions` for a subscription's automatic charges (`POST permission`).
1133
+ *
1134
+ * Quasar takes a permission only from a wallet that confirmed the invoice: with `sign`, a refusal for that has the
1135
+ * wallet sign the free confirmation and the permission is passed again.
1136
+ *
1137
+ * @param params - The granting account, the permission context, its delegation manager and dependencies.
1138
+ * @param sign - Has the wallet sign Quasar's message, as for {@link CheckoutState.confirmPayer}.
1139
+ * @returns Whether Quasar accepted it; `checkout.autoCharge` is then `active`.
1140
+ */
1141
+ grantPermission: (params: GrantPermissionParams, sign?: PayerProofSigner) => Promise<boolean>;
1142
+ /**
1143
+ * Tells Quasar that the wallet sent the transaction that revokes the permission (`autoCharge.revocation`): Quasar asks
1144
+ * the chain (`POST permission/revoked`), and `checkout.autoCharge` follows what it finds. Neither the phase nor the
1145
+ * error of the store change: it may be asked from any screen, and what that screen says stands.
1146
+ *
1147
+ * @returns Whether the permission has ended; `false` while the chain does not show it yet; `null` when Quasar could not
1148
+ * be asked.
1149
+ */
1150
+ permissionRevoked: () => Promise<boolean | null>;
1151
+ /**
1152
+ * Goes back to the payment from `unconfirmed`, once the buyer checked that the payment did not leave the wallet:
1153
+ * `awaitingPayment` while the locked quote is valid, `underpaid` for the rest of an underpaid invoice, otherwise
1154
+ * `selectMethod` (the expired quote is dropped); the outcome of an invoice that no longer takes a payment. Nothing
1155
+ * changes in another phase, nor while `error.reason` is `tx_already_used` or `settlement_failed`: that payment left
1156
+ * the wallet and may still pay the invoice.
1157
+ */
1158
+ payAgain: () => void;
1159
+ /** Stops following the invoice (closes the event stream or the reads). Call it when the page goes away. */
1160
+ destroy: () => void;
1161
+ }
1162
+ /** The part of the browser's `EventSource` the store uses. */
1163
+ interface EventSourceLike {
1164
+ /** `2` once closed for good. */
1165
+ readyState: number;
1166
+ /** Called on a failure; the browser reconnects unless `readyState` is `2`. */
1167
+ onerror: ((event: unknown) => void) | null;
1168
+ /** Listens to named events. */
1169
+ addEventListener: (type: string, listener: (event: {
1170
+ data: string;
1171
+ }) => void) => void;
1172
+ /** Closes the stream. */
1173
+ close: () => void;
1174
+ }
1175
+ /** A constructor of {@link EventSourceLike}, such as the browser's `EventSource`. */
1176
+ type EventSourceConstructor = new (url: string) => EventSourceLike;
1177
+ /** Options of {@link createCheckoutStore}. */
1178
+ interface CheckoutStoreOptions {
1179
+ /** The checkout token of the invoice (from its `checkoutToken` or the end of its `payUrl`). */
1180
+ token: string;
1181
+ /** The Quasar API. Defaults to {@link BASE_API_URL}. */
1182
+ baseUrl?: string;
1183
+ /** How often to read the invoice when there is no event stream, in milliseconds. Defaults to `5000`. */
1184
+ pollMs?: number;
1185
+ /**
1186
+ * The `EventSource` to follow the invoice with. Defaults to the global one; `null` reads the invoice every `pollMs`
1187
+ * instead.
1188
+ */
1189
+ EventSource?: EventSourceConstructor | null;
1190
+ /**
1191
+ * The calls to make instead of the HTTP ones of `token` and `baseUrl`, for tests and simulations (the TUWA docs
1192
+ * Playground). With your own calls, pass `EventSource: null` too, or the store opens `api.url('events')`.
1193
+ */
1194
+ api?: CheckoutApi;
1195
+ /**
1196
+ * Where the store keeps the hand-overs of transactions the wallet sent that Quasar has not taken yet
1197
+ * ({@link CheckoutState.submit}), so that the next {@link CheckoutState.load} of the same checkout hands them over
1198
+ * again. Defaults to the browser's `sessionStorage` (the tab, until it closes), or none with your own `api`; `null`
1199
+ * keeps nothing. The entry is `quasar-sdk:checkoutHandOvers:<token>`: a JSON array of
1200
+ * `{ txKey, from?, connectorType?, at }` (the transaction, the sending wallet and the wallet connector as passed to
1201
+ * `submit`, and when the store first got it, in milliseconds since 1970), removed once Quasar takes or refuses it.
1202
+ * Failures of the storage are ignored.
1203
+ */
1204
+ handOverStorage?: CheckoutHandOverStorage | null;
1205
+ }
1206
+ /** The part of the browser's `Storage` (`sessionStorage`) the checkout store keeps its hand-overs in. */
1207
+ interface CheckoutHandOverStorage {
1208
+ /** Reads an entry; `null` when there is none. */
1209
+ getItem: (key: string) => string | null;
1210
+ /** Writes an entry. */
1211
+ setItem: (key: string, value: string) => void;
1212
+ /** Removes an entry. */
1213
+ removeItem: (key: string) => void;
1214
+ }
1215
+ /** The store {@link createCheckoutStore} returns: a vanilla Zustand store (use it with `useStore` of `zustand`). */
1216
+ type CheckoutStore = StoreApi<CheckoutState>;
1217
+ /**
1218
+ * Creates the headless checkout of one invoice for the buyer's browser: a vanilla Zustand store with the invoice,
1219
+ * its methods, the locked quote and where the payment stands, and the actions that move it on. The wallet is the
1220
+ * app's: the store never sends a transaction, it returns `quote.instructions` (and `quote.gasless`) for Pulsar or the
1221
+ * wallet to execute, and takes the result with `submit` or `relay`. It needs no key: the checkout token opens this
1222
+ * invoice only. `@tuwaio/nova-payments` renders it.
1223
+ *
1224
+ * Side effects: requests to `<baseUrl>/v1/payments/checkout/<token>` (the page's origin must be one of the app's
1225
+ * domains, when it lists any; its `Origin` is the site the payer proof message names); a wallet prompt to sign a
1226
+ * message through the `sign` passed to {@link CheckoutState.confirmPayer}, never by itself; after `load`, an `EventSource` on its `events` route (or a `GET` every `pollMs`) until
1227
+ * the invoice reaches a final state or `destroy` is called (an invoice that expired or was cancelled while a payment
1228
+ * confirms is followed on, for a limited time, until that payment ends it as `paid` or `held`), and a `GET` of the
1229
+ * checkout when an event brings an outcome. A hand-over Quasar has not taken yet is kept in the tab's `sessionStorage` under
1230
+ * `quasar-sdk:checkoutHandOvers:<token>` ({@link CheckoutStoreOptions.handOverStorage}) until Quasar takes or refuses
1231
+ * it; nothing else is stored in the browser.
1232
+ *
1233
+ * @param options - The checkout token and optional overrides.
1234
+ * @returns The store; call `load()` first.
1235
+ *
1236
+ * @example
1237
+ * ```ts
1238
+ * import { createCheckoutStore } from '@tuwaio/quasar-sdk/checkout';
1239
+ *
1240
+ * const checkout = createCheckoutStore({ token });
1241
+ * await checkout.getState().load();
1242
+ * checkout.getState().selectMethod(checkout.getState().checkout!.methods[0].id);
1243
+ * checkout.getState().setPayer(`eip155:8453:${address}`);
1244
+ * // An EVM payment the wallet sends: the wallet confirms the invoice first, with a free signature
1245
+ * if (checkout.getState().payerProof === 'required') {
1246
+ * await checkout.getState().confirmPayer((message) => walletClient.signMessage({ account: address, message }));
1247
+ * }
1248
+ * const quote = await checkout.getState().requestQuote();
1249
+ * // Send quote.instructions with the wallet, then:
1250
+ * await checkout.getState().submit({ txKey: hash });
1251
+ * ```
1252
+ */
1253
+ declare function createCheckoutStore(options: CheckoutStoreOptions): CheckoutStore;
1254
+
1255
+ /**
1256
+ * @file The headless refund store: a merchant refunds a payment from a connected wallet in the browser.
1257
+ */
1258
+
1259
+ /**
1260
+ * Where a refund stands: `idle`; `requesting` (Quasar prices it); `awaitingTransfer` (send `instructions` from the
1261
+ * merchant's wallet, then {@link RefundState.submit}); `submitting`; `confirming` (Quasar follows the transaction);
1262
+ * `confirmed` (the credit note is issued) or `failed`.
1263
+ */
1264
+ type RefundPhase = 'idle' | 'requesting' | 'awaitingTransfer' | 'submitting' | 'confirming' | 'confirmed' | 'failed';
1265
+ /** A refund transaction the wallet sent, kept until Quasar takes it or refuses it for good. */
1266
+ interface SentRefundTransaction {
1267
+ /** The transaction (hash or Solana signature). */
1268
+ txHash: string;
1269
+ /** The wallet that sent it, which may not be the one connected now. */
1270
+ from: string;
1271
+ }
1272
+ /** The state and actions of {@link createRefundStore}. */
1273
+ interface RefundState {
1274
+ /** Where the refund stands. */
1275
+ phase: RefundPhase;
1276
+ /** The refund, once asked for. */
1277
+ refund: Refund | null;
1278
+ /** What to send where: chain, asset, recipient (the payer) and amount in base units. */
1279
+ instructions: RefundStart['instructions'] | null;
1280
+ /** Why the last action failed; cleared by the next one. */
1281
+ error: CheckoutError | null;
1282
+ /**
1283
+ * The transaction the wallet sent for this refund that Quasar has not taken yet. While it is set, never ask a wallet
1284
+ * again: hand it over with {@link RefundState.submit}, or the buyer is refunded twice.
1285
+ */
1286
+ sentTransaction: SentRefundTransaction | null;
1287
+ /**
1288
+ * What a refund would send, through the option `preview`: nothing is asked for.
1289
+ *
1290
+ * @param invoiceId - The paid or held invoice.
1291
+ * @param params - Amount (everything left when omitted), mode and reason.
1292
+ * @returns The preview; `null` without the option, or when it failed (`error` says why).
1293
+ */
1294
+ preview: (invoiceId: string, params?: RefundParams) => Promise<RefundPreview | null>;
1295
+ /**
1296
+ * Asks for a refund of an invoice through `request`. When a transaction sent for an earlier refund of the invoice was
1297
+ * not handed over, that refund comes back instead (`sentTransaction`), or is followed when Quasar took it after all, and
1298
+ * nothing new is asked for while Quasar does not say what became of it.
1299
+ *
1300
+ * @param invoiceId - The paid or held invoice.
1301
+ * @param params - Amount (everything left when omitted), mode and reason.
1302
+ * @returns Whether it was asked for or came back; `phase` is then `awaitingTransfer` (`confirming` when taken).
1303
+ */
1304
+ start: (invoiceId: string, params?: RefundParams) => Promise<boolean>;
1305
+ /**
1306
+ * Continues a refund that waits for its transaction (asked for earlier, or through the API).
1307
+ *
1308
+ * @param refund - The refund, `requested`.
1309
+ * @param instructions - What to send, as `request` returned it.
1310
+ */
1311
+ resume: (refund: Refund, instructions: RefundStart['instructions']) => void;
1312
+ /**
1313
+ * Reads the refund again before a wallet is asked to send it. A window can stay open while the refund is withdrawn or sent
1314
+ * elsewhere, and a wallet asked then would pay out money nobody asks for any more. What it finds shows in `phase`.
1315
+ *
1316
+ * Side effects: one `get`; follows a refund it finds submitted.
1317
+ *
1318
+ * @returns Whether a wallet may send the refund now: `false` when Quasar does not answer (`error` says so), as it cannot be
1319
+ * known then, and while `sentTransaction` holds a transaction already sent for it.
1320
+ */
1321
+ recheck: () => Promise<boolean>;
1322
+ /**
1323
+ * Hands over the refund transaction through `submit`, then follows the refund through `get` until it is final. The
1324
+ * transaction stays in `sentTransaction`, and in {@link RefundStoreOptions.handOverStorage}, until Quasar takes it or
1325
+ * refuses it for good: a refusal of 4xx, other than one that says nothing about it (401, 403, 408, 425, 429).
1326
+ *
1327
+ * @param params - The transaction and the merchant wallet it was sent from.
1328
+ * @returns Whether it was taken; `phase` is then `confirming`.
1329
+ */
1330
+ submit: (params: SubmitRefundParams) => Promise<boolean>;
1331
+ /** Stops reading the refund and keeps everything else, for when the window goes away. */
1332
+ stopFollowing: () => void;
1333
+ /** Reads the refund again after {@link RefundState.stopFollowing}, while it is `confirming`. */
1334
+ keepFollowing: () => void;
1335
+ /** Stops following and returns to `idle`. */
1336
+ reset: () => void;
1337
+ }
1338
+ /**
1339
+ * Options of {@link createRefundStore}: the three calls, which the app runs on its server with the secret key. A call refuses by
1340
+ * throwing: the `QuasarSDKError` of the SDK, or from an app's own route an `Error` with the `code` of the refusal, its `status`
1341
+ * and optionally `text`, what to show in the reader's language. A Server Action cannot pass such an error to the browser: wrap it
1342
+ * in {@link refundAction}. Any other error reads as `request_failed`: Quasar did not answer.
1343
+ */
1344
+ interface RefundStoreOptions {
1345
+ /** Asks for the refund, for example a Server Action that calls `quasar.payments.refund`. */
1346
+ request: (invoiceId: string, params: RefundParams) => Promise<RefundStart | RefusedRefundCall>;
1347
+ /** Shows what a refund would send, for example through `quasar.payments.previewRefund`. Optional. */
1348
+ preview?: (invoiceId: string, params: RefundParams) => Promise<RefundPreview | RefusedRefundCall>;
1349
+ /** Hands over its transaction, for example through `quasar.payments.submitRefund`. */
1350
+ submit: (refundId: string, params: SubmitRefundParams) => Promise<Refund | RefusedRefundCall>;
1351
+ /** Reads the refund, for example through `quasar.payments.getRefund`. */
1352
+ get: (refundId: string) => Promise<Refund | RefusedRefundCall>;
1353
+ /** How often to read a refund being confirmed, in milliseconds. Defaults to `5000`. */
1354
+ pollMs?: number;
1355
+ /**
1356
+ * Where a transaction the wallet sent is kept until Quasar takes or refuses it, so that a page closed or reloaded meanwhile
1357
+ * hands it over instead of having the wallet send the refund again. Defaults to the browser's `sessionStorage` (the tab,
1358
+ * until it closes); `null` keeps it in memory only.
1359
+ */
1360
+ handOverStorage?: CheckoutHandOverStorage | null;
1361
+ }
1362
+ /** What {@link refundAction} returns for a call Quasar refused. */
1363
+ interface RefusedRefundCall {
1364
+ /** The refusal: its `code`, HTTP `status` and message. */
1365
+ refused: {
1366
+ code: string;
1367
+ message: string;
1368
+ status?: number;
1369
+ };
1370
+ }
1371
+ /**
1372
+ * Wraps a call of the refund store for a Server Action: React passes no fields of a thrown error to the browser, so without it
1373
+ * every refusal reads as "Quasar did not answer". The action returns the refusal, and the store takes it as thrown.
1374
+ *
1375
+ * @param call - The call, for example `quasar.payments.refund` bound to its client.
1376
+ * @returns The same call, which returns a refusal of Quasar instead of throwing it. Any other error is thrown as it was.
1377
+ *
1378
+ * @example
1379
+ * ```ts
1380
+ * 'use server';
1381
+ * export const refundInvoice = refundAction((invoiceId: string, params: RefundParams) =>
1382
+ * quasar.payments.refund(invoiceId, params),
1383
+ * );
1384
+ * ```
1385
+ */
1386
+ declare function refundAction<Args extends unknown[], Result>(call: (...args: Args) => Promise<Result>): (...args: Args) => Promise<Result | RefusedRefundCall>;
1387
+ /** The store {@link createRefundStore} returns: a vanilla Zustand store. */
1388
+ type RefundStore = StoreApi<RefundState>;
1389
+ /**
1390
+ * Creates the headless refund of a payment from the merchant's connected wallet: ask for the refund, send its
1391
+ * `instructions` with the wallet (Pulsar or the wallet itself; the store sends nothing on-chain), hand over the
1392
+ * transaction, then follow the refund until Quasar confirms it and issues the credit note. The secret key never
1393
+ * reaches the browser: the store calls `request`, `submit` and `get`, which the app runs on its server (Server
1394
+ * Actions or its own routes calling `quasar.payments`).
1395
+ *
1396
+ * Side effects: the calls the app supplies; while `confirming`, `get` every `pollMs` for five minutes and every 30 seconds
1397
+ * after that, until the refund is final, `reset` or `stopFollowing` is called, or a read answers 401, 403 or 404; `sessionStorage` under `quasar-sdk:refundHandOvers` for a sent transaction Quasar has not taken
1398
+ * ({@link RefundStoreOptions.handOverStorage}).
1399
+ *
1400
+ * @param options - The three calls and the poll interval.
1401
+ * @returns The store.
1402
+ *
1403
+ * @example
1404
+ * ```ts
1405
+ * import { createRefundStore } from '@tuwaio/quasar-sdk/checkout';
1406
+ *
1407
+ * // refundInvoice, submitRefund and getRefund are Server Actions calling quasar.payments
1408
+ * const refunds = createRefundStore({ request: refundInvoice, submit: submitRefund, get: getRefund });
1409
+ * await refunds.getState().start(invoiceId, { reason: 'Order cancelled' });
1410
+ * // Send refunds.getState().instructions with the wallet, then:
1411
+ * await refunds.getState().submit({ txHash, from: address });
1412
+ * ```
1413
+ */
1414
+ declare function createRefundStore(options: RefundStoreOptions): RefundStore;
1415
+
1416
+ export { createCheckoutStore, createRefundStore, refundAction };
1417
+ export type { AutoChargeOffer, CheckoutApi, CheckoutConsent, CheckoutError, CheckoutHandOverStorage, CheckoutInvoice, CheckoutLine, CheckoutMerchant, CheckoutMethod, CheckoutPayerProof, CheckoutPhase, CheckoutQuote, CheckoutRejectedPayment, CheckoutReview, CheckoutState, CheckoutStatusEvent, CheckoutStore, CheckoutStoreOptions, CheckoutSubscription, CheckoutTheme, CheckoutView, EventSourceConstructor, EventSourceLike, GaslessOffer, GrantPermissionParams, PayerProofConfirmation, PayerProofKind, PayerProofMessage, PayerProofNeed, PayerProofSigner, PayerProofStatus, PaymentInstructions, PermissionRequest, RefundPhase, RefundState, RefundStore, RefundStoreOptions, RefusedRefundCall, SentRefundTransaction, TransferAuthorizationTypedData };