diff --git a/src/components/Payment/PaymentForm/index.tsx b/src/components/Payment/PaymentForm/index.tsx
index 3d6175b6f..e9ff34ba2 100644
--- a/src/components/Payment/PaymentForm/index.tsx
+++ b/src/components/Payment/PaymentForm/index.tsx
@@ -25,9 +25,8 @@ import { useAppDispatch, usePaymentStore } from '@/redux/hooks'
import { paymentActions } from '@/redux/slices/payment-slice'
import { walletActions } from '@/redux/slices/wallet-slice'
import { areEvmAddressesEqual, ErrorHandler, formatAmount, formatCurrency, getContributorsFromCharge } from '@/utils'
-import { initializeAppKit } from '@/config/wagmi.config'
import { useAppKit, useDisconnect } from '@reown/appkit/react'
-import * as Sentry from '@sentry/nextjs'
+import { initializeAppKit } from '@/config/wagmi.config'
import Image from 'next/image'
import { useRouter, useSearchParams } from 'next/navigation'
import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
@@ -41,6 +40,7 @@ import { invitesApi } from '@/services/invites'
import { EInviteType } from '@/services/services.types'
import ContributorCard from '@/components/Global/Contributors/ContributorCard'
import { getCardPosition } from '@/components/Global/Card'
+import * as Sentry from '@sentry/nextjs'
export type PaymentFlowProps = {
isExternalWalletFlow?: boolean
@@ -237,8 +237,12 @@ export const PaymentForm = ({
}
} else {
// regular send/pay
- if (isActivePeanutWallet && areEvmAddressesEqual(selectedTokenAddress, PEANUT_WALLET_TOKEN)) {
- // peanut wallet payment - ALWAYS check balance (including request pots)
+ if (
+ !showRequestPotInitialView && // don't apply balance check on request pot payment initial view
+ isActivePeanutWallet &&
+ areEvmAddressesEqual(selectedTokenAddress, PEANUT_WALLET_TOKEN)
+ ) {
+ // peanut wallet payment
const walletNumeric = parseFloat(String(peanutWalletBalance).replace(/,/g, ''))
if (walletNumeric < parsedInputAmount) {
dispatch(paymentActions.setError('Insufficient balance'))
@@ -370,8 +374,8 @@ export const PaymentForm = ({
if (inviteError) {
setInviteError(false)
}
- // Handle insufficient balance - redirect to add money
- if (isActivePeanutWallet && isInsufficientBalanceError && !isExternalWalletFlow) {
+ // Invites will be handled in the payment page, skip this step for request pots initial view
+ if (!showRequestPotInitialView && isActivePeanutWallet && isInsufficientBalanceError && !isExternalWalletFlow) {
// If the user doesn't have app access, accept the invite before claiming the link
if (recipient.recipientType === 'USERNAME' && !user?.user.hasAppAccess) {
const isAccepted = await handleAcceptInvite()
@@ -393,7 +397,6 @@ export const PaymentForm = ({
extra: { flow: 'external_wallet_payment' },
})
}
- return
}
// skip this step for request pots initial view
@@ -521,7 +524,10 @@ export const PaymentForm = ({
return 'Send'
}
- // Check insufficient balance BEFORE other conditions
+ if (showRequestPotInitialView) {
+ return 'Pay'
+ }
+
if (isActivePeanutWallet && isInsufficientBalanceError && !isExternalWalletFlow) {
return (
@@ -534,10 +540,6 @@ export const PaymentForm = ({
)
}
- if (showRequestPotInitialView) {
- return 'Pay'
- }
-
if (isActivePeanutWallet) {
return (
@@ -554,11 +556,12 @@ export const PaymentForm = ({
}
const getButtonIcon = (): IconName | undefined => {
- if (!isExternalWalletConnected && isExternalWalletFlow) return 'wallet-outline'
+ if (!showRequestPotInitialView && !isExternalWalletConnected && isExternalWalletFlow) return 'wallet-outline'
- if (isActivePeanutWallet && isInsufficientBalanceError && !isExternalWalletFlow) return 'arrow-down'
+ if (!showRequestPotInitialView && isActivePeanutWallet && isInsufficientBalanceError && !isExternalWalletFlow)
+ return 'arrow-down'
- if (!isProcessing && isActivePeanutWallet && !isExternalWalletFlow && !showRequestPotInitialView)
+ if (!showRequestPotInitialView && !isProcessing && isActivePeanutWallet && !isExternalWalletFlow)
return 'arrow-up-right'
return undefined
diff --git a/src/hooks/usePaymentInitiator.ts b/src/hooks/usePaymentInitiator.ts
index 16570630e..b2646ff8a 100644
--- a/src/hooks/usePaymentInitiator.ts
+++ b/src/hooks/usePaymentInitiator.ts
@@ -1,5 +1,4 @@
import { PEANUT_WALLET_CHAIN, PEANUT_WALLET_TOKEN } from '@/constants'
-import { BALANCE_DECREASE, INITIATE_PAYMENT } from '@/constants/query.consts'
import { tokenSelectorContext } from '@/context'
import { useWallet } from '@/hooks/wallet/useWallet'
import { type ParsedURL } from '@/lib/url-parser/types/payment'
@@ -27,7 +26,6 @@ import { getRoute, type PeanutCrossChainRoute } from '@/services/swap'
import { estimateTransactionCostUsd } from '@/app/actions/tokens'
import { captureException } from '@sentry/nextjs'
import { useRouter } from 'next/navigation'
-import { useQueryClient } from '@tanstack/react-query'
enum ELoadingStep {
IDLE = 'Idle',
@@ -84,7 +82,6 @@ export const usePaymentInitiator = () => {
const router = useRouter()
const config = useConfig()
const { chain: connectedWalletChain } = useWagmiAccount()
- const queryClient = useQueryClient()
const [slippagePercentage, setSlippagePercentage] = useState
(undefined)
const [unsignedTx, setUnsignedTx] = useState(null)
@@ -605,22 +602,12 @@ export const usePaymentInitiator = () => {
]
)
- // @dev Architecture Note: initiatePayment flow and mutation tracking
- //
- // Current: This async function uses state-based lifecycle (isProcessing, loadingStep)
- // rather than TanStack Query mutations. This is INTENTIONAL because:
- // 1. It has two phases: charge preparation (no balance change) + payment execution (balance decrease)
- // 2. Only the payment execution phase triggers balance-decreasing mutations (via sendMoney/sendTransactions)
- // 3. sendMoney already properly wraps mutations with mutationKey: [BALANCE_DECREASE, SEND_MONEY]
- // 4. usePendingTransactions tracks ALL balance-decreasing operations globally
- //
- // Bug fix (2025-10): Added guard at line 648 to prevent premature payment execution when
- // fetching existing charges. Previously, fetching an existing charge (chargeCreated=false)
- // without skipChargeCreation=true would fall through and trigger sendMoney() prematurely,
- // causing optimistic balance updates before user confirmed payment.
- //
- // Future consideration: Could wrap entire initiatePayment in useMutation, but complexity is HIGH
- // due to two-phase flow, Redux integration, and multiple return points. Current architecture works.
+ // @dev TODO: Refactor to TanStack Query mutation for architectural consistency
+ // Current: This async function works correctly (protected by isProcessing state)
+ // but is NOT tracked by usePendingTransactions mutation system.
+ // Future improvement: Wrap in useMutation for consistency with other balance-decreasing ops.
+ // mutationKey: [BALANCE_DECREASE, INITIATE_PAYMENT]
+ // Complexity: HIGH - complex state/Redux integration. Low priority.
//
// initiate and process payments
const initiatePayment = useCallback(
@@ -641,25 +628,19 @@ export const usePaymentInitiator = () => {
console.log('Proceeding with charge details:', determinedChargeDetails.uuid)
// 2. handle charge state
- // Return early if:
- // a) Explicitly told to return after charge creation (request pot initial view)
- // b) Charge was just created AND needs special handling (external wallet/cross-chain)
- // c) Fetching existing charge WITHOUT explicit skipChargeCreation (not from CONFIRM view)
- const shouldReturnAfterCharge =
- payload.returnAfterChargeCreation ||
+ if (
+ payload.returnAfterChargeCreation || // For request pot payment, return after charge creation
(chargeCreated &&
(payload.isExternalWalletFlow ||
!isPeanutWallet ||
(isPeanutWallet &&
(!areEvmAddressesEqual(determinedChargeDetails.tokenAddress, PEANUT_WALLET_TOKEN) ||
- determinedChargeDetails.chainId !== PEANUT_WALLET_CHAIN.id.toString())))) ||
- // NEW: If charge exists (not created) and we're NOT explicitly skipping charge creation,
- // then we're in "prepare" mode and shouldn't execute payment yet
- (!chargeCreated && payload.chargeId && !payload.skipChargeCreation)
-
- if (shouldReturnAfterCharge) {
+ determinedChargeDetails.chainId !== PEANUT_WALLET_CHAIN.id.toString()))))
+ ) {
console.log(
- `Charge ready. Returning without payment execution. (chargeCreated: ${chargeCreated}, skipChargeCreation: ${payload.skipChargeCreation})`
+ `Charge created. Transitioning to Confirm view for: ${
+ payload.isExternalWalletFlow ? 'Add Money Flow' : 'External Wallet'
+ }.`
)
setLoadingStep('Charge Created')
return { status: 'Charge Created', charge: determinedChargeDetails, success: false }