@unifold/connect-react 0.1.68-beta.1 → 0.1.68-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.
package/dist/index.mjs CHANGED
@@ -1144,7 +1144,7 @@ ${new this._window.XMLSerializer().serializeToString(e3)}`;
1144
1144
  });
1145
1145
 
1146
1146
  // src/provider.tsx
1147
- import React38, { useState as useState39, useCallback as useCallback13, useMemo as useMemo17, useEffect as useEffect40 } from "react";
1147
+ import React38, { useState as useState39, useCallback as useCallback13, useMemo as useMemo18, useEffect as useEffect40 } from "react";
1148
1148
 
1149
1149
  // ../react-provider/dist/index.mjs
1150
1150
  import { createContext, useContext, useState, useEffect, useMemo, useRef } from "react";
@@ -1218,7 +1218,7 @@ import {
1218
1218
  useLayoutEffect as useLayoutEffect22,
1219
1219
  useCallback as useCallback82,
1220
1220
  useRef as useRef122,
1221
- useMemo as useMemo12
1221
+ useMemo as useMemo13
1222
1222
  } from "react";
1223
1223
 
1224
1224
  // ../../node_modules/.pnpm/lucide-react@0.454.0_react@19.2.3/node_modules/lucide-react/dist/esm/createLucideIcon.js
@@ -7534,6 +7534,59 @@ async function exchangeOnrampVerificationToken(id, clientSecret, publishableKey)
7534
7534
  "Failed to exchange verification token"
7535
7535
  );
7536
7536
  }
7537
+ function isApplePayLimitReached(response) {
7538
+ return response.limits.some((l) => l.remaining === "0");
7539
+ }
7540
+ function getApplePayLimitUpgradeStatus(response) {
7541
+ const opt = response.limit_upgrade_options?.[0];
7542
+ if (!opt || typeof opt.status !== "string") return null;
7543
+ return opt.status;
7544
+ }
7545
+ async function getCoinbaseApplePayLimits(phone, publishableKey, signal) {
7546
+ const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
7547
+ validatePublishableKey(pk);
7548
+ const response = await fetch(
7549
+ `${API_BASE_URL}/v1/public/onramps/headless/coinbase/apple_pay/limits`,
7550
+ {
7551
+ method: "POST",
7552
+ headers: {
7553
+ accept: "application/json",
7554
+ "x-publishable-key": pk,
7555
+ "Content-Type": "application/json"
7556
+ },
7557
+ body: JSON.stringify({ phone }),
7558
+ signal
7559
+ }
7560
+ );
7561
+ const raw = await jsonOrThrow(response, "Failed to fetch Apple Pay limits");
7562
+ return {
7563
+ limits: raw.limits,
7564
+ limit_upgrade_options: raw.limit_upgrade_options,
7565
+ limit_reached: isApplePayLimitReached(raw),
7566
+ upgrade_status: getApplePayLimitUpgradeStatus(raw)
7567
+ };
7568
+ }
7569
+ async function requestCoinbaseApplePayLimitUpgrade(request, publishableKey, signal) {
7570
+ const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
7571
+ validatePublishableKey(pk);
7572
+ const response = await fetch(
7573
+ `${API_BASE_URL}/v1/public/onramps/headless/coinbase/apple_pay/limits/upgrade`,
7574
+ {
7575
+ method: "POST",
7576
+ headers: {
7577
+ accept: "application/json",
7578
+ "x-publishable-key": pk,
7579
+ "Content-Type": "application/json"
7580
+ },
7581
+ body: JSON.stringify(request),
7582
+ signal
7583
+ }
7584
+ );
7585
+ return jsonOrThrow(
7586
+ response,
7587
+ "Failed to request Apple Pay limit upgrade"
7588
+ );
7589
+ }
7537
7590
  async function createCoinbaseApplePaySession(request, onrampToken, publishableKey, signal) {
7538
7591
  const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
7539
7592
  validatePublishableKey(pk);
@@ -7716,21 +7769,22 @@ import { Fragment as Fragment23, jsx as jsx102, jsxs as jsxs9 } from "react/jsx-
7716
7769
  import { jsx as jsx112 } from "react/jsx-runtime";
7717
7770
  import { jsx as jsx122, jsxs as jsxs10 } from "react/jsx-runtime";
7718
7771
  import * as React52 from "react";
7719
- import { useCallback as useCallback10, useEffect as useEffect72, useMemo as useMemo22, useRef as useRef32, useState as useState112 } from "react";
7772
+ import { useCallback as useCallback10, useEffect as useEffect72, useMemo as useMemo32, useRef as useRef32, useState as useState112 } from "react";
7720
7773
  import { useQuery as useQuery3 } from "@tanstack/react-query";
7774
+ import { useMemo as useMemo22 } from "react";
7721
7775
  import { useQuery as useQuery4 } from "@tanstack/react-query";
7776
+ import { useQuery as useQuery5 } from "@tanstack/react-query";
7722
7777
  import { Fragment as Fragment42, jsx as jsx132, jsxs as jsxs11 } from "react/jsx-runtime";
7723
7778
  import { useState as useState122 } from "react";
7724
7779
  import { jsx as jsx142, jsxs as jsxs12 } from "react/jsx-runtime";
7725
7780
  import { useState as useState152, useEffect as useEffect102, useCallback as useCallback22 } from "react";
7726
7781
  var import_qr_code_styling = __toESM(require_qr_code_styling(), 1);
7727
- import { useEffect as useEffect82, useRef as useRef42, useState as useState132, useMemo as useMemo32 } from "react";
7782
+ import { useEffect as useEffect82, useRef as useRef42, useState as useState132, useMemo as useMemo42 } from "react";
7728
7783
  import { jsx as jsx152, jsxs as jsxs13 } from "react/jsx-runtime";
7729
7784
  import { useState as useState142, useEffect as useEffect92 } from "react";
7730
- import { useQuery as useQuery5 } from "@tanstack/react-query";
7731
- import { Fragment as Fragment52, jsx as jsx162, jsxs as jsxs14 } from "react/jsx-runtime";
7732
- import { useEffect as useEffect112, useMemo as useMemo42, useState as useState162 } from "react";
7733
7785
  import { useQuery as useQuery6 } from "@tanstack/react-query";
7786
+ import { Fragment as Fragment52, jsx as jsx162, jsxs as jsxs14 } from "react/jsx-runtime";
7787
+ import { useEffect as useEffect112, useMemo as useMemo52, useState as useState162 } from "react";
7734
7788
  import { useQuery as useQuery7 } from "@tanstack/react-query";
7735
7789
  import { jsx as jsx172, jsxs as jsxs15 } from "react/jsx-runtime";
7736
7790
  import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
@@ -7987,21 +8041,22 @@ import { useState as useState292, useEffect as useEffect242, useCallback as useC
7987
8041
  import { jsx as jsx43, jsxs as jsxs40 } from "react/jsx-runtime";
7988
8042
  import { useState as useState282, useEffect as useEffect232, useRef as useRef72, useCallback as useCallback32 } from "react";
7989
8043
  import { Fragment as Fragment62, jsx as jsx44, jsxs as jsxs41 } from "react/jsx-runtime";
7990
- import { useState as useState30, useEffect as useEffect252, useCallback as useCallback52, useMemo as useMemo62, useRef as useRef92 } from "react";
8044
+ import { useState as useState30, useEffect as useEffect252, useCallback as useCallback52, useMemo as useMemo72, useRef as useRef92 } from "react";
7991
8045
  import { useQuery as useQuery8 } from "@tanstack/react-query";
7992
8046
  import { useQuery as useQuery9 } from "@tanstack/react-query";
7993
- import { Fragment as Fragment72, jsx as jsx45, jsxs as jsxs422 } from "react/jsx-runtime";
7994
8047
  import { useQuery as useQuery10 } from "@tanstack/react-query";
8048
+ import { Fragment as Fragment72, jsx as jsx45, jsxs as jsxs422 } from "react/jsx-runtime";
7995
8049
  import { useQuery as useQuery11 } from "@tanstack/react-query";
7996
8050
  import { useQuery as useQuery12 } from "@tanstack/react-query";
7997
8051
  import { useQuery as useQuery13 } from "@tanstack/react-query";
7998
- import { useState as useState36, useEffect as useEffect302, useMemo as useMemo92 } from "react";
8052
+ import { useQuery as useQuery14 } from "@tanstack/react-query";
8053
+ import { useState as useState36, useEffect as useEffect302, useMemo as useMemo102 } from "react";
7999
8054
  import { useEffect as useEffect272, useState as useState31 } from "react";
8000
8055
  import * as React312 from "react";
8001
8056
  import { jsx as jsx46 } from "react/jsx-runtime";
8002
8057
  import { jsx as jsx47, jsxs as jsxs43 } from "react/jsx-runtime";
8003
8058
  import { Fragment as Fragment82, jsx as jsx48, jsxs as jsxs44 } from "react/jsx-runtime";
8004
- import { useState as useState322, useMemo as useMemo82, useEffect as useEffect282 } from "react";
8059
+ import { useState as useState322, useMemo as useMemo92, useEffect as useEffect282 } from "react";
8005
8060
 
8006
8061
  // ../../node_modules/.pnpm/fuse.js@7.4.0/node_modules/fuse.js/dist/fuse.mjs
8007
8062
  function isArray(value) {
@@ -12745,10 +12800,10 @@ var Content22 = TooltipContent;
12745
12800
 
12746
12801
  // ../ui-react/dist/index.mjs
12747
12802
  import { jsx as jsx522 } from "react/jsx-runtime";
12748
- import { useQuery as useQuery14 } from "@tanstack/react-query";
12803
+ import { useQuery as useQuery15 } from "@tanstack/react-query";
12749
12804
  import { jsx as jsx53, jsxs as jsxs48 } from "react/jsx-runtime";
12750
12805
  import { Fragment as Fragment10, jsx as jsx54, jsxs as jsxs49 } from "react/jsx-runtime";
12751
- import { useState as useState37, useEffect as useEffect312, useMemo as useMemo102 } from "react";
12806
+ import { useState as useState37, useEffect as useEffect312, useMemo as useMemo112 } from "react";
12752
12807
  import * as React332 from "react";
12753
12808
 
12754
12809
  // ../../node_modules/.pnpm/@radix-ui+react-select@2.2.6_@types+react-dom@19.2.3_@types+react@19.2.9__@types+react@19.2.9_cot3leusltbsophitaz3dgdqdm/node_modules/@radix-ui/react-select/dist/index.mjs
@@ -13987,8 +14042,8 @@ var Separator = SelectSeparator;
13987
14042
  import { jsx as jsx55, jsxs as jsxs50 } from "react/jsx-runtime";
13988
14043
  import { jsx as jsx56, jsxs as jsxs51 } from "react/jsx-runtime";
13989
14044
  import * as React342 from "react";
13990
- import { useQuery as useQuery15 } from "@tanstack/react-query";
13991
14045
  import { useQuery as useQuery16 } from "@tanstack/react-query";
14046
+ import { useQuery as useQuery17 } from "@tanstack/react-query";
13992
14047
  import { jsx as jsx57 } from "react/jsx-runtime";
13993
14048
  import { jsx as jsx58, jsxs as jsxs52 } from "react/jsx-runtime";
13994
14049
  import { Fragment as Fragment11, jsx as jsx59, jsxs as jsxs53 } from "react/jsx-runtime";
@@ -13997,26 +14052,26 @@ import { useEffect as useEffect322, useState as useState382 } from "react";
13997
14052
  import { Fragment as Fragment13, jsx as jsx61, jsxs as jsxs55 } from "react/jsx-runtime";
13998
14053
  import { jsx as jsx622, jsxs as jsxs56 } from "react/jsx-runtime";
13999
14054
  import { Fragment as Fragment14, jsx as jsx63, jsxs as jsxs57 } from "react/jsx-runtime";
14000
- import { useState as useState41, useEffect as useEffect35, useLayoutEffect as useLayoutEffect32, useCallback as useCallback92, useRef as useRef132, useMemo as useMemo132 } from "react";
14001
- import { useQuery as useQuery17 } from "@tanstack/react-query";
14055
+ import { useState as useState41, useEffect as useEffect35, useLayoutEffect as useLayoutEffect32, useCallback as useCallback92, useRef as useRef132, useMemo as useMemo142 } from "react";
14056
+ import { useQuery as useQuery18 } from "@tanstack/react-query";
14002
14057
  import { Fragment as Fragment15, jsx as jsx64, jsxs as jsxs58 } from "react/jsx-runtime";
14003
14058
  import { useState as useState45, useEffect as useEffect39, useLayoutEffect as useLayoutEffect42, useCallback as useCallback112, useRef as useRef152 } from "react";
14004
- import { useQuery as useQuery18 } from "@tanstack/react-query";
14005
14059
  import { useQuery as useQuery19 } from "@tanstack/react-query";
14006
14060
  import { useQuery as useQuery20 } from "@tanstack/react-query";
14007
14061
  import { useQuery as useQuery21 } from "@tanstack/react-query";
14062
+ import { useQuery as useQuery222 } from "@tanstack/react-query";
14008
14063
  import { useState as useState42, useEffect as useEffect36, useRef as useRef142 } from "react";
14009
14064
  import { jsx as jsx65, jsxs as jsxs59 } from "react/jsx-runtime";
14010
- import { useState as useState43, useCallback as useCallback102, useMemo as useMemo15, useEffect as useEffect37 } from "react";
14011
- import { useQuery as useQuery222 } from "@tanstack/react-query";
14012
- import { useMemo as useMemo142 } from "react";
14065
+ import { useState as useState43, useCallback as useCallback102, useMemo as useMemo16, useEffect as useEffect37 } from "react";
14013
14066
  import { useQuery as useQuery23 } from "@tanstack/react-query";
14067
+ import { useMemo as useMemo15 } from "react";
14068
+ import { useQuery as useQuery24 } from "@tanstack/react-query";
14014
14069
  import { Fragment as Fragment16, jsx as jsx66, jsxs as jsxs60 } from "react/jsx-runtime";
14015
14070
  import { jsx as jsx67, jsxs as jsxs61 } from "react/jsx-runtime";
14016
14071
  import { useState as useState44, useEffect as useEffect38 } from "react";
14017
14072
  import { Fragment as Fragment17, jsx as jsx68, jsxs as jsxs62 } from "react/jsx-runtime";
14018
14073
  import { Fragment as Fragment18, jsx as jsx69, jsxs as jsxs63 } from "react/jsx-runtime";
14019
- import { useState as useState46, useMemo as useMemo16 } from "react";
14074
+ import { useState as useState46, useMemo as useMemo17 } from "react";
14020
14075
  import { jsx as jsx70, jsxs as jsxs64 } from "react/jsx-runtime";
14021
14076
  function cn(...inputs) {
14022
14077
  return twMerge(clsx(inputs));
@@ -15406,7 +15461,7 @@ var en_default2 = {
15406
15461
  payWithStripeLink: {
15407
15462
  success: {
15408
15463
  processingTitle: "Payment confirmed",
15409
- processingSubtitle: "We\u2019ve received your payment and are preparing your purchase. This will only take a moment.",
15464
+ processingSubtitle: "We\u2019ve received your payment and are sending your purchase to your wallet. This usually takes just a few minutes.",
15410
15465
  completeTitle: "Your purchase is on the way",
15411
15466
  completeSubtitle: "Your purchase is complete and is being sent to your wallet.",
15412
15467
  failedTitle: "Purchase didn\u2019t go through",
@@ -17390,20 +17445,6 @@ function BuyWithCard({
17390
17445
  }
17391
17446
  );
17392
17447
  }
17393
- function useProjectConfig({
17394
- publishableKey,
17395
- enabled = true
17396
- }) {
17397
- const { data: projectConfig, isLoading } = useQuery3({
17398
- queryKey: ["unifold", "projectConfig", publishableKey],
17399
- queryFn: () => getProjectConfig(publishableKey),
17400
- enabled,
17401
- staleTime: 1e3 * 60 * 30,
17402
- refetchOnMount: true,
17403
- refetchOnWindowFocus: true
17404
- });
17405
- return { projectConfig, isLoading };
17406
- }
17407
17448
  var STORAGE_KEY_PREFIX = "unifold:apple-pay:session";
17408
17449
  var PHONE_FRESH_MS = 55 * 24 * 60 * 60 * 1e3;
17409
17450
  function isBrowser() {
@@ -17440,7 +17481,7 @@ function useCoinbaseLegalAgreements({
17440
17481
  publishableKey,
17441
17482
  enabled = true
17442
17483
  }) {
17443
- return useQuery4({
17484
+ return useQuery3({
17444
17485
  queryKey: ["unifold", "coinbaseLegalAgreements", publishableKey],
17445
17486
  queryFn: () => getCoinbaseLegalAgreements(publishableKey),
17446
17487
  enabled: enabled && !!publishableKey,
@@ -17451,47 +17492,200 @@ function useCoinbaseLegalAgreements({
17451
17492
  retry: 1
17452
17493
  });
17453
17494
  }
17454
- var COINBASE_PAY_ORIGIN = "https://pay.coinbase.com";
17455
- var DEFAULT_MIN_USD = 5;
17456
- var DEFAULT_MAX_USD = 500;
17457
- var DEFAULT_SUGGESTED = [25, 50, 100];
17495
+ var US_E164_REGEX = /^\+1\d{10}$/;
17496
+ function useApplePayLimits({
17497
+ phone,
17498
+ publishableKey,
17499
+ enabled = true
17500
+ }) {
17501
+ const phoneValid = US_E164_REGEX.test(phone);
17502
+ return useQuery4({
17503
+ queryKey: ["unifold", "applePayLimits", phone, publishableKey],
17504
+ queryFn: ({ signal }) => getCoinbaseApplePayLimits(phone, publishableKey, signal),
17505
+ enabled: enabled && phoneValid && !!publishableKey,
17506
+ // Coinbase's caps don't change second-to-second; 30s is plenty fresh
17507
+ // for the "user opened the modal, edited their phone, opened it
17508
+ // again" usage pattern, while keeping us off their API on rapid
17509
+ // tap-throughs.
17510
+ staleTime: 3e4,
17511
+ gcTime: 5 * 6e4,
17512
+ refetchOnWindowFocus: false,
17513
+ retry: false
17514
+ });
17515
+ }
17458
17516
  var PHONE_REGEX = /^\+1\d{10}$/;
17459
17517
  var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
17460
- function formatPhoneInput(raw) {
17461
- const digits = raw.replace(/\D/g, "");
17462
- if (raw.startsWith("+")) {
17463
- return `+${digits}`;
17518
+ function getViewForLimitStatus(limits) {
17519
+ if (!limits.limit_reached) return void 0;
17520
+ switch (limits.upgrade_status) {
17521
+ case "unrequested":
17522
+ return "limit_upgrade_intro";
17523
+ case "resubmit":
17524
+ return "limit_upgrade_form";
17525
+ case "pending":
17526
+ return "limit_upgrade_submitting";
17527
+ case "active":
17528
+ return void 0;
17529
+ default:
17530
+ return "guest_limit_reached";
17464
17531
  }
17465
- if (digits.length === 10) return `+1${digits}`;
17466
- if (digits.length === 11 && digits.startsWith("1")) return `+${digits}`;
17467
- return digits.length > 0 ? `+${digits}` : "";
17468
17532
  }
17469
- function maskEmail(email) {
17470
- const [local, domain] = email.split("@");
17471
- if (!local || !domain) return email;
17472
- const visible = local.slice(0, Math.min(2, local.length));
17473
- return `${visible}${"*".repeat(Math.max(local.length - visible.length, 1))}@${domain}`;
17533
+ function useApplePayInitialScreen({
17534
+ userId,
17535
+ publishableKey,
17536
+ normalizedPhone,
17537
+ userEmail,
17538
+ sessionPhoneVerified,
17539
+ isWaiting
17540
+ }) {
17541
+ const initialStoredSession = useMemo22(() => getStoredApplePaySession(userId), [userId]);
17542
+ const phoneVerified = PHONE_REGEX.test(normalizedPhone) && (sessionPhoneVerified || initialStoredSession?.phone === normalizedPhone && !!initialStoredSession?.phoneVerifiedAt);
17543
+ const {
17544
+ data: applePayLimits,
17545
+ isLoading: applePayLimitsLoading,
17546
+ refetch: refetchApplePayLimits
17547
+ } = useApplePayLimits({
17548
+ phone: normalizedPhone,
17549
+ publishableKey,
17550
+ enabled: phoneVerified
17551
+ });
17552
+ const action = useMemo22(() => {
17553
+ if (isWaiting || applePayLimitsLoading) return { kind: "pending" };
17554
+ const stored = initialStoredSession;
17555
+ const hasUserEmail = !!userEmail && EMAIL_REGEX.test(userEmail);
17556
+ if (!stored) {
17557
+ return { kind: "navigate", view: hasUserEmail ? "phone_input" : "email_input" };
17558
+ }
17559
+ if (hasUserEmail && stored.email.toLowerCase() !== userEmail.toLowerCase()) {
17560
+ return { kind: "navigate", view: "phone_input" };
17561
+ }
17562
+ if (applePayLimits) {
17563
+ const limitView = getViewForLimitStatus(applePayLimits);
17564
+ if (limitView) {
17565
+ return { kind: "limit_upgrade", view: limitView, status: applePayLimits.upgrade_status };
17566
+ }
17567
+ }
17568
+ if (isOnrampTokenFresh(stored) && stored.onrampToken && stored.onrampTokenExpiresAtMs) {
17569
+ return {
17570
+ kind: "resume_with_token",
17571
+ token: {
17572
+ token: stored.onrampToken,
17573
+ token_type: "Bearer",
17574
+ expires_in: Math.max(0, Math.floor((stored.onrampTokenExpiresAtMs - Date.now()) / 1e3))
17575
+ }
17576
+ };
17577
+ }
17578
+ return { kind: "resume_send_sms" };
17579
+ }, [isWaiting, applePayLimitsLoading, initialStoredSession, userEmail, applePayLimits]);
17580
+ return {
17581
+ action,
17582
+ applePayLimits,
17583
+ applePayLimitsLoading,
17584
+ refetchApplePayLimits,
17585
+ phoneVerified
17586
+ };
17474
17587
  }
17475
- function maskPhone(phone) {
17476
- if (!/^\+1\d{10}$/.test(phone)) return phone;
17477
- return `+1 (***) ***-${phone.slice(-4)}`;
17588
+ function useDefaultOnrampToken({
17589
+ publishableKey,
17590
+ tokenAddress,
17591
+ chainId,
17592
+ chainType,
17593
+ countryCode,
17594
+ subdivisionCode,
17595
+ enabled = true
17596
+ }) {
17597
+ const {
17598
+ data: defaultToken,
17599
+ isLoading,
17600
+ isError,
17601
+ error
17602
+ } = useQuery5({
17603
+ queryKey: [
17604
+ "unifold",
17605
+ "defaultOnrampToken",
17606
+ publishableKey,
17607
+ tokenAddress,
17608
+ chainId,
17609
+ chainType,
17610
+ countryCode,
17611
+ subdivisionCode
17612
+ ],
17613
+ queryFn: () => getDefaultOnrampToken(
17614
+ {
17615
+ token_address: tokenAddress,
17616
+ chain_id: chainId,
17617
+ chain_type: chainType,
17618
+ country_code: countryCode,
17619
+ subdivision_code: subdivisionCode
17620
+ },
17621
+ publishableKey
17622
+ ),
17623
+ enabled: enabled && !!tokenAddress && !!chainId && !!chainType,
17624
+ staleTime: 1e3 * 60 * 60,
17625
+ refetchOnMount: false,
17626
+ refetchOnWindowFocus: false
17627
+ });
17628
+ return { defaultToken, isLoading, isError, error: error ?? null };
17629
+ }
17630
+ var COINBASE_PAY_ORIGIN = "https://pay.coinbase.com";
17631
+ var COINBASE_ALLOWED_POSTMESSAGE_ORIGINS = /* @__PURE__ */ new Set([
17632
+ COINBASE_PAY_ORIGIN,
17633
+ "https://www.coinbase.com"
17634
+ ]);
17635
+ var UNIFOLD_SUPPORT_URL = "https://unifold.io/support";
17636
+ var DEFAULT_MIN_USD = 5;
17637
+ var DEFAULT_MAX_USD = 500;
17638
+ var DEFAULT_SUGGESTED = [25, 50, 100];
17639
+ var PHONE_REGEX2 = /^\+1\d{10}$/;
17640
+ var EMAIL_REGEX2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
17641
+ function parseCoinbasePostMessage(raw) {
17642
+ const parseString = (s) => {
17643
+ try {
17644
+ return JSON.parse(s);
17645
+ } catch {
17646
+ return null;
17647
+ }
17648
+ };
17649
+ const asObject = (value) => value && typeof value === "object" ? value : null;
17650
+ let candidate = raw;
17651
+ if (typeof candidate === "string") {
17652
+ candidate = parseString(candidate);
17653
+ }
17654
+ const obj = asObject(candidate);
17655
+ if (!obj) return null;
17656
+ const nestedData = obj.data;
17657
+ let eventName = typeof obj.eventName === "string" && obj.eventName || typeof obj.event_name === "string" && obj.event_name || typeof obj.type === "string" && obj.type || null;
17658
+ let dataObj = asObject(nestedData);
17659
+ if (!eventName && typeof nestedData === "string") {
17660
+ const nestedParsed = parseString(nestedData);
17661
+ const nestedObj = asObject(nestedParsed);
17662
+ if (nestedObj) {
17663
+ eventName = typeof nestedObj.eventName === "string" && nestedObj.eventName || typeof nestedObj.event_name === "string" && nestedObj.event_name || typeof nestedObj.type === "string" && nestedObj.type || null;
17664
+ dataObj = asObject(nestedObj.data);
17665
+ }
17666
+ }
17667
+ if (!eventName || !/^onramp_api\./i.test(eventName)) return null;
17668
+ return {
17669
+ eventName,
17670
+ data: dataObj ? {
17671
+ errorCode: typeof dataObj.errorCode === "string" ? dataObj.errorCode : typeof dataObj.error_code === "string" ? dataObj.error_code : void 0,
17672
+ errorMessage: typeof dataObj.errorMessage === "string" ? dataObj.errorMessage : typeof dataObj.error_message === "string" ? dataObj.error_message : void 0
17673
+ } : void 0
17674
+ };
17478
17675
  }
17479
17676
  var BuyWithApplePay = React52.forwardRef(
17480
17677
  function BuyWithApplePay2({
17481
17678
  userId,
17482
17679
  publishableKey,
17483
- recipientAddress,
17484
17680
  destinationChainType,
17485
17681
  destinationChainId,
17486
17682
  destinationTokenAddress,
17487
- destinationTokenSymbol = "USDC",
17488
- externalUserEmail,
17489
- enableApplePay,
17683
+ userEmail,
17490
17684
  amountUsd,
17491
17685
  minAmountUsd = DEFAULT_MIN_USD,
17492
17686
  maxAmountUsd = DEFAULT_MAX_USD,
17493
17687
  suggestedAmountsUsd = DEFAULT_SUGGESTED,
17494
- wallets: externalWallets,
17688
+ wallets: depositWallets,
17495
17689
  onDepositSuccess,
17496
17690
  onDepositError,
17497
17691
  onEvent,
@@ -17500,48 +17694,61 @@ var BuyWithApplePay = React52.forwardRef(
17500
17694
  onExit
17501
17695
  }, ref) {
17502
17696
  const { colors: colors2, fonts, components } = useTheme();
17503
- const { projectConfig, isLoading: configLoading } = useProjectConfig({ publishableKey });
17504
- const applePayEnabled = enableApplePay ?? projectConfig?.apple_pay?.enabled ?? true;
17505
17697
  const [view, setView] = useState112("loading_config");
17506
17698
  const [errorMessage, setErrorMessage] = useState112("");
17507
- const [email, setEmail] = useState112(externalUserEmail ?? "");
17508
- const [phoneInput, setPhoneInput] = useState112("");
17509
- const [, setTosAccepted] = useState112(false);
17699
+ const [email, setEmail] = useState112(() => {
17700
+ if (userEmail && EMAIL_REGEX2.test(userEmail)) return userEmail;
17701
+ return getStoredApplePaySession(userId)?.email ?? "";
17702
+ });
17703
+ const [phoneInput, setPhoneInput] = useState112(() => {
17704
+ const stored = getStoredApplePaySession(userId);
17705
+ if (!stored) return "";
17706
+ if (userEmail && EMAIL_REGEX2.test(userEmail) && stored.email.toLowerCase() !== userEmail.toLowerCase()) {
17707
+ return "";
17708
+ }
17709
+ return stored.phone;
17710
+ });
17510
17711
  const { data: legalAgreementsData, isLoading: legalAgreementsLoading } = useCoinbaseLegalAgreements({
17511
- publishableKey,
17512
- enabled: applePayEnabled
17712
+ publishableKey
17513
17713
  });
17514
17714
  const legalAgreements = legalAgreementsData ?? null;
17515
17715
  const [verificationSession, setVerificationSession] = useState112(null);
17516
17716
  const [clientSecret, setClientSecret] = useState112(null);
17517
- const [emailCode, setEmailCode] = useState112("");
17518
17717
  const [phoneCode, setPhoneCode] = useState112("");
17519
17718
  const [otpError, setOtpError] = useState112(null);
17520
17719
  const [otpSubmitting, setOtpSubmitting] = useState112(false);
17521
17720
  const [resendCooldown, setResendCooldown] = useState112(0);
17721
+ const [dobInput, setDobInput] = useState112("");
17722
+ const [ssnLast4, setSsnLast4] = useState112("");
17723
+ const [limitUpgradeError, setLimitUpgradeError] = useState112(null);
17724
+ const [upgradeSubmitting, setUpgradeSubmitting] = useState112(false);
17725
+ const upgradeFlowAbortRef = useRef32(null);
17522
17726
  const [amount, setAmount] = useState112(amountUsd ?? "");
17523
17727
  const [paymentSession, setPaymentSession] = useState112(
17524
17728
  null
17525
17729
  );
17526
17730
  const [paymentSessionAmount, setPaymentSessionAmount] = useState112(null);
17527
- const [preparingSession, setPreparingSession] = useState112(false);
17528
17731
  const popupRef = useRef32(null);
17529
17732
  const sessionAbortRef = useRef32(null);
17530
17733
  const [onrampToken, setOnrampToken] = useState112(null);
17531
- const { data: depositAddressResponse } = useDepositAddress({
17532
- userId,
17734
+ const { userIpInfo, isLoading: userIpInfoLoading } = useUserIp2();
17735
+ const {
17736
+ defaultToken,
17737
+ isLoading: defaultTokenLoading,
17738
+ isError: defaultTokenError
17739
+ } = useDefaultOnrampToken({
17533
17740
  publishableKey,
17534
- recipientAddress,
17535
- destinationChainType,
17536
- destinationChainId,
17537
- destinationTokenAddress,
17538
- enabled: !externalWallets?.length
17741
+ tokenAddress: destinationTokenAddress,
17742
+ chainId: destinationChainId,
17743
+ chainType: destinationChainType,
17744
+ countryCode: userIpInfo?.alpha2,
17745
+ subdivisionCode: userIpInfo?.subdivisionCode ?? void 0
17539
17746
  });
17540
- const wallets = externalWallets?.length ? externalWallets : depositAddressResponse?.data ?? [];
17541
- const depositWallet = useMemo22(() => {
17542
- if (!destinationChainType) return void 0;
17543
- return getWalletByChainType(wallets, destinationChainType);
17544
- }, [wallets, destinationChainType]);
17747
+ const depositWallet = useMemo32(() => {
17748
+ const routingChainType = defaultToken?.destination_token_metadata?.chain_type;
17749
+ if (!routingChainType) return void 0;
17750
+ return getWalletByChainType(depositWallets ?? [], routingChainType);
17751
+ }, [depositWallets, defaultToken]);
17545
17752
  const depositWalletId = depositWallet?.id;
17546
17753
  const { executions } = useDepositPolling({
17547
17754
  userId,
@@ -17565,49 +17772,10 @@ var BuyWithApplePay = React52.forwardRef(
17565
17772
  onDepositError(error);
17566
17773
  } : void 0
17567
17774
  });
17568
- const [emailLocked, setEmailLocked] = useState112(false);
17569
- useEffect72(() => {
17570
- if (configLoading) return;
17571
- if (!applePayEnabled) {
17572
- setView("disabled");
17573
- return;
17574
- }
17575
- setTosAccepted(true);
17576
- const applyStoredOnrampToken = (stored2) => {
17577
- if (!isOnrampTokenFresh(stored2) || !stored2.onrampToken || !stored2.onrampTokenExpiresAtMs) {
17578
- return false;
17579
- }
17580
- setOnrampToken({
17581
- token: stored2.onrampToken,
17582
- token_type: "Bearer",
17583
- expires_in: Math.max(0, Math.floor((stored2.onrampTokenExpiresAtMs - Date.now()) / 1e3))
17584
- });
17585
- setView("amount");
17586
- return true;
17587
- };
17588
- if (externalUserEmail && EMAIL_REGEX.test(externalUserEmail)) {
17589
- setEmail(externalUserEmail);
17590
- setEmailLocked(true);
17591
- const stored2 = getStoredApplePaySession(userId);
17592
- if (stored2?.email?.toLowerCase() === externalUserEmail.toLowerCase()) {
17593
- setPhoneInput(stored2.phone);
17594
- if (applyStoredOnrampToken(stored2)) return;
17595
- }
17596
- setView("phone_input");
17597
- return;
17598
- }
17599
- const stored = getStoredApplePaySession(userId);
17600
- if (stored) {
17601
- setEmail(stored.email);
17602
- setPhoneInput(stored.phone);
17603
- setEmailLocked(true);
17604
- if (applyStoredOnrampToken(stored)) return;
17605
- setView("phone_input");
17606
- return;
17607
- }
17608
- setEmailLocked(false);
17609
- setView("email_input");
17610
- }, [configLoading, applePayEnabled, externalUserEmail, userId]);
17775
+ const [emailLocked, setEmailLocked] = useState112(() => {
17776
+ if (userEmail && EMAIL_REGEX2.test(userEmail)) return true;
17777
+ return !!getStoredApplePaySession(userId);
17778
+ });
17611
17779
  useEffect72(() => {
17612
17780
  if (resendCooldown <= 0) return;
17613
17781
  const t13 = setInterval(() => setResendCooldown((c) => Math.max(0, c - 1)), 1e3);
@@ -17616,19 +17784,10 @@ var BuyWithApplePay = React52.forwardRef(
17616
17784
  useEffect72(() => {
17617
17785
  if (view !== "checking_deposit") return;
17618
17786
  const onMessage = (event) => {
17619
- if (event.origin !== COINBASE_PAY_ORIGIN) return;
17620
- let parsed = null;
17621
- const data = event.data;
17622
- if (typeof data === "string") {
17623
- try {
17624
- parsed = JSON.parse(data);
17625
- } catch {
17626
- return;
17627
- }
17628
- } else if (data && typeof data === "object" && "eventName" in data) {
17629
- parsed = data;
17630
- }
17787
+ if (!COINBASE_ALLOWED_POSTMESSAGE_ORIGINS.has(event.origin)) return;
17788
+ const parsed = parseCoinbasePostMessage(event.data);
17631
17789
  if (!parsed) return;
17790
+ const eventName = parsed.eventName.toLowerCase();
17632
17791
  const code = parsed.data?.errorCode;
17633
17792
  const closePopup = () => {
17634
17793
  try {
@@ -17637,16 +17796,20 @@ var BuyWithApplePay = React52.forwardRef(
17637
17796
  }
17638
17797
  popupRef.current = null;
17639
17798
  };
17640
- switch (parsed.eventName) {
17799
+ switch (eventName) {
17641
17800
  case "onramp_api.commit_success":
17642
17801
  case "onramp_api.polling_success":
17802
+ case "onramp_api.cancel":
17643
17803
  closePopup();
17644
17804
  break;
17645
17805
  case "onramp_api.commit_error":
17806
+ case "onramp_api.polling_failed":
17646
17807
  if (code === "ERROR_CODE_GUEST_TRANSACTION_COUNT" || code === "ERROR_CODE_GUEST_DAILY_TRANSACTION_COUNT" || code === "ERROR_CODE_GUEST_TRANSACTION_LIMIT") {
17647
17808
  closePopup();
17648
17809
  setView("guest_limit_reached");
17810
+ return;
17649
17811
  }
17812
+ closePopup();
17650
17813
  break;
17651
17814
  default:
17652
17815
  break;
@@ -17658,17 +17821,17 @@ var BuyWithApplePay = React52.forwardRef(
17658
17821
  useEffect72(() => {
17659
17822
  onViewChange?.(view);
17660
17823
  }, [view, onViewChange]);
17661
- useEffect72(
17662
- () => () => {
17824
+ useEffect72(() => {
17825
+ return () => {
17663
17826
  sessionAbortRef.current?.abort();
17827
+ upgradeFlowAbortRef.current?.abort();
17664
17828
  try {
17665
17829
  popupRef.current?.close();
17666
17830
  } catch {
17667
17831
  }
17668
17832
  popupRef.current = null;
17669
- },
17670
- []
17671
- );
17833
+ };
17834
+ }, []);
17672
17835
  React52.useImperativeHandle(
17673
17836
  ref,
17674
17837
  () => ({
@@ -17678,14 +17841,20 @@ var BuyWithApplePay = React52.forwardRef(
17678
17841
  setView("email_input");
17679
17842
  return true;
17680
17843
  }
17681
- if (view === "email_otp") {
17682
- setView("phone_input");
17683
- setEmailCode("");
17684
- return true;
17685
- }
17686
17844
  if (view === "phone_otp") {
17687
17845
  setView("phone_input");
17688
17846
  setPhoneCode("");
17847
+ setOtpSubmitting(false);
17848
+ return true;
17849
+ }
17850
+ if (view === "limit_upgrade_intro" || view === "limit_upgrade_failed") {
17851
+ return false;
17852
+ }
17853
+ if (view === "limit_upgrade_form") {
17854
+ setView("limit_upgrade_intro");
17855
+ return true;
17856
+ }
17857
+ if (view === "limit_upgrade_submitting") {
17689
17858
  return true;
17690
17859
  }
17691
17860
  if (view === "checking_deposit") {
@@ -17705,22 +17874,21 @@ var BuyWithApplePay = React52.forwardRef(
17705
17874
  }),
17706
17875
  [view, emailLocked]
17707
17876
  );
17708
- const normalizedPhone = useMemo22(() => formatPhoneInput(phoneInput), [phoneInput]);
17709
- const isContactValid = EMAIL_REGEX.test(email) && PHONE_REGEX.test(normalizedPhone);
17710
- const persistContact = useCallback10(
17877
+ const normalizedPhone = useMemo32(() => formatPhoneInput(phoneInput), [phoneInput]);
17878
+ const isContactValid = EMAIL_REGEX2.test(email) && PHONE_REGEX2.test(normalizedPhone);
17879
+ const storeApplePaySession = useCallback10(
17711
17880
  (patch = {}) => {
17712
- const base = {
17881
+ const session = {
17713
17882
  email,
17714
17883
  phone: normalizedPhone,
17715
17884
  termsAcceptedAt: (/* @__PURE__ */ new Date()).toISOString(),
17716
17885
  ...patch
17717
17886
  };
17718
- setStoredApplePaySession(userId, base);
17887
+ setStoredApplePaySession(userId, session);
17719
17888
  },
17720
17889
  [email, normalizedPhone, userId]
17721
17890
  );
17722
- const startVerification = useCallback10(async () => {
17723
- if (!isContactValid) return;
17891
+ const createSessionAndSendOtp = useCallback10(async () => {
17724
17892
  setView("submitting_session");
17725
17893
  setErrorMessage("");
17726
17894
  try {
@@ -17738,71 +17906,192 @@ var BuyWithApplePay = React52.forwardRef(
17738
17906
  throw new Error("Server did not return a client_secret");
17739
17907
  }
17740
17908
  setClientSecret(session.client_secret);
17741
- persistContact();
17742
- if (session.email.status === "verified") {
17743
- await sendOnrampVerificationOtp(
17744
- session.id,
17745
- "phone",
17746
- session.client_secret,
17747
- publishableKey
17748
- );
17749
- setResendCooldown(30);
17750
- setView("phone_otp");
17751
- } else {
17752
- await sendOnrampVerificationOtp(
17753
- session.id,
17754
- "email",
17755
- session.client_secret,
17756
- publishableKey
17757
- );
17758
- setResendCooldown(30);
17759
- setView("email_otp");
17760
- }
17909
+ storeApplePaySession();
17910
+ await sendOnrampVerificationOtp(session.id, "phone", session.client_secret, publishableKey);
17911
+ setResendCooldown(30);
17912
+ setView("phone_otp");
17761
17913
  } catch (err) {
17762
17914
  const raw = err instanceof Error ? err.message : "Failed to start verification";
17763
17915
  setErrorMessage(prettifyVerificationError(raw));
17764
17916
  setView("phone_input");
17765
17917
  }
17766
- }, [isContactValid, email, normalizedPhone, publishableKey, persistContact]);
17767
- const submitEmailOtp = useCallback10(async () => {
17768
- if (!verificationSession || !clientSecret || emailCode.length !== 6) return;
17769
- if (otpSubmitting) return;
17770
- setOtpError(null);
17771
- setOtpSubmitting(true);
17918
+ }, [email, normalizedPhone, publishableKey, storeApplePaySession]);
17919
+ const clearUpgradeFields = useCallback10(() => {
17920
+ setSsnLast4("");
17921
+ setDobInput("");
17922
+ }, []);
17923
+ const { action: initialAction, refetchApplePayLimits } = useApplePayInitialScreen({
17924
+ userId,
17925
+ publishableKey,
17926
+ normalizedPhone,
17927
+ userEmail,
17928
+ sessionPhoneVerified: verificationSession?.phone?.status === "verified",
17929
+ isWaiting: defaultTokenLoading || legalAgreementsLoading || userIpInfoLoading
17930
+ });
17931
+ const pollLimitUpgradeStatus = useCallback10(
17932
+ async (signal) => {
17933
+ const POLL_INTERVAL_MS4 = 1500;
17934
+ const POLL_TIMEOUT_MS = 3e4;
17935
+ const startedAt = Date.now();
17936
+ let firstIteration = true;
17937
+ while (Date.now() - startedAt < POLL_TIMEOUT_MS) {
17938
+ if (signal.aborted) return;
17939
+ if (!firstIteration) {
17940
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS4));
17941
+ }
17942
+ firstIteration = false;
17943
+ if (signal.aborted) return;
17944
+ let fresh;
17945
+ try {
17946
+ fresh = await getCoinbaseApplePayLimits(normalizedPhone, publishableKey, signal);
17947
+ } catch {
17948
+ if (signal.aborted) return;
17949
+ continue;
17950
+ }
17951
+ const { upgrade_status: status, limit_reached: stillCapped } = fresh;
17952
+ if (status === "active" || !stillCapped) {
17953
+ await refetchApplePayLimits();
17954
+ clearUpgradeFields();
17955
+ setLimitUpgradeError(null);
17956
+ if (onrampToken) {
17957
+ setView("amount");
17958
+ } else {
17959
+ if (signal.aborted) return;
17960
+ await createSessionAndSendOtp();
17961
+ }
17962
+ return;
17963
+ }
17964
+ if (status === "inactive") {
17965
+ await refetchApplePayLimits();
17966
+ clearUpgradeFields();
17967
+ setLimitUpgradeError(null);
17968
+ setView("guest_limit_reached");
17969
+ return;
17970
+ }
17971
+ if (status === "resubmit") {
17972
+ await refetchApplePayLimits();
17973
+ setSsnLast4("");
17974
+ setLimitUpgradeError(
17975
+ "We couldn't verify your information. Please check your SSN and date of birth and try again."
17976
+ );
17977
+ setView("limit_upgrade_form");
17978
+ return;
17979
+ }
17980
+ }
17981
+ setLimitUpgradeError(null);
17982
+ setView("limit_upgrade_failed");
17983
+ },
17984
+ [
17985
+ normalizedPhone,
17986
+ publishableKey,
17987
+ refetchApplePayLimits,
17988
+ onrampToken,
17989
+ createSessionAndSendOtp,
17990
+ clearUpgradeFields
17991
+ ]
17992
+ );
17993
+ const startUpgradePolling = useCallback10(async () => {
17994
+ upgradeFlowAbortRef.current?.abort();
17995
+ const controller = new AbortController();
17996
+ upgradeFlowAbortRef.current = controller;
17772
17997
  try {
17773
- const updated = await verifyOnrampVerificationOtp(
17774
- verificationSession.id,
17775
- "email",
17776
- clientSecret,
17777
- emailCode,
17778
- publishableKey
17779
- );
17780
- setVerificationSession(updated);
17781
- if (updated.email.status === "verified") {
17782
- persistContact({ emailVerifiedAt: updated.email.verified_at });
17783
- await sendOnrampVerificationOtp(updated.id, "phone", clientSecret, publishableKey);
17784
- setEmailCode("");
17785
- setResendCooldown(30);
17786
- setView("phone_otp");
17998
+ await pollLimitUpgradeStatus(controller.signal);
17999
+ } finally {
18000
+ if (upgradeFlowAbortRef.current === controller) {
18001
+ upgradeFlowAbortRef.current = null;
18002
+ }
18003
+ }
18004
+ }, [pollLimitUpgradeStatus]);
18005
+ const applyLimitRouting = useCallback10(
18006
+ (nextView, status) => {
18007
+ if (nextView === "limit_upgrade_form" && status === "resubmit") {
18008
+ setLimitUpgradeError(
18009
+ "We couldn't verify your information. Please check your SSN and date of birth and try again."
18010
+ );
17787
18011
  } else {
17788
- setOtpError("Incorrect code. Please try again.");
17789
- setEmailCode("");
18012
+ setLimitUpgradeError(null);
17790
18013
  }
17791
- } catch (err) {
17792
- setOtpError(
17793
- prettifyVerificationError(err instanceof Error ? err.message : "Verification failed")
17794
- );
17795
- setEmailCode("");
18014
+ setView(nextView);
18015
+ if (nextView === "limit_upgrade_submitting") {
18016
+ void startUpgradePolling();
18017
+ }
18018
+ },
18019
+ [startUpgradePolling]
18020
+ );
18021
+ const hasInitializedRef = useRef32(false);
18022
+ useEffect72(() => {
18023
+ if (hasInitializedRef.current) return;
18024
+ if (initialAction.kind === "pending") return;
18025
+ hasInitializedRef.current = true;
18026
+ switch (initialAction.kind) {
18027
+ case "navigate":
18028
+ setView(initialAction.view);
18029
+ break;
18030
+ case "limit_upgrade":
18031
+ applyLimitRouting(initialAction.view, initialAction.status);
18032
+ break;
18033
+ case "resume_with_token":
18034
+ setOnrampToken(initialAction.token);
18035
+ setView("amount");
18036
+ break;
18037
+ case "resume_send_sms":
18038
+ void createSessionAndSendOtp();
18039
+ break;
18040
+ }
18041
+ }, [initialAction, applyLimitRouting, createSessionAndSendOtp]);
18042
+ const startVerification = useCallback10(async () => {
18043
+ if (!isContactValid) return;
18044
+ await createSessionAndSendOtp();
18045
+ }, [isContactValid, createSessionAndSendOtp]);
18046
+ const submitLimitUpgrade = useCallback10(async () => {
18047
+ if (upgradeSubmitting) return;
18048
+ try {
18049
+ const dob = parseDobInput(dobInput);
18050
+ if (!dob || !/^\d{4}$/.test(ssnLast4)) return;
18051
+ setUpgradeSubmitting(true);
18052
+ setLimitUpgradeError(null);
18053
+ setView("limit_upgrade_submitting");
18054
+ const requestController = new AbortController();
18055
+ upgradeFlowAbortRef.current?.abort();
18056
+ upgradeFlowAbortRef.current = requestController;
18057
+ try {
18058
+ await requestCoinbaseApplePayLimitUpgrade(
18059
+ {
18060
+ phone: normalizedPhone,
18061
+ fields: { ssnLast4, dateOfBirth: dob }
18062
+ },
18063
+ publishableKey,
18064
+ requestController.signal
18065
+ );
18066
+ } catch (err) {
18067
+ if (requestController.signal.aborted) return;
18068
+ const raw = err instanceof Error ? err.message : "Failed to request limit upgrade";
18069
+ if (isLimitUpgradeTerminalBlock(raw)) {
18070
+ clearUpgradeFields();
18071
+ setLimitUpgradeError(null);
18072
+ setView("guest_limit_reached");
18073
+ return;
18074
+ }
18075
+ setLimitUpgradeError(prettifyVerificationError(raw));
18076
+ setView("limit_upgrade_form");
18077
+ return;
18078
+ } finally {
18079
+ if (upgradeFlowAbortRef.current === requestController) {
18080
+ upgradeFlowAbortRef.current = null;
18081
+ }
18082
+ }
18083
+ await startUpgradePolling();
17796
18084
  } finally {
17797
- setOtpSubmitting(false);
18085
+ setUpgradeSubmitting(false);
17798
18086
  }
17799
18087
  }, [
17800
- verificationSession,
17801
- clientSecret,
17802
- emailCode,
18088
+ upgradeSubmitting,
18089
+ dobInput,
18090
+ ssnLast4,
18091
+ normalizedPhone,
17803
18092
  publishableKey,
17804
- persistContact,
17805
- otpSubmitting
18093
+ startUpgradePolling,
18094
+ clearUpgradeFields
17806
18095
  ]);
17807
18096
  const submitPhoneOtp = useCallback10(async () => {
17808
18097
  if (!verificationSession || !clientSecret || phoneCode.length !== 6) return;
@@ -17825,7 +18114,7 @@ var BuyWithApplePay = React52.forwardRef(
17825
18114
  publishableKey
17826
18115
  );
17827
18116
  setOnrampToken(tokenRes);
17828
- persistContact({
18117
+ storeApplePaySession({
17829
18118
  emailVerifiedAt: updated.email.verified_at,
17830
18119
  phoneVerifiedAt: updated.phone.verified_at,
17831
18120
  onrampToken: tokenRes.token,
@@ -17834,17 +18123,30 @@ var BuyWithApplePay = React52.forwardRef(
17834
18123
  setEmailLocked(true);
17835
18124
  setPhoneCode("");
17836
18125
  if (amountUsd) setAmount(amountUsd);
18126
+ try {
18127
+ const result = await refetchApplePayLimits();
18128
+ if (result.data) {
18129
+ const nextView = getViewForLimitStatus(result.data);
18130
+ if (nextView) {
18131
+ setOtpSubmitting(false);
18132
+ applyLimitRouting(nextView, result.data.upgrade_status);
18133
+ return;
18134
+ }
18135
+ }
18136
+ } catch {
18137
+ }
18138
+ setOtpSubmitting(false);
17837
18139
  setView("amount");
17838
- } else {
17839
- setOtpError("Incorrect code. Please try again.");
17840
- setPhoneCode("");
18140
+ return;
17841
18141
  }
18142
+ setOtpError("Incorrect code. Please try again.");
18143
+ setPhoneCode("");
18144
+ setOtpSubmitting(false);
17842
18145
  } catch (err) {
17843
18146
  setOtpError(
17844
18147
  prettifyVerificationError(err instanceof Error ? err.message : "Verification failed")
17845
18148
  );
17846
18149
  setPhoneCode("");
17847
- } finally {
17848
18150
  setOtpSubmitting(false);
17849
18151
  }
17850
18152
  }, [
@@ -17852,22 +18154,25 @@ var BuyWithApplePay = React52.forwardRef(
17852
18154
  clientSecret,
17853
18155
  phoneCode,
17854
18156
  publishableKey,
17855
- persistContact,
18157
+ storeApplePaySession,
17856
18158
  amountUsd,
17857
- otpSubmitting
18159
+ otpSubmitting,
18160
+ refetchApplePayLimits,
18161
+ getViewForLimitStatus,
18162
+ applyLimitRouting
17858
18163
  ]);
17859
18164
  const prepareSession = useCallback10(
17860
18165
  async (token, amt) => {
18166
+ setErrorMessage("");
17861
18167
  if (!destinationChainType || !destinationChainId) {
17862
18168
  setErrorMessage("Deposit destination not configured. Please contact support.");
17863
18169
  return;
17864
18170
  }
17865
- const destinationNetwork = destinationChainNameFor(
17866
- destinationChainType,
17867
- destinationChainId
17868
- );
17869
- if (!destinationNetwork) {
17870
- setErrorMessage("Coinbase Apple Pay is not available for this network.");
18171
+ if (defaultTokenLoading) {
18172
+ return;
18173
+ }
18174
+ if (defaultTokenError || !defaultToken) {
18175
+ setErrorMessage("Unable to load token information. Please try again.");
17871
18176
  return;
17872
18177
  }
17873
18178
  const settlementAddress = depositWallet?.address;
@@ -17878,19 +18183,17 @@ var BuyWithApplePay = React52.forwardRef(
17878
18183
  sessionAbortRef.current?.abort();
17879
18184
  const controller = new AbortController();
17880
18185
  sessionAbortRef.current = controller;
17881
- setPreparingSession(true);
17882
- setErrorMessage("");
17883
18186
  try {
17884
18187
  const externalId = generatePrefixedKSUID("orsext");
17885
18188
  const session = await createCoinbaseApplePaySession(
17886
18189
  {
17887
18190
  source_currency: "usd",
17888
18191
  source_amount: amt,
17889
- destination_currency: destinationTokenSymbol.toLowerCase(),
17890
- destination_network: destinationNetwork,
18192
+ destination_currency: defaultToken.destination_currency,
18193
+ destination_network: defaultToken.destination_network,
17891
18194
  wallet_address: settlementAddress,
17892
18195
  external_id: externalId,
17893
- ...externalUserEmail ? { email: externalUserEmail } : {}
18196
+ ...userEmail ? { email: userEmail } : {}
17894
18197
  },
17895
18198
  token,
17896
18199
  publishableKey,
@@ -17934,18 +18237,16 @@ var BuyWithApplePay = React52.forwardRef(
17934
18237
  setPaymentSession(null);
17935
18238
  setPaymentSessionAmount(null);
17936
18239
  setErrorMessage("Unable to process your request. Please try again later.");
17937
- } finally {
17938
- if (sessionAbortRef.current === controller) {
17939
- setPreparingSession(false);
17940
- }
17941
18240
  }
17942
18241
  },
17943
18242
  [
17944
- destinationTokenSymbol,
17945
18243
  destinationChainType,
17946
18244
  destinationChainId,
18245
+ defaultToken,
18246
+ defaultTokenLoading,
18247
+ defaultTokenError,
17947
18248
  depositWallet?.address,
17948
- externalUserEmail,
18249
+ userEmail,
17949
18250
  publishableKey,
17950
18251
  onEvent,
17951
18252
  userId
@@ -17989,42 +18290,8 @@ var BuyWithApplePay = React52.forwardRef(
17989
18290
  if (view === "loading_config") {
17990
18291
  return /* @__PURE__ */ jsx132("div", { className: "uf-flex uf-items-center uf-justify-center uf-py-16", style: containerStyle, children: /* @__PURE__ */ jsx132(LoaderCircle, { className: "uf-w-6 uf-h-6 uf-animate-spin", style: { color: colors2.primary } }) });
17991
18292
  }
17992
- if (view === "disabled") {
17993
- return /* @__PURE__ */ jsxs11(
17994
- "div",
17995
- {
17996
- className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-12 uf-px-4",
17997
- style: containerStyle,
17998
- children: [
17999
- /* @__PURE__ */ jsx132(
18000
- CircleAlert,
18001
- {
18002
- className: "uf-w-8 uf-h-8 uf-mb-4",
18003
- style: { color: colors2.foregroundMuted }
18004
- }
18005
- ),
18006
- /* @__PURE__ */ jsx132(
18007
- "h3",
18008
- {
18009
- className: "uf-text-base uf-font-medium uf-mb-1",
18010
- style: { color: colors2.foreground, fontFamily: fonts.medium },
18011
- children: "Apple Pay is unavailable"
18012
- }
18013
- ),
18014
- /* @__PURE__ */ jsx132(
18015
- "p",
18016
- {
18017
- className: "uf-text-sm uf-text-center",
18018
- style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
18019
- children: "Apple Pay is disabled for this project. Try another payment method."
18020
- }
18021
- )
18022
- ]
18023
- }
18024
- );
18025
- }
18026
18293
  if (view === "email_input") {
18027
- const emailValid = EMAIL_REGEX.test(email);
18294
+ const emailValid = EMAIL_REGEX2.test(email);
18028
18295
  return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pb-1 uf-pt-2", style: containerStyle, children: [
18029
18296
  /* @__PURE__ */ jsx132(
18030
18297
  "label",
@@ -18041,9 +18308,7 @@ var BuyWithApplePay = React52.forwardRef(
18041
18308
  value: email,
18042
18309
  onChange: (e) => setEmail(e.target.value.trim()),
18043
18310
  onKeyDown: (e) => {
18044
- if (e.key === "Enter" && emailValid) {
18045
- setView("phone_input");
18046
- }
18311
+ if (e.key === "Enter" && emailValid) setView("phone_input");
18047
18312
  },
18048
18313
  placeholder: "you@example.com",
18049
18314
  autoComplete: "email",
@@ -18079,7 +18344,7 @@ var BuyWithApplePay = React52.forwardRef(
18079
18344
  }
18080
18345
  if (view === "phone_input" || view === "submitting_session") {
18081
18346
  const submitting = view === "submitting_session";
18082
- const phoneValid = PHONE_REGEX.test(normalizedPhone);
18347
+ const phoneValid = PHONE_REGEX2.test(normalizedPhone);
18083
18348
  const rawDigits = (() => {
18084
18349
  const digits = phoneInput.replace(/\D/g, "");
18085
18350
  return digits.startsWith("1") ? digits.slice(1, 11) : digits.slice(0, 10);
@@ -18160,15 +18425,7 @@ var BuyWithApplePay = React52.forwardRef(
18160
18425
  !phoneInput && /* @__PURE__ */ jsx132("div", { className: "uf-text-xs uf-mb-2 uf-px-1", style: { color: colors2.foregroundMuted }, children: "Re-verified every 60 days." }),
18161
18426
  errorMessage && /* @__PURE__ */ jsx132("div", { className: "uf-text-xs uf-mt-2 uf-px-1", style: { color: colors2.error }, children: errorMessage }),
18162
18427
  /* @__PURE__ */ jsx132("div", { className: "uf-flex-1" }),
18163
- /* @__PURE__ */ jsx132(
18164
- LegalDisclaimer,
18165
- {
18166
- legalAgreements,
18167
- loading: legalAgreementsLoading,
18168
- colors: colors2,
18169
- fonts
18170
- }
18171
- ),
18428
+ /* @__PURE__ */ jsx132(LegalDisclaimer, { legalAgreements, loading: legalAgreementsLoading }),
18172
18429
  /* @__PURE__ */ jsx132(
18173
18430
  "button",
18174
18431
  {
@@ -18187,12 +18444,9 @@ var BuyWithApplePay = React52.forwardRef(
18187
18444
  )
18188
18445
  ] });
18189
18446
  }
18190
- if (view === "email_otp" || view === "phone_otp") {
18191
- const isEmail = view === "email_otp";
18192
- const code = isEmail ? emailCode : phoneCode;
18193
- const setCode = isEmail ? setEmailCode : setPhoneCode;
18194
- const target = isEmail ? maskEmail(email) : maskPhone(normalizedPhone);
18195
- const submit = isEmail ? submitEmailOtp : submitPhoneOtp;
18447
+ if (view === "phone_otp") {
18448
+ const code = phoneCode;
18449
+ const target = maskPhone(normalizedPhone);
18196
18450
  const canSubmit = code.length === 6 && !otpSubmitting;
18197
18451
  return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pb-1 uf-items-center uf-pt-2", style: containerStyle, children: [
18198
18452
  /* @__PURE__ */ jsxs11(
@@ -18215,13 +18469,12 @@ var BuyWithApplePay = React52.forwardRef(
18215
18469
  maxLength: 6,
18216
18470
  value: code,
18217
18471
  onChange: (e) => {
18218
- const v = e.target.value.replace(/\D/g, "").slice(0, 6);
18219
- setCode(v);
18472
+ setPhoneCode(e.target.value.replace(/\D/g, "").slice(0, 6));
18220
18473
  setOtpError(null);
18221
18474
  },
18222
18475
  onKeyDown: (e) => {
18223
18476
  if (e.key === "Enter" && code.length === 6 && !otpSubmitting) {
18224
- void submit();
18477
+ void submitPhoneOtp();
18225
18478
  }
18226
18479
  },
18227
18480
  autoFocus: true,
@@ -18248,7 +18501,7 @@ var BuyWithApplePay = React52.forwardRef(
18248
18501
  if (!verificationSession || !clientSecret) return;
18249
18502
  void sendOnrampVerificationOtp(
18250
18503
  verificationSession.id,
18251
- isEmail ? "email" : "phone",
18504
+ "phone",
18252
18505
  clientSecret,
18253
18506
  publishableKey
18254
18507
  ).then(() => setResendCooldown(30));
@@ -18261,7 +18514,7 @@ var BuyWithApplePay = React52.forwardRef(
18261
18514
  /* @__PURE__ */ jsx132(
18262
18515
  "button",
18263
18516
  {
18264
- onClick: () => void submit(),
18517
+ onClick: submitPhoneOtp,
18265
18518
  disabled: !canSubmit,
18266
18519
  className: "uf-w-full uf-py-3 uf-mt-6 uf-text-sm uf-font-medium uf-transition-colors disabled:uf-opacity-50 disabled:uf-cursor-not-allowed uf-flex uf-items-center uf-justify-center uf-gap-2",
18267
18520
  style: {
@@ -18409,7 +18662,7 @@ var BuyWithApplePay = React52.forwardRef(
18409
18662
  border: `${components.button.borderWidth}px solid ${components.button.borderColor}`,
18410
18663
  opacity: 0.5
18411
18664
  },
18412
- children: !hasInput || !validAmount ? "Enter amount" : /* @__PURE__ */ jsxs11(Fragment42, { children: [
18665
+ children: !hasInput || !validAmount ? "Enter amount" : errorMessage ? "Unable to prepare Apple Pay" : defaultTokenLoading ? "Loading token information\u2026" : defaultTokenError ? "Token routing unavailable" : /* @__PURE__ */ jsxs11(Fragment42, { children: [
18413
18666
  /* @__PURE__ */ jsx132(LoaderCircle, { className: "uf-w-4 uf-h-4 uf-animate-spin" }),
18414
18667
  "Preparing Apple Pay"
18415
18668
  ] })
@@ -18462,6 +18715,274 @@ var BuyWithApplePay = React52.forwardRef(
18462
18715
  }
18463
18716
  );
18464
18717
  }
18718
+ if (view === "limit_upgrade_intro") {
18719
+ return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pt-8 uf-pb-2 uf-px-2", style: containerStyle, children: [
18720
+ /* @__PURE__ */ jsx132(
18721
+ "h3",
18722
+ {
18723
+ className: "uf-text-xl uf-mb-3 uf-text-center",
18724
+ style: { color: colors2.foreground, fontFamily: fonts.medium },
18725
+ children: "Apple Pay limit reached"
18726
+ }
18727
+ ),
18728
+ /* @__PURE__ */ jsx132(
18729
+ "p",
18730
+ {
18731
+ className: "uf-text-sm uf-text-center uf-mb-10 uf-max-w-[320px] uf-mx-auto",
18732
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
18733
+ children: "Submit your date of birth and SSN to increase your Apple Pay limit."
18734
+ }
18735
+ ),
18736
+ /* @__PURE__ */ jsx132(
18737
+ "button",
18738
+ {
18739
+ onClick: () => {
18740
+ setLimitUpgradeError(null);
18741
+ setView("limit_upgrade_form");
18742
+ },
18743
+ className: "uf-w-full uf-py-3 uf-mb-2 uf-text-sm uf-font-medium uf-transition-colors",
18744
+ style: {
18745
+ backgroundColor: colors2.primary,
18746
+ color: colors2.primaryForeground,
18747
+ fontFamily: fonts.medium,
18748
+ borderRadius: components.button.borderRadius,
18749
+ border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
18750
+ },
18751
+ children: "Continue"
18752
+ }
18753
+ ),
18754
+ /* @__PURE__ */ jsx132(
18755
+ "button",
18756
+ {
18757
+ onClick: () => {
18758
+ setLimitUpgradeError(null);
18759
+ onExit?.();
18760
+ },
18761
+ className: "uf-w-full uf-py-3 uf-text-sm uf-font-medium uf-transition-colors",
18762
+ style: {
18763
+ backgroundColor: "transparent",
18764
+ color: colors2.error,
18765
+ fontFamily: fonts.medium,
18766
+ borderRadius: components.button.borderRadius,
18767
+ border: `${components.button.borderWidth}px solid transparent`
18768
+ },
18769
+ children: "Cancel"
18770
+ }
18771
+ )
18772
+ ] });
18773
+ }
18774
+ if (view === "limit_upgrade_submitting") {
18775
+ return /* @__PURE__ */ jsxs11(
18776
+ "div",
18777
+ {
18778
+ className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-16 uf-px-4",
18779
+ style: containerStyle,
18780
+ children: [
18781
+ /* @__PURE__ */ jsx132(
18782
+ "div",
18783
+ {
18784
+ className: "uf-w-20 uf-h-20 uf-rounded-full uf-flex uf-items-center uf-justify-center uf-mb-6",
18785
+ style: { backgroundColor: `${colors2.primary}20` },
18786
+ children: /* @__PURE__ */ jsx132(
18787
+ LoaderCircle,
18788
+ {
18789
+ className: "uf-w-10 uf-h-10 uf-animate-spin",
18790
+ style: { color: colors2.primary }
18791
+ }
18792
+ )
18793
+ }
18794
+ ),
18795
+ /* @__PURE__ */ jsx132(
18796
+ "h3",
18797
+ {
18798
+ className: "uf-text-xl uf-mb-2 uf-text-center",
18799
+ style: { color: colors2.foreground, fontFamily: fonts.medium },
18800
+ children: "Verifying your information"
18801
+ }
18802
+ ),
18803
+ /* @__PURE__ */ jsx132(
18804
+ "p",
18805
+ {
18806
+ className: "uf-text-sm uf-text-center uf-max-w-[280px]",
18807
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
18808
+ children: "Your limit increase request is under review. This usually takes just a few seconds."
18809
+ }
18810
+ )
18811
+ ]
18812
+ }
18813
+ );
18814
+ }
18815
+ if (view === "limit_upgrade_form") {
18816
+ const dobValid = isDobInputValid(dobInput);
18817
+ const ssnValid = /^\d{4}$/.test(ssnLast4);
18818
+ const formValid = dobValid && ssnValid;
18819
+ const formInputStyle = {
18820
+ backgroundColor: components.card.backgroundColor,
18821
+ color: components.input.textColor,
18822
+ fontFamily: fonts.regular,
18823
+ borderRadius: components.card.borderRadius,
18824
+ border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
18825
+ };
18826
+ return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pb-1 uf-pt-2", style: containerStyle, children: [
18827
+ /* @__PURE__ */ jsx132(
18828
+ "label",
18829
+ {
18830
+ className: "uf-text-xs uf-font-medium uf-mb-1.5 uf-px-1",
18831
+ style: { color: components.card.labelColor, fontFamily: fonts.medium },
18832
+ children: "Date of birth"
18833
+ }
18834
+ ),
18835
+ /* @__PURE__ */ jsx132(
18836
+ "input",
18837
+ {
18838
+ type: "text",
18839
+ inputMode: "numeric",
18840
+ maxLength: 10,
18841
+ value: dobInput,
18842
+ onChange: (e) => {
18843
+ setDobInput(formatDobInput(e.target.value));
18844
+ if (limitUpgradeError) setLimitUpgradeError(null);
18845
+ },
18846
+ onKeyDown: (e) => {
18847
+ if (e.key === "Enter" && formValid) void submitLimitUpgrade();
18848
+ },
18849
+ placeholder: "MM/DD/YYYY",
18850
+ autoComplete: "bday",
18851
+ "aria-label": "Date of birth",
18852
+ className: "uf-w-full uf-px-3 uf-py-3 uf-mb-3 uf-text-sm uf-outline-none",
18853
+ style: formInputStyle
18854
+ }
18855
+ ),
18856
+ /* @__PURE__ */ jsx132(
18857
+ "label",
18858
+ {
18859
+ className: "uf-text-xs uf-font-medium uf-mb-1.5 uf-px-1",
18860
+ style: { color: components.card.labelColor, fontFamily: fonts.medium },
18861
+ children: "Last 4 of SSN"
18862
+ }
18863
+ ),
18864
+ /* @__PURE__ */ jsx132(
18865
+ "input",
18866
+ {
18867
+ type: "password",
18868
+ inputMode: "numeric",
18869
+ pattern: "[0-9]*",
18870
+ maxLength: 4,
18871
+ value: ssnLast4,
18872
+ onChange: (e) => {
18873
+ setSsnLast4(e.target.value.replace(/\D/g, "").slice(0, 4));
18874
+ if (limitUpgradeError) setLimitUpgradeError(null);
18875
+ },
18876
+ onKeyDown: (e) => {
18877
+ if (e.key === "Enter" && formValid) void submitLimitUpgrade();
18878
+ },
18879
+ placeholder: "\u2022\u2022\u2022\u2022",
18880
+ autoComplete: "off",
18881
+ name: "off",
18882
+ autoCorrect: "off",
18883
+ autoCapitalize: "none",
18884
+ spellCheck: false,
18885
+ "data-1p-ignore": "true",
18886
+ "data-lpignore": "true",
18887
+ "data-form-type": "other",
18888
+ "aria-label": "Last 4 digits of SSN",
18889
+ "aria-autocomplete": "none",
18890
+ className: "uf-w-full uf-px-3 uf-py-3 uf-mb-2 uf-text-sm uf-outline-none",
18891
+ style: formInputStyle
18892
+ }
18893
+ ),
18894
+ limitUpgradeError && /* @__PURE__ */ jsx132(
18895
+ "div",
18896
+ {
18897
+ className: "uf-text-xs uf-mt-2 uf-px-1",
18898
+ style: { color: colors2.error, fontFamily: fonts.regular },
18899
+ children: limitUpgradeError
18900
+ }
18901
+ ),
18902
+ /* @__PURE__ */ jsx132("div", { className: "uf-flex-1" }),
18903
+ /* @__PURE__ */ jsx132(
18904
+ "button",
18905
+ {
18906
+ onClick: () => void submitLimitUpgrade(),
18907
+ disabled: !formValid || upgradeSubmitting,
18908
+ className: "uf-w-full uf-py-3 uf-mt-3 uf-text-sm uf-font-medium uf-transition-colors disabled:uf-opacity-50 disabled:uf-cursor-not-allowed",
18909
+ style: {
18910
+ backgroundColor: colors2.primary,
18911
+ color: colors2.primaryForeground,
18912
+ fontFamily: fonts.medium,
18913
+ borderRadius: components.button.borderRadius,
18914
+ border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
18915
+ },
18916
+ children: upgradeSubmitting ? /* @__PURE__ */ jsxs11("span", { className: "uf-flex uf-items-center uf-justify-center uf-gap-2", children: [
18917
+ /* @__PURE__ */ jsx132(LoaderCircle, { className: "uf-w-4 uf-h-4 uf-animate-spin" }),
18918
+ "Submitting\u2026"
18919
+ ] }) : "Submit"
18920
+ }
18921
+ )
18922
+ ] });
18923
+ }
18924
+ if (view === "limit_upgrade_failed") {
18925
+ return /* @__PURE__ */ jsxs11("div", { className: "uf-flex uf-flex-col uf-pt-8 uf-pb-2 uf-px-2", style: containerStyle, children: [
18926
+ /* @__PURE__ */ jsx132(
18927
+ "h3",
18928
+ {
18929
+ className: "uf-text-xl uf-mb-3 uf-text-center",
18930
+ style: { color: colors2.foreground, fontFamily: fonts.medium },
18931
+ children: "Apple Pay limit reached"
18932
+ }
18933
+ ),
18934
+ /* @__PURE__ */ jsxs11(
18935
+ "p",
18936
+ {
18937
+ className: "uf-text-sm uf-text-center uf-mb-2 uf-max-w-[320px] uf-mx-auto",
18938
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
18939
+ children: [
18940
+ "Failed to increase. Please try again later or",
18941
+ " ",
18942
+ /* @__PURE__ */ jsx132(
18943
+ "a",
18944
+ {
18945
+ href: UNIFOLD_SUPPORT_URL,
18946
+ target: "_blank",
18947
+ rel: "noreferrer noopener",
18948
+ className: "uf-underline",
18949
+ style: { color: colors2.primary },
18950
+ children: "contact support"
18951
+ }
18952
+ ),
18953
+ "."
18954
+ ]
18955
+ }
18956
+ ),
18957
+ limitUpgradeError && /* @__PURE__ */ jsx132(
18958
+ "p",
18959
+ {
18960
+ className: "uf-text-xs uf-text-center uf-mb-2 uf-px-2",
18961
+ style: { color: colors2.error, fontFamily: fonts.regular },
18962
+ children: limitUpgradeError
18963
+ }
18964
+ ),
18965
+ /* @__PURE__ */ jsx132("div", { className: "uf-mb-8" }),
18966
+ /* @__PURE__ */ jsx132(
18967
+ "button",
18968
+ {
18969
+ onClick: () => {
18970
+ setLimitUpgradeError(null);
18971
+ onExit?.();
18972
+ },
18973
+ className: "uf-w-full uf-py-3 uf-text-sm uf-font-medium uf-transition-colors",
18974
+ style: {
18975
+ backgroundColor: colors2.primary,
18976
+ color: colors2.primaryForeground,
18977
+ fontFamily: fonts.medium,
18978
+ borderRadius: components.button.borderRadius,
18979
+ border: `${components.button.borderWidth}px solid ${components.button.borderColor}`
18980
+ },
18981
+ children: "Return"
18982
+ }
18983
+ )
18984
+ ] });
18985
+ }
18465
18986
  if (view === "guest_limit_reached") {
18466
18987
  return /* @__PURE__ */ jsxs11(
18467
18988
  "div",
@@ -18482,7 +19003,7 @@ var BuyWithApplePay = React52.forwardRef(
18482
19003
  {
18483
19004
  className: "uf-text-sm uf-text-center uf-mb-12 uf-max-w-[300px]",
18484
19005
  style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
18485
- children: "Your account has reached Apple Pay limit. It's not eligible for an increase right now."
19006
+ children: "Your account is not eligible for a limit increase at this time."
18486
19007
  }
18487
19008
  ),
18488
19009
  /* @__PURE__ */ jsx132(
@@ -18508,7 +19029,95 @@ var BuyWithApplePay = React52.forwardRef(
18508
19029
  }
18509
19030
  );
18510
19031
  BuyWithApplePay.displayName = "BuyWithApplePay";
18511
- function LegalDisclaimer({ legalAgreements, loading, colors: colors2, fonts }) {
19032
+ function formatPhoneInput(raw) {
19033
+ const digits = raw.replace(/\D/g, "");
19034
+ if (raw.startsWith("+")) {
19035
+ return `+${digits}`;
19036
+ }
19037
+ if (digits.length === 10) return `+1${digits}`;
19038
+ if (digits.length === 11 && digits.startsWith("1")) return `+${digits}`;
19039
+ return digits.length > 0 ? `+${digits}` : "";
19040
+ }
19041
+ function maskPhone(phone) {
19042
+ if (!/^\+1\d{10}$/.test(phone)) return phone;
19043
+ return `+1 (***) ***-${phone.slice(-4)}`;
19044
+ }
19045
+ function isGuestLimitError(err) {
19046
+ const message = err instanceof Error ? err.message : String(err ?? "");
19047
+ if (/coinbase_onramp_guest_limit_reached/i.test(message)) return true;
19048
+ if (/guest_(daily_)?transaction_(count|limit)/i.test(message)) return true;
19049
+ return false;
19050
+ }
19051
+ function isLimitUpgradeTerminalBlock(message) {
19052
+ if (/coinbase_onramp_guest_limit_reached/i.test(message)) return true;
19053
+ if (/limit upgrade is not available/i.test(message)) return true;
19054
+ if (/limits.?upgrade.?blocked/i.test(message)) return true;
19055
+ return false;
19056
+ }
19057
+ function prettifyVerificationError(message) {
19058
+ if (/\b429\b|too many requests|rate.?limit|throttle/i.test(message)) {
19059
+ return "Too many attempts. Please wait a few minutes and try again.";
19060
+ }
19061
+ if (/network|failed to fetch|abort/i.test(message)) {
19062
+ return "Network error. Check your connection and try again.";
19063
+ }
19064
+ if (/voip|not a valid US|not a cell/i.test(message)) {
19065
+ return "We can only verify real US cell phones (not VoIP). Try a different number.";
19066
+ }
19067
+ const cleaned = message.replace(/\s*\[[\w_]+\]:\s*.*$/, "").trim();
19068
+ return cleaned || message;
19069
+ }
19070
+ function formatDobInput(raw) {
19071
+ const digits = raw.replace(/\D/g, "").slice(0, 8);
19072
+ if (digits.length <= 2) return digits;
19073
+ if (digits.length <= 4) return `${digits.slice(0, 2)}/${digits.slice(2)}`;
19074
+ return `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4)}`;
19075
+ }
19076
+ function isDobInputValid(input) {
19077
+ return parseDobInput(input) !== null;
19078
+ }
19079
+ function parseDobInput(input) {
19080
+ const m = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(input);
19081
+ if (!m) return null;
19082
+ const [, month, day, year] = m;
19083
+ if (!/^(0[1-9]|1[0-2])$/.test(month)) return null;
19084
+ if (!/^(0[1-9]|[12]\d|3[01])$/.test(day)) return null;
19085
+ if (!/^(19|20)\d{2}$/.test(year)) return null;
19086
+ return { month, day, year };
19087
+ }
19088
+ var POPUP_W = 500;
19089
+ var POPUP_H = 700;
19090
+ function openCoinbasePaymentPopup(url) {
19091
+ if (typeof window === "undefined") return null;
19092
+ const screenW = window.screen.availWidth ?? window.innerWidth;
19093
+ const screenH = window.screen.availHeight ?? window.innerHeight;
19094
+ const left = Math.max(0, Math.round((screenW - POPUP_W) / 2));
19095
+ const top = Math.max(0, Math.round((screenH - POPUP_H) / 2));
19096
+ return window.open(
19097
+ url,
19098
+ "unifold_coinbase_apple_pay",
19099
+ `popup,width=${POPUP_W},height=${POPUP_H},left=${left},top=${top}`
19100
+ );
19101
+ }
19102
+ var FALLBACK_AGREEMENTS = [
19103
+ {
19104
+ key: "guest_checkout_terms",
19105
+ name: "Guest Checkout Terms of Service",
19106
+ url: "https://www.coinbase.com/legal/cb-pay/onramp-guest-checkout-terms"
19107
+ },
19108
+ {
19109
+ key: "user_agreement",
19110
+ name: "User Agreement",
19111
+ url: "https://www.coinbase.com/legal/user_agreement"
19112
+ },
19113
+ {
19114
+ key: "privacy_policy",
19115
+ name: "Privacy Policy",
19116
+ url: "https://www.coinbase.com/legal/privacy"
19117
+ }
19118
+ ];
19119
+ function LegalDisclaimer({ legalAgreements, loading }) {
19120
+ const { colors: colors2, fonts } = useTheme();
18512
19121
  if (loading) {
18513
19122
  return /* @__PURE__ */ jsxs11(
18514
19123
  "div",
@@ -18552,78 +19161,6 @@ function LegalDisclaimer({ legalAgreements, loading, colors: colors2, fonts }) {
18552
19161
  }
18553
19162
  );
18554
19163
  }
18555
- function isGuestLimitError(err) {
18556
- const message = err instanceof Error ? err.message : String(err ?? "");
18557
- if (/coinbase_onramp_guest_limit_reached/i.test(message)) return true;
18558
- if (/guest_(daily_)?transaction_(count|limit)/i.test(message)) return true;
18559
- return false;
18560
- }
18561
- function prettifyVerificationError(message) {
18562
- if (/\b429\b|too many requests|rate.?limit|throttle/i.test(message)) {
18563
- return "Too many attempts. Please wait a few minutes and try again.";
18564
- }
18565
- if (/network|failed to fetch|abort/i.test(message)) {
18566
- return "Network error. Check your connection and try again.";
18567
- }
18568
- if (/voip|not a valid US|not a cell/i.test(message)) {
18569
- return "We can only verify real US cell phones (not VoIP). Try a different number.";
18570
- }
18571
- return message;
18572
- }
18573
- var FALLBACK_AGREEMENTS = [
18574
- {
18575
- key: "guest_checkout_terms",
18576
- name: "Guest Checkout Terms of Service",
18577
- url: "https://www.coinbase.com/legal/cb-pay/onramp-guest-checkout-terms"
18578
- },
18579
- {
18580
- key: "user_agreement",
18581
- name: "User Agreement",
18582
- url: "https://www.coinbase.com/legal/user_agreement"
18583
- },
18584
- {
18585
- key: "privacy_policy",
18586
- name: "Privacy Policy",
18587
- url: "https://www.coinbase.com/legal/privacy"
18588
- }
18589
- ];
18590
- var POPUP_W = 500;
18591
- var POPUP_H = 700;
18592
- function openCoinbasePaymentPopup(url) {
18593
- if (typeof window === "undefined") return null;
18594
- const screenW = window.screen.availWidth ?? window.innerWidth;
18595
- const screenH = window.screen.availHeight ?? window.innerHeight;
18596
- const left = Math.max(0, Math.round((screenW - POPUP_W) / 2));
18597
- const top = Math.max(0, Math.round((screenH - POPUP_H) / 2));
18598
- return window.open(
18599
- url,
18600
- "unifold_coinbase_apple_pay",
18601
- `popup,width=${POPUP_W},height=${POPUP_H},left=${left},top=${top}`
18602
- );
18603
- }
18604
- function destinationChainNameFor(chainType, chainId) {
18605
- if (chainType === "solana") return "solana";
18606
- if (chainType === "bitcoin") return "bitcoin";
18607
- if (chainType === "ethereum") {
18608
- switch (chainId) {
18609
- case "1":
18610
- return "ethereum";
18611
- case "8453":
18612
- return "base";
18613
- case "137":
18614
- return "polygon";
18615
- case "43114":
18616
- return "avalanche";
18617
- case "42161":
18618
- return "arbitrum";
18619
- case "10":
18620
- return "optimism";
18621
- default:
18622
- return null;
18623
- }
18624
- }
18625
- return null;
18626
- }
18627
19164
  var t2 = i18n2.payWithExchange;
18628
19165
  function PayWithExchange({
18629
19166
  userId,
@@ -18921,7 +19458,7 @@ function QRCodeSkeleton({ size: size4 = 180, darkMode = false }) {
18921
19458
  const spacing = size4 / gridCount;
18922
19459
  const fillColor = darkMode ? "rgba(255,255,255,0.10)" : "rgba(0,0,0,0.08)";
18923
19460
  const cornerColor = darkMode ? "rgba(255,255,255,0.14)" : "rgba(0,0,0,0.12)";
18924
- const dots = useMemo32(() => {
19461
+ const dots = useMemo42(() => {
18925
19462
  const result = [];
18926
19463
  const cornerSize = 7;
18927
19464
  const centerStart = Math.floor(gridCount / 2) - 2;
@@ -19021,7 +19558,7 @@ function useIsMobileViewport() {
19021
19558
  return isMobile;
19022
19559
  }
19023
19560
  function useCashAppLimits({ publishableKey, currency = "usd" }) {
19024
- return useQuery5({
19561
+ return useQuery6({
19025
19562
  queryKey: ["unifold", "cashAppLimits", currency, publishableKey],
19026
19563
  queryFn: () => getCashAppLimits(currency, publishableKey),
19027
19564
  enabled: !!publishableKey,
@@ -19534,43 +20071,6 @@ function PayWithCashApp({
19534
20071
  }
19535
20072
  return null;
19536
20073
  }
19537
- function useDefaultOnrampToken({
19538
- publishableKey,
19539
- tokenAddress,
19540
- chainId,
19541
- chainType,
19542
- countryCode,
19543
- subdivisionCode,
19544
- enabled = true
19545
- }) {
19546
- const { data: defaultToken, isLoading } = useQuery6({
19547
- queryKey: [
19548
- "unifold",
19549
- "defaultOnrampToken",
19550
- publishableKey,
19551
- tokenAddress,
19552
- chainId,
19553
- chainType,
19554
- countryCode,
19555
- subdivisionCode
19556
- ],
19557
- queryFn: () => getDefaultOnrampToken(
19558
- {
19559
- token_address: tokenAddress,
19560
- chain_id: chainId,
19561
- chain_type: chainType,
19562
- country_code: countryCode,
19563
- subdivision_code: subdivisionCode
19564
- },
19565
- publishableKey
19566
- ),
19567
- enabled: enabled && !!tokenAddress && !!chainId && !!chainType,
19568
- staleTime: 1e3 * 60 * 60,
19569
- refetchOnMount: false,
19570
- refetchOnWindowFocus: false
19571
- });
19572
- return { defaultToken, isLoading };
19573
- }
19574
20074
  function useBankTransferProviders({
19575
20075
  publishableKey,
19576
20076
  enabled = true,
@@ -19649,7 +20149,7 @@ function BankTransfer({
19649
20149
  countryCode: userIpInfo?.alpha2
19650
20150
  });
19651
20151
  const providers = providersResponse?.data ?? [];
19652
- const pollingWalletId = useMemo42(() => {
20152
+ const pollingWalletId = useMemo52(() => {
19653
20153
  if (!defaultToken) return void 0;
19654
20154
  return getWalletByChainType(
19655
20155
  wallets,
@@ -19670,7 +20170,7 @@ function BankTransfer({
19670
20170
  const currencySymbol = getCurrencySymbol2(sourceCurrency);
19671
20171
  const parsedAmount = parseFloat(amount);
19672
20172
  const amountValid = !!amount && Number.isFinite(parsedAmount) && parsedAmount >= MIN_AMOUNT;
19673
- const displayTokenSymbol = useMemo42(
20173
+ const displayTokenSymbol = useMemo52(
19674
20174
  () => destinationTokenSymbol?.toUpperCase() ?? defaultToken?.destination_token_metadata?.symbol?.toUpperCase() ?? defaultToken?.destination_currency?.toUpperCase() ?? "USDC",
19675
20175
  [destinationTokenSymbol, defaultToken]
19676
20176
  );
@@ -23645,16 +24145,114 @@ function AmexIcon({ size: size4 = 24 }) {
23645
24145
  )
23646
24146
  ] });
23647
24147
  }
24148
+ function ApplePayIcon({ size: size4 = 24, color = "#000" }) {
24149
+ const h = size4 * 0.62;
24150
+ const w = h * (512 / 210.2);
24151
+ return /* @__PURE__ */ jsx43("svg", { width: w, height: h, viewBox: "0 0 512 210.2", fill: "none", children: /* @__PURE__ */ jsx43(
24152
+ "path",
24153
+ {
24154
+ fill: color,
24155
+ d: "M93.6,27.1C87.6,34.2,78,39.8,68.4,39c-1.2-9.6,3.5-19.8,9-26.1c6-7.3,16.5-12.5,25-12.9 C103.4,10,99.5,19.8,93.6,27.1 M102.3,40.9c-13.9-0.8-25.8,7.9-32.4,7.9c-6.7,0-16.8-7.5-27.8-7.3c-14.3,0.2-27.6,8.3-34.9,21.2 c-15,25.8-3.9,64,10.6,85c7.1,10.4,15.6,21.8,26.8,21.4c10.6-0.4,14.8-6.9,27.6-6.9c12.9,0,16.6,6.9,27.8,6.7 c11.6-0.2,18.9-10.4,26-20.8c8.1-11.8,11.4-23.3,11.6-23.9c-0.2-0.2-22.4-8.7-22.6-34.3c-0.2-21.4,17.5-31.6,18.3-32.2 C123.3,42.9,107.7,41.3,102.3,40.9 M182.6,11.9v155.9h24.2v-53.3h33.5c30.6,0,52.1-21,52.1-51.4c0-30.4-21.1-51.2-51.3-51.2H182.6z M206.8,32.3h27.9c21,0,33,11.2,33,30.9c0,19.7-12,31-33.1,31h-27.8V32.3z M336.6,169c15.2,0,29.3-7.7,35.7-19.9h0.5v18.7h22.4V90.2 c0-22.5-18-37-45.7-37c-25.7,0-44.7,14.7-45.4,34.9h21.8c1.8-9.6,10.7-15.9,22.9-15.9c14.8,0,23.1,6.9,23.1,19.6v8.6l-30.2,1.8 c-28.1,1.7-43.3,13.2-43.3,33.2C298.4,155.6,314.1,169,336.6,169z M343.1,150.5c-12.9,0-21.1-6.2-21.1-15.7c0-9.8,7.9-15.5,23-16.4 l26.9-1.7v8.8C371.9,140.1,359.5,150.5,343.1,150.5z M425.1,210.2c23.6,0,34.7-9,44.4-36.3L512,54.7h-24.6l-28.5,92.1h-0.5 l-28.5-92.1h-25.3l41,113.5l-2.2,6.9c-3.7,11.7-9.7,16.2-20.4,16.2c-1.9,0-5.6-0.2-7.1-0.4v18.7C417.3,210,423.3,210.2,425.1,210.2z"
24156
+ }
24157
+ ) });
24158
+ }
24159
+ function GooglePayIcon({ size: size4 = 24, color = "#202124" }) {
24160
+ const h = size4 * 0.62;
24161
+ const w = h * (80 / 38.1);
24162
+ return /* @__PURE__ */ jsxs40("svg", { width: w, height: h, viewBox: "0 0 80 38.1", fill: "none", children: [
24163
+ /* @__PURE__ */ jsx43(
24164
+ "path",
24165
+ {
24166
+ fill: color,
24167
+ d: "M37.8,19.7V29h-3V6h7.8c1.9,0,3.7,0.7,5.1,2c1.4,1.2,2.1,3,2.1,4.9c0,1.9-0.7,3.6-2.1,4.9c-1.4,1.3-3.1,2-5.1,2 L37.8,19.7L37.8,19.7z M37.8,8.8v8h5c1.1,0,2.2-0.4,2.9-1.2c1.6-1.5,1.6-4,0.1-5.5c0,0-0.1-0.1-0.1-0.1c-0.8-0.8-1.8-1.3-2.9-1.2 L37.8,8.8L37.8,8.8z"
24168
+ }
24169
+ ),
24170
+ /* @__PURE__ */ jsx43(
24171
+ "path",
24172
+ {
24173
+ fill: color,
24174
+ d: "M56.7,12.8c2.2,0,3.9,0.6,5.2,1.8s1.9,2.8,1.9,4.8V29H61v-2.2h-0.1c-1.2,1.8-2.9,2.7-4.9,2.7 c-1.7,0-3.2-0.5-4.4-1.5c-1.1-1-1.8-2.4-1.8-3.9c0-1.6,0.6-2.9,1.8-3.9c1.2-1,2.9-1.4,4.9-1.4c1.8,0,3.2,0.3,4.3,1v-0.7 c0-1-0.4-2-1.2-2.6c-0.8-0.7-1.8-1.1-2.9-1.1c-1.7,0-3,0.7-3.9,2.1l-2.6-1.6C51.8,13.8,53.9,12.8,56.7,12.8z M52.9,24.2 c0,0.8,0.4,1.5,1,1.9c0.7,0.5,1.5,0.8,2.3,0.8c1.2,0,2.4-0.5,3.3-1.4c1-0.9,1.5-2,1.5-3.2c-0.9-0.7-2.2-1.1-3.9-1.1 c-1.2,0-2.2,0.3-3,0.9C53.3,22.6,52.9,23.3,52.9,24.2z"
24175
+ }
24176
+ ),
24177
+ /* @__PURE__ */ jsx43(
24178
+ "path",
24179
+ {
24180
+ fill: color,
24181
+ d: "M80,13.3l-9.9,22.7h-3l3.7-7.9l-6.5-14.7h3.2l4.7,11.3h0.1l4.6-11.3H80z"
24182
+ }
24183
+ ),
24184
+ /* @__PURE__ */ jsx43(
24185
+ "path",
24186
+ {
24187
+ fill: "#4285F4",
24188
+ d: "M25.9,17.7c0-0.9-0.1-1.8-0.2-2.7H13.2v5.1h7.1c-0.3,1.6-1.2,3.1-2.6,4v3.3H22C24.5,25.1,25.9,21.7,25.9,17.7z"
24189
+ }
24190
+ ),
24191
+ /* @__PURE__ */ jsx43(
24192
+ "path",
24193
+ {
24194
+ fill: "#34A853",
24195
+ d: "M13.2,30.6c3.6,0,6.6-1.2,8.8-3.2l-4.3-3.3c-1.2,0.8-2.7,1.3-4.5,1.3c-3.4,0-6.4-2.3-7.4-5.5H1.4v3.4 C3.7,27.8,8.2,30.6,13.2,30.6z"
24196
+ }
24197
+ ),
24198
+ /* @__PURE__ */ jsx43(
24199
+ "path",
24200
+ {
24201
+ fill: "#FBBC04",
24202
+ d: "M5.8,19.9c-0.6-1.6-0.6-3.4,0-5.1v-3.4H1.4c-1.9,3.7-1.9,8.1,0,11.9L5.8,19.9z"
24203
+ }
24204
+ ),
24205
+ /* @__PURE__ */ jsx43(
24206
+ "path",
24207
+ {
24208
+ fill: "#EA4335",
24209
+ d: "M13.2,9.4c1.9,0,3.7,0.7,5.1,2l0,0l3.8-3.8c-2.4-2.2-5.6-3.5-8.8-3.4c-5,0-9.6,2.8-11.8,7.3l4.4,3.4 C6.8,11.7,9.8,9.4,13.2,9.4z"
24210
+ }
24211
+ )
24212
+ ] });
24213
+ }
23648
24214
  function GenericCardIcon({ size: size4 = 24, color = "#888" }) {
23649
24215
  return /* @__PURE__ */ jsx43(CreditCard, { width: size4 * 0.8, height: size4 * 0.8, style: { color }, strokeWidth: 1.5 });
23650
24216
  }
24217
+ function parseColor(input) {
24218
+ if (!input) return null;
24219
+ const s = input.trim();
24220
+ if (s.startsWith("#")) {
24221
+ let hex = s.slice(1);
24222
+ if (hex.length === 3)
24223
+ hex = hex.split("").map((c) => c + c).join("");
24224
+ if (hex.length !== 6) return null;
24225
+ const n = parseInt(hex, 16);
24226
+ if (Number.isNaN(n)) return null;
24227
+ return [n >> 16 & 255, n >> 8 & 255, n & 255];
24228
+ }
24229
+ const m = s.match(/rgba?\(([^)]+)\)/i);
24230
+ if (m) {
24231
+ const parts = m[1].split(",").map((p) => parseFloat(p));
24232
+ if (parts.length >= 3 && parts.every((p) => !Number.isNaN(p)))
24233
+ return [parts[0], parts[1], parts[2]];
24234
+ }
24235
+ return null;
24236
+ }
24237
+ function monochromeOn(background) {
24238
+ const rgb = parseColor(background);
24239
+ if (!rgb) return "#000000";
24240
+ const [r2, g, b] = rgb;
24241
+ const luminance = (0.299 * r2 + 0.587 * g + 0.114 * b) / 255;
24242
+ return luminance > 0.6 ? "#000000" : "#FFFFFF";
24243
+ }
23651
24244
  function CardBrandIcon({
23652
24245
  brand,
23653
24246
  size: size4 = 24,
23654
- fallbackColor = "#888"
24247
+ fallbackColor = "#888",
24248
+ monochromeColor = "#000000"
23655
24249
  }) {
23656
24250
  if (!brand) return /* @__PURE__ */ jsx43(GenericCardIcon, { size: size4, color: fallbackColor });
23657
24251
  const lower = brand.toLowerCase();
24252
+ if (lower.includes("apple_pay") || lower.includes("apple pay") || lower === "applepay")
24253
+ return /* @__PURE__ */ jsx43(ApplePayIcon, { size: size4, color: monochromeColor });
24254
+ if (lower.includes("google_pay") || lower.includes("google pay") || lower === "googlepay")
24255
+ return /* @__PURE__ */ jsx43(GooglePayIcon, { size: size4, color: monochromeColor === "#FFFFFF" ? "#FFFFFF" : "#202124" });
23658
24256
  if (lower.includes("visa")) return /* @__PURE__ */ jsx43(VisaIcon, { size: size4 });
23659
24257
  if (lower.includes("mastercard") || lower.includes("master"))
23660
24258
  return /* @__PURE__ */ jsx43(MastercardIcon, { size: size4 });
@@ -23663,17 +24261,28 @@ function CardBrandIcon({
23663
24261
  }
23664
24262
  function formatPaymentToken(token) {
23665
24263
  if (token.card) {
23666
- const brand = token.card.brand ?? "Card";
24264
+ const cardBrand = token.card.brand ?? "Card";
24265
+ const last4 = token.card.last4;
24266
+ const walletType = token.card.wallet?.type;
24267
+ if (walletType === "apple_pay" || walletType === "google_pay") {
24268
+ const funding = token.card.funding ? capitalize(token.card.funding) : null;
24269
+ return {
24270
+ label: last4 ? `${capitalize(cardBrand)} \u2022\u2022\u2022\u2022 ${last4}` : capitalize(cardBrand),
24271
+ sublabel: funding ?? "",
24272
+ brand: walletType
24273
+ };
24274
+ }
23667
24275
  return {
23668
- label: `${capitalize(brand)} \u2022\u2022\u2022\u2022 ${token.card.last4}`,
24276
+ label: `${capitalize(cardBrand)} \u2022\u2022\u2022\u2022 ${last4}`,
23669
24277
  sublabel: `${capitalize(token.card.funding ?? "card")} \xB7 Expires ${token.card.exp_month}/${token.card.exp_year}`,
23670
- brand
24278
+ brand: cardBrand
23671
24279
  };
23672
24280
  }
23673
24281
  if (token.us_bank_account) {
24282
+ const accountType = token.us_bank_account.account_type;
23674
24283
  return {
23675
24284
  label: `${token.us_bank_account.bank_name ?? "Bank"} \u2022\u2022\u2022\u2022 ${token.us_bank_account.last4}`,
23676
- sublabel: "Bank account"
24285
+ sublabel: accountType ? `${capitalize(accountType)} account` : "Bank account"
23677
24286
  };
23678
24287
  }
23679
24288
  return { label: "Payment method", sublabel: token.type };
@@ -23681,59 +24290,82 @@ function formatPaymentToken(token) {
23681
24290
  function capitalize(s) {
23682
24291
  return s.charAt(0).toUpperCase() + s.slice(1);
23683
24292
  }
23684
- function useStripeOnramp(stripePublishableKey) {
24293
+ function useStripeOnramp(stripePublishableKey, isDark = false) {
23685
24294
  const [coordinator, setCoordinator] = useState282(null);
23686
24295
  const [isLoading, setIsLoading] = useState282(false);
23687
24296
  const [error, setError] = useState282(null);
23688
24297
  const coordinatorRef = useRef72(null);
23689
- const initialize = useCallback32(async () => {
23690
- if (coordinatorRef.current) return coordinatorRef.current;
23691
- if (!stripePublishableKey) return null;
23692
- setIsLoading(true);
23693
- setError(null);
23694
- try {
23695
- console.log("[PayWithStripeLink] Importing @stripe/crypto...");
23696
- const { loadCryptoOnrampAndInitialize } = await import(
23697
- /* @vite-ignore */
23698
- "@stripe/crypto"
23699
- );
23700
- console.log(
23701
- "[PayWithStripeLink] @stripe/crypto imported, initializing with key:",
23702
- stripePublishableKey?.slice(0, 12) + "..."
23703
- );
23704
- const initPromise = loadCryptoOnrampAndInitialize(stripePublishableKey, { theme: "stripe" });
23705
- const timeoutPromise = new Promise(
23706
- (_, reject) => setTimeout(
23707
- () => reject(
23708
- new Error(
23709
- "Stripe SDK initialization timed out after 15 seconds. Check that your Stripe account has the Crypto Onramp feature enabled."
23710
- )
23711
- ),
23712
- 15e3
23713
- )
23714
- );
23715
- const instance = await Promise.race([initPromise, timeoutPromise]);
23716
- if (!instance) {
23717
- throw new Error("Stripe SDK returned null.");
23718
- }
23719
- console.log("[PayWithStripeLink] Stripe SDK initialized successfully");
23720
- coordinatorRef.current = instance;
23721
- setCoordinator(instance);
23722
- return coordinatorRef.current;
23723
- } catch (err) {
23724
- const raw = err instanceof Error ? err.message : String(err);
23725
- const msg = raw.includes("Cannot find module") || raw.includes("Module not found") || raw.includes("Failed to fetch") ? "Install @stripe/crypto to use Stripe Link: npm install @stripe/crypto" : `Failed to initialize Stripe SDK: ${raw}`;
23726
- setError(msg);
23727
- console.error("[PayWithStripeLink]", msg);
23728
- return null;
23729
- } finally {
23730
- setIsLoading(false);
23731
- }
24298
+ const coordinatorThemeRef = useRef72(null);
24299
+ const initPromiseRef = useRef72(null);
24300
+ const isDarkRef = useRef72(isDark);
24301
+ isDarkRef.current = isDark;
24302
+ const initialize = useCallback32(() => {
24303
+ if (coordinatorRef.current) return Promise.resolve(coordinatorRef.current);
24304
+ if (initPromiseRef.current) return initPromiseRef.current;
24305
+ if (!stripePublishableKey) return Promise.resolve(null);
24306
+ const theme = isDarkRef.current;
24307
+ const promise = (async () => {
24308
+ setIsLoading(true);
24309
+ setError(null);
24310
+ try {
24311
+ console.log("[PayWithStripeLink] Importing @stripe/crypto...");
24312
+ const { loadCryptoOnrampAndInitialize } = await import(
24313
+ /* @vite-ignore */
24314
+ "@stripe/crypto"
24315
+ );
24316
+ console.log(
24317
+ "[PayWithStripeLink] @stripe/crypto imported, initializing with key:",
24318
+ stripePublishableKey?.slice(0, 12) + "..."
24319
+ );
24320
+ const initPromise = loadCryptoOnrampAndInitialize(stripePublishableKey, {
24321
+ theme: theme ? "night" : "stripe"
24322
+ });
24323
+ const timeoutPromise = new Promise(
24324
+ (_, reject) => setTimeout(
24325
+ () => reject(
24326
+ new Error(
24327
+ "Stripe SDK initialization timed out after 15 seconds. Check that your Stripe account has the Crypto Onramp feature enabled."
24328
+ )
24329
+ ),
24330
+ 15e3
24331
+ )
24332
+ );
24333
+ const instance = await Promise.race([initPromise, timeoutPromise]);
24334
+ if (!instance) {
24335
+ throw new Error("Stripe SDK returned null.");
24336
+ }
24337
+ console.log("[PayWithStripeLink] Stripe SDK initialized successfully");
24338
+ coordinatorRef.current = instance;
24339
+ coordinatorThemeRef.current = theme;
24340
+ setCoordinator(instance);
24341
+ return coordinatorRef.current;
24342
+ } catch (err) {
24343
+ const raw = err instanceof Error ? err.message : String(err);
24344
+ const msg = raw.includes("Cannot find module") || raw.includes("Module not found") || raw.includes("Failed to fetch") ? "Install @stripe/crypto to use Stripe Link: npm install @stripe/crypto" : `Failed to initialize Stripe SDK: ${raw}`;
24345
+ setError(msg);
24346
+ console.error("[PayWithStripeLink]", msg);
24347
+ return null;
24348
+ } finally {
24349
+ setIsLoading(false);
24350
+ initPromiseRef.current = null;
24351
+ }
24352
+ })();
24353
+ initPromiseRef.current = promise;
24354
+ return promise;
23732
24355
  }, [stripePublishableKey]);
24356
+ useEffect232(() => {
24357
+ if (!coordinatorRef.current) return;
24358
+ if (coordinatorThemeRef.current === isDark) return;
24359
+ coordinatorRef.current.destroy();
24360
+ coordinatorRef.current = null;
24361
+ coordinatorThemeRef.current = null;
24362
+ setCoordinator(null);
24363
+ }, [isDark, coordinator]);
23733
24364
  useEffect232(() => {
23734
24365
  return () => {
23735
24366
  coordinatorRef.current?.destroy();
23736
24367
  coordinatorRef.current = null;
24368
+ coordinatorThemeRef.current = null;
23737
24369
  };
23738
24370
  }, []);
23739
24371
  return { coordinator, isLoading, error, initialize };
@@ -23884,7 +24516,7 @@ function PayWithStripeLink({
23884
24516
  onDepositSuccess,
23885
24517
  onDepositError
23886
24518
  }) {
23887
- const { colors: colors2, fonts, components } = useTheme();
24519
+ const { colors: colors2, fonts, components, isDark } = useTheme();
23888
24520
  const [step, setStepInternal] = useState292(controlledStep ?? "amount");
23889
24521
  const setStep = useCallback42(
23890
24522
  (s) => {
@@ -23893,6 +24525,8 @@ function PayWithStripeLink({
23893
24525
  },
23894
24526
  [onStepChange]
23895
24527
  );
24528
+ const stepRef = useRef82(step);
24529
+ stepRef.current = step;
23896
24530
  useEffect242(() => {
23897
24531
  if (controlledStep && controlledStep !== step) {
23898
24532
  setStepInternal(controlledStep);
@@ -23960,7 +24594,7 @@ function PayWithStripeLink({
23960
24594
  isLoading: sdkLoading,
23961
24595
  error: sdkError,
23962
24596
  initialize
23963
- } = useStripeOnramp(resolvedStripeKey);
24597
+ } = useStripeOnramp(resolvedStripeKey, isDark);
23964
24598
  useEffect242(() => {
23965
24599
  if (step === "auth") {
23966
24600
  console.log("[PayWithStripeLink] Auth step state:", {
@@ -23983,6 +24617,9 @@ function PayWithStripeLink({
23983
24617
  const [errorReturnStep, setErrorReturnStep] = useState292("email");
23984
24618
  const [authIntentId, setAuthIntentId] = useState292(null);
23985
24619
  const sdkAuthenticatedRef = useRef82(false);
24620
+ const everAuthenticatedRef = useRef82(false);
24621
+ const reauthReturnStepRef = useRef82(null);
24622
+ const attemptedWalletReauthRef = useRef82(false);
23986
24623
  const pendingAddPaymentRef = useRef82(false);
23987
24624
  const autoOpenedAddPaymentRef = useRef82(false);
23988
24625
  const checkoutGenRef = useRef82(0);
@@ -24181,6 +24818,7 @@ function PayWithStripeLink({
24181
24818
  if (step === "auth" && (pendingAddPaymentRef.current || !!accessTokenRef.current)) {
24182
24819
  pendingAddPaymentRef.current = false;
24183
24820
  autoOpenedAddPaymentRef.current = false;
24821
+ reauthReturnStepRef.current = null;
24184
24822
  setError(null);
24185
24823
  setStep("amount");
24186
24824
  return true;
@@ -24198,17 +24836,34 @@ function PayWithStripeLink({
24198
24836
  );
24199
24837
  const [selectedPaymentToken, setSelectedPaymentToken] = useState292(null);
24200
24838
  const [paymentDisplay, setPaymentDisplay] = useState292(null);
24839
+ const selectedTokenSingleUseRef = useRef82(false);
24201
24840
  const selectPaymentToken = useCallback42(
24202
24841
  (token) => {
24842
+ selectedTokenSingleUseRef.current = !!token.singleUse;
24203
24843
  setSelectedPaymentToken(token.id);
24204
24844
  setPaymentDisplay(
24205
24845
  token.label ? { label: token.label, sublabel: token.sublabel, brand: token.brand } : null
24206
24846
  );
24207
24847
  setStoredSelectedTokenState(token);
24208
- if (customerId) setStoredSelectedToken(customerId, token);
24848
+ if (customerId && !token.singleUse) setStoredSelectedToken(customerId, token);
24209
24849
  },
24210
24850
  [customerId]
24211
24851
  );
24852
+ const clearSelectedPaymentToken = useCallback42(() => {
24853
+ selectedTokenSingleUseRef.current = false;
24854
+ const persisted = customerId ? getStoredSelectedToken(customerId) : null;
24855
+ if (persisted && !persisted.singleUse) {
24856
+ setSelectedPaymentToken(persisted.id);
24857
+ setPaymentDisplay(
24858
+ persisted.label ? { label: persisted.label, sublabel: persisted.sublabel, brand: persisted.brand } : null
24859
+ );
24860
+ setStoredSelectedTokenState(persisted);
24861
+ } else {
24862
+ setSelectedPaymentToken(null);
24863
+ setPaymentDisplay(null);
24864
+ setStoredSelectedTokenState(null);
24865
+ }
24866
+ }, [customerId]);
24212
24867
  const displayPaymentTokens = (() => {
24213
24868
  const apiDisplay = paymentTokens.map((t13) => ({ id: t13.id, ...formatPaymentToken(t13) }));
24214
24869
  if (storedSelectedToken && !paymentTokens.some((t13) => t13.id === storedSelectedToken.id)) {
@@ -24447,6 +25102,10 @@ function PayWithStripeLink({
24447
25102
  if (!resolvedStripeKey) return;
24448
25103
  initialize();
24449
25104
  }, [coordinator, sdkLoading, sdkError, resolvedStripeKey, initialize]);
25105
+ useEffect242(() => {
25106
+ sdkAuthenticatedRef.current = false;
25107
+ attemptedWalletReauthRef.current = false;
25108
+ }, [coordinator]);
24450
25109
  const handleAuthInterrupted = useCallback42(
24451
25110
  (message) => {
24452
25111
  const alreadySignedIn = !!accessTokenRef.current;
@@ -24460,6 +25119,26 @@ function PayWithStripeLink({
24460
25119
  },
24461
25120
  [emailProp, setStep]
24462
25121
  );
25122
+ const reauthenticateSdk = useCallback42(async () => {
25123
+ const current = stepRef.current;
25124
+ const preAuthSteps = ["email", "register", "auth"];
25125
+ reauthReturnStepRef.current = preAuthSteps.includes(current) ? null : current;
25126
+ const knownEmail = (email || emailProp || "").trim();
25127
+ if (!knownEmail) {
25128
+ reauthReturnStepRef.current = null;
25129
+ setStep("email");
25130
+ return;
25131
+ }
25132
+ try {
25133
+ const authResult = await stripeCreateAuthIntent(knownEmail, publishableKey);
25134
+ setAuthIntentId(authResult.auth_intent_id);
25135
+ setStep("auth");
25136
+ } catch (err) {
25137
+ reauthReturnStepRef.current = null;
25138
+ setError(err instanceof Error ? err.message : "Failed to re-verify. Please try again.");
25139
+ setStep("email");
25140
+ }
25141
+ }, [email, emailProp, publishableKey]);
24463
25142
  useEffect242(() => {
24464
25143
  if (step !== "auth" || !coordinator || !authIntentId) return;
24465
25144
  let mounted = true;
@@ -24470,6 +25149,7 @@ function PayWithStripeLink({
24470
25149
  if (result.result === "success" && result.crypto_customer_id) {
24471
25150
  setCustomerId(result.crypto_customer_id);
24472
25151
  sdkAuthenticatedRef.current = true;
25152
+ everAuthenticatedRef.current = true;
24473
25153
  const tokens = await stripeExchangeTokens(authIntentId, publishableKey);
24474
25154
  setOauthToken(tokens.access_token);
24475
25155
  const refresh = extractRefreshToken(tokens);
@@ -24483,16 +25163,22 @@ function PayWithStripeLink({
24483
25163
  tokens.expires_in ?? 3600,
24484
25164
  email.trim()
24485
25165
  );
25166
+ const resumeStep = reauthReturnStepRef.current;
25167
+ reauthReturnStepRef.current = null;
24486
25168
  if (pendingAddPaymentRef.current) {
24487
25169
  setStep("payment");
25170
+ } else if (resumeStep) {
25171
+ setStep(resumeStep);
24488
25172
  } else {
24489
25173
  setStep("kyc");
24490
25174
  }
24491
25175
  } else if (result.result === "abandoned") {
24492
25176
  pendingAddPaymentRef.current = false;
25177
+ reauthReturnStepRef.current = null;
24493
25178
  handleAuthInterrupted("Verification was cancelled.");
24494
25179
  } else if (result.result === "declined") {
24495
25180
  pendingAddPaymentRef.current = false;
25181
+ reauthReturnStepRef.current = null;
24496
25182
  handleAuthInterrupted("You must consent to continue.");
24497
25183
  }
24498
25184
  });
@@ -24504,6 +25190,7 @@ function PayWithStripeLink({
24504
25190
  }
24505
25191
  } catch (err) {
24506
25192
  if (!mounted) return;
25193
+ reauthReturnStepRef.current = null;
24507
25194
  const msg = err instanceof Error ? err.message : "Verification was cancelled.";
24508
25195
  handleAuthInterrupted(msg);
24509
25196
  }
@@ -24605,6 +25292,10 @@ function PayWithStripeLink({
24605
25292
  };
24606
25293
  const handleKycSubmit = async () => {
24607
25294
  if (!coordinator) return;
25295
+ if (!sdkAuthenticatedRef.current) {
25296
+ await reauthenticateSdk();
25297
+ return;
25298
+ }
24608
25299
  let kycInfo;
24609
25300
  if (kycFormMode === "address") {
24610
25301
  if (!kycForm.givenName.trim() || !kycForm.surname.trim()) {
@@ -24664,6 +25355,10 @@ function PayWithStripeLink({
24664
25355
  let mounted = true;
24665
25356
  const verify = async () => {
24666
25357
  try {
25358
+ if (!sdkAuthenticatedRef.current) {
25359
+ await reauthenticateSdk();
25360
+ return;
25361
+ }
24667
25362
  const result = await coordinator.verifyDocuments();
24668
25363
  if (!mounted) return;
24669
25364
  const outcome = typeof result === "string" ? result : result?.result;
@@ -24683,7 +25378,7 @@ function PayWithStripeLink({
24683
25378
  return () => {
24684
25379
  mounted = false;
24685
25380
  };
24686
- }, [step, coordinator]);
25381
+ }, [step, coordinator, reauthenticateSdk]);
24687
25382
  useEffect242(() => {
24688
25383
  if (step !== "wallet" || !oauthToken || !customerId) return;
24689
25384
  let mounted = true;
@@ -24709,6 +25404,10 @@ function PayWithStripeLink({
24709
25404
  if (!coordinator) {
24710
25405
  return;
24711
25406
  }
25407
+ if (!sdkAuthenticatedRef.current) {
25408
+ await reauthenticateSdk();
25409
+ return;
25410
+ }
24712
25411
  await coordinator.registerWalletAddress(onrampWalletAddress, network);
24713
25412
  }
24714
25413
  if (mounted) setRegisteredWalletKey(walletKeyFor(onrampWalletAddress));
@@ -24725,7 +25424,15 @@ function PayWithStripeLink({
24725
25424
  return () => {
24726
25425
  mounted = false;
24727
25426
  };
24728
- }, [step, oauthToken, customerId, publishableKey, onrampWalletAddress, coordinator]);
25427
+ }, [
25428
+ step,
25429
+ oauthToken,
25430
+ customerId,
25431
+ publishableKey,
25432
+ onrampWalletAddress,
25433
+ coordinator,
25434
+ reauthenticateSdk
25435
+ ]);
24729
25436
  useEffect242(() => {
24730
25437
  if (step !== "amount" && step !== "review") return;
24731
25438
  if (!customerId || !oauthToken || !onrampWalletAddress) return;
@@ -24754,6 +25461,10 @@ function PayWithStripeLink({
24754
25461
  if (coordinator && sdkAuthenticatedRef.current) {
24755
25462
  await coordinator.registerWalletAddress(onrampWalletAddress, network);
24756
25463
  if (!cancelled) setRegisteredWalletKey(walletKeyFor(onrampWalletAddress));
25464
+ } else if (coordinator && everAuthenticatedRef.current && !attemptedWalletReauthRef.current) {
25465
+ attemptedWalletReauthRef.current = true;
25466
+ await reauthenticateSdk();
25467
+ return;
24757
25468
  }
24758
25469
  } catch {
24759
25470
  }
@@ -24771,7 +25482,8 @@ function PayWithStripeLink({
24771
25482
  coordinator,
24772
25483
  stripeDestNetwork,
24773
25484
  stripeDestChainType,
24774
- publishableKey
25485
+ publishableKey,
25486
+ reauthenticateSdk
24775
25487
  ]);
24776
25488
  useEffect242(() => {
24777
25489
  if (step !== "payment" || !oauthToken || !customerId) return;
@@ -24868,6 +25580,32 @@ function PayWithStripeLink({
24868
25580
  if (!mounted) return;
24869
25581
  const tokenId = result.cryptoPaymentToken;
24870
25582
  let display = null;
25583
+ const pmd = result.paymentMethodDetails;
25584
+ if (pmd) {
25585
+ display = formatPaymentToken(pmd);
25586
+ }
25587
+ const singleUse = pmd?.type === "card" && !!pmd.card.wallet;
25588
+ const applyAndAdvance = () => {
25589
+ selectPaymentToken({
25590
+ id: tokenId,
25591
+ label: display?.label ?? "",
25592
+ sublabel: display?.sublabel ?? "",
25593
+ brand: display?.brand,
25594
+ singleUse
25595
+ });
25596
+ autoOpenedAddPaymentRef.current = false;
25597
+ setStep("amount");
25598
+ };
25599
+ if (display) {
25600
+ applyAndAdvance();
25601
+ if (customerId && accessTokenRef.current) {
25602
+ stripeListPaymentTokens(customerId, accessTokenRef.current, publishableKey).then((tokens) => {
25603
+ if (mounted) setPaymentTokens(tokens.data);
25604
+ }).catch(() => {
25605
+ });
25606
+ }
25607
+ return;
25608
+ }
24871
25609
  try {
24872
25610
  if (customerId && accessTokenRef.current) {
24873
25611
  const tokens = await stripeListPaymentTokens(
@@ -24875,28 +25613,15 @@ function PayWithStripeLink({
24875
25613
  accessTokenRef.current,
24876
25614
  publishableKey
24877
25615
  );
25616
+ if (!mounted) return;
25617
+ setPaymentTokens(tokens.data);
24878
25618
  const match = tokens.data.find((t13) => t13.id === tokenId);
24879
- if (match) {
24880
- display = formatPaymentToken(match);
24881
- setPaymentTokens(tokens.data);
24882
- }
25619
+ if (match) display = formatPaymentToken(match);
24883
25620
  }
24884
25621
  } catch {
24885
25622
  }
24886
- if (!display) {
24887
- const dd = result.displayData;
24888
- if (dd?.label) {
24889
- display = { label: dd.label, sublabel: dd.sublabel ?? "", brand: dd.type };
24890
- }
24891
- }
24892
- selectPaymentToken({
24893
- id: tokenId,
24894
- label: display?.label ?? "",
24895
- sublabel: display?.sublabel ?? "",
24896
- brand: display?.brand
24897
- });
24898
- autoOpenedAddPaymentRef.current = false;
24899
- setStep("amount");
25623
+ if (!mounted) return;
25624
+ applyAndAdvance();
24900
25625
  }
24901
25626
  );
24902
25627
  if (mounted && el) {
@@ -24933,6 +25658,10 @@ function PayWithStripeLink({
24933
25658
  const handleCheckout = async () => {
24934
25659
  if (!amount || !oauthToken || !customerId || !selectedPaymentToken || !onrampWalletAddress || !isOnrampWalletRegistered || !coordinator)
24935
25660
  return;
25661
+ if (!sdkAuthenticatedRef.current) {
25662
+ await reauthenticateSdk();
25663
+ return;
25664
+ }
24936
25665
  const gen = ++checkoutGenRef.current;
24937
25666
  const isStale = () => checkoutGenRef.current !== gen;
24938
25667
  const surfaceStepUpOnAmount = (err, message, tier) => {
@@ -25044,6 +25773,7 @@ function PayWithStripeLink({
25044
25773
  executionReconciledSessionRef.current = null;
25045
25774
  setConfirmedSessionId(activeSessionId);
25046
25775
  setSessionStatus(STRIPE_SESSION_STATUS.FULFILLMENT_PROCESSING);
25776
+ if (selectedTokenSingleUseRef.current) clearSelectedPaymentToken();
25047
25777
  setStep("success");
25048
25778
  } else if (lastError === "quote_rate_drifted" || lastError === "charged_with_expired_quote") {
25049
25779
  setSessionForCheckout(null);
@@ -25095,6 +25825,25 @@ function PayWithStripeLink({
25095
25825
  sessionForCheckoutRef.current = s;
25096
25826
  setSessionForCheckoutState(s);
25097
25827
  };
25828
+ const loadAndSelectPreferredToken = useCallback42(async () => {
25829
+ if (!customerId) return;
25830
+ try {
25831
+ const existing = await withTokenRefresh(
25832
+ (tok) => stripeListPaymentTokens(customerId, tok, publishableKey)
25833
+ );
25834
+ setPaymentTokens(existing.data);
25835
+ const stored = getStoredSelectedToken(customerId);
25836
+ const merged = [
25837
+ ...existing.data.map((t13) => ({ id: t13.id, ...formatPaymentToken(t13) })),
25838
+ ...stored && !existing.data.some((t13) => t13.id === stored.id) ? [stored] : []
25839
+ ];
25840
+ if (merged.length > 0) {
25841
+ const preferred = stored && merged.find((m) => m.id === stored.id) || merged[0];
25842
+ selectPaymentToken(preferred);
25843
+ }
25844
+ } catch {
25845
+ }
25846
+ }, [customerId, publishableKey, selectPaymentToken, withTokenRefresh]);
25098
25847
  const handleDone = () => {
25099
25848
  handledTerminalSessionRef.current = null;
25100
25849
  executionReconciledSessionRef.current = null;
@@ -25102,6 +25851,7 @@ function PayWithStripeLink({
25102
25851
  setSessionStatus(null);
25103
25852
  setSessionForCheckout(null);
25104
25853
  setStep("amount");
25854
+ if (!selectedPaymentToken) void loadAndSelectPreferredToken();
25105
25855
  };
25106
25856
  const [quoteError, setQuoteError] = useState292(null);
25107
25857
  const [quoteNonce, setQuoteNonce] = useState292(0);
@@ -25941,7 +26691,8 @@ function PayWithStripeLink({
25941
26691
  {
25942
26692
  brand: display.brand,
25943
26693
  size: 28,
25944
- fallbackColor: colors2.foregroundMuted
26694
+ fallbackColor: colors2.foregroundMuted,
26695
+ monochromeColor: monochromeOn(components.card.backgroundColor)
25945
26696
  }
25946
26697
  )
25947
26698
  }
@@ -26526,7 +27277,8 @@ function PayWithStripeLink({
26526
27277
  {
26527
27278
  brand: paymentDisplay.brand,
26528
27279
  size: 20,
26529
- fallbackColor: colors2.foregroundMuted
27280
+ fallbackColor: colors2.foregroundMuted,
27281
+ monochromeColor: monochromeOn(components.card.backgroundColor)
26530
27282
  }
26531
27283
  ),
26532
27284
  /* @__PURE__ */ jsx44(
@@ -27010,6 +27762,20 @@ function PayWithStripeLink({
27010
27762
  }
27011
27763
  return null;
27012
27764
  }
27765
+ function useProjectConfig({
27766
+ publishableKey,
27767
+ enabled = true
27768
+ }) {
27769
+ const { data: projectConfig, isLoading } = useQuery8({
27770
+ queryKey: ["unifold", "projectConfig", publishableKey],
27771
+ queryFn: () => getProjectConfig(publishableKey),
27772
+ enabled,
27773
+ staleTime: 1e3 * 60 * 30,
27774
+ refetchOnMount: true,
27775
+ refetchOnWindowFocus: true
27776
+ });
27777
+ return { projectConfig, isLoading };
27778
+ }
27013
27779
  function useSupportedDepositTokens(publishableKey, options) {
27014
27780
  const hasDestination = options?.destination_token_address && options?.destination_chain_id && options?.destination_chain_type;
27015
27781
  const filteredOptions = {
@@ -27021,7 +27787,7 @@ function useSupportedDepositTokens(publishableKey, options) {
27021
27787
  ...options?.product_type ? { product_type: options.product_type } : {}
27022
27788
  };
27023
27789
  const hasFilteredOptions = Object.keys(filteredOptions).length > 0;
27024
- return useQuery8({
27790
+ return useQuery9({
27025
27791
  queryKey: [
27026
27792
  "unifold",
27027
27793
  "supportedDepositTokens",
@@ -27045,7 +27811,7 @@ function useIntegrationTransferDefaultToken({
27045
27811
  publishableKey,
27046
27812
  enabled = true
27047
27813
  }) {
27048
- return useQuery9({
27814
+ return useQuery10({
27049
27815
  queryKey: [
27050
27816
  "unifold",
27051
27817
  "integrationTransferDefaultToken",
@@ -27121,12 +27887,12 @@ function CoinbaseConnect({
27121
27887
  destination_chain_id: destinationChainId,
27122
27888
  destination_chain_type: destinationChainType
27123
27889
  });
27124
- const supportedSymbols = useMemo62(() => {
27890
+ const supportedSymbols = useMemo72(() => {
27125
27891
  const set = /* @__PURE__ */ new Set();
27126
27892
  supportedTokensData?.data.forEach((token) => set.add(token.symbol.toLowerCase()));
27127
27893
  return set;
27128
27894
  }, [supportedTokensData]);
27129
- const stablecoinSymbols = useMemo62(() => {
27895
+ const stablecoinSymbols = useMemo72(() => {
27130
27896
  const set = /* @__PURE__ */ new Set();
27131
27897
  supportedTokensData?.data.forEach((token) => {
27132
27898
  if (token.is_stablecoin) set.add(token.symbol.toLowerCase());
@@ -27159,12 +27925,12 @@ function CoinbaseConnect({
27159
27925
  const [transferDepositWalletId, setTransferDepositWalletId] = useState30(
27160
27926
  void 0
27161
27927
  );
27162
- const exchangeSupportedCurrencies = useMemo62(() => {
27928
+ const exchangeSupportedCurrencies = useMemo72(() => {
27163
27929
  const set = /* @__PURE__ */ new Set();
27164
27930
  selectedExchange?.supported_currencies.forEach((c) => set.add(c.toLowerCase()));
27165
27931
  return set;
27166
27932
  }, [selectedExchange]);
27167
- const defaultTokenParams = useMemo62(
27933
+ const defaultTokenParams = useMemo72(
27168
27934
  () => selectedAsset ? {
27169
27935
  integration_provider: IntegrationProvider.COINBASE,
27170
27936
  source_currency: selectedAsset.currency.toLowerCase(),
@@ -27187,7 +27953,7 @@ function CoinbaseConnect({
27187
27953
  params: defaultTokenParams,
27188
27954
  publishableKey
27189
27955
  });
27190
- const defaultSourceCurrency = useMemo62(
27956
+ const defaultSourceCurrency = useMemo72(
27191
27957
  () => resolveDefaultSourceSymbol(supportedTokensData?.data, {
27192
27958
  defaultSourceChainType,
27193
27959
  defaultSourceChainId,
@@ -27202,7 +27968,7 @@ function CoinbaseConnect({
27202
27968
  defaultSourceSymbol
27203
27969
  ]
27204
27970
  );
27205
- const sortedHoldings = useMemo62(() => {
27971
+ const sortedHoldings = useMemo72(() => {
27206
27972
  const supported = [];
27207
27973
  const unsupported = [];
27208
27974
  holdings.forEach((account) => {
@@ -27222,7 +27988,7 @@ function CoinbaseConnect({
27222
27988
  }
27223
27989
  return [...supported, ...unsupported];
27224
27990
  }, [holdings, supportedSymbols, exchangeSupportedCurrencies, defaultSourceCurrency]);
27225
- const selectedHoldingIsSupported = useMemo62(() => {
27991
+ const selectedHoldingIsSupported = useMemo72(() => {
27226
27992
  if (!selectedHolding) return false;
27227
27993
  const currencyLower = selectedHolding.currency.toLowerCase();
27228
27994
  return (supportedSymbols.size === 0 || supportedSymbols.has(currencyLower)) && (exchangeSupportedCurrencies.size === 0 || exchangeSupportedCurrencies.has(currencyLower));
@@ -27369,7 +28135,7 @@ function CoinbaseConnect({
27369
28135
  if (pollRef.current) clearInterval(pollRef.current);
27370
28136
  };
27371
28137
  }, []);
27372
- const minDepositUsd = useMemo62(() => {
28138
+ const minDepositUsd = useMemo72(() => {
27373
28139
  if (!selectedAsset || !defaultTokenData) return 0;
27374
28140
  const supportedToken = supportedTokensData?.data.find(
27375
28141
  (t14) => t14.symbol.toLowerCase() === selectedAsset.currency.toLowerCase()
@@ -27380,7 +28146,7 @@ function CoinbaseConnect({
27380
28146
  );
27381
28147
  return matchingChain?.minimum_deposit_amount_usd ?? Math.min(...supportedToken.chains.map((c) => c.minimum_deposit_amount_usd));
27382
28148
  }, [selectedAsset, supportedTokensData, defaultTokenData]);
27383
- const estimatedProcessingTime = useMemo62(() => {
28149
+ const estimatedProcessingTime = useMemo72(() => {
27384
28150
  if (!selectedAsset || !defaultTokenData) return null;
27385
28151
  const supportedToken = supportedTokensData?.data.find(
27386
28152
  (t14) => t14.symbol.toLowerCase() === selectedAsset.currency.toLowerCase()
@@ -29022,7 +29788,7 @@ function useExchanges({
29022
29788
  publishableKey,
29023
29789
  enabled = true
29024
29790
  }) {
29025
- const { data: exchanges = [], isLoading } = useQuery10({
29791
+ const { data: exchanges = [], isLoading } = useQuery11({
29026
29792
  queryKey: ["unifold", "exchanges", publishableKey],
29027
29793
  queryFn: () => getExchanges(void 0, publishableKey).then((res) => res.data),
29028
29794
  enabled,
@@ -29036,7 +29802,7 @@ function useApplePayProviders({
29036
29802
  publishableKey,
29037
29803
  enabled = true
29038
29804
  }) {
29039
- const { data: providers, isLoading } = useQuery11({
29805
+ const { data: providers, isLoading } = useQuery12({
29040
29806
  queryKey: ["unifold", "applePayProviders", publishableKey],
29041
29807
  queryFn: () => getApplePayProviders(publishableKey),
29042
29808
  enabled,
@@ -29051,7 +29817,7 @@ function useAllowedCountry(publishableKey) {
29051
29817
  data: ipData,
29052
29818
  isLoading: isIpLoading,
29053
29819
  error: ipError
29054
- } = useQuery12({
29820
+ } = useQuery13({
29055
29821
  queryKey: ["unifold", "ipAddress"],
29056
29822
  queryFn: () => getIpAddress(),
29057
29823
  refetchOnMount: false,
@@ -29066,7 +29832,7 @@ function useAllowedCountry(publishableKey) {
29066
29832
  data: configData,
29067
29833
  isLoading: isConfigLoading,
29068
29834
  error: configError
29069
- } = useQuery12({
29835
+ } = useQuery13({
29070
29836
  queryKey: ["unifold", "projectConfig", publishableKey],
29071
29837
  queryFn: () => getProjectConfig(publishableKey),
29072
29838
  refetchOnMount: false,
@@ -29113,7 +29879,7 @@ function useAddressValidation({
29113
29879
  refetchOnMount = false
29114
29880
  }) {
29115
29881
  const shouldValidate = enabled && !!recipientAddress && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress;
29116
- const { data, isLoading, error } = useQuery13({
29882
+ const { data, isLoading, error } = useQuery14({
29117
29883
  queryKey: [
29118
29884
  "unifold",
29119
29885
  "addressValidation",
@@ -29550,7 +30316,7 @@ function TokenSelectorSheet({
29550
30316
  useEffect282(() => {
29551
30317
  setRecentTokens(getRecentTokens());
29552
30318
  }, []);
29553
- const allOptions = useMemo82(() => {
30319
+ const allOptions = useMemo92(() => {
29554
30320
  const options = [];
29555
30321
  tokens.forEach((token) => {
29556
30322
  token.chains.forEach((chain) => {
@@ -29559,7 +30325,7 @@ function TokenSelectorSheet({
29559
30325
  });
29560
30326
  return options;
29561
30327
  }, [tokens]);
29562
- const quickSelectOptions = useMemo82(() => {
30328
+ const quickSelectOptions = useMemo92(() => {
29563
30329
  const result = [];
29564
30330
  const seen = /* @__PURE__ */ new Set();
29565
30331
  const addOption = (symbol, chainType, chainId, isRecent) => {
@@ -29591,7 +30357,7 @@ function TokenSelectorSheet({
29591
30357
  });
29592
30358
  setRecentTokens(updated);
29593
30359
  };
29594
- const fuse = useMemo82(
30360
+ const fuse = useMemo92(
29595
30361
  () => new Fuse(allOptions, {
29596
30362
  keys: [
29597
30363
  { name: "token.symbol", weight: 2 },
@@ -29604,7 +30370,7 @@ function TokenSelectorSheet({
29604
30370
  }),
29605
30371
  [allOptions]
29606
30372
  );
29607
- const filteredOptions = useMemo82(() => {
30373
+ const filteredOptions = useMemo92(() => {
29608
30374
  if (!searchQuery.trim()) return allOptions;
29609
30375
  const query = searchQuery.trim();
29610
30376
  const results = fuse.search(query);
@@ -30415,7 +31181,7 @@ function useHypercoreActivation(params) {
30415
31181
  const recipient = recipientAddress?.trim() ?? "";
30416
31182
  const source = sourceAddress?.trim() ?? "";
30417
31183
  const hasAddresses = !!recipient && !!source;
30418
- const { data, isLoading } = useQuery14({
31184
+ const { data, isLoading } = useQuery15({
30419
31185
  queryKey: ["unifold", "hypercoreActivation", source, recipient, publishableKey],
30420
31186
  queryFn: () => checkHypercoreActivation(
30421
31187
  {
@@ -30541,7 +31307,7 @@ function TransferCryptoSingleInput({
30541
31307
  const wallets = externalWallets?.length ? externalWallets : depositAddressResponse?.data ?? [];
30542
31308
  const loading = externalWallets?.length ? false : walletsLoading;
30543
31309
  const error = walletsError?.message ?? null;
30544
- const allAvailableChains = useMemo92(() => {
31310
+ const allAvailableChains = useMemo102(() => {
30545
31311
  const chainsMap = /* @__PURE__ */ new Map();
30546
31312
  supportedTokens.forEach((t13) => {
30547
31313
  t13.chains.forEach((c) => {
@@ -31317,7 +32083,7 @@ function TransferCryptoDoubleInput({
31317
32083
  const wallets = externalWallets?.length ? externalWallets : depositAddressResponse?.data ?? [];
31318
32084
  const loading = externalWallets?.length ? false : walletsLoading;
31319
32085
  const error = walletsError?.message ?? null;
31320
- const allAvailableChains = useMemo102(() => {
32086
+ const allAvailableChains = useMemo112(() => {
31321
32087
  const chainsMap = /* @__PURE__ */ new Map();
31322
32088
  supportedTokens.forEach((t13) => {
31323
32089
  t13.chains.forEach((c) => {
@@ -31913,7 +32679,7 @@ function useDepositQuote(params) {
31913
32679
  ...adjustForSlippage ? { adjust_for_slippage: true } : {},
31914
32680
  ...stablecoinParity ? { stablecoin_parity: true } : {}
31915
32681
  };
31916
- return useQuery15({
32682
+ return useQuery16({
31917
32683
  queryKey: [
31918
32684
  "unifold",
31919
32685
  "depositQuote",
@@ -31943,7 +32709,7 @@ function useExternalWallets({
31943
32709
  publishableKey,
31944
32710
  enabled = true
31945
32711
  }) {
31946
- const { data: wallets = [], isLoading } = useQuery16({
32712
+ const { data: wallets = [], isLoading } = useQuery17({
31947
32713
  queryKey: ["unifold", "external-wallets", publishableKey],
31948
32714
  queryFn: () => getExternalWallets(publishableKey).then((res) => res.data),
31949
32715
  enabled: enabled && !!publishableKey,
@@ -34547,10 +35313,9 @@ function DepositModal({
34547
35313
  applePaySubTitle = "Instant",
34548
35314
  enableBankTransfer,
34549
35315
  enableStripeLink = false,
34550
- email: emailProp,
35316
+ userEmail,
34551
35317
  hideDepositFlowInfo = false,
34552
35318
  hideDisplayDescription = false,
34553
- externalUserEmail,
34554
35319
  onDepositSuccess,
34555
35320
  onDepositError,
34556
35321
  onEvent,
@@ -34582,7 +35347,7 @@ function DepositModal({
34582
35347
  (method) => onDepositError ? (error) => onDepositError({ ...error, method }) : void 0,
34583
35348
  [onDepositError]
34584
35349
  );
34585
- const effectiveInitialScreen = useMemo12(() => {
35350
+ const effectiveInitialScreen = useMemo13(() => {
34586
35351
  const s = initialScreen ?? "main";
34587
35352
  if (s === "tracker" && hideDepositTracker === true) return "main";
34588
35353
  if (s === "cashapp" && enableCashApp === false) return "main";
@@ -34981,11 +35746,18 @@ function DepositModal({
34981
35746
  return "Success";
34982
35747
  case "guest_limit_reached":
34983
35748
  return "Limit reached";
35749
+ case "limit_upgrade_intro":
35750
+ case "limit_upgrade_failed":
35751
+ return "Limit reached";
35752
+ case "limit_upgrade_form":
35753
+ return "Verify identity";
35754
+ case "limit_upgrade_submitting":
35755
+ return "Verifying";
34984
35756
  default:
34985
35757
  return "Pay with Apple Pay";
34986
35758
  }
34987
35759
  })();
34988
- const applePayShowBack = sessionOpenedFromMenu || applePayView === "phone_input" || applePayView === "email_otp" || applePayView === "phone_otp" || applePayView === "submitting_session" || applePayView === "checking_deposit";
35760
+ const applePayShowBack = sessionOpenedFromMenu || applePayView === "phone_input" || applePayView === "email_otp" || applePayView === "phone_otp" || applePayView === "submitting_session" || applePayView === "checking_deposit" || applePayView === "limit_upgrade_intro" || applePayView === "limit_upgrade_form" || applePayView === "limit_upgrade_failed";
34989
35761
  const handleBack = () => {
34990
35762
  if (view === "card" && cardView === "quotes") {
34991
35763
  setCardView("amount");
@@ -35664,7 +36436,7 @@ function DepositModal({
35664
36436
  destinationChainId,
35665
36437
  destinationTokenAddress,
35666
36438
  wallets,
35667
- email: emailProp,
36439
+ email: userEmail,
35668
36440
  iconUrl: projectConfig?.asset_cdn_url ? `${projectConfig.asset_cdn_url}/api/public/icons/onramps/svg/link.svg` : void 0,
35669
36441
  step: stripeLinkStep,
35670
36442
  onStepChange: setStripeLinkStep,
@@ -35727,13 +36499,10 @@ function DepositModal({
35727
36499
  ref: applePayHandleRef,
35728
36500
  userId,
35729
36501
  publishableKey,
35730
- recipientAddress,
35731
36502
  destinationChainType,
35732
36503
  destinationChainId,
35733
36504
  destinationTokenAddress,
35734
- destinationTokenSymbol,
35735
- externalUserEmail,
35736
- enableApplePay,
36505
+ userEmail,
35737
36506
  wallets,
35738
36507
  onViewChange: setApplePayView,
35739
36508
  onEvent,
@@ -35761,7 +36530,7 @@ function DepositModal({
35761
36530
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["succeeded", "expired", "refunded", "canceled"]);
35762
36531
  function usePaymentIntent(params) {
35763
36532
  const { clientSecret, publishableKey, enabled = true, pollingInterval = 3e3 } = params;
35764
- return useQuery17({
36533
+ return useQuery18({
35765
36534
  queryKey: ["unifold", "paymentIntent", clientSecret, publishableKey],
35766
36535
  queryFn: () => retrievePaymentIntent(clientSecret, publishableKey),
35767
36536
  enabled: enabled && !!clientSecret && !!publishableKey,
@@ -35930,11 +36699,11 @@ function CheckoutModal({
35930
36699
  );
35931
36700
  }
35932
36701
  }, [emitCheckoutSuccess, paymentIntent, view]);
35933
- const wallets = useMemo132(() => {
36702
+ const wallets = useMemo142(() => {
35934
36703
  if (!paymentIntent) return [];
35935
36704
  return mapDepositAddressesToWallets(paymentIntent.deposit_addresses, paymentIntent);
35936
36705
  }, [paymentIntent]);
35937
- const formatCryptoAmount = useMemo132(() => {
36706
+ const formatCryptoAmount = useMemo142(() => {
35938
36707
  if (!paymentIntent) return (_) => "";
35939
36708
  const decimals = paymentIntent.destination_token_decimals ?? 6;
35940
36709
  const symbol = paymentIntent.currency.toUpperCase();
@@ -35944,7 +36713,7 @@ function CheckoutModal({
35944
36713
  return `${formatted} ${symbol}`;
35945
36714
  };
35946
36715
  }, [paymentIntent]);
35947
- const remainingAmountUsd = useMemo132(() => {
36716
+ const remainingAmountUsd = useMemo142(() => {
35948
36717
  if (!paymentIntent) return void 0;
35949
36718
  const total = parseFloat(paymentIntent.destination_amount_usd || paymentIntent.amount_usd);
35950
36719
  const received = parseFloat(
@@ -35956,7 +36725,7 @@ function CheckoutModal({
35956
36725
  return remaining > 0 ? remaining.toFixed(2) : "0.00";
35957
36726
  }, [paymentIntent]);
35958
36727
  const [selectedSource, setSelectedSource] = useState41(null);
35959
- const remainingDestinationAmount = useMemo132(() => {
36728
+ const remainingDestinationAmount = useMemo142(() => {
35960
36729
  if (!paymentIntent) return "0";
35961
36730
  const remaining = BigInt(paymentIntent.destination_amount) - BigInt(paymentIntent.destination_amount_received);
35962
36731
  return remaining > 0n ? remaining.toString() : "0";
@@ -35978,7 +36747,7 @@ function CheckoutModal({
35978
36747
  stablecoinParity: paymentIntent?.stablecoin_parity ?? false,
35979
36748
  enabled: open && view === "transfer" && !!paymentIntent && !!selectedSource && remainingDestinationAmount !== "0"
35980
36749
  });
35981
- const effectiveCheckoutQuote = useMemo132(() => {
36750
+ const effectiveCheckoutQuote = useMemo142(() => {
35982
36751
  if (!sourceQuote || !selectedSource) return null;
35983
36752
  const baseQuote = {
35984
36753
  sourceAmount: sourceQuote.source_amount,
@@ -36441,7 +37210,7 @@ function CheckoutModal({
36441
37210
  ) }) });
36442
37211
  }
36443
37212
  function useSupportedDestinationTokens(publishableKey, enabled = true) {
36444
- return useQuery18({
37213
+ return useQuery19({
36445
37214
  queryKey: ["unifold", "supportedDestinationTokens", publishableKey],
36446
37215
  queryFn: () => getSupportedDestinationTokens(publishableKey),
36447
37216
  staleTime: 1e3 * 60 * 5,
@@ -36476,7 +37245,7 @@ function useSourceTokenValidation(params) {
36476
37245
  enabled = true
36477
37246
  } = params;
36478
37247
  const hasParams = !!sourceChainType && !!sourceChainId && !!sourceTokenAddress;
36479
- return useQuery19({
37248
+ return useQuery20({
36480
37249
  queryKey: [
36481
37250
  "unifold",
36482
37251
  "sourceTokenValidation",
@@ -36525,7 +37294,7 @@ function useSourceTokenValidation(params) {
36525
37294
  function useAddressBalance(params) {
36526
37295
  const { address, chainType, chainId, tokenAddress, publishableKey, enabled = true } = params;
36527
37296
  const hasParams = !!address && !!chainType && !!chainId && !!tokenAddress;
36528
- return useQuery20({
37297
+ return useQuery21({
36529
37298
  queryKey: [
36530
37299
  "unifold",
36531
37300
  "addressBalance",
@@ -36581,7 +37350,7 @@ function useAddressBalance(params) {
36581
37350
  }
36582
37351
  function useExecutions(userId, publishableKey, options) {
36583
37352
  const actionType = options?.actionType ?? ActionType.Deposit;
36584
- return useQuery21({
37353
+ return useQuery222({
36585
37354
  queryKey: ["unifold", "executions", actionType, userId, publishableKey],
36586
37355
  queryFn: () => queryExecutions(userId, publishableKey, actionType),
36587
37356
  enabled: (options?.enabled ?? true) && !!userId,
@@ -36907,7 +37676,7 @@ function useVerifyRecipientAddress(params) {
36907
37676
  } = params;
36908
37677
  const trimmedAddress = recipientAddress?.trim() || "";
36909
37678
  const hasAllParams = !!chainType && !!chainId && !!tokenAddress && trimmedAddress.length > 0;
36910
- return useQuery222({
37679
+ return useQuery23({
36911
37680
  queryKey: [
36912
37681
  "unifold",
36913
37682
  "verifyRecipientAddress",
@@ -36946,7 +37715,7 @@ function useGetDepositAddress(params) {
36946
37715
  enabled = true
36947
37716
  } = params;
36948
37717
  const canFire = !!userId && !!recipientAddress && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress;
36949
- return useQuery23({
37718
+ return useQuery24({
36950
37719
  queryKey: [
36951
37720
  "unifold",
36952
37721
  "getDepositAddress",
@@ -37013,7 +37782,7 @@ function useHypercoreWithdrawActivation(params) {
37013
37782
  actionType: ActionType.Withdraw,
37014
37783
  enabled: enabled && isHypercore(sourceChainId)
37015
37784
  });
37016
- const depositWalletAddress = useMemo142(() => {
37785
+ const depositWalletAddress = useMemo15(() => {
37017
37786
  const wallets = depositWalletLookup.data?.data ?? [];
37018
37787
  return wallets.find((w) => w.chain_type === sourceChainType)?.address;
37019
37788
  }, [depositWalletLookup.data, sourceChainType]);
@@ -37132,7 +37901,7 @@ function WithdrawForm({
37132
37901
  enabled: debouncedAddress.length > 5 && !!selectedChain
37133
37902
  });
37134
37903
  const isDebouncing = trimmedAddress !== debouncedAddress;
37135
- const addressError = useMemo15(() => {
37904
+ const addressError = useMemo16(() => {
37136
37905
  if (!trimmedAddress || trimmedAddress.length <= 5) return null;
37137
37906
  if (isDebouncing || isVerifyingAddress) return null;
37138
37907
  if (verifyError) return t10.invalidAddress;
@@ -37166,33 +37935,33 @@ function WithdrawForm({
37166
37935
  destinationTokenAddress: selectedChain?.token_address,
37167
37936
  enabled: isAddressValid
37168
37937
  });
37169
- const exchangeRate = useMemo15(() => {
37938
+ const exchangeRate = useMemo16(() => {
37170
37939
  if (!balanceData?.exchangeRate) return 0;
37171
37940
  return parseFloat(balanceData.exchangeRate);
37172
37941
  }, [balanceData]);
37173
- const balanceCrypto = useMemo15(() => {
37942
+ const balanceCrypto = useMemo16(() => {
37174
37943
  if (!balanceData?.balanceHuman) return 0;
37175
37944
  return parseFloat(balanceData.balanceHuman);
37176
37945
  }, [balanceData]);
37177
- const balanceUsdNum = useMemo15(() => {
37946
+ const balanceUsdNum = useMemo16(() => {
37178
37947
  if (!balanceData?.balanceUsd) return 0;
37179
37948
  return parseFloat(balanceData.balanceUsd);
37180
37949
  }, [balanceData]);
37181
37950
  const tokenSymbol = sourceTokenSymbol || balanceData?.symbol || "TOKEN";
37182
37951
  const sourceDecimals = balanceData?.decimals ?? 6;
37183
- const cryptoAmountFromInput = useMemo15(() => {
37952
+ const cryptoAmountFromInput = useMemo16(() => {
37184
37953
  const val = parseFloat(amount);
37185
37954
  if (!val || val <= 0) return 0;
37186
37955
  if (inputUnit === "crypto") return val;
37187
37956
  return exchangeRate > 0 ? val / exchangeRate : 0;
37188
37957
  }, [amount, inputUnit, exchangeRate]);
37189
- const fiatAmountFromInput = useMemo15(() => {
37958
+ const fiatAmountFromInput = useMemo16(() => {
37190
37959
  const val = parseFloat(amount);
37191
37960
  if (!val || val <= 0) return 0;
37192
37961
  if (inputUnit === "fiat") return val;
37193
37962
  return val * exchangeRate;
37194
37963
  }, [amount, inputUnit, exchangeRate]);
37195
- const convertedDisplay = useMemo15(() => {
37964
+ const convertedDisplay = useMemo16(() => {
37196
37965
  if (!amount || parseFloat(amount) <= 0) return null;
37197
37966
  if (inputUnit === "crypto") {
37198
37967
  return `$${fiatAmountFromInput.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
@@ -37211,7 +37980,7 @@ function WithdrawForm({
37211
37980
  isMaxed,
37212
37981
  isStablecoin
37213
37982
  ]);
37214
- const balanceDisplay = useMemo15(() => {
37983
+ const balanceDisplay = useMemo16(() => {
37215
37984
  if (isLoadingBalance || !balanceData) return null;
37216
37985
  if (inputUnit === "crypto") {
37217
37986
  const displayDecimals = isStablecoin ? 2 : 6;
@@ -38651,7 +39420,7 @@ function UnifoldProvider2({
38651
39420
  withdrawPromiseRef.current = null;
38652
39421
  }
38653
39422
  }, []);
38654
- const contextValue = useMemo17(
39423
+ const contextValue = useMemo18(
38655
39424
  () => ({
38656
39425
  beginDeposit,
38657
39426
  closeDeposit,
@@ -38746,7 +39515,7 @@ function UnifoldProvider2({
38746
39515
  defaultSourceChainId: depositConfig.defaultSourceChainId,
38747
39516
  defaultSourceTokenAddress: depositConfig.defaultSourceTokenAddress,
38748
39517
  defaultSourceSymbol: depositConfig.defaultSourceSymbol,
38749
- externalUserEmail: depositConfig.externalUserEmail,
39518
+ userEmail: depositConfig.user?.email,
38750
39519
  depositConfirmationMode: depositConfig.depositConfirmationMode ?? "auto_ui",
38751
39520
  hideDepositTracker: config?.hideDepositTracker,
38752
39521
  showBalanceHeader: config?.showBalanceHeader,
@@ -38759,7 +39528,6 @@ function UnifoldProvider2({
38759
39528
  enableConnectExchange: config?.enableConnectExchange,
38760
39529
  enableCashApp: config?.enableCashApp,
38761
39530
  enableStripeLink: config?.enableStripeLink,
38762
- email: depositConfig?.email,
38763
39531
  enableApplePay: config?.enableApplePay,
38764
39532
  applePayTitle: config?.applePayTitle,
38765
39533
  applePaySubTitle: config?.applePaySubTitle,