@coin-voyage/paykit 2.3.6-beta.0 → 2.3.6-beta.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.
Files changed (47) hide show
  1. package/dist/assets/icons.d.ts +3 -0
  2. package/dist/assets/icons.js +3 -0
  3. package/dist/components/Pages/CardPayment/index.d.ts +1 -0
  4. package/dist/components/Pages/CardPayment/index.js +214 -0
  5. package/dist/components/Pages/MobileConnectors/index.js +2 -2
  6. package/dist/components/Pages/PayToAddress/index.js +2 -2
  7. package/dist/components/Pages/PayWithToken/index.js +3 -3
  8. package/dist/components/Pages/SelectChain/index.js +2 -2
  9. package/dist/components/Pages/SelectMethod/index.js +2 -2
  10. package/dist/components/Pages/SelectToken/index.js +3 -3
  11. package/dist/components/contexts/pay/index.d.ts +3 -3
  12. package/dist/components/pay-button/index.js +1 -1
  13. package/dist/components/pay-modal/ConnectWithInjector/index.js +2 -2
  14. package/dist/components/pay-modal/ConnectWithQRCode.js +2 -2
  15. package/dist/components/pay-modal/index.js +5 -5
  16. package/dist/components/ui/ConnectorList/index.js +6 -6
  17. package/dist/components/ui/Modal/ModalPageRenderer.d.ts +3 -3
  18. package/dist/components/ui/Modal/ModalPageRenderer.js +4 -1
  19. package/dist/components/ui/Modal/index.d.ts +3 -3
  20. package/dist/components/ui/Modal/index.js +2 -2
  21. package/dist/components/ui/Modal/styles.js +21 -0
  22. package/dist/config/route-config.d.ts +2 -2
  23. package/dist/config/route-config.js +34 -26
  24. package/dist/hooks/useChainOptions.js +2 -2
  25. package/dist/hooks/useDepositAddressQuery.js +5 -1
  26. package/dist/hooks/useMethodOptions.d.ts +1 -0
  27. package/dist/hooks/useMethodOptions.js +85 -26
  28. package/dist/hooks/usePayToAddressChainOptions.js +2 -2
  29. package/dist/hooks/usePayToAddressTokens.js +2 -2
  30. package/dist/hooks/usePayWithCard.d.ts +10 -0
  31. package/dist/hooks/usePayWithCard.js +24 -0
  32. package/dist/hooks/usePayWithToken.js +19 -10
  33. package/dist/hooks/usePaymentLifecycle.js +3 -1
  34. package/dist/hooks/usePaymentState.d.ts +4 -4
  35. package/dist/hooks/usePaymentState.js +9 -2
  36. package/dist/hooks/useTokenOptions.js +2 -2
  37. package/dist/hooks/useWalletConnectModal.js +2 -2
  38. package/dist/hooks/useWalletConnectUri.js +2 -2
  39. package/dist/providers/paykit-provider.js +139 -122
  40. package/dist/types/route-config.d.ts +3 -3
  41. package/dist/types/routes.d.ts +17 -16
  42. package/dist/types/routes.js +20 -18
  43. package/dist/types/state.d.ts +2 -2
  44. package/dist/types.d.ts +4 -0
  45. package/package.json +5 -3
  46. package/dist/types/enums.d.ts +0 -4
  47. package/dist/types/enums.js +0 -5
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { getChainName } from "@coin-voyage/shared/common";
3
3
  import About from "../components/Pages/About";
4
+ import CardPayment from "../components/Pages/CardPayment";
4
5
  import Confirmation from "../components/Pages/Confirmation";
5
6
  import Connectors from "../components/Pages/Connectors";
6
7
  import DownloadApp from "../components/Pages/DownloadApp";
@@ -15,92 +16,99 @@ import SelectPayToAddressToken from "../components/Pages/SelectPayToAddressToken
15
16
  import SelectToken from "../components/Pages/SelectToken";
16
17
  import SwitchNetworks from "../components/Pages/SwitchNetworks";
17
18
  import ConnectUsing from "../components/pay-modal/ConnectUsing";
