@phala/pay 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +44 -22
  2. package/dist/checkout.d.ts +9 -1
  3. package/dist/checkout.d.ts.map +1 -1
  4. package/dist/checkout.js +22 -2
  5. package/dist/checkout.js.map +1 -1
  6. package/dist/client.d.ts +3 -3
  7. package/dist/client.d.ts.map +1 -1
  8. package/dist/client.js +2 -1
  9. package/dist/client.js.map +1 -1
  10. package/dist/deposit-address.d.ts +3 -0
  11. package/dist/deposit-address.d.ts.map +1 -1
  12. package/dist/deposit-address.js +6 -1
  13. package/dist/deposit-address.js.map +1 -1
  14. package/dist/react/Checkout.d.ts +1 -1
  15. package/dist/react/Checkout.d.ts.map +1 -1
  16. package/dist/react/Checkout.js +14 -9
  17. package/dist/react/Checkout.js.map +1 -1
  18. package/dist/react/DepositAddress.d.ts +1 -1
  19. package/dist/react/DepositAddress.d.ts.map +1 -1
  20. package/dist/react/DepositAddress.js +7 -9
  21. package/dist/react/DepositAddress.js.map +1 -1
  22. package/dist/react/appearance.d.ts +1 -22
  23. package/dist/react/appearance.d.ts.map +1 -1
  24. package/dist/react/appearance.js +1 -100
  25. package/dist/react/appearance.js.map +1 -1
  26. package/dist/react/useCheckout.d.ts +3 -9
  27. package/dist/react/useCheckout.d.ts.map +1 -1
  28. package/dist/react/useCheckout.js +5 -3
  29. package/dist/react/useCheckout.js.map +1 -1
  30. package/dist/request.d.ts +6 -0
  31. package/dist/request.d.ts.map +1 -0
  32. package/dist/request.js +6 -0
  33. package/dist/request.js.map +1 -0
  34. package/package.json +13 -9
  35. package/src/checkout.ts +32 -3
  36. package/src/client.ts +8 -2
  37. package/src/css.d.ts +1 -0
  38. package/src/deposit-address.ts +9 -1
  39. package/src/react/Checkout.tsx +13 -9
  40. package/src/react/DepositAddress.tsx +28 -31
  41. package/src/react/appearance.ts +1 -119
  42. package/src/react/useCheckout.ts +15 -9
  43. package/src/request.ts +8 -0
  44. package/src/styles.css +76 -0
@@ -1,122 +1,4 @@
1
- import type { CSSProperties } from "react";
2
-
3
- /** Theming in the spirit of Stripe's Appearance API: a base theme plus a few variables. */
1
+ /** Base theme; customize CSS properties on `.pp-root` in your stylesheet. */
4
2
  export interface Appearance {
5
3
  theme?: "light" | "dark";
6
- variables?: {
7
- colorPrimary?: string;
8
- /** The color of text appearing on top of any `colorPrimary` background, such as the pay button. */
9
- accessibleColorOnColorPrimary?: string;
10
- colorBackground?: string;
11
- colorText?: string;
12
- colorTextSecondary?: string;
13
- colorBorder?: string;
14
- colorDanger?: string;
15
- colorSuccess?: string;
16
- fontFamily?: string;
17
- borderRadius?: string;
18
- };
19
4
  }
