@daimo/pay 1.7.2 → 1.7.3

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/build/index.d.ts CHANGED
@@ -228,6 +228,10 @@ type PayButtonPaymentProps = {
228
228
  * Developer metadata. E.g. correlation ID.
229
229
  * */
230
230
  metadata?: DaimoPayUserMetadata;
231
+ /**
232
+ * The address to refund to if the payment bounces or a refund is requested.
233
+ */
234
+ refundAddress?: Address;
231
235
  } | {
232
236
  /** The payment ID, generated via the Daimo Pay API. Replaces params above. */
233
237
  payId: string;
@@ -239,6 +243,10 @@ type PayButtonCommonProps = PayButtonPaymentProps & {
239
243
  onPaymentCompleted?: (event: DaimoPayCompletedEvent) => void;
240
244
  /** Called when destination call reverts and funds are refunded */
241
245
  onPaymentBounced?: (event: DaimoPayBouncedEvent) => void;
246
+ /** Called when the modal is opened. */
247
+ onOpen?: () => void;
248
+ /** Called when the modal is closed. */
249
+ onClose?: () => void;
242
250
  /** Automatically close the modal after a successful payment. */
243
251
  closeOnSuccess?: boolean;
244
252
  /** Open the modal by default. */
@@ -453,6 +461,8 @@ interface PayParams {
453
461
  externalId?: string;
454
462
  /** Developer metadata. E.g. correlation ID. */
455
463
  metadata?: DaimoPayUserMetadata;
464
+ /** The address to refund to if the payment bounces or a refund is requested. */
465
+ refundAddress?: Address;
456
466
  }
457
467
  /** Creates (or loads) a payment and manages the corresponding modal. */
458
468
  interface PaymentState {
@@ -507,6 +517,8 @@ type PayContextValue = {
507
517
  setCustomTheme: React$1.Dispatch<React$1.SetStateAction<CustomTheme | undefined>>;
508
518
  lang: Languages$1;
509
519
  setLang: React$1.Dispatch<React$1.SetStateAction<Languages$1>>;
520
+ setOnOpen: (fn?: () => void) => void;
521
+ setOnClose: (fn?: () => void) => void;
510
522
  open: boolean;
511
523
  setOpen: (open: boolean, meta?: Record<string, any>) => void;
512
524
  route: string;
package/build/index.js CHANGED
@@ -7,7 +7,7 @@ import { http, useConfig, useAccountEffect, useWriteContract, useSendTransaction
7
7
  import { mainnet, base as base$1, polygon, optimism, arbitrum, linea, bsc, sepolia, baseSepolia, worldchain, blast, mantle } from 'wagmi/chains';
8
8
  import { safe, injected, coinbaseWallet, walletConnect } from '@wagmi/connectors';
9
9
  import { useConnection, useWallet as useWallet$1, ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react';
10
- import { hexToBytes, zeroAddress, erc20Abi, getAddress, parseUnits, formatUnits } from 'viem';
10
+ import { hexToBytes, zeroAddress, erc20Abi, getAddress, formatUnits, parseUnits } from 'viem';
11
11
  import { VersionedTransaction } from '@solana/web3.js';
12
12
  import { createTRPCClient, httpBatchLink } from '@trpc/client';
13
13
  import { motion, AnimatePresence, MotionConfig } from 'framer-motion';
@@ -22,7 +22,7 @@ import { WalletSignTransactionError, WalletSendTransactionError } from '@solana/
22
22
  import { normalize } from 'viem/ens';
23
23
 
24
24
  var name = "@daimo/pay";
25
- var version = "1.7.2";
25
+ var version = "1.7.3";
26
26
  var author = "Daimo";
27
27
  var homepage = "https://pay.daimo.com";
28
28
  var license = "BSD-2-Clause license";
@@ -61,7 +61,7 @@ var keywords = [
61
61
  "crypto"
62
62
  ];
63
63
  var dependencies = {
64
- "@daimo/pay-common": "1.7.2",
64
+ "@daimo/pay-common": "1.7.3",
65
65
  "@rollup/plugin-typescript": "^12.1.2",
66
66
  "@solana/wallet-adapter-base": "^0.9.23",
67
67
  "@solana/wallet-adapter-react": "^0.15.35",
@@ -997,7 +997,7 @@ function useOrderUsdLimits({ trpc }) {
997
997
  return { limits, loading };
998
998
  }
999
999
 
1000
- function usePayWithSolanaToken({ trpc, daimoPayOrder, setDaimoPayOrder, createOrHydrate, log, }) {
1000
+ function usePayWithSolanaToken({ trpc, refundAddress, daimoPayOrder, setDaimoPayOrder, createOrHydrate, log, }) {
1001
1001
  const { connection } = useConnection();
1002
1002
  const wallet = useWallet$1();
1003
1003
  const payWithSolanaToken = async (inputToken) => {
@@ -1006,6 +1006,7 @@ function usePayWithSolanaToken({ trpc, daimoPayOrder, setDaimoPayOrder, createOr
1006
1006
  const orderId = daimoPayOrder.id;
1007
1007
  const { hydratedOrder } = await createOrHydrate({
1008
1008
  order: daimoPayOrder,
1009
+ refundAddress,
1009
1010
  });
1010
1011
  log(`[CHECKOUT] Hydrated order: ${JSON.stringify(hydratedOrder)}, checking out with Solana ${inputToken}`);
1011
1012
  const txHash = await (async () => {
@@ -1040,7 +1041,7 @@ function usePayWithSolanaToken({ trpc, daimoPayOrder, setDaimoPayOrder, createOr
1040
1041
  return { payWithSolanaToken };
1041
1042
  }
1042
1043
 
1043
- function usePayWithToken({ trpc, senderAddr, daimoPayOrder, setDaimoPayOrder, createOrHydrate, log, }) {
1044
+ function usePayWithToken({ trpc, senderAddr, refundAddress, daimoPayOrder, setDaimoPayOrder, createOrHydrate, log, }) {
1044
1045
  const { writeContractAsync } = useWriteContract();
1045
1046
  const { sendTransactionAsync } = useSendTransaction();
1046
1047
  /** Commit to a token + amount = initiate payment. */
@@ -1051,7 +1052,8 @@ function usePayWithToken({ trpc, senderAddr, daimoPayOrder, setDaimoPayOrder, cr
1051
1052
  const paymentAmount = BigInt(required.amount) + BigInt(fees.amount);
1052
1053
  const { hydratedOrder } = await createOrHydrate({
1053
1054
  order: daimoPayOrder,
1054
- refundAddress: senderAddr,
1055
+ // Use the developer-provided refund address. Default to the sender.
1056
+ refundAddress: refundAddress ?? senderAddr,
1055
1057
  });
1056
1058
  log(`[PAY TOKEN] hydrated order: ${JSON.stringify(hydratedOrder)}, paying ${paymentAmount} of token ${required.token.token}`);
1057
1059
  setDaimoPayOrder(hydratedOrder);
@@ -1260,6 +1262,7 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
1260
1262
  const { payWithToken } = usePayWithToken({
1261
1263
  trpc,
1262
1264
  senderAddr,
1265
+ refundAddress: payParams?.refundAddress,
1263
1266
  daimoPayOrder,
1264
1267
  setDaimoPayOrder,
1265
1268
  createOrHydrate,
@@ -1267,6 +1270,7 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
1267
1270
  });
1268
1271
  const { payWithSolanaToken } = usePayWithSolanaToken({
1269
1272
  trpc,
1273
+ refundAddress: payParams?.refundAddress,
1270
1274
  daimoPayOrder,
1271
1275
  setDaimoPayOrder,
1272
1276
  createOrHydrate,
@@ -1394,6 +1398,7 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
1394
1398
  },
1395
1399
  externalId: payParams.externalId,
1396
1400
  userMetadata: payParams.metadata,
1401
+ refundAddress: payParams.refundAddress,
1397
1402
  });
1398
1403
  log(`[CHECKOUT] generated preview: ${JSON.stringify(orderPreview)}`);
1399
1404
  setDaimoPayOrder(orderPreview);
@@ -9757,7 +9762,7 @@ const ChainLogoContainer = styled(motion.div) `
9757
9762
  `;
9758
9763
 
9759
9764
  const TokenLogoSpinner = ({ token, showSpinner = true, }) => {
9760
- return (jsx(LoadingContainer$2, { children: jsx(AnimationContainer$1, { "$circle": true, children: jsxs(AnimatePresence, { children: [chainToLogo[token.chainId] && (jsx(ChainLogoContainer, { children: chainToLogo[token.chainId] }, "ChainLogoContainer")), jsx(CircleSpinner, { logo: jsx("img", { src: token.logoURI, alt: token.symbol }, token.logoURI), loading: showSpinner, unavailable: false }, "CircleSpinner")] }) }) }));
9765
+ return (jsx(LoadingContainer$2, { children: jsx(AnimationContainer$1, { "$circle": true, children: jsxs(AnimatePresence, { children: [chainToLogo[token.chainId] && (jsx(ChainLogoContainer, { children: chainToLogo[token.chainId] }, "ChainLogoContainer")), jsx(CircleSpinner, { logo: jsx("img", { src: token.logoURI, alt: token.symbol }), loading: showSpinner, unavailable: false }, "CircleSpinner")] }) }) }));
9761
9766
  };
9762
9767
 
9763
9768
  var PayState$1;
@@ -11452,18 +11457,43 @@ const DaimoPayProviderWithoutSolana = ({ children, theme = "auto", mode = "auto"
11452
11457
  const [ckMode, setMode] = useState(mode);
11453
11458
  const [ckCustomTheme, setCustomTheme] = useState(customTheme ?? {});
11454
11459
  const [ckLang, setLang] = useState("en-US");
11460
+ const onOpenRef = useRef();
11461
+ const onCloseRef = useRef();
11462
+ const setOnOpen = useCallback((fn) => {
11463
+ onOpenRef.current = fn;
11464
+ }, []);
11465
+ const setOnClose = useCallback((fn) => {
11466
+ onCloseRef.current = fn;
11467
+ }, []);
11455
11468
  const [open, setOpenState] = useState(false);
11456
- const setOpen = (open, meta) => {
11469
+ const [route, setRouteState] = useState(ROUTES.SELECT_METHOD);
11470
+ // Daimo Pay context
11471
+ const [daimoPayOrder, setDaimoPayOrderInner] = useState();
11472
+ const [pendingConnectorId, setPendingConnectorId] = useState(undefined);
11473
+ // Track sessions. Each generates separate intent IDs unless using externalId.
11474
+ const [sessionId] = useState(() => crypto.randomUUID().replaceAll("-", ""));
11475
+ const [solanaConnector, setSolanaConnector] = useState();
11476
+ // Other configuration
11477
+ const [errorMessage, setErrorMessage] = useState("");
11478
+ const [confirmationMessage, setConfirmationMessage] = useState(undefined);
11479
+ const [redirectReturnUrl, setRedirectReturnUrl] = useState(undefined);
11480
+ const log = debugMode ? console.log : () => { };
11481
+ // Connect to the Daimo Pay TRPC API
11482
+ const trpc = useMemo(() => createTrpcClient(payApiUrl, sessionId), [payApiUrl]);
11483
+ const [resize, onResize] = useState(0);
11484
+ const setOpen = useCallback((open, meta) => {
11457
11485
  setOpenState(open);
11458
11486
  trpc.nav.mutate({
11459
11487
  action: open ? "navOpenPay" : "navClosePay",
11460
11488
  orderId: daimoPayOrder?.id?.toString(),
11461
11489
  data: meta ?? {},
11462
11490
  });
11463
- };
11464
- const [solanaConnector, setSolanaConnector] = useState();
11465
- const [route, setRouteState] = useState(ROUTES.SELECT_METHOD);
11466
- const setRoute = (route, data) => {
11491
+ if (open)
11492
+ onOpenRef.current?.();
11493
+ else
11494
+ onCloseRef.current?.();
11495
+ }, [trpc, daimoPayOrder?.id]);
11496
+ const setRoute = useCallback((route, data) => {
11467
11497
  const action = route.replace("daimoPay", "");
11468
11498
  log(`[SET ROUTE] ${action} ${daimoPayOrder?.id} ${debugJson(data ?? {})}`);
11469
11499
  trpc.nav.mutate({
@@ -11472,15 +11502,10 @@ const DaimoPayProviderWithoutSolana = ({ children, theme = "auto", mode = "auto"
11472
11502
  data: data ?? {},
11473
11503
  });
11474
11504
  setRouteState(route);
11475
- };
11476
- const [errorMessage, setErrorMessage] = useState("");
11477
- const [confirmationMessage, setConfirmationMessage] = useState(undefined);
11478
- const [redirectReturnUrl, setRedirectReturnUrl] = useState(undefined);
11479
- const [resize, onResize] = useState(0);
11480
- const [pendingConnectorId, setPendingConnectorId] = useState(undefined);
11505
+ }, [trpc, daimoPayOrder?.id, log]);
11481
11506
  // Include Google Font that is needed for a themes
11482
11507
  if (opts.embedGoogleFonts)
11483
- useThemeFont(theme);
11508
+ useThemeFont(ckTheme);
11484
11509
  // Other Configuration
11485
11510
  useEffect(() => setTheme(theme), [theme]);
11486
11511
  useEffect(() => setLang(opts.language || "en-US"), [opts.language]);
@@ -11491,23 +11516,18 @@ const DaimoPayProviderWithoutSolana = ({ children, theme = "auto", mode = "auto"
11491
11516
  useEffect(() => {
11492
11517
  if (isConnected && opts.enforceSupportedChains && !isChainSupported) {
11493
11518
  setOpen(true);
11494
- setRoute(ROUTES.SWITCHNETWORKS);
11519
+ if (route !== ROUTES.SWITCHNETWORKS)
11520
+ setRoute(ROUTES.SWITCHNETWORKS);
11495
11521
  }
11496
11522
  }, [isConnected, isChainSupported, chain, route, open]);
11497
- const log = debugMode ? console.log : () => { };
11498
- // Track sessions. Each generates separate intent IDs unless using externalId.
11499
- const [sessionId] = useState(() => crypto.randomUUID().replaceAll("-", ""));
11500
11523
  // Single source of truth for the currently-connected wallet is the connector
11501
11524
  // exposed by wagmi. See useAccount(). We watch this connector and use it to
11502
11525
  // extract the current WalletConnect wallet, if any.
11503
11526
  const wcWallet = useExtractWcWallet({ connector, log });
11504
- // Connect to the Daimo Pay TRPC API
11505
- const trpc = useMemo(() => createTrpcClient(payApiUrl, sessionId), [payApiUrl]);
11506
11527
  // PaymentInfo is a second, inner context object containing a DaimoPayOrder
11507
11528
  // plus all associated status and callbacks. In order for useContext() and
11508
11529
  // downstream hooks like useDaimoPayStatus() to work correctly, we must set
11509
11530
  // set refresh context when payment status changes; done via setDaimoPayOrder.
11510
- const [daimoPayOrder, setDaimoPayOrderInner] = useState();
11511
11531
  const setDaimoPayOrder = useCallback((order) => {
11512
11532
  setDaimoPayOrderInner(order);
11513
11533
  let extra = `> $${order.destFinalCallTokenAmount.usd.toFixed(2)} to ${order.destFinalCallTokenAmount.token.chainId} ${order.destFinalCall.to}`;
@@ -11515,7 +11535,7 @@ const DaimoPayProviderWithoutSolana = ({ children, theme = "auto", mode = "auto"
11515
11535
  extra += ` via ${order.intentAddr} ${order.sourceStatus} ${order.intentStatus}`;
11516
11536
  }
11517
11537
  log(`[PAY] setDaimoPayOrder: ${order.id} ${extra}`);
11518
- }, [setDaimoPayOrderInner]);
11538
+ }, [log]);
11519
11539
  const paymentState = usePaymentState({
11520
11540
  trpc,
11521
11541
  daimoPayOrder,
@@ -11546,7 +11566,6 @@ const DaimoPayProviderWithoutSolana = ({ children, theme = "auto", mode = "auto"
11546
11566
  return () => clearTimeout(timeout);
11547
11567
  }, [daimoPayOrder]);
11548
11568
  const showPayment = async (modalOptions) => {
11549
- const { daimoPayOrder } = paymentState;
11550
11569
  const id = daimoPayOrder?.id;
11551
11570
  log(`[PAY] showing payment ${debugJson({ id, modalOptions })}`);
11552
11571
  paymentState.setModalOptions(modalOptions);
@@ -11572,6 +11591,8 @@ const DaimoPayProviderWithoutSolana = ({ children, theme = "auto", mode = "auto"
11572
11591
  setCustomTheme,
11573
11592
  lang: ckLang,
11574
11593
  setLang,
11594
+ setOnOpen,
11595
+ setOnClose,
11575
11596
  open,
11576
11597
  setOpen,
11577
11598
  route,
@@ -11857,10 +11878,12 @@ function DaimoPayButtonCustom(props) {
11857
11878
  evmChains: props.evmChains,
11858
11879
  externalId: props.externalId,
11859
11880
  metadata: props.metadata,
11881
+ refundAddress: props.refundAddress,
11860
11882
  }
11861
11883
  : null;
11862
11884
  let payId = "payId" in props ? props.payId : null;
11863
11885
  const { paymentState } = context;
11886
+ // Set the payId or payParams
11864
11887
  useEffect(() => {
11865
11888
  if (payId != null) {
11866
11889
  paymentState.setPayId(payId);
@@ -11869,18 +11892,30 @@ function DaimoPayButtonCustom(props) {
11869
11892
  paymentState.setPayParams(payParams);
11870
11893
  }
11871
11894
  }, [payId, JSON.stringify(payParams || {})]);
11895
+ // Set the confirmation message
11872
11896
  const { setConfirmationMessage } = context;
11873
11897
  useEffect(() => {
11874
11898
  if (props.confirmationMessage) {
11875
11899
  setConfirmationMessage(props.confirmationMessage);
11876
11900
  }
11877
11901
  }, [props.confirmationMessage, setConfirmationMessage]);
11902
+ // Set the redirect return url
11878
11903
  const { setRedirectReturnUrl } = context;
11879
11904
  useEffect(() => {
11880
11905
  if (props.redirectReturnUrl) {
11881
11906
  setRedirectReturnUrl(props.redirectReturnUrl);
11882
11907
  }
11883
11908
  }, [props.redirectReturnUrl, setRedirectReturnUrl]);
11909
+ // Set the onOpen and onClose callbacks
11910
+ const { setOnOpen, setOnClose } = context;
11911
+ useEffect(() => {
11912
+ setOnOpen(props.onOpen);
11913
+ return () => setOnOpen(undefined);
11914
+ }, [props.onOpen, setOnOpen]);
11915
+ useEffect(() => {
11916
+ setOnClose(props.onClose);
11917
+ return () => setOnClose(undefined);
11918
+ }, [props.onClose, setOnClose]);
11884
11919
  // Payment events: call these three event handlers.
11885
11920
  const { onPaymentStarted, onPaymentCompleted, onPaymentBounced } = props;
11886
11921
  const order = paymentState.daimoPayOrder;
@@ -11927,11 +11962,12 @@ function DaimoPayButtonCustom(props) {
11927
11962
  }
11928
11963
  }
11929
11964
  }, [hydOrder?.id, intentStatus]);
11965
+ // Open the modal by default if the defaultOpen prop is true
11930
11966
  useEffect(() => {
11931
- if (props.defaultOpen) {
11967
+ if (props.defaultOpen && order != null) {
11932
11968
  show();
11933
11969
  }
11934
- }, [order != null]);
11970
+ }, [order != null, props.defaultOpen]);
11935
11971
  // Validation
11936
11972
  if ((payId == null) == (payParams == null)) {
11937
11973
  throw new Error("Must specify either payId or appId, not both");