@coin-voyage/paykit 2.4.5-beta.3 → 2.4.5-beta.4
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.
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { StripeOnramp } from "@stripe/crypto/types";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
1
3
|
export default function CardPayment(): import("react/jsx-runtime").JSX.Element;
|
|
2
|
-
export declare const CryptoElements: ({ stripeOnramp, children }: {
|
|
3
|
-
stripeOnramp:
|
|
4
|
-
children:
|
|
4
|
+
export declare const CryptoElements: ({ stripeOnramp, children, }: {
|
|
5
|
+
stripeOnramp: StripeOnramp | Promise<StripeOnramp | null>;
|
|
6
|
+
children: ReactNode;
|
|
5
7
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
-
export declare const
|
|
8
|
+
export declare const useStripeOnrampContext: () => StripeOnramp | null | undefined;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { getFiatPaymentData } from "@coin-voyage/shared/payment";
|
|
3
3
|
import { PayOrderMode, PayOrderStatus } from "@coin-voyage/shared/types";
|
|
4
|
+
import { loadStripeOnramp } from "@stripe/crypto";
|
|
4
5
|
import { useQuery } from "@tanstack/react-query";
|
|
5
6
|
import { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
6
7
|
import { AlertIcon } from "../../../assets/icons";
|
|
@@ -14,11 +15,7 @@ import { OrderHeader } from "../../ui/OrderHeader";
|
|
|
14
15
|
import PoweredByFooter from "../../ui/PoweredByFooter";
|
|
15
16
|
import { Spinner } from "../../ui/Spinner";
|
|
16
17
|
export default function CardPayment() {
|
|
17
|
-
const { triggerResize } = usePayContext();
|
|
18
18
|
const { data, isLoading, error, refetch } = useCardPaymentData();
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
triggerResize();
|
|
21
|
-
}, [triggerResize, isLoading, data?.session_id, data?.client_secret, error]);
|
|
22
19
|
return (_jsxs(PageContent, { children: [_jsx(OrderHeader, { minified: true }), _jsx(CardPaymentContent, { paymentData: data, isLoading: isLoading, error: error instanceof Error ? error : null, onRetry: () => {
|
|
23
20
|
void refetch();
|
|
24
21
|
} }), _jsx(PoweredByFooter, {})] }));
|
|
@@ -66,33 +63,13 @@ function StatusCard({ title, body, actionLabel, onAction, loading = false, warni
|
|
|
66
63
|
}
|
|
67
64
|
function StripeOnrampCheckout({ paymentData }) {
|
|
68
65
|
const { mode, triggerResize } = usePayContext();
|
|
69
|
-
|
|
66
|
+
const { data: stripeOnramp, isLoading, error, refetch } = useLoadStripeOnramp(paymentData.stripe_publishable_key);
|
|
70
67
|
const refreshDebounceRef = useRef(null);
|
|
71
68
|
const appearance = useMemo(() => {
|
|
72
69
|
return {
|
|
73
|
-
theme: mode === "dark" ? "
|
|
74
|
-
variables: {
|
|
75
|
-
colorBackground: "var(--ck-body-background-secondary)",
|
|
76
|
-
},
|
|
70
|
+
theme: mode === "dark" ? "dark" : "light",
|
|
77
71
|
};
|
|
78
72
|
}, [mode]);
|
|
79
|
-
// const handleSessionUpdate = useCallback(
|
|
80
|
-
// (status: OnrampSessionStatus) => {
|
|
81
|
-
// if (status === lastStatusRef.current) return
|
|
82
|
-
// lastStatusRef.current = status
|
|
83
|
-
// const shouldRefresh = status === "fulfillment_complete" || status === "rejected"
|
|
84
|
-
// if (!shouldRefresh) {
|
|
85
|
-
// return
|
|
86
|
-
// }
|
|
87
|
-
// if (refreshDebounceRef.current) {
|
|
88
|
-
// window.clearTimeout(refreshDebounceRef.current)
|
|
89
|
-
// }
|
|
90
|
-
// refreshDebounceRef.current = window.setTimeout(() => {
|
|
91
|
-
// void refreshOrderRef.current()
|
|
92
|
-
// }, 500)
|
|
93
|
-
// },
|
|
94
|
-
// [refreshOrderRef]
|
|
95
|
-
// )
|
|
96
73
|
useEffect(() => {
|
|
97
74
|
return () => {
|
|
98
75
|
if (refreshDebounceRef.current) {
|
|
@@ -100,23 +77,18 @@ function StripeOnrampCheckout({ paymentData }) {
|
|
|
100
77
|
}
|
|
101
78
|
};
|
|
102
79
|
}, []);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// }}
|
|
113
|
-
// />
|
|
114
|
-
// )
|
|
115
|
-
// }
|
|
116
|
-
return (_jsx(OnrampShell, { children: paymentData.client_secret ? (_jsx(OnrampElement, { id: "onramp-element", clientSecret: paymentData.client_secret, appearance: appearance, onReady: triggerResize })) : (_jsx(OnrampOverlay, { children: _jsx(Spinner, {}) })) }));
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
triggerResize();
|
|
82
|
+
}, [error, isLoading, stripeOnramp, triggerResize]);
|
|
83
|
+
if (error) {
|
|
84
|
+
return (_jsx(StatusCard, { warning: true, title: "Stripe unavailable", body: error.message, actionLabel: "Try again", onAction: () => {
|
|
85
|
+
void refetch();
|
|
86
|
+
} }));
|
|
87
|
+
}
|
|
88
|
+
return (_jsxs(OnrampShell, { children: [isLoading ? (_jsx(OnrampOverlay, { children: _jsx(Spinner, {}) })) : null, paymentData.client_secret && stripeOnramp ? (_jsx(CryptoElements, { stripeOnramp: stripeOnramp, children: _jsx(OnrampElement, { id: "onramp-element", clientSecret: paymentData.client_secret, appearance: appearance, onReady: triggerResize }) })) : (_jsx(OnrampOverlay, { children: _jsx(Spinner, {}) }))] }));
|
|
117
89
|
}
|
|
118
90
|
function OnrampElement({ clientSecret, appearance, onReady, onChange, ...props }) {
|
|
119
|
-
const stripeOnramp =
|
|
91
|
+
const stripeOnramp = useStripeOnrampContext();
|
|
120
92
|
const onrampElementRef = useRef(null);
|
|
121
93
|
const [session, setSession] = useState();
|
|
122
94
|
const appearanceJSON = JSON.stringify(appearance);
|
|
@@ -138,7 +110,7 @@ function OnrampElement({ clientSecret, appearance, onReady, onChange, ...props }
|
|
|
138
110
|
}, [appearanceJSON, clientSecret, stripeOnramp]);
|
|
139
111
|
useOnrampSessionListener("onramp_ui_loaded", session, onReady);
|
|
140
112
|
useOnrampSessionListener("onramp_session_updated", session, onChange);
|
|
141
|
-
return _jsx(
|
|
113
|
+
return _jsx(OnrampMount, { ...props, ref: onrampElementRef });
|
|
142
114
|
}
|
|
143
115
|
function useCardPaymentData() {
|
|
144
116
|
const { paymentState } = usePayContext();
|
|
@@ -169,10 +141,39 @@ function useCardPaymentData() {
|
|
|
169
141
|
data: paymentData ?? query.data,
|
|
170
142
|
};
|
|
171
143
|
}
|
|
144
|
+
const stripeOnrampPromises = new Map();
|
|
145
|
+
function getStripeOnramp(publishableKey) {
|
|
146
|
+
const existingPromise = stripeOnrampPromises.get(publishableKey);
|
|
147
|
+
if (existingPromise) {
|
|
148
|
+
return existingPromise;
|
|
149
|
+
}
|
|
150
|
+
const stripeOnrampPromise = loadStripeOnramp(publishableKey).catch((error) => {
|
|
151
|
+
stripeOnrampPromises.delete(publishableKey);
|
|
152
|
+
throw error;
|
|
153
|
+
});
|
|
154
|
+
stripeOnrampPromises.set(publishableKey, stripeOnrampPromise);
|
|
155
|
+
return stripeOnrampPromise;
|
|
156
|
+
}
|
|
157
|
+
function useLoadStripeOnramp(publishableKey) {
|
|
158
|
+
return useQuery({
|
|
159
|
+
queryKey: ["stripe-onramp", publishableKey],
|
|
160
|
+
enabled: Boolean(publishableKey),
|
|
161
|
+
staleTime: Infinity,
|
|
162
|
+
retry: 1,
|
|
163
|
+
refetchOnWindowFocus: false,
|
|
164
|
+
queryFn: async () => {
|
|
165
|
+
const stripeOnramp = await getStripeOnramp(publishableKey);
|
|
166
|
+
if (!stripeOnramp) {
|
|
167
|
+
throw new Error("Stripe checkout can only be loaded in a browser.");
|
|
168
|
+
}
|
|
169
|
+
return stripeOnramp;
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
|
172
173
|
// ReactContext to simplify access of StripeOnramp object
|
|
173
174
|
const CryptoElementsContext = createContext(null);
|
|
174
175
|
CryptoElementsContext.displayName = "CryptoElementsContext";
|
|
175
|
-
export const CryptoElements = ({ stripeOnramp, children }) => {
|
|
176
|
+
export const CryptoElements = ({ stripeOnramp, children, }) => {
|
|
176
177
|
const [ctx, setContext] = useState(() => ({
|
|
177
178
|
onramp: null,
|
|
178
179
|
}));
|
|
@@ -180,7 +181,7 @@ export const CryptoElements = ({ stripeOnramp, children }) => {
|
|
|
180
181
|
let isMounted = true;
|
|
181
182
|
Promise.resolve(stripeOnramp).then((onramp) => {
|
|
182
183
|
if (onramp && isMounted) {
|
|
183
|
-
setContext((ctx) => (ctx.onramp ? ctx : { onramp }));
|
|
184
|
+
setContext((ctx) => (ctx.onramp === onramp ? ctx : { onramp }));
|
|
184
185
|
}
|
|
185
186
|
});
|
|
186
187
|
return () => {
|
|
@@ -190,7 +191,7 @@ export const CryptoElements = ({ stripeOnramp, children }) => {
|
|
|
190
191
|
return _jsx(CryptoElementsContext.Provider, { value: ctx, children: children });
|
|
191
192
|
};
|
|
192
193
|
// React hook to get StripeOnramp from context
|
|
193
|
-
export const
|
|
194
|
+
export const useStripeOnrampContext = () => {
|
|
194
195
|
const context = useContext(CryptoElementsContext);
|
|
195
196
|
return context?.onramp;
|
|
196
197
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coin-voyage/paykit",
|
|
3
3
|
"description": "Seamless crypto payments. Onboard users from any chain, any coin into your app with one click.",
|
|
4
|
-
"version": "2.4.5-beta.
|
|
4
|
+
"version": "2.4.5-beta.4",
|
|
5
5
|
"private": false,
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"author": "Lars <lars@coinvoyage.io>",
|