18
- import { ROUTES } from "../types/routes";
19
+ import { ROUTE } from "../types/routes";
19
20
  export const routeConfig = {
20
- [ROUTES.SELECT_METHOD]: {
21
+ [ROUTE.SELECT_METHOD]: {
21
22
  component: _jsx(SelectMethod, {}),
22
23
  heading: (ctx) => ctx.locales.selectMethodScreen_heading,
23
24
  showBackButton: false,
24
25
  },
26
+ [ROUTE.CARD_PAYMENT]: {
27
+ component: _jsx(CardPayment, {}),
28
+ heading: () => "Pay with Card",
29
+ onBack: (actions) => {
30
+ actions.setRoute(ROUTE.SELECT_METHOD);
31
+ },
32
+ },
25
33
  // Pay to address routes
26
- [ROUTES.SELECT_PAY_TO_ADDRESS_CHAIN]: {
34
+ [ROUTE.ADDRESS_CHAIN_SELECT]: {
27
35
  component: _jsx(SelectPayToAddressChain, {}),
28
36
  heading: (ctx) => ctx.locales.selectPayToAddressChainScreen_heading,
29
37
  onBack: (actions) => {
30
- actions.setRoute(ROUTES.SELECT_METHOD);
38
+ actions.setRoute(ROUTE.SELECT_METHOD);
31
39
  },
32
40
  },
33
- [ROUTES.SELECT_PAY_TO_ADDRESS_TOKEN]: {
41
+ [ROUTE.ADDRESS_TOKEN_SELECT]: {
34
42
  component: _jsx(SelectPayToAddressToken, {}),
35
43
  heading: (ctx) => ctx.locales.selectPayToAddressTokenScreen_heading,
36
44
  onBack: (actions) => {
37
45
  actions.setPayToAddressChain(undefined);
38
- actions.setRoute(ROUTES.SELECT_PAY_TO_ADDRESS_CHAIN);
46
+ actions.setRoute(ROUTE.ADDRESS_CHAIN_SELECT);
39
47
  },
40
48
  },
41
- [ROUTES.PAY_TO_ADDRESS]: {
49
+ [ROUTE.PAY_TO_ADDRESS]: {
42
50
  component: _jsx(PayToAddress, {}),
43
51
  heading: (ctx) => ctx.locales.payToAddressWaitingScreen_heading,
44
52
  onBack: (actions) => {
45
53
  actions.setPayToAddressCurrency(undefined);
46
- actions.setRoute(ROUTES.SELECT_PAY_TO_ADDRESS_TOKEN);
54
+ actions.setRoute(ROUTE.ADDRESS_TOKEN_SELECT);
47
55
  },
48
56
  },
49
57
  // Pay with token routes
50
- [ROUTES.SELECT_CHAIN]: {
58
+ [ROUTE.WALLET_CHAIN_SELECT]: {
51
59
  component: _jsx(SelectChain, {}),
52
60
  heading: (ctx) => ctx.locales.selectChainScreen_heading,
53
61
  onBack: (actions) => {
54
- actions.setRoute(ROUTES.SELECT_METHOD);
62
+ actions.setRoute(ROUTE.SELECT_METHOD);
55
63
  },
56
64
  },
57
- [ROUTES.SELECT_TOKEN]: {
65
+ [ROUTE.WALLET_TOKEN_SELECT]: {
58
66
  component: _jsx(SelectToken, {}),
59
67
  heading: (ctx) => ctx.locales.selectTokenScreen_heading,
60
68
  onBack: (actions) => {
61
69
  actions.setConnectorChainType(undefined);
62
- actions.setRoute(ROUTES.SELECT_CHAIN);
70
+ actions.setRoute(ROUTE.WALLET_CHAIN_SELECT);
63
71
  },
64
72
  },
65
- [ROUTES.PAY_WITH_TOKEN]: {
73
+ [ROUTE.WALLET_PAYMENT]: {
66
74
  component: _jsx(PayWithToken, {}),
67
75
  heading: (ctx) => ctx.selectedCurrencyOption
68
76
  ? `Pay with ${ctx.selectedCurrencyOption.ticker} on ${getChainName(ctx.selectedCurrencyOption.chain_id)}`
69
77
  : undefined,
70
78
  onBack: (actions) => {
71
79
  actions.setSelectedCurrencyOption(undefined);
72
- actions.setRoute(ROUTES.SELECT_TOKEN);
80
+ actions.setRoute(ROUTE.WALLET_TOKEN_SELECT);
73
81
  },
74
82
  },
75
- [ROUTES.CONFIRMATION]: {
83
+ [ROUTE.CONFIRMATION]: {
76
84
  component: _jsx(Confirmation, {}),
77
85
  heading: (ctx) => ctx.locales.confirmationScreen_heading,
78
86
  showBackButton: false,
79
87
  showInfoButton: false,
80
88
  },
81
- [ROUTES.ONBOARDING]: {
89
+ [ROUTE.ONBOARDING]: {
82
90
  component: _jsx(Onboarding, {}),
83
91
  heading: (ctx) => ctx.locales.onboardingScreen_heading,
84
- onBack: ROUTES.SELECT_METHOD,
92
+ onBack: ROUTE.SELECT_METHOD,
85
93
  },
86
- [ROUTES.ABOUT]: {
94
+ [ROUTE.ABOUT]: {
87
95
  component: _jsx(About, {}),
88
96
  heading: (ctx) => ctx.locales.aboutScreen_heading,
89
97
  },
90
- [ROUTES.CONNECTORS]: {
98
+ [ROUTE.CONNECTORS]: {
91
99
  component: _jsx(Connectors, {}),
92
100
  heading: (ctx) => ctx.locales.connectorsScreen_heading,
93
101
  depth: 0,
94
102
  onBack: (actions) => {
95
103
  actions.setConnectorChainType(undefined);
96
- actions.setRoute(ROUTES.SELECT_METHOD);
104
+ actions.setRoute(ROUTE.SELECT_METHOD);
97
105
  },
98
106
  },
99
- [ROUTES.MOBILECONNECTORS]: {
107
+ [ROUTE.MOBILECONNECTORS]: {
100
108
  component: _jsx(MobileConnectors, {}),
101
109
  heading: (ctx) => ctx.locales.connectorsScreen_heading,
102
110
  },
103
- [ROUTES.CONNECT]: {
111
+ [ROUTE.CONNECT]: {
104
112
  component: _jsx(ConnectUsing, {}),
105
113
  heading: (ctx) => {
106
114
  if (ctx.shouldUseQrcode) {
@@ -110,15 +118,15 @@ export const routeConfig = {
110
118
  }
111
119
  return ctx.walletName;
112
120
  },
113
- onBack: ROUTES.CONNECTORS,
121
+ onBack: ROUTE.CONNECTORS,
114
122
  },
115
- [ROUTES.DOWNLOAD]: {
123
+ [ROUTE.DOWNLOAD]: {
116
124
  component: _jsx(DownloadApp, {}),
117
125
  heading: (ctx) => ctx.locales.downloadAppScreen_heading,
118
126
  depth: 2,
119
- onBack: ROUTES.CONNECT,
127
+ onBack: ROUTE.CONNECT,
120
128
  },
121
- [ROUTES.SWITCHNETWORKS]: {
129
+ [ROUTE.SWITCHNETWORKS]: {
122
130
  component: _jsx(SwitchNetworks, {}),
123
131
  heading: (ctx) => ctx.locales.switchNetworkScreen_heading,
124
132
  },
@@ -10,7 +10,7 @@ import Logos from "../assets/chains";
10
10
  import { MetaMask } from "../assets/logos";
11
11
  import usePayContext from "../components/contexts/pay";
12
12
  import { SquircleIcon } from "../components/ui/Icon";
13
- import { ROUTES } from "../types/routes";
13
+ import { ROUTE } from "../types/routes";
14
14
  function getTitle(subject, isDeposit) {
15
15
  return `${isDeposit ? "Deposit from" : "Pay on"} ${subject}`;
16
16
  }
@@ -29,7 +29,7 @@ export function useChainOptions({ account, mode, onClick }) {
29
29
  : "wallet", [paymentState.senderEnsName, account.address]);
30
30
  const handleConnectedWalletClick = useCallback(() => {
31
31
  paymentState.setConnectorChainType(account.chainType);
32
- setRoute(ROUTES.SELECT_TOKEN);
32
+ setRoute(ROUTE.WALLET_TOKEN_SELECT);
33
33
  }, [paymentState, account.chainType, setRoute]);
34
34
  const connectedWalletIcon = useMemo(() => {
35
35
  return account.connector?.icon
@@ -1,3 +1,4 @@
1
+ import { getDepositAddress } from "@coin-voyage/shared/common";
1
2
  import { useQuery } from "@tanstack/react-query";
2
3
  import usePayContext from "../components/contexts/pay";
3
4
  export function useDepositAddressQuery({ enabled }) {
@@ -22,9 +23,12 @@ export function useDepositAddressQuery({ enabled }) {
22
23
  });
23
24
  if (!response)
24
25
  throw new Error("no-response");
26
+ const depositAddress = getDepositAddress(response.data);
27
+ if (!depositAddress)
28
+ throw new Error("Deposit address is unavailable for this pay order");
25
29
  return {
26
30
  ...currency,
27
- depositAddress: response.data.deposit_address,
31
+ depositAddress,
28
32
  amount: response.data.src.total.ui_amount.toString(),
29
33
  ticker: response.data.src.ticker,
30
34
  expirationS: Math.floor(new Date(response.data.expires_at).getTime() / 1000),
@@ -6,5 +6,6 @@ interface UseMethodOptionsProps {
6
6
  }
7
7
  export declare function useMethodOptions({ mode, onClick }: UseMethodOptionsProps): {
8
8
  options: Option[];
9
+ isLoading: boolean;
9
10
  };
10
11
  export {};
@@ -1,40 +1,99 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { PayOrderMode } from "@coin-voyage/shared/types";
2
+ import { PaymentMethod, PayOrderMode } from "@coin-voyage/shared/types";
3
+ import { useQuery } from "@tanstack/react-query";
4
+ import { useMemo } from "react";
3
5
  import ChainLogos from "../assets/chains";
6
+ import { CreditCardIcon } from "../assets/icons";
4
7
  import Logos from "../assets/logos";
8
+ import { useBackendApi } from "../components/contexts/api";
5
9
  import usePayContext from "../components/contexts/pay";
6
- import { PaymentMethod } from "../types/enums";
7
- import { ROUTES } from "../types/routes";
10
+ import { ROUTE } from "../types/routes";
8
11
  function getTitle(subject, isDeposit) {
9
12
  return `${isDeposit ? "Deposit" : "Pay"} ${subject}`;
10
13
  }
11
14
  export function useMethodOptions({ mode, onClick }) {
12
- const { setRoute, paymentState } = usePayContext();
13
- const { setPaymentMethod } = paymentState;
15
+ const api = useBackendApi();
16
+ const { setRoute, paymentState, options: contextOptions } = usePayContext();
17
+ const { payOrder, setPaymentMethod } = paymentState;
14
18
  const isDeposit = mode === PayOrderMode.DEPOSIT;
15
- const options = [
16
- {
17
- id: PaymentMethod.Wallet,
18
- title: getTitle("with Wallet", isDeposit),
19
- iconShape: "squircle",
20
- icons: [_jsx(Logos.MetaMask, {}, "metamask"), _jsx(Logos.Rainbow, {}, "rainbow"), _jsx(Logos.Phantom, {}, "phantom")],
21
- onClick: () => {
22
- setPaymentMethod(PaymentMethod.Wallet);
23
- setRoute(ROUTES.SELECT_CHAIN);
24
- onClick();
25
- },
19
+ const shouldCheckCardAvailability = contextOptions?.experimentalFeatures?.cardPayments === true;
20
+ const { data: paymentMethods, isLoading } = useQuery({
21
+ queryKey: ["payment-methods", payOrder?.id],
22
+ enabled: shouldCheckCardAvailability && Boolean(payOrder?.id),
23
+ retry: false,
24
+ refetchOnWindowFocus: false,
25
+ queryFn: async () => {
26
+ if (!payOrder?.id) {
27
+ throw new Error("Missing pay order");
28
+ }
29
+ const response = await api.getPayOrderPaymentMethods(payOrder.id);
30
+ if (response.error || !response.data) {
31
+ throw new Error(response.error?.message ?? "Unable to load payment methods");
32
+ }
33
+ return response.data;
26
34
  },
27
- {
28
- id: PaymentMethod.DepositAddress,
29
- title: getTitle("to Address", isDeposit),
30
- iconShape: "circle",
31
- icons: [_jsx(ChainLogos.Ethereum, {}, "ethereum"), _jsx(ChainLogos.Base, {}, "base"), _jsx(ChainLogos.Arbitrum, {}, "arbitrum")],
35
+ });
36
+ const options = useMemo(() => {
37
+ const nextOptions = [
38
+ {
39
+ id: PaymentMethod.WALLET,
40
+ title: getTitle("with Wallet", isDeposit),
41
+ iconShape: "squircle",
42
+ icons: [_jsx(Logos.MetaMask, {}, "metamask"), _jsx(Logos.Rainbow, {}, "rainbow"), _jsx(Logos.Phantom, {}, "phantom")],
43
+ onClick: () => {
44
+ setPaymentMethod(PaymentMethod.WALLET);
45
+ setRoute(ROUTE.WALLET_CHAIN_SELECT);
46
+ onClick();
47
+ },
48
+ },
49
+ {
50
+ id: PaymentMethod.DEPOSIT_ADDRESS,
51
+ title: getTitle("to Address", isDeposit),
52
+ iconShape: "circle",
53
+ icons: [
54
+ _jsx(ChainLogos.Ethereum, {}, "ethereum"),
55
+ _jsx(ChainLogos.Base, {}, "base"),
56
+ _jsx(ChainLogos.Arbitrum, {}, "arbitrum"),
57
+ ],
58
+ onClick: () => {
59
+ setPaymentMethod(PaymentMethod.DEPOSIT_ADDRESS);
60
+ setRoute(ROUTE.ADDRESS_CHAIN_SELECT);
61
+ onClick();
62
+ },
63
+ },
64
+ ];
65
+ if (!shouldCheckCardAvailability) {
66
+ return nextOptions;
67
+ }
68
+ const cardMethod = paymentMethods?.methods.find((m) => m.method === PaymentMethod.CARD);
69
+ if (!cardMethod) {
70
+ return nextOptions;
71
+ }
72
+ nextOptions.push({
73
+ id: PaymentMethod.CARD,
74
+ title: getTitle("with Card", isDeposit),
75
+ subtitle: !cardMethod.available
76
+ ? (cardMethod.reason ?? formatMinimumAmount(cardMethod.minimum_amount))
77
+ : undefined,
78
+ disabled: !cardMethod.available,
79
+ iconShape: "squircle",
80
+ icons: [_jsx(CreditCardIcon, {}, "stripe")],
32
81
  onClick: () => {
33
- setPaymentMethod(PaymentMethod.DepositAddress);
34
- setRoute(ROUTES.SELECT_PAY_TO_ADDRESS_CHAIN);
82
+ if (!cardMethod.available) {
83
+ return;
84
+ }
85
+ setPaymentMethod(PaymentMethod.CARD);
86
+ setRoute(ROUTE.CARD_PAYMENT);
35
87
  onClick();
36
88
  },
37
- },
38
- ];
39
- return { options };
89
+ });
90
+ return nextOptions;
91
+ }, [isDeposit, onClick, paymentMethods?.methods, setPaymentMethod, setRoute, shouldCheckCardAvailability]);
92
+ return { options, isLoading };
93
+ }
94
+ function formatMinimumAmount(minimumAmount) {
95
+ if (!minimumAmount) {
96
+ return undefined;
97
+ }
98
+ return `Minimum ${minimumAmount.amount} ${minimumAmount.unit}`;
40
99
  }
@@ -5,7 +5,7 @@ import { useQuery } from "@tanstack/react-query";
5
5
  import { useMemo } from "react";
6
6
  import usePayContext from "../components/contexts/pay";
7
7
  import { SquircleIcon } from "../components/ui/Icon";
8
- import { ROUTES } from "../types/routes";
8
+ import { ROUTE } from "../types/routes";
9
9
  const BTC_MIN_USD_AMOUNT = 15;
10
10
  const TOKEN_LIST_QUERY_KEY = ["token-list"];
11
11
  export function usePayToAddressChainOptions({ fiatAmount }) {
@@ -23,7 +23,7 @@ export function usePayToAddressChainOptions({ fiatAmount }) {
23
23
  icons: [_jsx(SquircleIcon, { icon: chain.logoURI, alt: chain.name }, chain.chainId)],
24
24
  onClick: () => {
25
25
  setPayToAddressChain(chain.chainId);
26
- setRoute(ROUTES.SELECT_PAY_TO_ADDRESS_TOKEN);
26
+ setRoute(ROUTE.ADDRESS_TOKEN_SELECT);
27
27
  },
28
28
  disabled: chain.chainId === ChainId.BTC && typeof fiatAmount === "number" && fiatAmount < BTC_MIN_USD_AMOUNT,
29
29
  })), [chains, fiatAmount, setPayToAddressChain, setRoute]);
@@ -4,7 +4,7 @@ import { useQuery } from "@tanstack/react-query";
4
4
  import { useMemo } from "react";
5
5
  import usePayContext from "../components/contexts/pay";
6
6
  import { SquircleIcon } from "../components/ui/Icon";
7
- import { ROUTES } from "../types/routes";
7
+ import { ROUTE } from "../types/routes";
8
8
  const TOKEN_LIST_QUERY_KEY = ["token-list"];
9
9
  export function usePayToAddressTokens() {
10
10
  const { paymentState, setRoute } = usePayContext();
@@ -29,7 +29,7 @@ export function usePayToAddressTokens() {
29
29
  onClick: () => {
30
30
  const currency = tokenToCurrency(token);
31
31
  setPayToAddressCurrency(currency);
32
- setRoute(ROUTES.PAY_TO_ADDRESS);
32
+ setRoute(ROUTE.PAY_TO_ADDRESS);
33
33
  },
34
34
  })), [tokens, setPayToAddressCurrency, setRoute]);
35
35
  return { options, isLoading };
@@ -0,0 +1,10 @@
1
+ import { type PaymentDetails, type PayOrder } from "@coin-voyage/shared/types";
2
+ interface PayWithCardParams {
3
+ payOrder: PayOrder | undefined;
4
+ setPayOrder: (order: PayOrder) => void;
5
+ log: (message: string) => void;
6
+ }
7
+ export declare function usePayWithCard({ payOrder, setPayOrder, log }: PayWithCardParams): {
8
+ payWithCard: () => Promise<PaymentDetails>;
9
+ };
10
+ export {};
@@ -0,0 +1,24 @@
1
+ import { assert } from "@coin-voyage/shared/common";
2
+ import { PaymentRail } from "@coin-voyage/shared/types";
3
+ import { useBackendApi } from "../components/contexts/api";
4
+ import { fetchPaymentDetails } from "../lib/api/payment-details";
5
+ export function usePayWithCard({ payOrder, setPayOrder, log }) {
6
+ const api = useBackendApi();
7
+ const payWithCard = async () => {
8
+ assert(payOrder != undefined, "PayOrder is required");
9
+ const params = {
10
+ payorder_id: payOrder.id,
11
+ payment_rail: PaymentRail.FIAT,
12
+ };
13
+ // const paymentDetails = stub as unknown as PaymentDetails
14
+ const paymentDetails = await fetchPaymentDetails(api, params, payOrder);
15
+ log(`[PAY-WITH-CARD] Created Stripe onramp session: ${JSON.stringify(paymentDetails)}`);
16
+ setPayOrder({
17
+ ...payOrder,
18
+ payment: paymentDetails.data,
19
+ status: paymentDetails.status,
20
+ });
21
+ return paymentDetails;
22
+ };
23
+ return { payWithCard };
24
+ }
@@ -1,5 +1,5 @@
1
1
  import { usePrepareTransaction } from "@coin-voyage/crypto/hooks";
2
- import { assert } from "@coin-voyage/shared/common";
2
+ import { assert, getDepositAddress, getWalletPaymentData } from "@coin-voyage/shared/common";
3
3
  import { useBackendApi } from "../components/contexts/api";
4
4
  import { fetchPaymentDetails } from "../lib/api/payment-details";
5
5
  export function usePayFromWallet({ senderAddr, payOrder, setPayOrder, chainType, log }) {
@@ -23,15 +23,24 @@ export function usePayFromWallet({ senderAddr, payOrder, setPayOrder, chainType,
23
23
  const paymentData = paymentDetails.data;
24
24
  log(`[PAY-WITH-TOKEN] Final Quote for Order: ${JSON.stringify(paymentDetails)}, params: ${JSON.stringify(params)}`);
25
25
  try {
26
- const txHash = await actions.execute({
27
- amount: BigInt(paymentData.src.currency_amount.raw_amount),
28
- fromAddress: senderAddr,
29
- toAddress: paymentData.deposit_address,
30
- chainId: paymentData.src.chain_id,
31
- token: paymentData.src.address
32
- ? { address: paymentData.src.address, decimals: paymentData.src.decimals }
33
- : undefined,
34
- });
26
+ const walletPaymentData = getWalletPaymentData(paymentData);
27
+ const depositAddress = getDepositAddress(paymentData);
28
+ assert(Boolean(walletPaymentData || depositAddress), "Payment step is missing executable wallet data");
29
+ const txHash = walletPaymentData
30
+ ? await actions.execute({
31
+ from: senderAddr,
32
+ chainId: paymentData.src.chain_id,
33
+ paymentData: walletPaymentData,
34
+ })
35
+ : await actions.execute({
36
+ amount: BigInt(paymentData.src.currency_amount.raw_amount),
37
+ from: senderAddr,
38
+ to: depositAddress,
39
+ chainId: paymentData.src.chain_id,
40
+ token: paymentData.src.address
41
+ ? { address: paymentData.src.address, decimals: paymentData.src.decimals }
42
+ : undefined,
43
+ });
35
44
  setPayOrder({
36
45
  ...payOrder,
37
46
  deposit_tx_hash: txHash,
@@ -56,7 +56,7 @@ export function usePaymentLifecycle(order, handlers) {
56
56
  status: order.status,
57
57
  metadata: order.metadata,
58
58
  refund_address: order.payment.refund_address,
59
- refund_tx_hash: order.refund_tx_hash ?? "",
59
+ refund_tx_hash: order.payment.refund_tx_hash ?? "",
60
60
  });
61
61
  return;
62
62
  }
@@ -66,6 +66,8 @@ export function usePaymentLifecycle(order, handlers) {
66
66
  status: order.status,
67
67
  metadata: order.metadata,
68
68
  payment_data: order.payment,
69
+ source_tx_hash: order.payment.source_tx_hash ?? "",
70
+ destination_tx_hash: order.payment.destination_tx_hash ?? "",
69
71
  });
70
72
  }, [order, isFinalized, onPaymentCompleted, onPaymentBounced]);
71
73
  return { isStarted, isFinalized, order };
@@ -1,8 +1,7 @@
1
1
  import { WalletProps } from "@coin-voyage/crypto/types/wallet";
2
- import { ChainId, ChainType, Currency, CurrencyBase, type PayOrder } from "@coin-voyage/shared/types";
2
+ import { ChainId, ChainType, Currency, CurrencyBase, PaymentMethod, type PayOrder } from "@coin-voyage/shared/types";
3
3
  import { PaymentDetails, PayOrderParams } from "@coin-voyage/shared/types/api";
4
- import { PaymentMethod } from "../types/enums";
5
- import { ROUTES } from "../types/routes";
4
+ import { ROUTE } from "../types/routes";
6
5
  import { CurrencyAndQuoteID } from "../types/state";
7
6
  import { usePayOrderQuotes } from "./usePayOrderQuotes";
8
7
  /** Loads a PayOrder + manages the corresponding modal. */
@@ -22,6 +21,7 @@ export interface PaymentState {
22
21
  selectedCurrencyOption: CurrencyAndQuoteID | undefined;
23
22
  setSelectedCurrencyOption: (option: CurrencyAndQuoteID | undefined) => void;
24
23
  payFromWallet: (currency: CurrencyAndQuoteID) => Promise<string | undefined>;
24
+ payWithCard: () => Promise<PaymentDetails>;
25
25
  payToAddress: (currency: CurrencyBase) => Promise<PaymentDetails | undefined>;
26
26
  refreshOrder: () => Promise<void>;
27
27
  senderEnsName: string | undefined;
@@ -33,6 +33,6 @@ export interface PaymentState {
33
33
  export declare function usePaymentState({ payOrder, setPayOrder, setRoute, log, }: {
34
34
  payOrder: PayOrder | undefined;
35
35
  setPayOrder: (o: PayOrder) => void;
36
- setRoute: React.Dispatch<React.SetStateAction<ROUTES>>;
36
+ setRoute: React.Dispatch<React.SetStateAction<ROUTE>>;
37
37
  log: (...args: unknown[]) => void;
38
38
  }): PaymentState;
@@ -6,9 +6,10 @@ import { useCallback, useState } from "react";
6
6
  import { mainnet } from "viem/chains";
7
7
  import { useEnsName } from "wagmi";
8
8
  import { useBackendApi } from "../components/contexts/api";
9
- import { ROUTES } from "../types/routes";
9
+ import { ROUTE } from "../types/routes";
10
10
  import { usePayOrderQuotes } from "./usePayOrderQuotes";
11
11
  import { usePayToAddress } from "./usePayToAddress";
12
+ import { usePayWithCard } from "./usePayWithCard";
12
13
  import { usePayFromWallet } from "./usePayWithToken";
13
14
  export function usePaymentState({ payOrder, setPayOrder, setRoute, log, }) {
14
15
  const api = useBackendApi();
@@ -49,6 +50,11 @@ export function usePaymentState({ payOrder, setPayOrder, setRoute, log, }) {
49
50
  setPayOrder,
50
51
  log,
51
52
  });
53
+ const { payWithCard } = usePayWithCard({
54
+ payOrder,
55
+ setPayOrder,
56
+ log,
57
+ });
52
58
  const fetchPayOrder = useCallback(async (id) => {
53
59
  const { data: order, error } = await api.getPayOrder(id);
54
60
  if (!order || error) {
@@ -130,7 +136,7 @@ export function usePaymentState({ payOrder, setPayOrder, setRoute, log, }) {
130
136
  setSelectedCurrencyOption(undefined);
131
137
  setConnectorChainType(undefined);
132
138
  setSelectedWallet(undefined);
133
- setRoute(ROUTES.SELECT_METHOD);
139
+ setRoute(ROUTE.SELECT_METHOD);
134
140
  }, [setRoute]);
135
141
  return {
136
142
  setPayId: setPayOrderId,
@@ -148,6 +154,7 @@ export function usePaymentState({ payOrder, setPayOrder, setRoute, log, }) {
148
154
  setSelectedCurrencyOption,
149
155
  payOrderQuotes,
150
156
  payFromWallet,
157
+ payWithCard,
151
158
  payToAddress,
152
159
  refreshOrder,
153
160
  senderEnsName: evmEnsName ?? suiEnsName ?? undefined,
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { getChainName } from "@coin-voyage/shared/common";
3
3
  import usePayContext from "../components/contexts/pay";
4
4
  import TokenChainLogo from "../components/ui/TokenChainLogo";
5
- import { ROUTES } from "../types/routes";
5
+ import { ROUTE } from "../types/routes";
6
6
  export function useTokenOptions(disabled) {
7
7
  const { paymentState, setRoute } = usePayContext();
8
8
  const { setSelectedCurrencyOption, payOrderQuotes } = paymentState;
@@ -37,7 +37,7 @@ export function useTokenOptions(disabled) {
37
37
  ],
38
38
  onClick: () => {
39
39
  setSelectedCurrencyOption(quote);
40
- setRoute(ROUTES.PAY_WITH_TOKEN);
40
+ setRoute(ROUTE.WALLET_PAYMENT);
41
41
  },
42
42
  };
43
43
  }) ?? []);
@@ -6,7 +6,7 @@ import { useWallet } from "@solana/wallet-adapter-react";
6
6
  import { useState } from "react";
7
7
  import { useConnect as wagmiUseConnect } from "wagmi";
8
8
  import usePayContext from "../components/contexts/pay";
9
- import { ROUTES } from "../types/routes";
9
+ import { ROUTE } from "../types/routes";
10
10
  import { isWalletConnectConnector } from "../utils";
11
11
  export function useWalletConnectModal() {
12
12
  const { log, setRoute, paymentState } = usePayContext();
@@ -15,7 +15,7 @@ export function useWalletConnectModal() {
15
15
  const { connect } = useUniversalConnect({
16
16
  onSuccess: (_, variables) => {
17
17
  if (variables?.connector) {
18
- setRoute(ROUTES.SELECT_TOKEN);
18
+ setRoute(ROUTE.WALLET_TOKEN_SELECT);
19
19
  }
20
20
  },
21
21
  onError: (error) => {
@@ -2,7 +2,7 @@ import { useAccount, useUniversalConnect } from "@coin-voyage/crypto/hooks";
2
2
  import { ChainType } from "@coin-voyage/shared/types";
3
3
  import { useEffect, useRef, useState } from "react";
4
4
  import usePayContext from "../components/contexts/pay";
5
- import { ROUTES } from "../types/routes";
5
+ import { ROUTE } from "../types/routes";
6
6
  import { useWallets } from "./useWallets";
7
7
  export function useWalletConnectUri({ enabled } = { enabled: true }) {
8
8
  const { log, setRoute, paymentState } = usePayContext();
@@ -19,7 +19,7 @@ export function useWalletConnectUri({ enabled } = { enabled: true }) {
19
19
  onError: (err) => log("WC Connect Error", err),
20
20
  onSuccess: () => {
21
21
  setUri(undefined);
22
- setRoute(ROUTES.SELECT_TOKEN);
22
+ setRoute(ROUTE.WALLET_TOKEN_SELECT);
23
23
  },
24
24
  });
25
25
  const isConnectingRef = useRef(false);