@phala/pay 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +202 -0
  3. package/README.md +142 -0
  4. package/dist/chains.d.ts +7 -0
  5. package/dist/chains.d.ts.map +1 -0
  6. package/dist/chains.js +16 -0
  7. package/dist/chains.js.map +1 -0
  8. package/dist/checkout.d.ts +63 -0
  9. package/dist/checkout.d.ts.map +1 -0
  10. package/dist/checkout.js +146 -0
  11. package/dist/checkout.js.map +1 -0
  12. package/dist/client.d.ts +26 -0
  13. package/dist/client.d.ts.map +1 -0
  14. package/dist/client.js +35 -0
  15. package/dist/client.js.map +1 -0
  16. package/dist/format.d.ts +13 -0
  17. package/dist/format.d.ts.map +1 -0
  18. package/dist/format.js +37 -0
  19. package/dist/format.js.map +1 -0
  20. package/dist/index.d.ts +8 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +8 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/payment.d.ts +19 -0
  25. package/dist/payment.d.ts.map +1 -0
  26. package/dist/payment.js +34 -0
  27. package/dist/payment.js.map +1 -0
  28. package/dist/quote.d.ts +36 -0
  29. package/dist/quote.d.ts.map +1 -0
  30. package/dist/quote.js +59 -0
  31. package/dist/quote.js.map +1 -0
  32. package/dist/react/Checkout.d.ts +21 -0
  33. package/dist/react/Checkout.d.ts.map +1 -0
  34. package/dist/react/Checkout.js +168 -0
  35. package/dist/react/Checkout.js.map +1 -0
  36. package/dist/react/QrCode.d.ts +8 -0
  37. package/dist/react/QrCode.d.ts.map +1 -0
  38. package/dist/react/QrCode.js +20 -0
  39. package/dist/react/QrCode.js.map +1 -0
  40. package/dist/react/appearance.d.ts +24 -0
  41. package/dist/react/appearance.d.ts.map +1 -0
  42. package/dist/react/appearance.js +95 -0
  43. package/dist/react/appearance.js.map +1 -0
  44. package/dist/react/index.d.ts +4 -0
  45. package/dist/react/index.d.ts.map +1 -0
  46. package/dist/react/index.js +4 -0
  47. package/dist/react/index.js.map +1 -0
  48. package/dist/react/useCheckout.d.ts +13 -0
  49. package/dist/react/useCheckout.d.ts.map +1 -0
  50. package/dist/react/useCheckout.js +29 -0
  51. package/dist/react/useCheckout.js.map +1 -0
  52. package/dist/wallet.d.ts +42 -0
  53. package/dist/wallet.d.ts.map +1 -0
  54. package/dist/wallet.js +135 -0
  55. package/dist/wallet.js.map +1 -0
  56. package/package.json +93 -0
  57. package/src/chains.ts +20 -0
  58. package/src/checkout.ts +225 -0
  59. package/src/client.ts +54 -0
  60. package/src/format.ts +41 -0
  61. package/src/index.ts +27 -0
  62. package/src/payment.ts +52 -0
  63. package/src/quote.ts +96 -0
  64. package/src/react/Checkout.tsx +409 -0
  65. package/src/react/QrCode.tsx +39 -0
  66. package/src/react/appearance.ts +114 -0
  67. package/src/react/index.ts +5 -0
  68. package/src/react/useCheckout.ts +48 -0
  69. package/src/wallet.ts +175 -0
