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