20
-
21
- const VARIABLES: Record<keyof NonNullable<Appearance["variables"]>, string> = {
22
- colorPrimary: "--pp-color-primary",
23
- accessibleColorOnColorPrimary: "--pp-accessible-color-on-color-primary",
24
- colorBackground: "--pp-color-background",
25
- colorText: "--pp-color-text",
26
- colorTextSecondary: "--pp-color-text-secondary",
27
- colorBorder: "--pp-color-border",
28
- colorDanger: "--pp-color-danger",
29
- colorSuccess: "--pp-color-success",
30
- fontFamily: "--pp-font-family",
31
- borderRadius: "--pp-border-radius",
32
- };
33
-
34
- /** The root element's custom properties for `appearance.variables`. */
35
- export function appearanceStyle(appearance: Appearance | undefined): CSSProperties {
36
- const variables = appearance?.variables ?? {};
37
- const style: Record<string, string> = {};
38
- for (const [name, property] of Object.entries(VARIABLES)) {
39
- const value = variables[name as keyof typeof VARIABLES];
40
- if (value !== undefined) {
41
- style[property] = value;
42
- }
43
- }
44
- return style;
45
- }
46
-
47
- /**
48
- * Every rule is scoped under `.pp-root`; the variables can also be set from the page's own CSS on
49
- * `.pp-root`.
50
- */
51
- export const STYLES = `
52
- .pp-root {
53
- --pp-color-primary: #0f62fe;
54
- --pp-accessible-color-on-color-primary: #ffffff;
55
- --pp-color-background: #ffffff;
56
- --pp-color-text: #1a1a1a;
57
- --pp-color-text-secondary: #5c5f66;
58
- --pp-color-border: #d9dce1;
59
- --pp-color-danger: #c62828;
60
- --pp-color-success: #1b7f3b;
61
- --pp-font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
62
- --pp-border-radius: 8px;
63
- box-sizing: border-box;
64
- max-width: 440px;
65
- padding: 20px;
66
- border: 1px solid var(--pp-color-border);
67
- border-radius: var(--pp-border-radius);
68
- background: var(--pp-color-background);
69
- color: var(--pp-color-text);
70
- font-family: var(--pp-font-family);
71
- font-size: 14px;
72
- line-height: 1.45;
73
- }
74
- .pp-root[data-theme="dark"] {
75
- --pp-color-primary: #78a9ff;
76
- --pp-color-background: #161616;
77
- --pp-color-text: #f4f4f4;
78
- --pp-color-text-secondary: #a8a8a8;
79
- --pp-color-border: #393939;
80
- --pp-color-danger: #ff8389;
81
- --pp-color-success: #42be65;
82
- }
83
- .pp-root *, .pp-root *::before, .pp-root *::after { box-sizing: inherit; }
84
- .pp-amount { margin: 0; overflow-wrap: anywhere; font-size: 22px; font-weight: 600; }
85
- .pp-subtitle { margin: 2px 0 16px; color: var(--pp-color-text-secondary); }
86
- .pp-badge { font-weight: 600; }
87
- .pp-payments { list-style: none; margin: 12px 0 0; padding: 0; }
88
- .pp-payments li { margin: 4px 0; }
89
- .pp-status { display: flex; gap: 8px; align-items: baseline; justify-content: space-between;
90
- margin-bottom: 12px; padding: 10px 12px; border-radius: var(--pp-border-radius);
91
- border: 1px solid var(--pp-color-border); }
92
- .pp-status[data-tone="success"] { border-color: var(--pp-color-success); color: var(--pp-color-success); }
93
- .pp-status[data-tone="danger"] { border-color: var(--pp-color-danger); color: var(--pp-color-danger); }
94
- .pp-countdown { font-variant-numeric: tabular-nums; white-space: nowrap; }
95
- .pp-notice { margin: 0 0 16px; color: var(--pp-color-text-secondary); font-size: 13px; }
96
- .pp-notice strong { color: var(--pp-color-text); }
97
- .pp-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--pp-color-border); margin-bottom: 16px; }
98
- .pp-tab { appearance: none; border: 0; border-bottom: 2px solid transparent; background: none;
99
- color: var(--pp-color-text-secondary); font: inherit; padding: 8px 10px; cursor: pointer; }
100
- .pp-tab[aria-selected="true"] { color: var(--pp-color-text); border-bottom-color: var(--pp-color-primary); font-weight: 600; }
101
- .pp-root button:focus-visible, .pp-root a:focus-visible { outline: 2px solid var(--pp-color-primary); outline-offset: 2px; }
102
- .pp-wallets { display: grid; gap: 8px; }
103
- .pp-button { display: flex; gap: 10px; align-items: center; justify-content: center; width: 100%;
104
- appearance: none; border: 1px solid var(--pp-color-primary); border-radius: var(--pp-border-radius);
105
- background: var(--pp-color-primary); color: var(--pp-accessible-color-on-color-primary); font: inherit; font-weight: 600; padding: 10px 14px; cursor: pointer; }
106
- .pp-button:disabled { opacity: 0.6; cursor: progress; }
107
- .pp-button img { width: 20px; height: 20px; }
108
- .pp-wallet-name { font-weight: 400; opacity: 0.85; }
109
- .pp-message { margin: 10px 0 0; color: var(--pp-color-text-secondary); }
110
- .pp-message[data-tone="danger"] { color: var(--pp-color-danger); }
111
- .pp-qr-panel { display: grid; justify-items: center; gap: 10px; text-align: center; }
112
- .pp-qr { border-radius: 4px; }
113
- .pp-fields { display: grid; gap: 10px; margin: 0; }
114
- .pp-field dt { color: var(--pp-color-text-secondary); font-size: 12px; }
115
- .pp-field dd { display: flex; gap: 8px; align-items: center; margin: 2px 0 0; }
116
- .pp-value { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; word-break: break-all; }
117
- .pp-copy { flex: none; appearance: none; border: 1px solid var(--pp-color-border); border-radius: 6px;
118
- background: none; color: var(--pp-color-text); font: inherit; font-size: 12px; padding: 3px 8px; cursor: pointer; }
119
- .pp-tx { margin: 0 0 12px; font-size: 13px; }
120
- .pp-tx a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
121
- .pp-tx .pp-value { overflow-wrap: anywhere; }
122
- `;
@@ -1,13 +1,15 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
- import { createCheckout, type CheckoutSession, type CheckoutState } from "../checkout.js";
2
+ import {
3
+ createCheckout,
4
+ type CheckoutOptions,
5
+ type CheckoutSession,
6
+ type CheckoutState,
7
+ } from "../checkout.js";
3
8
 
