@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.
- package/README.md +44 -22
- package/dist/checkout.d.ts +9 -1
- package/dist/checkout.d.ts.map +1 -1
- package/dist/checkout.js +22 -2
- package/dist/checkout.js.map +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -1
- package/dist/client.js.map +1 -1
- package/dist/deposit-address.d.ts +3 -0
- package/dist/deposit-address.d.ts.map +1 -1
- package/dist/deposit-address.js +6 -1
- package/dist/deposit-address.js.map +1 -1
- package/dist/react/Checkout.d.ts +1 -1
- package/dist/react/Checkout.d.ts.map +1 -1
- package/dist/react/Checkout.js +14 -9
- package/dist/react/Checkout.js.map +1 -1
- package/dist/react/DepositAddress.d.ts +1 -1
- package/dist/react/DepositAddress.d.ts.map +1 -1
- package/dist/react/DepositAddress.js +7 -9
- package/dist/react/DepositAddress.js.map +1 -1
- package/dist/react/appearance.d.ts +1 -22
- package/dist/react/appearance.d.ts.map +1 -1
- package/dist/react/appearance.js +1 -100
- package/dist/react/appearance.js.map +1 -1
- package/dist/react/useCheckout.d.ts +3 -9
- package/dist/react/useCheckout.d.ts.map +1 -1
- package/dist/react/useCheckout.js +5 -3
- package/dist/react/useCheckout.js.map +1 -1
- package/dist/request.d.ts +6 -0
- package/dist/request.d.ts.map +1 -0
- package/dist/request.js +6 -0
- package/dist/request.js.map +1 -0
- package/package.json +13 -9
- package/src/checkout.ts +32 -3
- package/src/client.ts +8 -2
- package/src/css.d.ts +1 -0
- package/src/deposit-address.ts +9 -1
- package/src/react/Checkout.tsx +13 -9
- package/src/react/DepositAddress.tsx +28 -31
- package/src/react/appearance.ts +1 -119
- package/src/react/useCheckout.ts +15 -9
- package/src/request.ts +8 -0
- package/src/styles.css +76 -0
package/src/react/appearance.ts
CHANGED
|
@@ -1,122 +1,4 @@
|
|
|
1
|
-
|
|
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
|
-
`;
|
package/src/react/useCheckout.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
createCheckout,
|
|
4
|
+
type CheckoutOptions,
|
|
5
|
+
type CheckoutSession,
|
|
6
|
+
type CheckoutState,
|
|
7
|
+
} from "../checkout.js";
|
|
3
8
|
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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; }
|