@rio-pay/sdk 0.0.0-stage → 0.0.1-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,245 @@
1
+ import { type ReactNode } from "react";
2
+ import { type Problem, type CheckoutCopy, type Stage } from "../lib/payments";
3
+ import type { DecoratorItem, PayLine } from "../lib/payments-options";
4
+ export declare const CSS: string;
5
+ export declare const Spinner: ({ small }: {
6
+ small?: boolean;
7
+ }) => import("react").JSX.Element;
8
+ declare const CheckMark: ({ size }: {
9
+ size?: number;
10
+ }) => import("react").JSX.Element;
11
+ declare const CopyIcon: () => import("react").JSX.Element;
12
+ export declare const Alert: () => import("react").JSX.Element;
13
+ export declare const Info: () => import("react").JSX.Element;
14
+ export { CheckMark, CopyIcon };
15
+ /** The USDC mark, inline so it needs no request and no dependency. */
16
+ export declare function UsdcLogo({ size }: {
17
+ size?: number;
18
+ }): import("react").JSX.Element;
19
+ export interface ChainBadge {
20
+ letter: string;
21
+ hue: number;
22
+ /** Official chain mark to draw instead of the letter (known chains only). */
23
+ icon?: "sei" | "base" | "solana" | null;
24
+ }
25
+ /** A plain inline mark for a token we ship no logo for: a coloured disc with its symbol's first letter (no request, no image). */
26
+ export declare function TokenGlyph({ token, size }: {
27
+ token: {
28
+ symbol: string;
29
+ logo: string;
30
+ };
31
+ size?: number;
32
+ }): import("react").JSX.Element;
33
+ /** The token's mark (the USDC logo unless another token is given) with a small generated chain badge (a letter on a hue, no image) at its corner. */
34
+ export declare function TokenMark({ size, badge, token }: {
35
+ size?: number;
36
+ badge?: ChainBadge | null;
37
+ token?: {
38
+ symbol: string;
39
+ logo: string;
40
+ } | null;
41
+ }): import("react").JSX.Element;
42
+ /** The token's symbol (USDC unless another token is given) with the chain name as a small subscript. The chain name is service data. */
43
+ export declare function TokenLabel({ chain, symbol }: {
44
+ chain: string;
45
+ symbol?: string;
46
+ }): import("react").JSX.Element;
47
+ export interface ModalOption {
48
+ id: string;
49
+ chain: string;
50
+ /** The token this option spends when it is not USDC ("ETH"); with `logo` it picks the token mark. */
51
+ symbol?: string;
52
+ logo?: string;
53
+ badge?: ChainBadge | null;
54
+ balanceText: string | null;
55
+ disabled: boolean;
56
+ /** Short line under (or instead of) the balance: "Not enough", "Needs a different wallet", "Couldn't check". */
57
+ reason: string | null;
58
+ /** The balance is being read for the first time: show a skeleton. */
59
+ loading?: boolean;
60
+ }
61
+ export declare function OptionPicker({ options, selectedId, open, locked, label, amount, onOpenChange, onSelect, }: {
62
+ options: ModalOption[];
63
+ selectedId: string | null;
64
+ open: boolean;
65
+ locked: boolean;
66
+ label: string;
67
+ /** The read-only amount, shown on the right of the box. */
68
+ amount: ReactNode;
69
+ onOpenChange: (open: boolean) => void;
70
+ onSelect: (id: string) => void;
71
+ }): import("react").JSX.Element;
72
+ /**
73
+ * The chosen chain's balances (USDC, then gas), one line above the amount box. It cycles every ~2.5 s with a
74
+ * fade; hovering it, focusing into it or pressing its pause control holds it, and under prefers-reduced-motion
75
+ * the CSS shows both values side by side with nothing animating. A low-gas warning stays on screen instead of
76
+ * cycling away. Screen readers get one polite sentence, re-announced only when the chosen option changes.
77
+ */
78
+ export declare function Decorator({ items, paused, lowGas, pauseLabel, optionKey, interval, }: {
79
+ items: DecoratorItem[];
80
+ paused: boolean;
81
+ lowGas: string;
82
+ pauseLabel: string;
83
+ optionKey?: string | null;
84
+ interval?: number;
85
+ }): import("react").JSX.Element | null;
86
+ export interface WalletChipInfo {
87
+ /** "MetaMask"; null when the wallet did not name itself. */
88
+ name: string | null;
89
+ /** Shortened address for the chip. */
90
+ short: string;
91
+ address: string;
92
+ }
93
+ export interface WalletActions {
94
+ onCopy: () => void;
95
+ onSwitch: () => void;
96
+ onDisconnect: () => void;
97
+ }
98
+ /** The connected wallet: a button that opens copy address / switch wallet / disconnect (inert while a payment is in flight). */
99
+ export declare function WalletChip({ wallet, copy, actions, inert }: {
100
+ wallet: WalletChipInfo;
101
+ copy: CheckoutCopy;
102
+ actions: WalletActions | null;
103
+ inert: boolean;
104
+ }): import("react").JSX.Element;
105
+ export interface PickerWallet {
106
+ id: string;
107
+ name: string;
108
+ icon: string | null;
109
+ lastUsed: boolean;
110
+ }
111
+ /** The inline wallet chooser shown when several wallets are installed. */
112
+ export declare function WalletList({ wallets, busyId, copy, onPick }: {
113
+ wallets: PickerWallet[];
114
+ busyId: string | null;
115
+ copy: CheckoutCopy;
116
+ onPick: (id: string) => void;
117
+ }): import("react").JSX.Element;
118
+ export declare function Stepper({ stages, copy, tx, walletName }: {
119
+ stages: Stage[];
120
+ copy: CheckoutCopy;
121
+ tx: {
122
+ i: number;
123
+ total: number;
124
+ } | null;
125
+ walletName?: string | null;
126
+ }): import("react").JSX.Element;
127
+ export declare function ProblemNotice({ problem, copy, noticeRef, children, }: {
128
+ problem: Problem;
129
+ copy: CheckoutCopy;
130
+ noticeRef?: React.RefObject<HTMLDivElement | null>;
131
+ children?: ReactNode;
132
+ }): import("react").JSX.Element;
133
+ export declare function DoneView({ copy, amountText, receipt, finalizing, slot, returnHref, doneRef, onDone, }: {
134
+ copy: CheckoutCopy;
135
+ amountText: string;
136
+ receipt: string;
137
+ finalizing: boolean;
138
+ slot?: ReactNode;
139
+ returnHref: string | null;
140
+ doneRef?: React.RefObject<HTMLElement | null>;
141
+ onDone?: () => void;
142
+ }): import("react").JSX.Element;
143
+ /** Mirrors the real geometry (title row, label row, box, button) so nothing moves when the content arrives. */
144
+ export declare function PayingSkeleton(): import("react").JSX.Element;
145
+ /**
146
+ * A native modal <dialog>: browser-provided focus trap, Esc, inert background and top layer (so it also
147
+ * works inside a host's own dialog, whose transforms would break `position: fixed`). Closing plays a short
148
+ * exit (the dialog has none of its own) and keeps the content mounted until it ends.
149
+ */
150
+ export declare function ModalShell({ open, onClose, onBackdrop, titleId, children, initialFocus, }: {
151
+ open: boolean;
152
+ onClose: () => void;
153
+ /** Backdrop click. Defaults to `onClose`; pass a no-op to make the backdrop inert (mid-payment). */
154
+ onBackdrop?: () => void;
155
+ titleId: string;
156
+ children: ReactNode;
157
+ initialFocus?: React.RefObject<HTMLElement | null>;
158
+ }): import("react").JSX.Element;
159
+ export declare function Header({ title, titleId, titleRef, closeLabel, onClose, back, }: {
160
+ title: string;
161
+ titleId: string;
162
+ titleRef?: React.RefObject<HTMLHeadingElement | null>;
163
+ closeLabel: string;
164
+ onClose: (() => void) | null;
165
+ back?: {
166
+ label: string;
167
+ onClick: () => void;
168
+ } | null;
169
+ }): import("react").JSX.Element;
170
+ export interface Cta {
171
+ label: string;
172
+ disabled: boolean;
173
+ /** A step is running: the button keeps its place and colour and shows a spinner. */
174
+ busy: boolean;
175
+ /** A spinner on a disabled (muted) button, e.g. while balances are being read. */
176
+ spinner?: boolean;
177
+ onClick: () => void;
178
+ ref?: React.RefObject<HTMLButtonElement | null>;
179
+ }
180
+ /** What the buyer pays in their own token, as a polite status (so a price change is announced, never silent). */
181
+ export declare function PayLineView({ line, copy, onRetry }: {
182
+ line: PayLine;
183
+ copy: CheckoutCopy;
184
+ onRetry?: () => void;
185
+ }): import("react").JSX.Element;
186
+ export interface ModalCardProps {
187
+ copy: CheckoutCopy;
188
+ titleId: string;
189
+ titleRef?: React.RefObject<HTMLHeadingElement | null>;
190
+ /** Header text; defaults to `copy.title`. */
191
+ title?: string;
192
+ amountText: string;
193
+ /** "pick" = choose + connect + pay; "paying" = stepper; "done"; "manual" = pay another way; "wallets" = wallet chooser. */
194
+ phase: "loading" | "pick" | "paying" | "done" | "manual" | "wallets";
195
+ wallet?: WalletChipInfo | null;
196
+ walletActions?: WalletActions | null;
197
+ walletPicker?: {
198
+ wallets: PickerWallet[];
199
+ busyId: string | null;
200
+ onPick: (id: string) => void;
201
+ } | null;
202
+ onClose: (() => void) | null;
203
+ onBack?: (() => void) | null;
204
+ options: ModalOption[];
205
+ selectedId: string | null;
206
+ menuOpen: boolean;
207
+ onMenuChange: (open: boolean) => void;
208
+ onSelect: (id: string) => void;
209
+ decorator: DecoratorItem[];
210
+ /** "You pay 0.0004 ETH": what the buyer pays when the chosen option is another token. Absent for USDC options. */
211
+ payLine?: PayLine | null;
212
+ onPayLineRetry?: () => void;
213
+ /** Re-announce the decorator only when this changes (the chosen option). */
214
+ decoratorKey?: string | null;
215
+ /** Balances are being re-read in the background; the old numbers stay on screen. */
216
+ refreshing?: boolean;
217
+ cta: Cta | null;
218
+ message?: ReactNode;
219
+ notice?: ReactNode;
220
+ stages: Stage[] | null;
221
+ tx: {
222
+ i: number;
223
+ total: number;
224
+ } | null;
225
+ /** The connected wallet's name, for "Confirm in {wallet}". */
226
+ walletName?: string | null;
227
+ /** One calm line shown in stage 3 only (derived from the service's estimate). */
228
+ slow: boolean;
229
+ /** Asks before closing mid-payment; the card shows it inline. */
230
+ closeGuard?: {
231
+ onStay: () => void;
232
+ onClose: () => void;
233
+ } | null;
234
+ done: {
235
+ receipt: string;
236
+ finalizing: boolean;
237
+ slot?: ReactNode;
238
+ returnHref: string | null;
239
+ ref?: React.RefObject<HTMLElement | null>;
240
+ onDone?: () => void;
241
+ } | null;
242
+ manual?: ReactNode;
243
+ onPayAnotherWay: (() => void) | null;
244
+ }
245
+ export declare function ModalCard(p: ModalCardProps): import("react").JSX.Element;
@@ -0,0 +1,3 @@
1
+ export type { ChargeCreateResult, ChargeStatus, Confirmation, CreatedCharge, CheckoutCopy, PlatformChargeStatus, } from "./lib/payments";
2
+ export type { PostCreateChargeOptions } from "./lib/create-charge";
3
+ export type { WebhookEvent, WebhookEventType } from "./lib/events";
package/dist/index.js ADDED
File without changes
@@ -0,0 +1,28 @@
1
+ import type { ChargeCreateResult } from "./payments";
2
+ export interface PostCreateChargeOptions {
3
+ /** Default `"POST"`. */
4
+ method?: "POST" | "PUT" | "PATCH";
5
+ /** Merged over the default `content-type: application/json`. A function is called on every request, so it can read a token at call time. */
6
+ headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
7
+ /** Passed to `fetch` as is; when omitted, `fetch`'s own default applies. */
8
+ credentials?: RequestCredentials;
9
+ signal?: AbortSignal;
10
+ /** A custom `fetch`. Default: the global `fetch`, looked up at call time. */
11
+ fetch?: typeof fetch;
12
+ /** Picks the charge out of a success body. Default: the body itself (the flat charge). */
13
+ selectCharge?: (json: unknown) => unknown;
14
+ /** Reads the error text and code out of a failure body. Default: the top-level `error` and `code` strings. */
15
+ selectError?: (json: unknown) => {
16
+ error?: string;
17
+ code?: string;
18
+ } | null | undefined;
19
+ }
20
+ /**
21
+ * POSTs `body` (JSON-stringified unless it is already a string) to your route and resolves a `ChargeCreateResult`.
22
+ *
23
+ * Your route answers a success (2xx) with the created charge as JSON (flat by default; see `selectCharge`) and a failure
24
+ * with an error status and `{ error, code? }` (see `selectError`). A 409 means the order already has a different charge
25
+ * (for example its amount changed): create a new order id. A 2xx without an object charge, a non-JSON body, a network
26
+ * error or an abort resolve to status 503.
27
+ */
28
+ export declare function postCreateCharge(url: string, body: unknown, options?: PostCreateChargeOptions): Promise<ChargeCreateResult>;
@@ -0,0 +1,10 @@
1
+ export type WebhookEventType = "charge.confirmed" | "charge.confirmation_revoked" | "charge.funded" | "charge.settled" | "charge.underpaid" | "charge.expired" | "merchant.claimed" | "merchant.activated" | "subscription.created" | "subscription.activated" | "subscription.renewed" | "subscription.past_due" | "subscription.expired" | "subscription.canceled" | "subscription.reminder_due" | "ping";
2
+ export interface WebhookEvent<T = unknown> {
3
+ /** Unique per event; delivery is at-least-once, so dedupe on it. */
4
+ id: string;
5
+ /** A `WebhookEventType`, or a type this SDK version does not know yet (the service may add types; handle unknown ones by ignoring them). */
6
+ type: WebhookEventType | (string & {});
7
+ /** ISO 8601 timestamp. */
8
+ created: string;
9
+ data: T;
10
+ }
@@ -0,0 +1,48 @@
1
+ import { type SseFrame } from "./payments-sse";
2
+ export declare class VendorApiError extends Error {
3
+ status: number;
4
+ code: string;
5
+ constructor(status: number, code: string, message: string);
6
+ }
7
+ export interface RetryOptions {
8
+ /** Extra attempts after the first. */
9
+ retries?: number;
10
+ baseMs?: number;
11
+ }
12
+ /** Transient = nothing reached the service (status 0), a timeout, rate limiting or a 5xx. Everything else is deterministic. */
13
+ export declare function isTransient(e: unknown, opts?: {
14
+ retryRateLimit?: boolean;
15
+ }): boolean;
16
+ /** Exponential backoff with jitter. Only for calls that are idempotent (every call this client makes is). */
17
+ export declare function withRetry<T>(fn: () => Promise<T>, opts?: RetryOptions & {
18
+ retryRateLimit?: boolean;
19
+ }): Promise<T>;
20
+ export declare function vendorApi(serviceUrl: string, chargeId: string, secret: string, retry?: RetryOptions): {
21
+ charge: <T>() => Promise<T>;
22
+ /** GET /events: reads the charge's event stream with fetch (the secret stays in the header). Resolves when the stream ends or `signal` aborts; rejects when it cannot be opened or breaks. */
23
+ events: (onFrame: (f: SseFrame) => void, signal?: AbortSignal) => Promise<void>;
24
+ /** GET /options: the USDC options the buyer can pay with. */
25
+ options: () => Promise<unknown>;
26
+ /** POST /pay: what the buyer's wallet must sign or send for `optionId`. The response never carries fee or quote details. */
27
+ pay: (optionId: string, buyer: string) => Promise<unknown>;
28
+ authorize: (body: unknown) => Promise<unknown>;
29
+ /** POST /rails/cctp/submit: the service checks the buyer-signed Solana transaction, pays its fee and sends it. Returns `{ signature }`. Safe to retry. */
30
+ submitSolana: (transaction: string) => Promise<unknown>;
31
+ /** Reports a payment the buyer just sent so the service can follow it. `buyer` binds the hint to the paying wallet (LI.FI). */
32
+ fundingAttempt: (sourceChain: string, txHash: string, opts?: {
33
+ rail?: string;
34
+ buyer?: string;
35
+ }) => Promise<unknown>;
36
+ /**
37
+ * GET /rails/lifi/quote: what it takes to pay with another token, priced and already validated by the service
38
+ * (the browser is never handed unvalidated transaction data). Not retried: a quote is cheap to ask again and goes stale.
39
+ */
40
+ lifiQuote: (optionId: string, buyer: string) => Promise<unknown>;
41
+ };
42
+ /**
43
+ * Read-only JSON-RPC against a list of public endpoints (an option's `rpc_urls`): tries each in
44
+ * order and throws the last failure. Used for balances only; never carries a secret.
45
+ */
46
+ export declare function jsonRpc(urls: string[], method: string, params: unknown[], opts?: {
47
+ timeoutMs?: number;
48
+ }): Promise<unknown>;
@@ -0,0 +1,225 @@
1
+ import { type AddChainParams, type CreatedCharge, type CheckoutCopy } from "./payments";
2
+ export type OptionKind = "evm" | "solana";
3
+ export interface PaymentOption {
4
+ id: string;
5
+ kind: OptionKind;
6
+ /** `lifi` = "pay with another token": the buyer's source token on a source chain, priced by a quote shown before paying. Absent for the USDC options. */
7
+ rail?: "lifi";
8
+ chain: {
9
+ id: number | string;
10
+ name: string;
11
+ display_name: string | null;
12
+ };
13
+ /** The token this option spends. For USDC options that is USDC; for `lifi` options any supported token (`native` = the chain's gas token, address 0x0). */
14
+ token: {
15
+ symbol: string;
16
+ address: string;
17
+ decimals: number;
18
+ logo: string;
19
+ native?: boolean;
20
+ };
21
+ native: {
22
+ symbol: string;
23
+ decimals: number;
24
+ } | null;
25
+ default: boolean;
26
+ route: "direct" | "bridge";
27
+ /** The service pays the network fee for this option, so the buyer needs no gas. */
28
+ gasless?: true;
29
+ add_chain: AddChainParams | null;
30
+ rpc_urls: string[];
31
+ }
32
+ /** GET /options body (`{ data: [...] }`) or a bare array; malformed entries are dropped, order is kept. */
33
+ export declare function parseOptions(raw: unknown): PaymentOption[];
34
+ /** The chain name shown on the token chip: service data, never hardcoded. */
35
+ export declare function optionLabel(o: PaymentOption): string;
36
+ /** The settlement chain's direct option, synthesised from the created charge: what "pay another way" shows when /options is unavailable. */
37
+ export declare function settlementOption(created: Pick<CreatedCharge, "chain">): PaymentOption;
38
+ /**
39
+ * The option "pay another way" shows the charge address on (a plain transfer, e.g. an exchange withdrawal): the settlement
40
+ * chain's USDC option, or one built from the charge when the options could not be loaded, so the address always carries its
41
+ * chain name.
42
+ */
43
+ export declare const manualOption: (created: Pick<CreatedCharge, "chain">, options: PaymentOption[]) => PaymentOption;
44
+ /** Direct EVM pays and gasless options are a signature only (the service pays); everything else spends the buyer's own gas. */
45
+ export declare function needsGas(o: PaymentOption): boolean;
46
+ export type OptionBalance = {
47
+ status: "loading";
48
+ } | {
49
+ status: "ok";
50
+ usdc: bigint;
51
+ native: bigint | null;
52
+ } | {
53
+ status: "error";
54
+ };
55
+ export type Balances = Record<string, OptionBalance | undefined>;
56
+ /** "19.60", "0.1234", "1,234.00": at least 2 and at most 4 decimals, truncated (never rounded up). */
57
+ export declare function formatTokenAmount(raw: bigint, decimals: number): string;
58
+ export declare const isLifi: (o: Pick<PaymentOption, "rail">) => boolean;
59
+ /**
60
+ * Whether a known balance can pay. USDC options need the whole amount. A LI.FI option's price in its own token is only known
61
+ * after a quote, so a balance only has to be non-zero here; the quote (quoteAffordability) makes the exact call.
62
+ */
63
+ export declare function hasEnough(o: PaymentOption, bal: Extract<OptionBalance, {
64
+ status: "ok";
65
+ }>, amount: string): boolean;
66
+ export interface RowState {
67
+ balanceText: string | null;
68
+ disabled: boolean;
69
+ /** Why the row says what it says: short on USDC, a different wallet kind is needed, or the read failed. */
70
+ reason: "not_enough" | "needs_wallet" | "unreadable" | null;
71
+ /** The balance is being read for the first time: the row shows a skeleton. */
72
+ loading: boolean;
73
+ }
74
+ /**
75
+ * What one dropdown row shows: the balance once known; disabled only when it is known to be short.
76
+ * With `connected`, a row whose wallet kind is not connected (while the other kind is) says so instead of staying blank.
77
+ */
78
+ export declare function rowState(o: PaymentOption, bal: OptionBalance | undefined, amount: string, connected?: Record<OptionKind, boolean>): RowState;
79
+ /**
80
+ * Display order for the dropdown: options the buyer can pay with (largest balance first, settlement option
81
+ * first on a tie), then options we cannot judge (other wallet kind, unreadable), then short ones. Every option
82
+ * stays listed. While any connected option is still being read the service order is kept, so rows never
83
+ * jump around mid-load.
84
+ */
85
+ export declare function sortOptions(options: PaymentOption[], balances: Balances, connected: Record<OptionKind, boolean>, amount: string): PaymentOption[];
86
+ /**
87
+ * The rows the dropdown lists: nothing until a wallet is connected (the chip just says USDC), then only the connected
88
+ * wallet's options that hold something. A balance below the amount still counts (the row shows it, disabled). Options
89
+ * still being read or that could not be read stay, since a zero is not known; every other zero drops out.
90
+ */
91
+ export declare function visibleOptions(options: PaymentOption[], balances: Balances, connected: Record<OptionKind, boolean>): PaymentOption[];
92
+ /**
93
+ * A small generated chain mark for every option (no images): a letter from the chain's label and one of six hues.
94
+ * Hues come from a hash of the label and are probed so the options in one list never share one (up to six).
95
+ */
96
+ export declare function chainBadges(labels: string[]): {
97
+ letter: string;
98
+ hue: number;
99
+ }[];
100
+ /**
101
+ * Best option to pre-select: sufficient balance first (settlement option preferred, then the largest balance).
102
+ * Options whose balance could not be read stay eligible as a last resort so a flaky endpoint is never a dead end.
103
+ */
104
+ export declare function pickBestOption(options: PaymentOption[], balances: Balances, amount: string): string | null;
105
+ export interface DecoratorItem {
106
+ label: string;
107
+ value: string;
108
+ /** Gentle "low gas" flag; only ever set where the option spends the buyer's own gas. */
109
+ warn: boolean;
110
+ }
111
+ export type GasState = "na" | "ok" | "low" | "none";
112
+ /**
113
+ * Whether the buyer can cover gas for this option. `none` (a known zero) blocks paying; `low` only warns;
114
+ * `na` = the option spends no gas of the buyer's or the balance is unknown.
115
+ */
116
+ export declare function gasState(o: PaymentOption, bal: OptionBalance | undefined): GasState;
117
+ /** The balances the decorator row cycles through, each shown as a plain "amount SYMBOL" (e.g. "19.60 USDC", then "4.31 SEI"). `label` is only a stable key. */
118
+ export declare function decoratorItems(o: PaymentOption, bal: OptionBalance | undefined, copy: CheckoutCopy): DecoratorItem[];
119
+ export type BalanceSummary = "idle" | "loading" | "empty" | "short" | "ok";
120
+ /**
121
+ * How the connected wallet(s) look overall, for the calm message under the amount:
122
+ * `empty` = every readable option is at zero, `short` = some USDC but not enough anywhere.
123
+ * An option whose balance could not be read never counts against the buyer.
124
+ */
125
+ export declare function summarizeBalances(options: PaymentOption[], balances: Balances, connected: Record<OptionKind, boolean>, amount: string): BalanceSummary;
126
+ export interface PrimaryAction {
127
+ /** `connect` opens the wallet for the option's kind; `pay` starts the payment. */
128
+ kind: "connect" | "pay";
129
+ disabled: boolean;
130
+ /** Why a `pay` is disabled; the button says it instead of "Pay". `unavailable`: the price for another token could not be had. */
131
+ reason: "checking" | "not_enough" | "needs_gas" | "unavailable" | null;
132
+ }
133
+ /** What the single primary button does for the chosen option. `lifi` is the price state of a "pay with another token" option. */
134
+ export declare function primaryAction(option: PaymentOption | null, connected: Record<OptionKind, boolean>, balance: OptionBalance | undefined, amount: string, lifi?: LifiQuoteState): PrimaryAction;
135
+ /** A priced, validated payment for one "other token" option, as the service returned it (GET /rails/lifi/quote). */
136
+ export interface LifiQuote {
137
+ optionId: string;
138
+ /** What leaves the buyer's wallet in the source token (for the gas token: the transaction value). */
139
+ pay: {
140
+ amount: bigint;
141
+ symbol: string;
142
+ decimals: number;
143
+ };
144
+ /** Approval(s) first, then the payment: the same shape every EVM pay plan uses. */
145
+ txs: {
146
+ description: string;
147
+ to: string;
148
+ data: string;
149
+ value: string;
150
+ }[];
151
+ chainName: string | null;
152
+ addChain: AddChainParams | null;
153
+ estimatedSeconds: number | null;
154
+ /** Epoch ms after which the service no longer stands behind the transactions. */
155
+ expiresAt: number;
156
+ /** Source-chain gas the route is expected to use, native base units. */
157
+ gasEstimate: bigint | null;
158
+ }
159
+ export type LifiQuoteState = {
160
+ status: "idle";
161
+ } | {
162
+ status: "loading";
163
+ previous: LifiQuote | null;
164
+ } | {
165
+ status: "ready";
166
+ quote: LifiQuote;
167
+ } | {
168
+ status: "unavailable";
169
+ };
170
+ /** Validates a service quote; throws on anything the widget cannot act on or could not show truthfully. */
171
+ export declare function parseLifiQuote(raw: unknown): LifiQuote;
172
+ export type LifiQuoteEvent = {
173
+ type: "reset";
174
+ } | {
175
+ type: "fetch";
176
+ } | {
177
+ type: "ok";
178
+ quote: LifiQuote;
179
+ optionId?: string;
180
+ } | {
181
+ type: "fail";
182
+ };
183
+ /**
184
+ * The price state of the chosen "other token" option. A refresh keeps the old price on screen (marked as updating) until the
185
+ * new one lands; a failure drops the price entirely, so a stale one can never be paid. `ok` for another option is ignored.
186
+ */
187
+ export declare function lifiQuoteReducer(state: LifiQuoteState, ev: LifiQuoteEvent): LifiQuoteState;
188
+ /** A quote may be paid only while the service still stands behind it, with a margin for the wallet prompts. */
189
+ export declare function quoteStillGood(q: {
190
+ expiresAt: number;
191
+ }, nowMs: number): boolean;
192
+ /** A fresh price that is dearer than the one the buyer was shown must be shown again before anything is signed. */
193
+ export declare function priceWentUp(shown: LifiQuote | null, fresh: LifiQuote): boolean;
194
+ /** The wait before refreshing a quote: 15 s before it expires, never sooner than a second. */
195
+ export declare function quoteRefreshDelayMs(q: {
196
+ expiresAt: number;
197
+ }, nowMs: number): number;
198
+ /**
199
+ * An amount in the token's own units at a readable precision: up to 4 digits after the leading zeros (more for a small value,
200
+ * fewer for a big one), grouped thousands. `up` (what the buyer pays) never understates; `down` (what they hold) never overstates.
201
+ */
202
+ export declare function formatPayAmount(raw: bigint, decimals: number, round?: "up" | "down"): string;
203
+ export type Affordability = "ok" | "not_enough" | "needs_gas";
204
+ /**
205
+ * Whether the connected wallet can afford a quote: the token amount, and gas in the SOURCE chain's native token. For the gas token
206
+ * itself the payment and the gas come out of one balance. An unreadable or unknown balance never blocks (the wallet will say if short).
207
+ */
208
+ export declare function quoteAffordability(o: PaymentOption, bal: OptionBalance | undefined, q: {
209
+ pay: LifiQuote["pay"];
210
+ gasEstimate: bigint | null;
211
+ }): Affordability;
212
+ export interface PayLine {
213
+ kind: "loading" | "refreshing" | "ready" | "unavailable";
214
+ /** "You pay 0.0004018 ETH", or the calm status. */
215
+ text: string;
216
+ /** "Usually takes about 18 min". */
217
+ eta: string | null;
218
+ /** Shown while the old price is on screen and a new one is coming. */
219
+ note?: string;
220
+ }
221
+ /** The line that tells the buyer what they pay in their own token. Absent for every other option. */
222
+ export declare function lifiPayLine(o: PaymentOption, state: LifiQuoteState, copy: CheckoutCopy): PayLine | null;
223
+ export type ChainIconKey = "sei" | "base" | "solana";
224
+ /** Which official chain mark an option gets, from its (service-provided) chain id. Unknown chains fall back to the letter badge. */
225
+ export declare function chainIconKey(o: Pick<PaymentOption, "kind" | "chain">): ChainIconKey | null;
@@ -0,0 +1,82 @@
1
+ import { type AddChainParams, type PayStep, type CheckoutCopy, type TypedDataPayload } from "./payments";
2
+ import { type LifiQuote, type PaymentOption } from "./payments-options";
3
+ import { type Eip1193Provider } from "./payments-wallet";
4
+ import { type SolanaSession } from "./payments-solana";
5
+ export type PaySession = {
6
+ kind: "evm";
7
+ provider: Eip1193Provider;
8
+ account: string;
9
+ } | {
10
+ kind: "solana";
11
+ session: SolanaSession;
12
+ account: string;
13
+ };
14
+ export interface PayApi {
15
+ /** For a "pay with another token" option this is the already-priced quote the buyer was shown (see the checkout), not POST /pay. */
16
+ pay(optionId: string, buyer: string): Promise<unknown>;
17
+ authorize(body: unknown): Promise<unknown>;
18
+ /** Hands a buyer-signed Solana transaction to the service, which pays the fee and sends it. */
19
+ submitSolana(transaction: string): Promise<unknown>;
20
+ /** `opts.rail`/`opts.buyer` name the rail and the paying wallet for rails that bind the hint to the buyer (LI.FI). */
21
+ fundingAttempt(sourceChain: string, txHash: string, opts?: {
22
+ rail?: string;
23
+ buyer?: string;
24
+ }): Promise<unknown>;
25
+ }
26
+ export interface PayHooks {
27
+ step(step: Exclude<PayStep, "idle" | "processing" | "paid">): void;
28
+ /** Wallet prompt `i` (0-based) of `total` transactions. */
29
+ tx?(i: number, total: number): void;
30
+ }
31
+ export type PayPlan =
32
+ /** `extra`: fields the service wants back with the signature (a gasless bridge payment: which chain, the nonce's salt). */
33
+ {
34
+ type: "sign";
35
+ typedData: TypedDataPayload;
36
+ extra: Record<string, string>;
37
+ } | {
38
+ type: "evm";
39
+ txs: {
40
+ description: string;
41
+ to: string;
42
+ data: string;
43
+ value: string;
44
+ }[];
45
+ chainName: string | null;
46
+ addChain: AddChainParams | null;
47
+ estimatedSeconds: number | null;
48
+ /** Present for another-token quotes: what the buyer pays in their own token. */
49
+ pay: LifiQuote["pay"] | null;
50
+ gasEstimate: bigint | null;
51
+ }
52
+ /** `sponsored`: the wallet only signs; the service pays the fee and sends it. */
53
+ | {
54
+ type: "solana";
55
+ transaction: string;
56
+ sponsored: boolean;
57
+ estimatedSeconds: number | null;
58
+ };
59
+ /** The quote the buyer was shown, in the response shape `parsePayPlan` reads: paying it is the same code path as every other EVM plan. */
60
+ export declare function quotePlanInput(q: LifiQuote): unknown;
61
+ /** Validates a POST /pay response; throws on anything the widget cannot act on. */
62
+ export declare function parsePayPlan(raw: unknown): PayPlan;
63
+ type PayArgs = {
64
+ option: PaymentOption;
65
+ session: PaySession;
66
+ amount: string;
67
+ api: PayApi;
68
+ hooks: PayHooks;
69
+ copy: CheckoutCopy;
70
+ signal?: AbortSignal;
71
+ intervalMs?: number;
72
+ /** How long to follow a sent payment on chain before leaving it to the charge's status (defaults per chain). */
73
+ timeoutMs?: number;
74
+ };
75
+ /**
76
+ * Resolves once the payment is on its way. When the service says another payment of this charge already is (an earlier
77
+ * try, another tab), it resolves the same way: the buyer waits for that one instead of paying twice.
78
+ */
79
+ export declare function executePayment(args: PayArgs): Promise<{
80
+ estimatedSeconds: number | null;
81
+ }>;
82
+ export {};