4
- export interface UseCheckoutOptions {
5
- clientSecret: string;
6
- /** The quote's address as your backend recomputed it; another one fails closed. */
7
- expectedAddress: string;
8
- apiBase: string;
9
- pollInterval?: number;
10
- }
9
+ export type UseCheckoutOptions = Pick<
10
+ CheckoutOptions,
11
+ "clientSecret" | "expectedAddress" | "apiBase" | "pollInterval" | "signal" | "requestTimeout"
12
+ >;
11
13
 
12
14
  export interface UseCheckoutResult extends CheckoutState {
13
15
  /** Reads the quote now, for example right after the wallet broadcast the payment. */
@@ -22,6 +24,8 @@ export function useCheckout({
22
24
  expectedAddress,
23
25
  apiBase,
24
26
  pollInterval,
27
+ signal,
28
+ requestTimeout,
25
29
  }: UseCheckoutOptions): UseCheckoutResult {
26
30
  const [current, setCurrent] = useState({ key: "", state: LOADING });
27
31
  const checkout = useRef<CheckoutSession | null>(null);
@@ -33,6 +37,8 @@ export function useCheckout({
33
37
  expectedAddress,
34
38
  apiBase,
35
39
  ...(pollInterval === undefined ? {} : { pollInterval }),
40
+ ...(signal === undefined ? {} : { signal }),
41
+ ...(requestTimeout === undefined ? {} : { requestTimeout }),
36
42
  });
37
43
  checkout.current = instance;
38
44
  const unsubscribe = instance.subscribe((state) => setCurrent({ key, state }));
@@ -41,7 +47,7 @@ export function useCheckout({
41
47
  instance.destroy();
42
48
  checkout.current = null;
43
49
  };
44
- }, [key, clientSecret, expectedAddress, apiBase, pollInterval]);
50
+ }, [key, clientSecret, expectedAddress, apiBase, pollInterval, signal, requestTimeout]);
45
51
 