package/dist/client.js ADDED
@@ -0,0 +1,35 @@
1
+ import { createCheckout, retrieveQuote, } from "./checkout.js";
2
+ /**
3
+ * The browser client, in the shape of Stripe.js: it holds no key, only the service origin, and
4
+ * works with the `client_secret` your backend got from `POST /v1/quotes`.
5
+ *
6
+ * const pay = new PhalaPay({ apiBase: "https://pay.example.com" });
7
+ * const quote = await pay.retrieveQuote(clientSecret);
8
+ * const session = pay.checkout(clientSecret);
9
+ * session.subscribe(({ status }) => render(status));
10
+ */
11
+ export class PhalaPay {
12
+ apiBase;
13
+ #fetch;
14
+ constructor(options) {
15
+ this.apiBase = options.apiBase;
16
+ this.#fetch = options.fetch;
17
+ }
18
+ /** Reads the quote's public view once. */
19
+ retrieveQuote(clientSecret) {
20
+ return retrieveQuote({ clientSecret, apiBase: this.apiBase, ...this.#fetchOption() });
21
+ }
22
+ /** Follows the quote until it is credited, rejected, canceled, or expired. */
23
+ checkout(clientSecret, options = {}) {
24
+ return createCheckout({
25
+ clientSecret,
26
+ apiBase: this.apiBase,
27
+ ...options,
28
+ ...this.#fetchOption(),
29
+ });
30
+ }
31
+ #fetchOption() {
32
+ return this.#fetch === undefined ? {} : { fetch: this.#fetch };
33
+ }
34
+ }
35
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,aAAa,GAGd,MAAM,eAAe,CAAC;AASvB;;;;;;;;GAQG;AACH,MAAM,OAAO,QAAQ;IACV,OAAO,CAAS;IAChB,MAAM,CAAsC;IAErD,YAAY,OAAwB;QAClC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAC9B,CAAC;IAED,0CAA0C;IAC1C,aAAa,CAAC,YAAoB;QAChC,OAAO,aAAa,CAAC,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACxF,CAAC;IAED,8EAA8E;IAC9E,QAAQ,CACN,YAAoB,EACpB,UAAiD,EAAE;QAEnD,OAAO,cAAc,CAAC;YACpB,YAAY;YACZ,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,GAAG,OAAO;YACV,GAAG,IAAI,CAAC,YAAY,EAAE;SACvB,CAAC,CAAC;IACL,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IACjE,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ import type { ClientQuote } from "./quote.js";
2
+ /**
3
+ * The exact token amount to show, grouped for `locale` and without trailing zeros, for example
4
+ * `1,273.9185`. Every digit is kept: the service rounds a quote to a few decimals.
5
+ */
6
+ export declare function formatTokenAmount(quote: ClientQuote, locale?: string): string;
7
+ /** The exact token amount as a plain decimal, for copying into a wallet, for example `1273.9185`. */
8
+ export declare function tokenAmount(quote: ClientQuote): string;
9
+ /** The credit in the quote's currency, for example `$25.00`. */
10
+ export declare function formatAmount(quote: ClientQuote, locale?: string): string;
11
+ /** `mm:ss` (or `h:mm:ss`) until `expiresAt` Unix seconds, never negative. */
12
+ export declare function formatCountdown(expiresAt: number, nowMs: number): string;
13
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAS7E;AAED,qGAAqG;AACrG,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAEtD;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAKxE;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAQxE"}
package/dist/format.js ADDED
@@ -0,0 +1,37 @@
1
+ import { formatUnits } from "viem";
2
+ /**
3
+ * The exact token amount to show, grouped for `locale` and without trailing zeros, for example
4
+ * `1,273.9185`. Every digit is kept: the service rounds a quote to a few decimals.
5
+ */
6
+ export function formatTokenAmount(quote, locale) {
7
+ const [whole = "0", fraction] = tokenAmount(quote).split(".");
8
+ const format = new Intl.NumberFormat(locale);
9
+ const grouped = format.format(BigInt(whole));
10
+ if (fraction === undefined) {
11
+ return grouped;
12
+ }
13
+ const separator = format.formatToParts(0.5).find((part) => part.type === "decimal")?.value ?? ".";
14
+ return `${grouped}${separator}${fraction}`;
15
+ }
16
+ /** The exact token amount as a plain decimal, for copying into a wallet, for example `1273.9185`. */
17
+ export function tokenAmount(quote) {
18
+ return formatUnits(BigInt(quote.amount_atomic), quote.decimals);
19
+ }
20
+ /** The credit in the quote's currency, for example `$25.00`. */
21
+ export function formatAmount(quote, locale) {
22
+ return new Intl.NumberFormat(locale, {
23
+ style: "currency",
24
+ currency: quote.currency.toUpperCase(),
25
+ }).format(quote.amount / 100);
26
+ }
27
+ /** `mm:ss` (or `h:mm:ss`) until `expiresAt` Unix seconds, never negative. */
28
+ export function formatCountdown(expiresAt, nowMs) {
29
+ const total = Math.max(0, Math.ceil(expiresAt - nowMs / 1000));
30
+ const hours = Math.floor(total / 3600);
31
+ const minutes = Math.floor((total % 3600) / 60);
32
+ const seconds = String(total % 60).padStart(2, "0");
33
+ return hours > 0
34
+ ? `${hours}:${String(minutes).padStart(2, "0")}:${seconds}`
35
+ : `${minutes}:${seconds}`;
36
+ }
37
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AAGnC;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAkB,EAAE,MAAe;IACnE,MAAM,CAAC,KAAK,GAAG,GAAG,EAAE,QAAQ,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC;IAClG,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,EAAE,CAAC;AAC7C,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,WAAW,CAAC,KAAkB;IAC5C,OAAO,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;AAClE,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,YAAY,CAAC,KAAkB,EAAE,MAAe;IAC9D,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE;QACnC,KAAK,EAAE,UAAU;QACjB,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE;KACvC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;AAChC,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,eAAe,CAAC,SAAiB,EAAE,KAAa;IAC9D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,OAAO,KAAK,GAAG,CAAC;QACd,CAAC,CAAC,GAAG,KAAK,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,OAAO,EAAE;QAC3D,CAAC,CAAC,GAAG,OAAO,IAAI,OAAO,EAAE,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { CheckoutError, checkoutStatus, createCheckout, retrieveQuote, type CheckoutErrorCode, type CheckoutOptions, type CheckoutSession, type CheckoutState, type CheckoutStatus, type RetrieveQuoteOptions, } from "./checkout.js";
2
+ export { PhalaPay, type PhalaPayOptions } from "./client.js";
3
+ export { knownChain, networkName, transactionUrl } from "./chains.js";
4
+ export { formatAmount, formatCountdown, formatTokenAmount, tokenAmount } from "./format.js";
5
+ export { quoteTransfer, type TokenTransfer } from "./payment.js";
6
+ export { parseClientQuote, quoteIdFromClientSecret, type ClientQuote } from "./quote.js";
7
+ export { INJECTED_WALLET_UUID, WalletError, payWithWallet, watchWallets, type EthereumProvider, type Wallet, type WalletErrorCode, type WalletInfo, } from "./wallet.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,cAAc,EACd,cAAc,EACd,aAAa,EACb,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,aAAa,EACb,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,MAAM,EACX,KAAK,eAAe,EACpB,KAAK,UAAU,GAChB,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { CheckoutError, checkoutStatus, createCheckout, retrieveQuote, } from "./checkout.js";
2
+ export { PhalaPay } from "./client.js";
3
+ export { knownChain, networkName, transactionUrl } from "./chains.js";
4
+ export { formatAmount, formatCountdown, formatTokenAmount, tokenAmount } from "./format.js";
5
+ export { quoteTransfer } from "./payment.js";
6
+ export { parseClientQuote, quoteIdFromClientSecret } from "./quote.js";
7
+ export { INJECTED_WALLET_UUID, WalletError, payWithWallet, watchWallets, } from "./wallet.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,cAAc,EACd,cAAc,EACd,aAAa,GAOd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,EAAwB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,aAAa,EAAsB,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAoB,MAAM,YAAY,CAAC;AACzF,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,aAAa,EACb,YAAY,GAKb,MAAM,aAAa,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type Address } from "viem";
2
+ import type { ClientQuote } from "./quote.js";
3
+ /** An ERC-20 transfer request, as EIP-681 encodes it and a wallet sends it. */
4
+ export interface TokenTransfer {
5
+ chainId: number;
6
+ /** The token contract. */
7
+ token: Address;
8
+ /** The recipient: the quote's deposit address. */
9
+ to: Address;
10
+ /** Token amount in the smallest unit. */
11
+ amount: bigint;
12
+ }
13
+ /**
14
+ * Reads the token transfer from a quote's EIP-681 `payment_uri`, and checks that it pays exactly
15
+ * the quote's `amount_atomic` to the quote's `address` on the quote's chain, so the wallet, the QR
16
+ * code, and the manual instructions all state the same payment.
17
+ */
18
+ export declare function quoteTransfer(quote: ClientQuote): TokenTransfer;
19
+ //# sourceMappingURL=payment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment.d.ts","sourceRoot":"","sources":["../src/payment.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,OAAO,EAAE,MAAM,MAAM,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,kDAAkD;IAClD,EAAE,EAAE,OAAO,CAAC;IACZ,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;CAChB;AAKD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,aAAa,CA6B/D"}
@@ -0,0 +1,34 @@
1
+ import { getAddress, isAddressEqual } from "viem";
2
+ const EIP681_TRANSFER = /^ethereum:(?:pay-)?(0x[0-9a-fA-F]{40})@([1-9][0-9]*)\/transfer\?([^#]*)$/;
3
+ /**
4
+ * Reads the token transfer from a quote's EIP-681 `payment_uri`, and checks that it pays exactly
5
+ * the quote's `amount_atomic` to the quote's `address` on the quote's chain, so the wallet, the QR
6
+ * code, and the manual instructions all state the same payment.
7
+ */
8
+ export function quoteTransfer(quote) {
9
+ const match = EIP681_TRANSFER.exec(quote.payment_uri);
10
+ const params = new URLSearchParams(match?.[3] ?? "");
11
+ const to = params.get("address");
12
+ const amount = params.get("uint256");
13
+ if (match?.[1] === undefined ||
14
+ match[2] === undefined ||
15
+ to === null ||
16
+ !/^0x[0-9a-fA-F]{40}$/.test(to) ||
17
+ amount === null ||
18
+ !/^\d+$/.test(amount)) {
19
+ throw new TypeError("payment_uri is not an EIP-681 ERC-20 transfer");
20
+ }
21
+ const transfer = {
22
+ chainId: Number(match[2]),
23
+ token: getAddress(match[1]),
24
+ to: getAddress(to),
25
+ amount: BigInt(amount),
26
+ };
27
+ if (transfer.chainId !== quote.chain_id ||
28
+ !isAddressEqual(transfer.to, getAddress(quote.address)) ||
29
+ transfer.amount !== BigInt(quote.amount_atomic)) {
30
+ throw new TypeError("payment_uri does not match the quote");
31
+ }
32
+ return transfer;
33
+ }
34
+ //# sourceMappingURL=payment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment.js","sourceRoot":"","sources":["../src/payment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAgB,MAAM,MAAM,CAAC;AAchE,MAAM,eAAe,GACnB,0EAA0E,CAAC;AAE7E;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAkB;IAC9C,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACrD,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACrC,IACE,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS;QACxB,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS;QACtB,EAAE,KAAK,IAAI;QACX,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,MAAM,KAAK,IAAI;QACf,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EACrB,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,QAAQ,GAAkB;QAC9B,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACzB,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC3B,EAAE,EAAE,UAAU,CAAC,EAAE,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;KACvB,CAAC;IACF,IACE,QAAQ,CAAC,OAAO,KAAK,KAAK,CAAC,QAAQ;QACnC,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACvD,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,EAC/C,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The public view of a quote, as `GET /v1/quotes/{id}?client_secret=…` returns it to a browser.
3
+ */
4
+ export interface ClientQuote {
5
+ id: string;
6
+ object: "quote";
7
+ /** `open` until a matching payment completes it, it expires by chain time, or it is canceled. */
8
+ status: "open" | "complete" | "expired" | "canceled";
9
+ /** Credit in the currency's minor unit (US cents). */
10
+ amount: number;
11
+ currency: string;
12
+ /** The token's code, for example `pha`. */
13
+ asset: string;
14
+ decimals: number;
15
+ chain_id: number;
16
+ /** The exact token amount to send, in the token's smallest unit, as a decimal string. */
17
+ amount_atomic: string;
18
+ /** The quote's single-use deposit address. */
19
+ address: string;
20
+ /** The EIP-681 payment request: token transfer of `amount_atomic` to `address`. */
21
+ payment_uri: string;
22
+ /** Unix seconds; after this the address must no longer be shown. */
23
+ expires_at: number;
24
+ /**
25
+ * `seen` before finality (a reorg can remove it), `confirming` while a final payment is valued
26
+ * and screened, `credited` once it is credited, `rejected` when it is final but will not be.
27
+ */
28
+ payment_status: "none" | "seen" | "confirming" | "credited" | "rejected";
29
+ /** Block confirmations while `payment_status` is `seen`, otherwise `null`. */
30
+ confirmations: number | null;
31
+ }
32
+ /** Returns the quote id a client secret belongs to (`qt_…_secret_…`), or throws. */
33
+ export declare function quoteIdFromClientSecret(clientSecret: string): string;
34
+ /** Validates a response body against the public quote view; throws on anything else. */
35
+ export declare function parseClientQuote(value: unknown): ClientQuote;
36
+ //# sourceMappingURL=quote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote.d.ts","sourceRoot":"","sources":["../src/quote.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;IAChB,iGAAiG;IACjG,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,CAAC;IACrD,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,yFAAyF;IACzF,aAAa,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,YAAY,GAAG,UAAU,GAAG,UAAU,CAAC;IACzE,8EAA8E;IAC9E,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAKD,oFAAoF;AACpF,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAMpE;AAED,wFAAwF;AACxF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CA0C5D"}
package/dist/quote.js ADDED
@@ -0,0 +1,59 @@
1
+ const QUOTE_STATUSES = ["open", "complete", "expired", "canceled"];
2
+ const PAYMENT_STATUSES = ["none", "seen", "confirming", "credited", "rejected"];
3
+ /** Returns the quote id a client secret belongs to (`qt_…_secret_…`), or throws. */
4
+ export function quoteIdFromClientSecret(clientSecret) {
5
+ const match = /^(qt_[0-9a-f]+)_secret_[0-9a-f]+$/.exec(clientSecret);
6
+ if (match?.[1] === undefined) {
7
+ throw new TypeError("clientSecret is not a quote client secret");
8
+ }
9
+ return match[1];
10
+ }
11
+ /** Validates a response body against the public quote view; throws on anything else. */
12
+ export function parseClientQuote(value) {
13
+ if (typeof value !== "object" || value === null) {
14
+ throw new TypeError("quote response is not an object");
15
+ }
16
+ const v = value;
17
+ const confirmations = v["confirmations"];
18
+ if (typeof v["id"] !== "string" ||
19
+ v["object"] !== "quote" ||
20
+ !oneOf(v["status"], QUOTE_STATUSES) ||
21
+ !isSafeInteger(v["amount"]) ||
22
+ typeof v["currency"] !== "string" ||
23
+ typeof v["asset"] !== "string" ||
24
+ !isSafeInteger(v["decimals"]) ||
25
+ !isSafeInteger(v["chain_id"]) ||
26
+ typeof v["amount_atomic"] !== "string" ||
27
+ !/^\d+$/.test(v["amount_atomic"]) ||
28
+ typeof v["address"] !== "string" ||
29
+ !/^0x[0-9a-fA-F]{40}$/.test(v["address"]) ||
30
+ typeof v["payment_uri"] !== "string" ||
31
+ !isSafeInteger(v["expires_at"]) ||
32
+ !oneOf(v["payment_status"], PAYMENT_STATUSES) ||
33
+ !(confirmations === null || confirmations === undefined || isSafeInteger(confirmations))) {
34
+ throw new TypeError("quote response does not match the public quote view");
35
+ }
36
+ return {
37
+ id: v["id"],
38
+ object: "quote",
39
+ status: v["status"],
40
+ amount: v["amount"],
41
+ currency: v["currency"],
42
+ asset: v["asset"],
43
+ decimals: v["decimals"],
44
+ chain_id: v["chain_id"],
45
+ amount_atomic: v["amount_atomic"],
46
+ address: v["address"],
47
+ payment_uri: v["payment_uri"],
48
+ expires_at: v["expires_at"],
49
+ payment_status: v["payment_status"],
50
+ confirmations: confirmations ?? null,
51
+ };
52
+ }
53
+ function oneOf(value, allowed) {
54
+ return typeof value === "string" && allowed.includes(value);
55
+ }
56
+ function isSafeInteger(value) {
57
+ return Number.isSafeInteger(value);
58
+ }
59
+ //# sourceMappingURL=quote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote.js","sourceRoot":"","sources":["../src/quote.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { ClientQuote } from "../quote.js";
2
+ import { type Appearance } from "./appearance.js";
3
+ export interface CheckoutProps {
4
+ /** The quote's `client_secret`, from your backend's `POST /v1/quotes`. */
5
+ clientSecret: string;
6
+ /** The service origin, for example `https://topup.example.com`. */
7
+ apiBase: string;
8
+ /** Called once when the payment is credited. Fulfil from the `deposit.credited` webhook, not here. */
9
+ onSuccess?: (quote: ClientQuote) => void;
10
+ /** Called once when the quote expires or is canceled without a payment. */
11
+ onExpire?: (quote: ClientQuote) => void;
12
+ appearance?: Appearance;
13
+ /** Milliseconds between status reads; default 3000. */
14
+ pollInterval?: number;
15
+ className?: string;
16
+ /** The wallet button's label; default "Pay with crypto". */
17
+ buttonText?: string;
18
+ }
19
+ /** A checkout for one quote: pay from a browser wallet, by QR code, or manually, with live status. */
20
+ export declare function Checkout({ clientSecret, apiBase, onSuccess, onExpire, appearance, pollInterval, className, buttonText, }: CheckoutProps): import("react").JSX.Element;
21
+ //# sourceMappingURL=Checkout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Checkout.d.ts","sourceRoot":"","sources":["../../src/react/Checkout.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAA2B,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI3E,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,sGAAsG;IACtG,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACzC,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,sGAAsG;AACtG,wBAAgB,QAAQ,CAAC,EACvB,YAAY,EACZ,OAAO,EACP,SAAS,EACT,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,SAAS,EACT,UAA8B,GAC/B,EAAE,aAAa,+BA2Df"}
@@ -0,0 +1,168 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useEffect, useId, useRef, useState } from "react";
4
+ import { networkName, transactionUrl } from "../chains.js";
5
+ import { formatAmount, formatCountdown, formatTokenAmount, tokenAmount } from "../format.js";
6
+ import { quoteTransfer } from "../payment.js";
7
+ import { WalletError, payWithWallet, watchWallets } from "../wallet.js";
8
+ import { STYLES, appearanceStyle } from "./appearance.js";
9
+ import { QrCode } from "./QrCode.js";
10
+ import { useCheckout } from "./useCheckout.js";
11
+ const METHODS = [
12
+ { id: "wallet", label: "Browser wallet" },
13
+ { id: "qr", label: "QR code" },
14
+ { id: "manual", label: "Send manually" },
15
+ ];
16
+ /** A checkout for one quote: pay from a browser wallet, by QR code, or manually, with live status. */
17
+ export function Checkout({ clientSecret, apiBase, onSuccess, onExpire, appearance, pollInterval, className, buttonText = "Pay with crypto", }) {
18
+ const { status, quote, error, refresh } = useCheckout({
19
+ clientSecret,
20
+ apiBase,
21
+ ...(pollInterval === undefined ? {} : { pollInterval }),
22
+ });
23
+ const [txHash, setTxHash] = useState(null);
24
+ const now = useNow(status === "waiting");
25
+ const callbacks = useRef({ onSuccess, onExpire });
26
+ useEffect(() => {
27
+ callbacks.current = { onSuccess, onExpire };
28
+ });
29
+ const notified = useRef(null);
30
+ useEffect(() => {
31
+ if (quote === null || notified.current === quote.id) {
32
+ return;
33
+ }
34
+ if (status === "credited") {
35
+ notified.current = quote.id;
36
+ callbacks.current.onSuccess?.(quote);
37
+ }
38
+ else if (status === "expired" || status === "canceled") {
39
+ notified.current = quote.id;
40
+ callbacks.current.onExpire?.(quote);
41
+ }
42
+ }, [status, quote]);
43
+ return (_jsxs("div", { className: className === undefined ? "pp-root" : `pp-root ${className}`, "data-theme": appearance?.theme ?? "light", style: appearanceStyle(appearance), children: [_jsx("style", { children: STYLES }), quote !== null && (_jsxs(_Fragment, { children: [_jsxs("p", { className: "pp-amount", children: [formatTokenAmount(quote), " ", quote.asset.toUpperCase()] }), _jsxs("p", { className: "pp-subtitle", children: [formatAmount(quote), " top-up \u00B7 ", networkName(quote.chain_id)] })] })), _jsx(StatusLine, { status: status, quote: quote, now: now, reconnecting: error !== null }), txHash !== null && quote !== null && _jsx(Transaction, { hash: txHash, chainId: quote.chain_id }), status === "waiting" && quote !== null && (_jsx(PaymentOptions, { quote: quote, buttonText: buttonText, now: now, onSent: (hash) => {
44
+ setTxHash(hash);
45
+ refresh();
46
+ } }))] }));
47
+ }
48
+ function StatusLine({ status, quote, now, reconnecting, }) {
49
+ const tone = status === "credited"
50
+ ? "success"
51
+ : ["rejected", "expired", "canceled", "error"].includes(status)
52
+ ? "danger"
53
+ : "neutral";
54
+ return (_jsxs("div", { className: "pp-status", "data-tone": tone, children: [_jsxs("span", { role: "status", "aria-live": "polite", children: [statusMessage(status, quote), reconnecting && status !== "error" ? " (reconnecting…)" : ""] }), status === "waiting" && quote !== null && (_jsx("span", { className: "pp-countdown", "aria-label": "Time left to pay", children: formatCountdown(quote.expires_at, now) }))] }));
55
+ }
56
+ function statusMessage(status, quote) {
57
+ switch (status) {
58
+ case "loading":
59
+ return "Loading payment details…";
60
+ case "waiting":
61
+ return "Waiting for your payment";
62
+ case "seen":
63
+ return quote?.confirmations == null
64
+ ? "Payment received, waiting for confirmations"
65
+ : `Payment received, ${quote.confirmations} confirmation${quote.confirmations === 1 ? "" : "s"}`;
66
+ case "confirming":
67
+ return "Payment confirmed on chain, crediting…";
68
+ case "credited":
69
+ return quote === null ? "Payment credited" : `Payment credited: ${formatAmount(quote)}`;
70
+ case "rejected":
71
+ return "This payment cannot be credited. Contact support with your transaction.";
72
+ case "expired":
73
+ return "This quote has expired. Do not send funds to this address; start a new top-up.";
74
+ case "canceled":
75
+ return "This quote was canceled. Do not send funds to this address.";
76
+ case "error":
77
+ return "This payment link is not valid. Start a new top-up.";
78
+ }
79
+ }
80
+ function Transaction({ hash, chainId }) {
81
+ const url = transactionUrl(chainId, hash);
82
+ const short = `${hash.slice(0, 10)}…${hash.slice(-8)}`;
83
+ return (_jsxs("p", { className: "pp-tx", children: ["Transaction sent:", " ", url === undefined ? (_jsx("span", { className: "pp-value", title: hash, children: short })) : (_jsx("a", { href: url, target: "_blank", rel: "noreferrer", title: hash, children: short }))] }));
84
+ }
85
+ function PaymentOptions({ quote, now, buttonText, onSent, }) {
86
+ const [method, setMethod] = useState("wallet");
87
+ const id = useId();
88
+ const tabs = useRef([]);
89
+ const amount = `${formatTokenAmount(quote)} ${quote.asset.toUpperCase()}`;
90
+ const onKeyDown = (event) => {
91
+ const index = METHODS.findIndex((m) => m.id === method);
92
+ const next = event.key === "ArrowRight"
93
+ ? (index + 1) % METHODS.length
94
+ : event.key === "ArrowLeft"
95
+ ? (index - 1 + METHODS.length) % METHODS.length
96
+ : event.key === "Home"
97
+ ? 0
98
+ : event.key === "End"
99
+ ? METHODS.length - 1
100
+ : null;
101
+ const target = next === null ? undefined : METHODS[next];
102
+ if (next !== null && target !== undefined) {
103
+ event.preventDefault();
104
+ setMethod(target.id);
105
+ tabs.current[next]?.focus();
106
+ }
107
+ };
108
+ return (_jsxs(_Fragment, { children: [_jsxs("p", { className: "pp-notice", children: ["Send ", _jsxs("strong", { children: ["exactly ", amount] }), " on ", networkName(quote.chain_id), " in one transfer before the timer ends. A different amount, or a payment after expiry, is credited at the market price instead of this quote. Exchanges may deduct a withdrawal fee, so the amount that arrives must be exact."] }), _jsx("div", { className: "pp-tabs", role: "tablist", "aria-label": "Payment method", onKeyDown: onKeyDown, children: METHODS.map((m, index) => (_jsx("button", { ref: (element) => {
109
+ tabs.current[index] = element;
110
+ }, type: "button", role: "tab", className: "pp-tab", id: `${id}-tab-${m.id}`, "aria-selected": method === m.id, "aria-controls": `${id}-panel-${m.id}`, tabIndex: method === m.id ? 0 : -1, onClick: () => setMethod(m.id), children: m.label }, m.id))) }), _jsxs("div", { role: "tabpanel", id: `${id}-panel-${method}`, "aria-labelledby": `${id}-tab-${method}`, tabIndex: 0, children: [method === "wallet" && _jsx(WalletPanel, { quote: quote, buttonText: buttonText, onSent: onSent }), method === "qr" && (_jsxs("div", { className: "pp-qr-panel", children: [_jsx(QrCode, { value: quote.payment_uri, label: `Payment request for ${amount}` }), _jsxs("p", { className: "pp-message", children: ["Scan with a wallet app that reads payment links, and check that it shows ", amount, " on", " ", networkName(quote.chain_id), " before you confirm."] })] })), method === "manual" && _jsx(ManualPanel, { quote: quote, now: now })] })] }));
111
+ }
112
+ function WalletPanel({ quote, buttonText, onSent, }) {
113
+ const [wallets, setWallets] = useState([]);
114
+ const [step, setStep] = useState({ kind: "idle" });
115
+ useEffect(() => watchWallets(setWallets), []);
116
+ const pay = async (wallet) => {
117
+ setStep({ kind: "pending", wallet: wallet.info.name });
118
+ try {
119
+ onSent(await payWithWallet(wallet.provider, quote));
120
+ setStep({ kind: "idle" });
121
+ }
122
+ catch (error) {
123
+ setStep({
124
+ kind: "failed",
125
+ message: error instanceof WalletError ? error.message : "The payment could not be sent",
126
+ });
127
+ }
128
+ };
129
+ if (wallets.length === 0) {
130
+ return (_jsx("p", { className: "pp-message", children: "No browser wallet found. Scan the QR code with a mobile wallet, or send the payment manually." }));
131
+ }
132
+ return (_jsxs("div", { className: "pp-wallets", children: [wallets.map((wallet) => (_jsxs("button", { type: "button", className: "pp-button", disabled: step.kind === "pending", onClick: () => void pay(wallet), "aria-label": `${buttonText} (${wallet.info.name})`, children: [wallet.info.icon !== "" && _jsx("img", { src: wallet.info.icon, alt: "" }), _jsx("span", { children: buttonText }), _jsx("span", { className: "pp-wallet-name", children: wallet.info.name })] }, wallet.info.uuid))), _jsxs("p", { className: "pp-message", "data-tone": step.kind === "failed" ? "danger" : undefined, "aria-live": "polite", children: [step.kind === "pending" && `Confirm the payment in ${step.wallet}…`, step.kind === "failed" && step.message] })] }));
133
+ }
134
+ function ManualPanel({ quote, now }) {
135
+ const token = quoteTransfer(quote).token;
136
+ return (_jsxs("dl", { className: "pp-fields", children: [_jsx(Field, { label: "Network", value: `${networkName(quote.chain_id)} (chain ID ${quote.chain_id})` }), _jsx(Field, { label: `Token (${quote.asset.toUpperCase()}) contract`, value: token, copy: true }), _jsx(Field, { label: "Send to address", value: quote.address, copy: true }), _jsx(Field, { label: "Exact amount", value: formatTokenAmount(quote), copy: tokenAmount(quote) }), _jsx(Field, { label: "Time left", value: formatCountdown(quote.expires_at, now) })] }));
137
+ }
138
+ /** `copy` adds a copy button, copying `value`, or the given string when it differs from it. */
139
+ function Field({ label, value, copy = false, }) {
140
+ return (_jsxs("div", { className: "pp-field", children: [_jsx("dt", { children: label }), _jsxs("dd", { children: [_jsx("span", { className: copy === false ? undefined : "pp-value", children: value }), copy !== false && (_jsx(CopyButton, { value: typeof copy === "string" ? copy : value, label: label }))] })] }));
141
+ }
142
+ function CopyButton({ value, label }) {
143
+ const [copied, setCopied] = useState(null);
144
+ useEffect(() => {
145
+ if (copied === null) {
146
+ return;
147
+ }
148
+ const timer = setTimeout(() => setCopied(null), 2000);
149
+ return () => clearTimeout(timer);
150
+ }, [copied]);
151
+ const onClick = () => {
152
+ navigator.clipboard.writeText(value).then(() => setCopied(true), () => setCopied(false));
153
+ };
154
+ return (_jsx("button", { type: "button", className: "pp-copy", onClick: onClick, "aria-label": `Copy ${label}`, children: _jsx("span", { "aria-live": "polite", children: copied === null ? "Copy" : copied ? "Copied" : "Copy failed" }) }));
155
+ }
156
+ /** The current time in milliseconds, updated every second while `active`. */
157
+ function useNow(active) {
158
+ const [now, setNow] = useState(() => Date.now());
159
+ useEffect(() => {
160
+ if (!active) {
161
+ return;
162
+ }
163
+ const timer = setInterval(() => setNow(Date.now()), 1000);
164
+ return () => clearInterval(timer);
165
+ }, [active]);
166
+ return now;
167
+ }
168
+ //# sourceMappingURL=Checkout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../src/react/Checkout.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface QrCodeProps {
2
+ value: string;
3
+ label: string;
4
+ size?: number;
5
+ }
6
+ /** A QR code as an SVG path, one unit per module. */
7
+ export declare function QrCode({ value, label, size }: QrCodeProps): import("react").JSX.Element;
8
+ //# sourceMappingURL=QrCode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QrCode.d.ts","sourceRoot":"","sources":["../../src/react/QrCode.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qDAAqD;AACrD,wBAAgB,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAU,EAAE,EAAE,WAAW,+BA4B/D"}
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import { encode } from "uqr";
4
+ /** A QR code as an SVG path, one unit per module. */
5
+ export function QrCode({ value, label, size = 208 }) {
6
+ const { path, width } = useMemo(() => {
7
+ const { data, size: modules } = encode(value, { ecc: "M", border: 2 });
8
+ let d = "";
9
+ data.forEach((row, y) => {
10
+ row.forEach((dark, x) => {
11
+ if (dark) {
12
+ d += `M${x} ${y}h1v1h-1z`;
13
+ }
14
+ });
15
+ });
16
+ return { path: d, width: modules };
17
+ }, [value]);
18
+ return (_jsxs("svg", { className: "pp-qr", role: "img", "aria-label": label, width: size, height: size, viewBox: `0 0 ${width} ${width}`, shapeRendering: "crispEdges", children: [_jsx("rect", { width: width, height: width, fill: "#fff" }), _jsx("path", { d: path, fill: "#000" })] }));
19
+ }
20
+ //# sourceMappingURL=QrCode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QrCode.js","sourceRoot":"","sources":["../../src/react/QrCode.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAQ7B,qDAAqD;AACrD,MAAM,UAAU,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,GAAG,GAAG,EAAe;IAC9D,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE;QACnC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QACvE,IAAI,CAAC,GAAG,EAAE,CAAC;QACX,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;YACtB,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;gBACtB,IAAI,IAAI,EAAE,CAAC;oBACT,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;gBAC5B,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACrC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,OAAO,CACL,eACE,SAAS,EAAC,OAAO,EACjB,IAAI,EAAC,KAAK,gBACE,KAAK,EACjB,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,IAAI,EACZ,OAAO,EAAE,OAAO,KAAK,IAAI,KAAK,EAAE,EAChC,cAAc,EAAC,YAAY,aAE3B,eAAM,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAC,MAAM,GAAG,EACjD,eAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAC,MAAM,GAAG,IACzB,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { CSSProperties } from "react";
2
+ /** Theming in the spirit of Stripe's Appearance API: a base theme plus a few variables. */
3
+ export interface Appearance {
4
+ theme?: "light" | "dark";
5
+ variables?: {
6
+ colorPrimary?: string;
7
+ colorBackground?: string;
8
+ colorText?: string;
9
+ colorTextSecondary?: string;
10
+ colorBorder?: string;
11
+ colorDanger?: string;
12
+ colorSuccess?: string;
13
+ fontFamily?: string;
14
+ borderRadius?: string;
15
+ };
16
+ }
17
+ /** The root element's custom properties for `appearance.variables`. */
18
+ export declare function appearanceStyle(appearance: Appearance | undefined): CSSProperties;
19
+ /**
20
+ * Every rule is scoped under `.pp-root`; the variables can also be set from the page's own CSS on
21
+ * `.pp-root`.
22
+ */
23
+ export declare const STYLES = "\n.pp-root {\n --pp-color-primary: #0f62fe;\n --pp-color-background: #ffffff;\n --pp-color-text: #1a1a1a;\n --pp-color-text-secondary: #5c5f66;\n --pp-color-border: #d9dce1;\n --pp-color-danger: #c62828;\n --pp-color-success: #1b7f3b;\n --pp-font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n --pp-border-radius: 8px;\n box-sizing: border-box;\n max-width: 440px;\n padding: 20px;\n border: 1px solid var(--pp-color-border);\n border-radius: var(--pp-border-radius);\n background: var(--pp-color-background);\n color: var(--pp-color-text);\n font-family: var(--pp-font-family);\n font-size: 14px;\n line-height: 1.45;\n}\n.pp-root[data-theme=\"dark\"] {\n --pp-color-primary: #78a9ff;\n --pp-color-background: #161616;\n --pp-color-text: #f4f4f4;\n --pp-color-text-secondary: #a8a8a8;\n --pp-color-border: #393939;\n --pp-color-danger: #ff8389;\n --pp-color-success: #42be65;\n}\n.pp-root *, .pp-root *::before, .pp-root *::after { box-sizing: inherit; }\n.pp-amount { margin: 0; font-size: 22px; font-weight: 600; }\n.pp-subtitle { margin: 2px 0 16px; color: var(--pp-color-text-secondary); }\n.pp-status { display: flex; gap: 8px; align-items: baseline; justify-content: space-between;\n margin-bottom: 12px; padding: 10px 12px; border-radius: var(--pp-border-radius);\n border: 1px solid var(--pp-color-border); }\n.pp-status[data-tone=\"success\"] { border-color: var(--pp-color-success); color: var(--pp-color-success); }\n.pp-status[data-tone=\"danger\"] { border-color: var(--pp-color-danger); color: var(--pp-color-danger); }\n.pp-countdown { font-variant-numeric: tabular-nums; white-space: nowrap; }\n.pp-notice { margin: 0 0 16px; color: var(--pp-color-text-secondary); font-size: 13px; }\n.pp-notice strong { color: var(--pp-color-text); }\n.pp-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--pp-color-border); margin-bottom: 16px; }\n.pp-tab { appearance: none; border: 0; border-bottom: 2px solid transparent; background: none;\n color: var(--pp-color-text-secondary); font: inherit; padding: 8px 10px; cursor: pointer; }\n.pp-tab[aria-selected=\"true\"] { color: var(--pp-color-text); border-bottom-color: var(--pp-color-primary); font-weight: 600; }\n.pp-root button:focus-visible, .pp-root a:focus-visible { outline: 2px solid var(--pp-color-primary); outline-offset: 2px; }\n.pp-wallets { display: grid; gap: 8px; }\n.pp-button { display: flex; gap: 10px; align-items: center; justify-content: center; width: 100%;\n appearance: none; border: 1px solid var(--pp-color-primary); border-radius: var(--pp-border-radius);\n background: var(--pp-color-primary); color: #fff; font: inherit; font-weight: 600; padding: 10px 14px; cursor: pointer; }\n.pp-button:disabled { opacity: 0.6; cursor: progress; }\n.pp-button img { width: 20px; height: 20px; }\n.pp-wallet-name { font-weight: 400; opacity: 0.85; }\n.pp-message { margin: 10px 0 0; color: var(--pp-color-text-secondary); }\n.pp-message[data-tone=\"danger\"] { color: var(--pp-color-danger); }\n.pp-qr-panel { display: grid; justify-items: center; gap: 10px; text-align: center; }\n.pp-qr { border-radius: 4px; }\n.pp-fields { display: grid; gap: 10px; margin: 0; }\n.pp-field dt { color: var(--pp-color-text-secondary); font-size: 12px; }\n.pp-field dd { display: flex; gap: 8px; align-items: center; margin: 2px 0 0; }\n.pp-value { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; word-break: break-all; }\n.pp-copy { flex: none; appearance: none; border: 1px solid var(--pp-color-border); border-radius: 6px;\n background: none; color: var(--pp-color-text); font: inherit; font-size: 12px; padding: 3px 8px; cursor: pointer; }\n.pp-tx { margin: 0 0 12px; font-size: 13px; }\n.pp-tx a { color: var(--pp-color-primary); }\n";
24
+ //# sourceMappingURL=appearance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appearance.d.ts","sourceRoot":"","sources":["../../src/react/appearance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,2FAA2F;AAC3F,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE;QACV,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAcD,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,GAAG,aAAa,CAUjF;AAED;;;GAGG;AACH,eAAO,MAAM,MAAM,osHAkElB,CAAC"}