46
52
  const refresh = useCallback(() => {
47
53
  void checkout.current?.refresh();
package/src/request.ts ADDED
@@ -0,0 +1,8 @@
1
+ /** Compose caller cancellation with a finite deadline, including response-body reads. */
2
+ export function requestSignal(options: {
3
+ signal?: AbortSignal;
4
+ requestTimeout?: number;
5
+ }): AbortSignal {
6
+ const deadline = AbortSignal.timeout(options.requestTimeout ?? 10_000);
7
+ return options.signal === undefined ? deadline : AbortSignal.any([options.signal, deadline]);
8
+ }
package/src/styles.css ADDED
@@ -0,0 +1,76 @@
1
+ .pp-root {
2
+ --pp-color-primary: #0f62fe;
3
+ --pp-accessible-color-on-color-primary: #ffffff;
4
+ --pp-color-background: #ffffff;
5
+ --pp-color-text: #1a1a1a;
6
+ --pp-color-text-secondary: #5c5f66;
7
+ --pp-color-border: #d9dce1;
8
+ --pp-color-danger: #c62828;
9
+ --pp-color-success: #1b7f3b;
10
+ --pp-font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
11
+ --pp-border-radius: 8px;
12
+ box-sizing: border-box;
13
+ max-width: 440px;
14
+ padding: 20px;
15
+ border: 1px solid var(--pp-color-border);
16
+ border-radius: var(--pp-border-radius);
17
+ background: var(--pp-color-background);
18
+ color: var(--pp-color-text);
19
+ font-family: var(--pp-font-family);
20
+ font-size: 14px;
21
+ line-height: 1.45;
22
+ }
23
+ .pp-root[data-theme="dark"] {
24
+ --pp-color-primary: #78a9ff;
25
+ --pp-color-background: #161616;
26
+ --pp-color-text: #f4f4f4;
27
+ --pp-color-text-secondary: #a8a8a8;
28
+ --pp-color-border: #393939;
29
+ --pp-color-danger: #ff8389;
30
+ --pp-color-success: #42be65;
31
+ }
32
+ .pp-root *, .pp-root *::before, .pp-root *::after { box-sizing: inherit; }
33
+ .pp-amount { margin: 0; overflow-wrap: anywhere; font-size: 22px; font-weight: 600; }
34
+ .pp-subtitle { margin: 2px 0 16px; color: var(--pp-color-text-secondary); }
35
+ .pp-badge { font-weight: 600; }
36
+ .pp-payments { list-style: none; margin: 12px 0 0; padding: 0; }
37
+ .pp-payments li { margin: 4px 0; }
38
+ .pp-status { display: flex; gap: 8px; align-items: baseline; justify-content: space-between;
39
+ margin-bottom: 12px; padding: 10px 12px; border-radius: var(--pp-border-radius);
40
+ border: 1px solid var(--pp-color-border); }
41
+ .pp-status[data-tone="success"] { border-color: var(--pp-color-success); color: var(--pp-color-success); }
42
+ .pp-status[data-tone="danger"] { border-color: var(--pp-color-danger); color: var(--pp-color-danger); }
43
+ .pp-countdown { font-variant-numeric: tabular-nums; white-space: nowrap; }
44
+ .pp-notice { margin: 0 0 16px; color: var(--pp-color-text-secondary); font-size: 13px; }
45
+ .pp-notice strong { color: var(--pp-color-text); }
46
+ .pp-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--pp-color-border); margin-bottom: 16px; }
47
+ .pp-tab { appearance: none; border: 0; border-bottom: 2px solid transparent; background: none;
48
+ color: var(--pp-color-text-secondary); font: inherit; padding: 8px 10px; cursor: pointer; }
49
+ .pp-tab[aria-selected="true"] { color: var(--pp-color-text); border-bottom-color: var(--pp-color-primary); font-weight: 600; }
50
+ .pp-root button:focus-visible, .pp-root a:focus-visible { outline: 2px solid var(--pp-color-primary); outline-offset: 2px; }
51
+ .pp-wallets { display: grid; gap: 8px; }
52
+ .pp-button { display: flex; gap: 10px; align-items: center; justify-content: center; width: 100%;
53
+ appearance: none; border: 1px solid var(--pp-color-primary); border-radius: var(--pp-border-radius);
54
+ background: var(--pp-color-primary); color: var(--pp-accessible-color-on-color-primary); font: inherit; font-weight: 600; padding: 10px 14px; cursor: pointer; }
55
+ .pp-button:disabled { opacity: 0.6; cursor: progress; }
56
+ .pp-button img { width: 20px; height: 20px; }
57
+ .pp-wallet-name { font-weight: 400; opacity: 0.85; }
58
+ .pp-message { margin: 10px 0 0; color: var(--pp-color-text-secondary); }
59
+ .pp-message[data-tone="danger"] { color: var(--pp-color-danger); }
60
+ .pp-qr-panel { display: grid; justify-items: center; gap: 10px; text-align: center; }
61
+ .pp-qr { border-radius: 4px; }
62
+ .pp-fields { display: grid; gap: 10px; margin: 0; }
63
+ .pp-field dt { color: var(--pp-color-text-secondary); font-size: 12px; }
64
+ .pp-field dd { display: flex; gap: 8px; align-items: center; margin: 2px 0 0; }
65
+ .pp-value { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; word-break: break-all; }
66
+ .pp-copy { flex: none; appearance: none; border: 1px solid var(--pp-color-border); border-radius: 6px;
67
+ background: none; color: var(--pp-color-text); font: inherit; font-size: 12px; padding: 3px 8px; cursor: pointer; }
68
+ .pp-tx { margin: 0 0 12px; font-size: 13px; }
69
+ .pp-tx a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
70
+ .pp-tx .pp-value { overflow-wrap: anywhere; }
71
+
72
+ .pp-choices { display: flex; gap: 12px; border: 0; padding: 0; margin: 0 0 16px; }
73
+ .pp-choices legend { padding: 0; margin-bottom: 6px; font-weight: 600; }
74
+ .pp-choices label { display: flex; align-items: center; gap: 4px; cursor: pointer; }
75
+ .pp-choices input { accent-color: var(--pp-color-primary); }
76
+ .pp-choices input:focus-visible { outline: 2px solid var(--pp-color-primary); outline-offset: 2px; }