From 19e62b40d05a963e67f1e3fcde5caaf52eb409e7 Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Wed, 10 Sep 2025 14:21:45 +0530 Subject: [PATCH 01/13] add claim to mercado pago UI --- src/components/Claim/Link/Initial.view.tsx | 18 +++++ .../Claim/Link/MantecaFlowManager.tsx | 80 +++++++++++++++++++ .../Link/views/MantecaDetailsStep.view.tsx | 29 +++++++ .../Claim/Link/views/MantecaReviewStep.tsx | 55 +++++++++++++ src/components/Common/ActionList.tsx | 10 ++- .../Global/MantecaDetailsCard/index.tsx | 23 ++++++ .../Global/PeanutActionDetailsCard/index.tsx | 10 ++- src/components/Payment/PaymentInfoRow.tsx | 20 ++--- src/components/Profile/AvatarWithBadge.tsx | 35 ++++++++ src/constants/actionlist.consts.ts | 2 +- src/context/ClaimBankFlowContext.tsx | 8 ++ src/types/manteca.types.ts | 6 ++ 12 files changed, 283 insertions(+), 13 deletions(-) create mode 100644 src/components/Claim/Link/MantecaFlowManager.tsx create mode 100644 src/components/Claim/Link/views/MantecaDetailsStep.view.tsx create mode 100644 src/components/Claim/Link/views/MantecaReviewStep.tsx create mode 100644 src/components/Global/MantecaDetailsCard/index.tsx diff --git a/src/components/Claim/Link/Initial.view.tsx b/src/components/Claim/Link/Initial.view.tsx index 9b06079f4..2ceed0e2a 100644 --- a/src/components/Claim/Link/Initial.view.tsx +++ b/src/components/Claim/Link/Initial.view.tsx @@ -51,6 +51,7 @@ import { Button } from '@/components/0_Bruddle' import Image from 'next/image' import { PEANUT_LOGO_BLACK, PEANUTMAN_LOGO } from '@/assets' import { GuestVerificationModal } from '@/components/Global/GuestVerificationModal' +import MantecaFlowManager from './MantecaFlowManager' export const InitialClaimLinkView = (props: IClaimScreenProps) => { const { @@ -91,6 +92,7 @@ export const InitialClaimLinkView = (props: IClaimScreenProps) => { setShowVerificationModal, setClaimToExternalWallet, resetFlow: resetClaimBankFlow, + claimToMercadoPago, } = useClaimBankFlow() const { setLoadingState, isLoading } = useContext(loadingStateContext) const { @@ -627,6 +629,22 @@ export const InitialClaimLinkView = (props: IClaimScreenProps) => { return } + if (claimToMercadoPago) { + return ( + + ) + } + return (
{!!user?.user.userId || claimBankFlowStep || claimToExternalWallet ? ( diff --git a/src/components/Claim/Link/MantecaFlowManager.tsx b/src/components/Claim/Link/MantecaFlowManager.tsx new file mode 100644 index 000000000..9e0063cd8 --- /dev/null +++ b/src/components/Claim/Link/MantecaFlowManager.tsx @@ -0,0 +1,80 @@ +import { MERCADO_PAGO } from '@/assets' +import NavHeader from '@/components/Global/NavHeader' +import PeanutActionDetailsCard from '@/components/Global/PeanutActionDetailsCard' +import { useClaimBankFlow } from '@/context/ClaimBankFlowContext' +import { ClaimLinkData } from '@/services/sendLinks' +import { FC, useState } from 'react' +import MantecaDetailsStep from './views/MantecaDetailsStep.view' +import { MercadoPagoStep } from '@/types/manteca.types' +import MantecaReviewStep from './views/MantecaReviewStep' +import { Button } from '@/components/0_Bruddle' +import { useRouter } from 'next/navigation' + +interface MantecaFlowManagerProps { + claimLinkData: ClaimLinkData + amount: string + attachment: { message: string | undefined; attachmentUrl: string | undefined } +} + +const MantecaFlowManager: FC = ({ claimLinkData, amount, attachment }) => { + const { setClaimToMercadoPago } = useClaimBankFlow() + const [currentStep, setCurrentStep] = useState(MercadoPagoStep.DETAILS) + const router = useRouter() + + const isSuccess = currentStep === MercadoPagoStep.SUCCESS + + const renderStepDetails = () => { + if (currentStep === MercadoPagoStep.DETAILS) { + return + } + if (currentStep === MercadoPagoStep.REVIEW) { + return + } + + if (currentStep === MercadoPagoStep.SUCCESS) { + return + } + return null + } + + const onPrev = () => { + if (currentStep === MercadoPagoStep.DETAILS) { + setClaimToMercadoPago(false) + return + } + + if (currentStep === MercadoPagoStep.REVIEW) { + setCurrentStep(MercadoPagoStep.DETAILS) + return + } + if (currentStep === MercadoPagoStep.SUCCESS) { + router.push('/home') + return + } + } + + return ( +
+ + +
+ + + {renderStepDetails()} +
+
+ ) +} + +export default MantecaFlowManager diff --git a/src/components/Claim/Link/views/MantecaDetailsStep.view.tsx b/src/components/Claim/Link/views/MantecaDetailsStep.view.tsx new file mode 100644 index 000000000..897b1f512 --- /dev/null +++ b/src/components/Claim/Link/views/MantecaDetailsStep.view.tsx @@ -0,0 +1,29 @@ +import { Button } from '@/components/0_Bruddle' +import BaseInput from '@/components/0_Bruddle/BaseInput' +import { Icon } from '@/components/Global/Icons/Icon' +import { MercadoPagoStep } from '@/types/manteca.types' +import { Dispatch, FC, SetStateAction } from 'react' + +interface MantecaDetailsStepProps { + setCurrentStep: Dispatch> +} + +const MantecaDetailsStep: FC = ({ setCurrentStep }) => { + return ( + <> +

Enter Mercado Pago account details

+ + +
+ + You can only withdraw to accounts under your name. +
+ + + + ) +} + +export default MantecaDetailsStep diff --git a/src/components/Claim/Link/views/MantecaReviewStep.tsx b/src/components/Claim/Link/views/MantecaReviewStep.tsx new file mode 100644 index 000000000..7d495b79c --- /dev/null +++ b/src/components/Claim/Link/views/MantecaReviewStep.tsx @@ -0,0 +1,55 @@ +import { Button } from '@/components/0_Bruddle' +import MantecaDetailsCard, { MantecaCardRow } from '@/components/Global/MantecaDetailsCard' +import { MercadoPagoStep } from '@/types/manteca.types' +import { Dispatch, FC, SetStateAction } from 'react' + +interface MantecaReviewStepProps { + setCurrentStep: Dispatch> +} + +const MantecaReviewStep: FC = ({ setCurrentStep }) => { + const detailsCardRows: MantecaCardRow[] = [ + { + key: 'fullName', + label: 'Full name', + value: 'Manuel Rodríguez Roldán', + allowCopy: true, + }, + { + key: 'cuilCuit', + label: '[CUIL/CUIT]', + value: '20-39951628-6', + allowCopy: true, + }, + { + key: 'alias', + label: 'Alias', + value: 'manurr.mp', + allowCopy: true, + }, + { + key: 'exchangeRate', + label: 'Exchange Rate', + value: '1 USD = 1200 ARS', + allowCopy: true, + }, + { + key: 'fee', + label: 'Fee', + value: '1000', + allowCopy: true, + hideBottomBorder: true, + }, + ] + + return ( + <> + + + + ) +} + +export default MantecaReviewStep diff --git a/src/components/Common/ActionList.tsx b/src/components/Common/ActionList.tsx index e02994fe8..ca4893561 100644 --- a/src/components/Common/ActionList.tsx +++ b/src/components/Common/ActionList.tsx @@ -42,7 +42,12 @@ interface IActionListProps { */ export default function ActionList({ claimLinkData, isLoggedIn, flow, requestLinkData }: IActionListProps) { const router = useRouter() - const { setClaimToExternalWallet, setFlowStep: setClaimBankFlowStep, setShowVerificationModal } = useClaimBankFlow() + const { + setClaimToExternalWallet, + setFlowStep: setClaimBankFlowStep, + setShowVerificationModal, + setClaimToMercadoPago, + } = useClaimBankFlow() const [showMinAmountError, setShowMinAmountError] = useState(false) const { claimType } = useDetermineBankClaimType(claimLinkData?.sender?.userId ?? '') const { chargeDetails } = usePaymentStore() @@ -79,7 +84,8 @@ export default function ActionList({ claimLinkData, isLoggedIn, flow, requestLin } break case 'mercadopago': - break // soon tag, so no action needed + setClaimToMercadoPago(true) + break case 'exchange-or-wallet': setClaimToExternalWallet(true) break diff --git a/src/components/Global/MantecaDetailsCard/index.tsx b/src/components/Global/MantecaDetailsCard/index.tsx new file mode 100644 index 000000000..45fe3e4a5 --- /dev/null +++ b/src/components/Global/MantecaDetailsCard/index.tsx @@ -0,0 +1,23 @@ +import React, { FC } from 'react' +import Card from '../Card' +import { PaymentInfoRow, PaymentInfoRowProps } from '@/components/Payment/PaymentInfoRow' + +export interface MantecaCardRow extends PaymentInfoRowProps { + key: React.Key +} + +interface MantecaDetailsCardProps { + rows: MantecaCardRow[] +} + +const MantecaDetailsCard: FC = ({ rows }) => { + return ( + + {rows.map((row) => ( + + ))} + + ) +} + +export default MantecaDetailsCard diff --git a/src/components/Global/PeanutActionDetailsCard/index.tsx b/src/components/Global/PeanutActionDetailsCard/index.tsx index 0f1c2db58..eaf3af2fc 100644 --- a/src/components/Global/PeanutActionDetailsCard/index.tsx +++ b/src/components/Global/PeanutActionDetailsCard/index.tsx @@ -23,6 +23,7 @@ export type PeanutActionDetailsCardTransactionType = | 'WITHDRAW' | 'WITHDRAW_BANK_ACCOUNT' | 'ADD_MONEY_BANK_ACCOUNT' + | 'REGIONAL_METHOD_CLAIM' export type PeanutActionDetailsCardRecipientType = RecipientType | 'BANK_ACCOUNT' @@ -85,7 +86,12 @@ export default function PeanutActionDetailsCard({ if (transactionType === 'ADD_MONEY' || transactionType === 'CLAIM_LINK_BANK_ACCOUNT') return 'arrow-down' if (transactionType === 'REQUEST' || transactionType === 'RECEIVED_LINK') return 'arrow-down-left' if (transactionType === 'CLAIM_LINK') return viewType !== 'SUCCESS' ? 'arrow-down' : undefined - if (transactionType === 'WITHDRAW' || transactionType === 'WITHDRAW_BANK_ACCOUNT') return 'arrow-up' + if ( + transactionType === 'WITHDRAW' || + transactionType === 'WITHDRAW_BANK_ACCOUNT' || + transactionType === 'REGIONAL_METHOD_CLAIM' + ) + return 'arrow-up' } const getTitle = () => { @@ -107,6 +113,7 @@ export default function PeanutActionDetailsCard({ title = `You're about to receive` } } + if (transactionType === 'REGIONAL_METHOD_CLAIM') title = recipientName // Render the string as is for regional method return (

{icon && } {title} @@ -197,6 +204,7 @@ export default function PeanutActionDetailsCard({ backgroundColor: getAvatarBackgroundColor(), color: getAvatarTextColor(), }} + logo={logo} /> )}

diff --git a/src/components/Payment/PaymentInfoRow.tsx b/src/components/Payment/PaymentInfoRow.tsx index ce671684e..be0dc2d6e 100644 --- a/src/components/Payment/PaymentInfoRow.tsx +++ b/src/components/Payment/PaymentInfoRow.tsx @@ -4,6 +4,16 @@ import { Icon } from '../Global/Icons/Icon' import Loading from '../Global/Loading' import CopyToClipboard from '../Global/CopyToClipboard' +export interface PaymentInfoRowProps { + label: string | React.ReactNode + value: number | string | React.ReactNode + moreInfoText?: string + loading?: boolean + hideBottomBorder?: boolean + allowCopy?: boolean + copyValue?: string +} + export const PaymentInfoRow = ({ label, value, @@ -12,15 +22,7 @@ export const PaymentInfoRow = ({ hideBottomBorder, allowCopy, copyValue, -}: { - label: string | React.ReactNode - value: number | string | React.ReactNode - moreInfoText?: string - loading?: boolean - hideBottomBorder?: boolean - allowCopy?: boolean - copyValue?: string -}) => { +}: PaymentInfoRowProps) => { const [showMoreInfo, setShowMoreInfo] = useState(false) const tooltipId = useId() diff --git a/src/components/Profile/AvatarWithBadge.tsx b/src/components/Profile/AvatarWithBadge.tsx index 7c3cbeef2..3f6b5c6cf 100644 --- a/src/components/Profile/AvatarWithBadge.tsx +++ b/src/components/Profile/AvatarWithBadge.tsx @@ -4,6 +4,8 @@ import React, { useMemo } from 'react' import { twMerge } from 'tailwind-merge' import { Icon, IconName } from '../Global/Icons/Icon' import StatusPill, { StatusPillType } from '../Global/StatusPill' +import { StaticImport } from 'next/dist/shared/lib/get-img-props' +import Image from 'next/image' export type AvatarSize = 'extra-small' | 'small' | 'medium' | 'large' @@ -20,6 +22,7 @@ interface AvatarWithBadgeProps { iconFillColor?: string showStatusPill?: boolean statusPillStatus?: StatusPillType + logo?: StaticImport } /** @@ -36,6 +39,7 @@ const AvatarWithBadge: React.FC = ({ iconFillColor, showStatusPill, statusPillStatus, + logo, }) => { const sizeClasses: Record = { 'extra-small': 'h-8 w-8 text-xs', @@ -58,6 +62,28 @@ const AvatarWithBadge: React.FC = ({ return '' }, [name]) + if (logo) { + return ( +
+
+ {''} +
+
+ ) + } + return (
{/* the main avatar circle */} @@ -76,6 +102,15 @@ const AvatarWithBadge: React.FC = ({ ...inlineStyle, }} > + {logo && ( + {''} + )} {/* display icon if provided, otherwise display initials */} {icon ? ( diff --git a/src/constants/actionlist.consts.ts b/src/constants/actionlist.consts.ts index bb22d0a5b..7ed112d8d 100644 --- a/src/constants/actionlist.consts.ts +++ b/src/constants/actionlist.consts.ts @@ -27,7 +27,7 @@ export const ACTION_METHODS: PaymentMethod[] = [ title: 'Mercado Pago', description: 'Instant transfers', icons: [MERCADO_PAGO], - soon: true, + soon: false, }, { id: 'exchange-or-wallet', diff --git a/src/context/ClaimBankFlowContext.tsx b/src/context/ClaimBankFlowContext.tsx index b6f351fc6..deda0810f 100644 --- a/src/context/ClaimBankFlowContext.tsx +++ b/src/context/ClaimBankFlowContext.tsx @@ -42,6 +42,8 @@ interface ClaimBankFlowContextType { setSenderKycStatus: (status?: BridgeKycStatus) => void justCompletedKyc: boolean setJustCompletedKyc: (status: boolean) => void + claimToMercadoPago: boolean + setClaimToMercadoPago: (claimToMercadoPago: boolean) => void } const ClaimBankFlowContext = createContext(undefined) @@ -60,6 +62,7 @@ export const ClaimBankFlowContextProvider: React.FC<{ children: ReactNode }> = ( const [selectedBankAccount, setSelectedBankAccount] = useState(null) const [senderKycStatus, setSenderKycStatus] = useState() const [justCompletedKyc, setJustCompletedKyc] = useState(false) + const [claimToMercadoPago, setClaimToMercadoPago] = useState(false) const resetFlow = useCallback(() => { setClaimToExternalWallet(false) @@ -75,6 +78,7 @@ export const ClaimBankFlowContextProvider: React.FC<{ children: ReactNode }> = ( setSelectedBankAccount(null) setSenderKycStatus(undefined) setJustCompletedKyc(false) + setClaimToMercadoPago(false) }, []) const value = useMemo( @@ -106,6 +110,8 @@ export const ClaimBankFlowContextProvider: React.FC<{ children: ReactNode }> = ( setSenderKycStatus, justCompletedKyc, setJustCompletedKyc, + claimToMercadoPago, + setClaimToMercadoPago, }), [ claimToExternalWallet, @@ -122,6 +128,8 @@ export const ClaimBankFlowContextProvider: React.FC<{ children: ReactNode }> = ( selectedBankAccount, senderKycStatus, justCompletedKyc, + claimToMercadoPago, + setClaimToMercadoPago, ] ) diff --git a/src/types/manteca.types.ts b/src/types/manteca.types.ts index 0e9be9c30..b4e8efd5a 100644 --- a/src/types/manteca.types.ts +++ b/src/types/manteca.types.ts @@ -3,3 +3,9 @@ export interface MantecaDepositDetails { depositAlias: string depositAmount: string } + +export enum MercadoPagoStep { + DETAILS = 'details', + REVIEW = 'review', + SUCCESS = 'success', +} From 2a9f31133874292bb768ff53d864a586a3b30d94 Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Wed, 10 Sep 2025 15:20:49 +0530 Subject: [PATCH 02/13] refactor: replace Card component with MantecaDetailsCard in MantecaDepositCard for improved account details display --- .../components/MantecaDepositCard.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/AddMoney/components/MantecaDepositCard.tsx b/src/components/AddMoney/components/MantecaDepositCard.tsx index 25e26d825..46bdf551a 100644 --- a/src/components/AddMoney/components/MantecaDepositCard.tsx +++ b/src/components/AddMoney/components/MantecaDepositCard.tsx @@ -1,7 +1,6 @@ import { MERCADO_PAGO } from '@/assets' -import Card from '@/components/Global/Card' +import MantecaDetailsCard from '@/components/Global/MantecaDetailsCard' import PeanutActionDetailsCard from '@/components/Global/PeanutActionDetailsCard' -import { PaymentInfoRow } from '@/components/Payment/PaymentInfoRow' import { PEANUT_WALLET_TOKEN_SYMBOL } from '@/constants' interface MantecaDepositCardProps { @@ -40,12 +39,16 @@ const MantecaDepositCard = ({ />

Account details

- - {cbu && } - {alias && } - {depositAddress && } - {pixKey && } - +
) } From a333a5445f59280dcd253a8a461a18d3afe1fbf3 Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Wed, 10 Sep 2025 16:04:07 +0530 Subject: [PATCH 03/13] Add UI for Manteca req fulfilment flow --- src/app/[...recipient]/client.tsx | 5 +- src/components/Common/ActionList.tsx | 4 +- .../Global/CopyToClipboard/index.tsx | 2 +- src/components/Payment/PaymentForm/index.tsx | 8 ++- .../Payment/Views/MantecaFulfuilment.view.tsx | 67 +++++++++++++++++++ src/context/RequestFulfillmentFlowContext.tsx | 7 ++ 6 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 src/components/Payment/Views/MantecaFulfuilment.view.tsx diff --git a/src/app/[...recipient]/client.tsx b/src/app/[...recipient]/client.tsx index 01f612932..0b11d7e6e 100644 --- a/src/app/[...recipient]/client.tsx +++ b/src/app/[...recipient]/client.tsx @@ -61,7 +61,8 @@ export default function PaymentPage({ recipient, flow = 'request_pay' }: Props) const [currencyAmount, setCurrencyAmount] = useState('') const { isDrawerOpen, selectedTransaction, openTransactionDetails } = useTransactionDetailsDrawer() const [isLinkCancelling, setisLinkCancelling] = useState(false) - const { showExternalWalletFulfilMethods, showRequestFulfilmentBankFlowManager } = useRequestFulfillmentFlow() + const { showExternalWalletFulfilMethods, showRequestFulfilmentBankFlowManager, fulfilUsingManteca } = + useRequestFulfillmentFlow() // determine if the current user is the recipient of the transaction const isCurrentUserRecipient = chargeDetails?.requestLink.recipientAccount?.userId === user?.user.userId @@ -482,7 +483,7 @@ export default function PaymentPage({ recipient, flow = 'request_pay' }: Props) currencyAmount={currencyAmount} />
- {showActionList && ( + {!fulfilUsingManteca && showActionList && ( diff --git a/src/components/Payment/PaymentForm/index.tsx b/src/components/Payment/PaymentForm/index.tsx index f97e615cf..06bcd8365 100644 --- a/src/components/Payment/PaymentForm/index.tsx +++ b/src/components/Payment/PaymentForm/index.tsx @@ -36,6 +36,7 @@ import { useAccount } from 'wagmi' import { useUserInteractions } from '@/hooks/useUserInteractions' import { useUserByUsername } from '@/hooks/useUserByUsername' import { PaymentFlow } from '@/app/[...recipient]/client' +import MantecaFulfuilment from '../Views/MantecaFulfuilment.view' export type PaymentFlowProps = { isPintaReq?: boolean @@ -80,7 +81,8 @@ export const PaymentForm = ({ error: paymentStoreError, attachmentOptions, } = usePaymentStore() - const { setShowExternalWalletFulfilMethods, setExternalWalletFulfilMethod } = useRequestFulfillmentFlow() + const { setShowExternalWalletFulfilMethods, setExternalWalletFulfilMethod, fulfilUsingManteca } = + useRequestFulfillmentFlow() const recipientUsername = !chargeDetails && recipient?.recipientType === 'USERNAME' ? recipient.identifier : null const { user: recipientUser } = useUserByUsername(recipientUsername) @@ -639,6 +641,10 @@ export const PaymentForm = ({ } } + if (fulfilUsingManteca) { + return + } + return (
diff --git a/src/components/Payment/Views/MantecaFulfuilment.view.tsx b/src/components/Payment/Views/MantecaFulfuilment.view.tsx new file mode 100644 index 000000000..e7437a77d --- /dev/null +++ b/src/components/Payment/Views/MantecaFulfuilment.view.tsx @@ -0,0 +1,67 @@ +import { MERCADO_PAGO } from '@/assets' +import MantecaDetailsCard, { MantecaCardRow } from '@/components/Global/MantecaDetailsCard' +import NavHeader from '@/components/Global/NavHeader' +import PeanutActionDetailsCard from '@/components/Global/PeanutActionDetailsCard' +import ShareButton from '@/components/Global/ShareButton' +import { useRequestFulfillmentFlow } from '@/context/RequestFulfillmentFlowContext' +import { usePaymentStore } from '@/redux/hooks' +import React from 'react' + +const MantecaFulfuilment = () => { + const { setFulfilUsingManteca } = useRequestFulfillmentFlow() + const { requestDetails, chargeDetails } = usePaymentStore() + + const rows: MantecaCardRow[] = [ + { key: 'cbu', label: 'CBU', value: '[CVU/ALIAS]', allowCopy: true }, + { key: 'alias', label: 'Alias', value: 'manurr.mp', hideBottomBorder: true }, + ] + + const generateShareText = () => { + const textParts = [] + textParts.push(`CBU: ${rows[0].value}`) + textParts.push(`Alias: ${rows[1].value}`) + textParts.push(`Deposit Address: ${rows[2].value}`) + + return textParts.join('\n') + } + + return ( +
+ { + setFulfilUsingManteca(false) + }} + /> + +
+ + +

Account details

+ + + + generateShareText()} + title="Account Details" + variant="purple" + className="w-full" + > + Share Details + +
+
+ ) +} + +export default MantecaFulfuilment diff --git a/src/context/RequestFulfillmentFlowContext.tsx b/src/context/RequestFulfillmentFlowContext.tsx index b742712ce..0a86ab8c8 100644 --- a/src/context/RequestFulfillmentFlowContext.tsx +++ b/src/context/RequestFulfillmentFlowContext.tsx @@ -32,6 +32,8 @@ interface RequestFulfillmentFlowContextType { setShowVerificationModal: (show: boolean) => void requesterDetails: User | null setRequesterDetails: (details: User | null) => void + fulfilUsingManteca: boolean + setFulfilUsingManteca: (fulfilUsingManteca: boolean) => void } const RequestFulfillmentFlowContext = createContext(undefined) @@ -47,6 +49,7 @@ export const RequestFulfilmentFlowContextProvider: React.FC<{ children: ReactNod const [onrampData, setOnrampData] = useState(null) const [showVerificationModal, setShowVerificationModal] = useState(false) const [requesterDetails, setRequesterDetails] = useState(null) + const [fulfilUsingManteca, setFulfilUsingManteca] = useState(false) const resetFlow = useCallback(() => { setExternalWalletFulfilMethod(null) @@ -57,6 +60,7 @@ export const RequestFulfilmentFlowContextProvider: React.FC<{ children: ReactNod setOnrampData(null) setShowVerificationModal(false) setRequesterDetails(null) + setFulfilUsingManteca(false) }, []) const value = useMemo( @@ -78,6 +82,8 @@ export const RequestFulfilmentFlowContextProvider: React.FC<{ children: ReactNod setShowVerificationModal, requesterDetails, setRequesterDetails, + fulfilUsingManteca, + setFulfilUsingManteca, }), [ resetFlow, @@ -89,6 +95,7 @@ export const RequestFulfilmentFlowContextProvider: React.FC<{ children: ReactNod onrampData, showVerificationModal, requesterDetails, + fulfilUsingManteca, ] ) From ad7602acf8b74defabb5cfeddefe8e048ea90b1f Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Fri, 12 Sep 2025 18:52:33 +0530 Subject: [PATCH 04/13] Integrate request fulfilment API --- src/app/actions/onramp.ts | 4 + .../Payment/Views/MantecaFulfuilment.view.tsx | 82 ++++++++++++++----- src/services/manteca.ts | 29 +++++++ 3 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 src/services/manteca.ts diff --git a/src/app/actions/onramp.ts b/src/app/actions/onramp.ts index 5a542494a..088c98cab 100644 --- a/src/app/actions/onramp.ts +++ b/src/app/actions/onramp.ts @@ -117,6 +117,7 @@ export async function createOnrampForGuest( interface CreateMantecaOnrampParams { usdAmount: string currency: string + chargeId?: string } export async function createMantecaOnramp( @@ -132,6 +133,7 @@ export async function createMantecaOnramp( } try { + console.log('called hahahahha') const response = await fetchWithSentry(`${apiUrl}/manteca/deposit`, { method: 'POST', headers: { @@ -142,6 +144,7 @@ export async function createMantecaOnramp( body: JSON.stringify({ usdAmount: params.usdAmount, currency: params.currency, + chargeId: params.chargeId, }), }) @@ -154,6 +157,7 @@ export async function createMantecaOnramp( return { data } } catch (error) { + console.log('error', error) console.error('Error calling create manteca on-ramp API:', error) if (error instanceof Error) { return { error: error.message } diff --git a/src/components/Payment/Views/MantecaFulfuilment.view.tsx b/src/components/Payment/Views/MantecaFulfuilment.view.tsx index e7437a77d..f4650681a 100644 --- a/src/components/Payment/Views/MantecaFulfuilment.view.tsx +++ b/src/components/Payment/Views/MantecaFulfuilment.view.tsx @@ -1,30 +1,43 @@ +import { createMantecaOnramp } from '@/app/actions/onramp' import { MERCADO_PAGO } from '@/assets' -import MantecaDetailsCard, { MantecaCardRow } from '@/components/Global/MantecaDetailsCard' +import ErrorAlert from '@/components/Global/ErrorAlert' +import MantecaDetailsCard from '@/components/Global/MantecaDetailsCard' import NavHeader from '@/components/Global/NavHeader' import PeanutActionDetailsCard from '@/components/Global/PeanutActionDetailsCard' +import PeanutLoading from '@/components/Global/PeanutLoading' import ShareButton from '@/components/Global/ShareButton' import { useRequestFulfillmentFlow } from '@/context/RequestFulfillmentFlowContext' import { usePaymentStore } from '@/redux/hooks' +import { useQuery } from '@tanstack/react-query' import React from 'react' const MantecaFulfuilment = () => { const { setFulfilUsingManteca } = useRequestFulfillmentFlow() const { requestDetails, chargeDetails } = usePaymentStore() - - const rows: MantecaCardRow[] = [ - { key: 'cbu', label: 'CBU', value: '[CVU/ALIAS]', allowCopy: true }, - { key: 'alias', label: 'Alias', value: 'manurr.mp', hideBottomBorder: true }, - ] + const { data: depositData, isLoading: isLoadingDeposit } = useQuery({ + queryKey: ['manteca-deposit', chargeDetails?.uuid], + queryFn: () => + createMantecaOnramp({ + usdAmount: requestDetails?.tokenAmount || chargeDetails?.tokenAmount || '0', + currency: 'ARS', + chargeId: chargeDetails?.uuid, + }), + refetchOnWindowFocus: false, + staleTime: Infinity, // don't refetch the data + }) const generateShareText = () => { const textParts = [] - textParts.push(`CBU: ${rows[0].value}`) - textParts.push(`Alias: ${rows[1].value}`) - textParts.push(`Deposit Address: ${rows[2].value}`) + textParts.push(`CBU: ${depositData?.data?.depositAddress}`) + textParts.push(`Alias: ${depositData?.data?.depositAlias}`) return textParts.join('\n') } + if (isLoadingDeposit) { + return + } + return (
{ transactionType="REQUEST_PAYMENT" recipientType="USERNAME" recipientName={requestDetails?.recipientAccount.user.username ?? ''} - amount={requestDetails?.tokenAmount ?? '0'} + amount={requestDetails?.tokenAmount || chargeDetails?.tokenAmount || '0'} tokenSymbol={requestDetails?.tokenSymbol || 'USDC'} message={requestDetails?.reference || chargeDetails?.requestLink?.reference || ''} fileUrl={requestDetails?.attachmentUrl || chargeDetails?.requestLink?.attachmentUrl || ''} logo={MERCADO_PAGO} /> -

Account details

+ {depositData?.error && } + + {depositData?.data && ( + <> +

Account details

- + - generateShareText()} - title="Account Details" - variant="purple" - className="w-full" - > - Share Details - + generateShareText()} + title="Account Details" + variant="purple" + className="w-full" + > + Share Details + + + )}
) diff --git a/src/services/manteca.ts b/src/services/manteca.ts new file mode 100644 index 000000000..602c4c7ca --- /dev/null +++ b/src/services/manteca.ts @@ -0,0 +1,29 @@ +import { PEANUT_API_URL } from '@/constants' +import { fetchWithSentry } from '@/utils' +import Cookies from 'js-cookie' + +export type WithdrawRequestType = { + amount: string + accountId: string + depositAddress: string +} + +export const mantecaApi = { + initiateWithdraw: async (data: WithdrawRequestType) => { + const response = await fetchWithSentry(`${PEANUT_API_URL}/manteca/withdraw`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${Cookies.get('jwt-token')}`, + }, + body: JSON.stringify(data), + }) + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + throw new Error(errorData.message || `Withdraw failed: ${response.statusText}`) + } + + return response.json() + }, +} From 899e5f00022dfc77c8bf1cd80abe5c9012351620 Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Fri, 12 Sep 2025 20:07:28 +0530 Subject: [PATCH 05/13] implement claim flow --- src/app/actions/claimLinks.ts | 3 +- src/app/actions/manteca.ts | 46 ++++++++++++++++ src/app/actions/types/manteca.types.ts | 55 +++++++++++++++++++ .../Claim/Link/MantecaFlowManager.tsx | 18 +++++- .../Link/views/MantecaDetailsStep.view.tsx | 50 +++++++++++++++-- .../Claim/Link/views/MantecaReviewStep.tsx | 52 +++++++++++------- src/services/manteca.ts | 29 ---------- src/services/sendLinks.ts | 5 +- 8 files changed, 200 insertions(+), 58 deletions(-) create mode 100644 src/app/actions/manteca.ts create mode 100644 src/app/actions/types/manteca.types.ts delete mode 100644 src/services/manteca.ts diff --git a/src/app/actions/claimLinks.ts b/src/app/actions/claimLinks.ts index 5de52eb72..4a8ef217c 100644 --- a/src/app/actions/claimLinks.ts +++ b/src/app/actions/claimLinks.ts @@ -82,7 +82,7 @@ export async function getNextDepositIndex(contractVersion: string): Promise { + const apiUrl = process.env.PEANUT_API_URL + const cookieStore = cookies() + const jwtToken = (await cookieStore).get('jwt-token')?.value + + if (!apiUrl || !API_KEY) { + console.error('API URL or API Key is not configured.') + return { error: 'Server configuration error.' } + } + + try { + const response = await fetchWithSentry(`${apiUrl}/manteca/withdraw`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${jwtToken}`, + 'api-key': API_KEY, + }, + body: JSON.stringify(params), + }) + + const data = await response.json() + + if (!response.ok) { + console.log('error', response) + return { error: data.error || 'Failed to create manteca withdraw.' } + } + + return { data } + } catch (error) { + console.log('error', error) + console.error('Error calling create manteca withdraw API:', error) + if (error instanceof Error) { + return { error: error.message } + } + return { error: 'An unexpected error occurred.' } + } +} diff --git a/src/app/actions/types/manteca.types.ts b/src/app/actions/types/manteca.types.ts new file mode 100644 index 000000000..413de7c61 --- /dev/null +++ b/src/app/actions/types/manteca.types.ts @@ -0,0 +1,55 @@ +export type MantecaWithdrawData = { + amount: string + destinationAddress: string +} + +export type MantecaWithdrawResponseData = { + id: string + numberId: string + userId: string + userNumberId: string + userExternalId: string + status: string + type: string + details: { + depositAddresses: { + ARBITRUM: string + } + depositAddress: string + depositAvailableNetworks: string[] + withdrawCostInAgainst: string + withdrawCostInAsset: string + price: string + priceExpireAt: string + } + currentStage: number + stages: { + 1: { + stageType: string + side: string + type: string + asset: string + against: string + assetAmount: string + price: string + priceCode: string + } + 2: { + stageType: string + asset: string + amount: string + to: string + destination: { + address: string + bankCode: string + } + } + } + creationTime: string + updatedAt: string +} + +export type MantecaWithdrawResponse = { + data?: MantecaWithdrawResponseData + error?: string +} diff --git a/src/components/Claim/Link/MantecaFlowManager.tsx b/src/components/Claim/Link/MantecaFlowManager.tsx index 9e0063cd8..e32d362fa 100644 --- a/src/components/Claim/Link/MantecaFlowManager.tsx +++ b/src/components/Claim/Link/MantecaFlowManager.tsx @@ -9,6 +9,7 @@ import { MercadoPagoStep } from '@/types/manteca.types' import MantecaReviewStep from './views/MantecaReviewStep' import { Button } from '@/components/0_Bruddle' import { useRouter } from 'next/navigation' +import { MantecaWithdrawResponseData } from '@/app/actions/types/manteca.types' interface MantecaFlowManagerProps { claimLinkData: ClaimLinkData @@ -20,15 +21,28 @@ const MantecaFlowManager: FC = ({ claimLinkData, amount const { setClaimToMercadoPago } = useClaimBankFlow() const [currentStep, setCurrentStep] = useState(MercadoPagoStep.DETAILS) const router = useRouter() + const [withdrawDetails, setWithdrawDetails] = useState() const isSuccess = currentStep === MercadoPagoStep.SUCCESS const renderStepDetails = () => { if (currentStep === MercadoPagoStep.DETAILS) { - return + return ( + + ) } if (currentStep === MercadoPagoStep.REVIEW) { - return + return ( + + ) } if (currentStep === MercadoPagoStep.SUCCESS) { diff --git a/src/components/Claim/Link/views/MantecaDetailsStep.view.tsx b/src/components/Claim/Link/views/MantecaDetailsStep.view.tsx index 897b1f512..f4b5b4c22 100644 --- a/src/components/Claim/Link/views/MantecaDetailsStep.view.tsx +++ b/src/components/Claim/Link/views/MantecaDetailsStep.view.tsx @@ -1,25 +1,67 @@ +import { mantecaWithdraw } from '@/app/actions/manteca' +import { MantecaWithdrawResponseData } from '@/app/actions/types/manteca.types' import { Button } from '@/components/0_Bruddle' import BaseInput from '@/components/0_Bruddle/BaseInput' +import ErrorAlert from '@/components/Global/ErrorAlert' import { Icon } from '@/components/Global/Icons/Icon' import { MercadoPagoStep } from '@/types/manteca.types' -import { Dispatch, FC, SetStateAction } from 'react' +import { Dispatch, FC, SetStateAction, useCallback, useState } from 'react' interface MantecaDetailsStepProps { setCurrentStep: Dispatch> + amount: string + setWithdrawDetails: Dispatch> } -const MantecaDetailsStep: FC = ({ setCurrentStep }) => { +const MantecaDetailsStep: FC = ({ setCurrentStep, amount, setWithdrawDetails }) => { + const [destinationAddress, setDestinationAddress] = useState('') + const [isSubmitting, setIsSubmitting] = useState(false) + const [error, setError] = useState(null) + + const createMantecaWithdraw = async () => { + setError(null) + setIsSubmitting(true) + const response = await mantecaWithdraw({ + amount, + destinationAddress, + }) + + if (response.error) { + console.error(response.error) + setError(response.error) + setIsSubmitting(false) + return + } + + if (response.data) { + setWithdrawDetails(response.data) + setCurrentStep(MercadoPagoStep.REVIEW) + } + setIsSubmitting(false) + } + return ( <>

Enter Mercado Pago account details

- + setDestinationAddress(e.target.value)} + placeholder="CVU or Alias" + />
You can only withdraw to accounts under your name.
- diff --git a/src/components/Claim/Link/views/MantecaReviewStep.tsx b/src/components/Claim/Link/views/MantecaReviewStep.tsx index 7d495b79c..37815fd40 100644 --- a/src/components/Claim/Link/views/MantecaReviewStep.tsx +++ b/src/components/Claim/Link/views/MantecaReviewStep.tsx @@ -1,51 +1,63 @@ +import { MantecaWithdrawResponseData } from '@/app/actions/types/manteca.types' import { Button } from '@/components/0_Bruddle' +import ErrorAlert from '@/components/Global/ErrorAlert' import MantecaDetailsCard, { MantecaCardRow } from '@/components/Global/MantecaDetailsCard' +import { sendLinksApi } from '@/services/sendLinks' import { MercadoPagoStep } from '@/types/manteca.types' -import { Dispatch, FC, SetStateAction } from 'react' +import { Dispatch, FC, SetStateAction, useState } from 'react' interface MantecaReviewStepProps { setCurrentStep: Dispatch> + withdrawDetails: MantecaWithdrawResponseData | undefined + claimLink: string } -const MantecaReviewStep: FC = ({ setCurrentStep }) => { +const MantecaReviewStep: FC = ({ setCurrentStep, withdrawDetails, claimLink }) => { + const [isSubmitting, setIsSubmitting] = useState(false) + const [error, setError] = useState(null) + const detailsCardRows: MantecaCardRow[] = [ - { - key: 'fullName', - label: 'Full name', - value: 'Manuel Rodríguez Roldán', - allowCopy: true, - }, - { - key: 'cuilCuit', - label: '[CUIL/CUIT]', - value: '20-39951628-6', - allowCopy: true, - }, { key: 'alias', label: 'Alias', - value: 'manurr.mp', + value: withdrawDetails?.stages[2].destination.address, allowCopy: true, }, { key: 'exchangeRate', label: 'Exchange Rate', - value: '1 USD = 1200 ARS', - allowCopy: true, + value: `1 USD = ${withdrawDetails?.details.price} ARS`, }, { key: 'fee', label: 'Fee', - value: '1000', - allowCopy: true, + value: 'Sponsored by Peanut', hideBottomBorder: true, }, ] + const handleWithdraw = async () => { + if (withdrawDetails) { + try { + setIsSubmitting(true) + const destinationAddress = withdrawDetails.stages[2].destination.address + await sendLinksApi.claim(destinationAddress, claimLink, destinationAddress) + setCurrentStep(MercadoPagoStep.SUCCESS) + } catch (error) { + setError(error instanceof Error ? error.message : 'Something went wrong') + console.error('Error claiming link:', error) + } finally { + setIsSubmitting(false) + } + } + } + return ( <> - diff --git a/src/services/manteca.ts b/src/services/manteca.ts deleted file mode 100644 index 602c4c7ca..000000000 --- a/src/services/manteca.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { PEANUT_API_URL } from '@/constants' -import { fetchWithSentry } from '@/utils' -import Cookies from 'js-cookie' - -export type WithdrawRequestType = { - amount: string - accountId: string - depositAddress: string -} - -export const mantecaApi = { - initiateWithdraw: async (data: WithdrawRequestType) => { - const response = await fetchWithSentry(`${PEANUT_API_URL}/manteca/withdraw`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${Cookies.get('jwt-token')}`, - }, - body: JSON.stringify(data), - }) - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})) - throw new Error(errorData.message || `Withdraw failed: ${response.statusText}`) - } - - return response.json() - }, -} diff --git a/src/services/sendLinks.ts b/src/services/sendLinks.ts index 0571ef076..eca43ce15 100644 --- a/src/services/sendLinks.ts +++ b/src/services/sendLinks.ts @@ -197,12 +197,13 @@ export const sendLinksApi = { * * @param recipient - The recipient's address or username * @param link - The link to claim + * @param destinationAddress - The destination address to claim the link to (for manteca claims) * @returns The claim link data */ - claim: async (recipient: string, link: string): Promise => { + claim: async (recipient: string, link: string, destinationAddress?: string): Promise => { const params = getParamsFromLink(link) const pubKey = generateKeysFromString(params.password).address - return await claimSendLink(pubKey, recipient, params.password) + return await claimSendLink(pubKey, recipient, params.password, destinationAddress) }, /** From 7e84b757511f7f21af5da6ad01d17fb01b24df5f Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Fri, 12 Sep 2025 20:23:19 +0530 Subject: [PATCH 06/13] refactor: enhance MantecaDepositCard to dynamically manage row visibility and improve rendering of account details --- .../components/MantecaDepositCard.tsx | 22 ++++++++++++------- .../Claim/Link/MantecaFlowManager.tsx | 6 ++++- .../Global/MantecaDetailsCard/index.tsx | 4 ++-- .../Global/PeanutActionDetailsCard/index.tsx | 5 ++--- .../Payment/Views/MantecaFulfuilment.view.tsx | 1 + 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/components/AddMoney/components/MantecaDepositCard.tsx b/src/components/AddMoney/components/MantecaDepositCard.tsx index 46bdf551a..cef3af572 100644 --- a/src/components/AddMoney/components/MantecaDepositCard.tsx +++ b/src/components/AddMoney/components/MantecaDepositCard.tsx @@ -39,16 +39,22 @@ const MantecaDepositCard = ({ />

Account details

- { + const rows = [ + ...(cbu + ? [{ key: 'cbu', label: 'CBU', value: cbu, allowCopy: true, hideBottomBorder: false }] + : []), + ...(alias ? [{ key: 'alias', label: 'Alias', value: alias, hideBottomBorder: false }] : []), ...(depositAddress - ? [{ key: 'deposit', label: 'Deposit Address', value: depositAddress, hideBottomBorder: true }] + ? [{ key: 'deposit', label: 'Deposit Address', value: depositAddress, hideBottomBorder: false }] : []), - ...(pixKey ? [{ key: 'pix', label: 'Pix Key', value: pixKey, hideBottomBorder: true }] : []), - ]} - /> + ...(pixKey ? [{ key: 'pix', label: 'Pix Key', value: pixKey, hideBottomBorder: false }] : []), + ] + if (rows.length) { + rows[rows.length - 1].hideBottomBorder = true + } + return + })()}
) } diff --git a/src/components/Claim/Link/MantecaFlowManager.tsx b/src/components/Claim/Link/MantecaFlowManager.tsx index e32d362fa..43809341d 100644 --- a/src/components/Claim/Link/MantecaFlowManager.tsx +++ b/src/components/Claim/Link/MantecaFlowManager.tsx @@ -46,7 +46,11 @@ const MantecaFlowManager: FC = ({ claimLinkData, amount } if (currentStep === MercadoPagoStep.SUCCESS) { - return + return ( + + ) } return null } diff --git a/src/components/Global/MantecaDetailsCard/index.tsx b/src/components/Global/MantecaDetailsCard/index.tsx index 45fe3e4a5..346d46413 100644 --- a/src/components/Global/MantecaDetailsCard/index.tsx +++ b/src/components/Global/MantecaDetailsCard/index.tsx @@ -13,8 +13,8 @@ interface MantecaDetailsCardProps { const MantecaDetailsCard: FC = ({ rows }) => { return ( - {rows.map((row) => ( - + {rows.map(({ key, ...row }) => ( + ))} ) diff --git a/src/components/Global/PeanutActionDetailsCard/index.tsx b/src/components/Global/PeanutActionDetailsCard/index.tsx index eaf3af2fc..3b1e875c7 100644 --- a/src/components/Global/PeanutActionDetailsCard/index.tsx +++ b/src/components/Global/PeanutActionDetailsCard/index.tsx @@ -9,9 +9,8 @@ import Attachment from '../Attachment' import Card from '../Card' import { Icon, IconName } from '../Icons/Icon' import RouteExpiryTimer from '../RouteExpiryTimer' -import Image from 'next/image' +import Image, { type StaticImageData } from 'next/image' import Loading from '../Loading' -import { StaticImport } from 'next/dist/shared/lib/get-img-props' export type PeanutActionDetailsCardTransactionType = | 'REQUEST' @@ -49,7 +48,7 @@ export interface PeanutActionDetailsCardProps { disableTimerRefetch?: boolean timerError?: string | null isLoading?: boolean - logo?: StaticImport + logo?: StaticImageData } export default function PeanutActionDetailsCard({ diff --git a/src/components/Payment/Views/MantecaFulfuilment.view.tsx b/src/components/Payment/Views/MantecaFulfuilment.view.tsx index f4650681a..49e2c9199 100644 --- a/src/components/Payment/Views/MantecaFulfuilment.view.tsx +++ b/src/components/Payment/Views/MantecaFulfuilment.view.tsx @@ -24,6 +24,7 @@ const MantecaFulfuilment = () => { }), refetchOnWindowFocus: false, staleTime: Infinity, // don't refetch the data + enabled: Boolean(chargeDetails?.uuid), }) const generateShareText = () => { From c7e8c4c28a49752e3780c4524c2df393f6fc790e Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Fri, 12 Sep 2025 20:25:44 +0530 Subject: [PATCH 07/13] refactor: update AvatarWithBadge to use StaticImageData for logo prop and enhance alt text for accessibility --- src/components/Profile/AvatarWithBadge.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/Profile/AvatarWithBadge.tsx b/src/components/Profile/AvatarWithBadge.tsx index 3f6b5c6cf..410249741 100644 --- a/src/components/Profile/AvatarWithBadge.tsx +++ b/src/components/Profile/AvatarWithBadge.tsx @@ -4,8 +4,7 @@ import React, { useMemo } from 'react' import { twMerge } from 'tailwind-merge' import { Icon, IconName } from '../Global/Icons/Icon' import StatusPill, { StatusPillType } from '../Global/StatusPill' -import { StaticImport } from 'next/dist/shared/lib/get-img-props' -import Image from 'next/image' +import Image, { type StaticImageData } from 'next/image' export type AvatarSize = 'extra-small' | 'small' | 'medium' | 'large' @@ -22,7 +21,7 @@ interface AvatarWithBadgeProps { iconFillColor?: string showStatusPill?: boolean statusPillStatus?: StatusPillType - logo?: StaticImport + logo?: StaticImageData } /** @@ -74,7 +73,7 @@ const AvatarWithBadge: React.FC = ({ > {''} Date: Tue, 16 Sep 2025 12:13:26 +0530 Subject: [PATCH 08/13] fix: typo and add suggested changes --- src/app/[...recipient]/client.tsx | 13 ++--- src/app/actions/manteca.ts | 2 +- src/app/actions/onramp.ts | 1 - src/app/actions/types/manteca.types.ts | 55 ------------------ .../Claim/Link/MantecaFlowManager.tsx | 3 +- .../Link/views/MantecaDetailsStep.view.tsx | 3 +- .../Claim/Link/views/MantecaReviewStep.tsx | 3 +- src/components/Common/ActionList.tsx | 4 +- src/components/Payment/PaymentForm/index.tsx | 8 +-- ....view.tsx => MantecaFulfuillment.view.tsx} | 8 +-- src/context/RequestFulfillmentFlowContext.tsx | 14 ++--- src/types/manteca.types.ts | 56 +++++++++++++++++++ 12 files changed, 83 insertions(+), 87 deletions(-) delete mode 100644 src/app/actions/types/manteca.types.ts rename src/components/Payment/Views/{MantecaFulfuilment.view.tsx => MantecaFulfuillment.view.tsx} (95%) diff --git a/src/app/[...recipient]/client.tsx b/src/app/[...recipient]/client.tsx index 0b11d7e6e..eca476023 100644 --- a/src/app/[...recipient]/client.tsx +++ b/src/app/[...recipient]/client.tsx @@ -61,7 +61,7 @@ export default function PaymentPage({ recipient, flow = 'request_pay' }: Props) const [currencyAmount, setCurrencyAmount] = useState('') const { isDrawerOpen, selectedTransaction, openTransactionDetails } = useTransactionDetailsDrawer() const [isLinkCancelling, setisLinkCancelling] = useState(false) - const { showExternalWalletFulfilMethods, showRequestFulfilmentBankFlowManager, fulfilUsingManteca } = + const { showExternalWalletFulfilMethods, showRequestFulfilmentBankFlowManager, fulfillUsingManteca } = useRequestFulfillmentFlow() // determine if the current user is the recipient of the transaction @@ -385,11 +385,10 @@ export default function PaymentPage({ recipient, flow = 'request_pay' }: Props) } }, [transactionForDrawer, currentView, dispatch, openTransactionDetails, isExternalWalletFlow, chargeId]) - let showActionList = flow !== 'direct_pay' - - if (flow === 'direct_pay' && !user) { - showActionList = true - } + const showActionList = + flow !== 'direct_pay' || // Always show for non-direct-pay flows + (flow === 'direct_pay' && !user) || // Show for direct-pay when user is not logged in + !fulfillUsingManteca // Show when not fulfilling using Manteca if (error) { return ( @@ -483,7 +482,7 @@ export default function PaymentPage({ recipient, flow = 'request_pay' }: Props) currencyAmount={currencyAmount} />
- {!fulfilUsingManteca && showActionList && ( + {showActionList && ( + if (fulfillUsingManteca) { + return } return ( diff --git a/src/components/Payment/Views/MantecaFulfuilment.view.tsx b/src/components/Payment/Views/MantecaFulfuillment.view.tsx similarity index 95% rename from src/components/Payment/Views/MantecaFulfuilment.view.tsx rename to src/components/Payment/Views/MantecaFulfuillment.view.tsx index 49e2c9199..2fcb25d23 100644 --- a/src/components/Payment/Views/MantecaFulfuilment.view.tsx +++ b/src/components/Payment/Views/MantecaFulfuillment.view.tsx @@ -11,8 +11,8 @@ import { usePaymentStore } from '@/redux/hooks' import { useQuery } from '@tanstack/react-query' import React from 'react' -const MantecaFulfuilment = () => { - const { setFulfilUsingManteca } = useRequestFulfillmentFlow() +const MantecaFulfuillment = () => { + const { setFulfillUsingManteca } = useRequestFulfillmentFlow() const { requestDetails, chargeDetails } = usePaymentStore() const { data: depositData, isLoading: isLoadingDeposit } = useQuery({ queryKey: ['manteca-deposit', chargeDetails?.uuid], @@ -44,7 +44,7 @@ const MantecaFulfuilment = () => { { - setFulfilUsingManteca(false) + setFulfillUsingManteca(false) }} /> @@ -107,4 +107,4 @@ const MantecaFulfuilment = () => { ) } -export default MantecaFulfuilment +export default MantecaFulfuillment diff --git a/src/context/RequestFulfillmentFlowContext.tsx b/src/context/RequestFulfillmentFlowContext.tsx index 0a86ab8c8..0fe808925 100644 --- a/src/context/RequestFulfillmentFlowContext.tsx +++ b/src/context/RequestFulfillmentFlowContext.tsx @@ -32,8 +32,8 @@ interface RequestFulfillmentFlowContextType { setShowVerificationModal: (show: boolean) => void requesterDetails: User | null setRequesterDetails: (details: User | null) => void - fulfilUsingManteca: boolean - setFulfilUsingManteca: (fulfilUsingManteca: boolean) => void + fulfillUsingManteca: boolean + setFulfillUsingManteca: (fulfillUsingManteca: boolean) => void } const RequestFulfillmentFlowContext = createContext(undefined) @@ -49,7 +49,7 @@ export const RequestFulfilmentFlowContextProvider: React.FC<{ children: ReactNod const [onrampData, setOnrampData] = useState(null) const [showVerificationModal, setShowVerificationModal] = useState(false) const [requesterDetails, setRequesterDetails] = useState(null) - const [fulfilUsingManteca, setFulfilUsingManteca] = useState(false) + const [fulfillUsingManteca, setFulfillUsingManteca] = useState(false) const resetFlow = useCallback(() => { setExternalWalletFulfilMethod(null) @@ -60,7 +60,7 @@ export const RequestFulfilmentFlowContextProvider: React.FC<{ children: ReactNod setOnrampData(null) setShowVerificationModal(false) setRequesterDetails(null) - setFulfilUsingManteca(false) + setFulfillUsingManteca(false) }, []) const value = useMemo( @@ -82,8 +82,8 @@ export const RequestFulfilmentFlowContextProvider: React.FC<{ children: ReactNod setShowVerificationModal, requesterDetails, setRequesterDetails, - fulfilUsingManteca, - setFulfilUsingManteca, + fulfillUsingManteca, + setFulfillUsingManteca, }), [ resetFlow, @@ -95,7 +95,7 @@ export const RequestFulfilmentFlowContextProvider: React.FC<{ children: ReactNod onrampData, showVerificationModal, requesterDetails, - fulfilUsingManteca, + fulfillUsingManteca, ] ) diff --git a/src/types/manteca.types.ts b/src/types/manteca.types.ts index b4e8efd5a..d60fca1e1 100644 --- a/src/types/manteca.types.ts +++ b/src/types/manteca.types.ts @@ -9,3 +9,59 @@ export enum MercadoPagoStep { REVIEW = 'review', SUCCESS = 'success', } + +export type MantecaWithdrawData = { + amount: string + destinationAddress: string +} + +export type MantecaWithdrawResponseData = { + id: string + numberId: string + userId: string + userNumberId: string + userExternalId: string + status: string + type: string + details: { + depositAddresses: { + ARBITRUM: string + } + depositAddress: string + depositAvailableNetworks: string[] + withdrawCostInAgainst: string + withdrawCostInAsset: string + price: string + priceExpireAt: string + } + currentStage: number + stages: { + 1: { + stageType: string + side: string + type: string + asset: string + against: string + assetAmount: string + price: string + priceCode: string + } + 2: { + stageType: string + asset: string + amount: string + to: string + destination: { + address: string + bankCode: string + } + } + } + creationTime: string + updatedAt: string +} + +export type MantecaWithdrawResponse = { + data?: MantecaWithdrawResponseData + error?: string +} From 2932f75a1b02263374a6b3b63be2c040b4dfbd67 Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Tue, 16 Sep 2025 19:03:01 +0530 Subject: [PATCH 09/13] refactor: Update flow --- src/app/actions/claimLinks.ts | 3 +- .../Claim/Link/MantecaFlowManager.tsx | 11 ++-- .../Link/views/MantecaDetailsStep.view.tsx | 53 +++++-------------- .../Claim/Link/views/MantecaReviewStep.tsx | 32 +++++++---- src/constants/index.ts | 1 + src/constants/manteca.consts.ts | 1 + src/services/sendLinks.ts | 4 +- src/types/manteca.types.ts | 2 + 8 files changed, 50 insertions(+), 57 deletions(-) create mode 100644 src/constants/manteca.consts.ts diff --git a/src/app/actions/claimLinks.ts b/src/app/actions/claimLinks.ts index 4a8ef217c..5de52eb72 100644 --- a/src/app/actions/claimLinks.ts +++ b/src/app/actions/claimLinks.ts @@ -82,7 +82,7 @@ export async function getNextDepositIndex(contractVersion: string): Promise = ({ claimLinkData, amount const { setClaimToMercadoPago } = useClaimBankFlow() const [currentStep, setCurrentStep] = useState(MercadoPagoStep.DETAILS) const router = useRouter() - const [withdrawDetails, setWithdrawDetails] = useState() + const [destinationAddress, setDestinationAddress] = useState('') const isSuccess = currentStep === MercadoPagoStep.SUCCESS @@ -28,9 +28,9 @@ const MantecaFlowManager: FC = ({ claimLinkData, amount if (currentStep === MercadoPagoStep.DETAILS) { return ( ) } @@ -38,8 +38,9 @@ const MantecaFlowManager: FC = ({ claimLinkData, amount return ( ) } diff --git a/src/components/Claim/Link/views/MantecaDetailsStep.view.tsx b/src/components/Claim/Link/views/MantecaDetailsStep.view.tsx index a0597ca54..199c27ed9 100644 --- a/src/components/Claim/Link/views/MantecaDetailsStep.view.tsx +++ b/src/components/Claim/Link/views/MantecaDetailsStep.view.tsx @@ -1,42 +1,24 @@ -import { mantecaWithdraw } from '@/app/actions/manteca' +'use client' + import { Button } from '@/components/0_Bruddle' import BaseInput from '@/components/0_Bruddle/BaseInput' -import ErrorAlert from '@/components/Global/ErrorAlert' import { Icon } from '@/components/Global/Icons/Icon' -import { MantecaWithdrawResponseData, MercadoPagoStep } from '@/types/manteca.types' -import { Dispatch, FC, SetStateAction, useCallback, useState } from 'react' +import { MercadoPagoStep } from '@/types/manteca.types' +import { Dispatch, FC, SetStateAction } from 'react' interface MantecaDetailsStepProps { setCurrentStep: Dispatch> - amount: string - setWithdrawDetails: Dispatch> + destinationAddress: string + setDestinationAddress: Dispatch> } -const MantecaDetailsStep: FC = ({ setCurrentStep, amount, setWithdrawDetails }) => { - const [destinationAddress, setDestinationAddress] = useState('') - const [isSubmitting, setIsSubmitting] = useState(false) - const [error, setError] = useState(null) - - const createMantecaWithdraw = async () => { - setError(null) - setIsSubmitting(true) - const response = await mantecaWithdraw({ - amount, - destinationAddress, - }) - - if (response.error) { - console.error(response.error) - setError(response.error) - setIsSubmitting(false) - return - } - - if (response.data) { - setWithdrawDetails(response.data) - setCurrentStep(MercadoPagoStep.REVIEW) - } - setIsSubmitting(false) +const MantecaDetailsStep: FC = ({ + setCurrentStep, + destinationAddress, + setDestinationAddress, +}) => { + const handleOnClick = async () => { + setCurrentStep(MercadoPagoStep.REVIEW) } return ( @@ -53,14 +35,7 @@ const MantecaDetailsStep: FC = ({ setCurrentStep, amoun You can only withdraw to accounts under your name.
- {error && } - - diff --git a/src/components/Claim/Link/views/MantecaReviewStep.tsx b/src/components/Claim/Link/views/MantecaReviewStep.tsx index 79027a88e..b330ed7bc 100644 --- a/src/components/Claim/Link/views/MantecaReviewStep.tsx +++ b/src/components/Claim/Link/views/MantecaReviewStep.tsx @@ -1,31 +1,37 @@ +import { mantecaWithdraw } from '@/app/actions/manteca' import { Button } from '@/components/0_Bruddle' import ErrorAlert from '@/components/Global/ErrorAlert' import MantecaDetailsCard, { MantecaCardRow } from '@/components/Global/MantecaDetailsCard' +import PeanutLoading from '@/components/Global/PeanutLoading' +import { MANTECA_DEPOSIT_ADDRESS } from '@/constants' +import { useCurrency } from '@/hooks/useCurrency' import { sendLinksApi } from '@/services/sendLinks' -import { MantecaWithdrawResponseData, MercadoPagoStep } from '@/types/manteca.types' +import { MercadoPagoStep } from '@/types/manteca.types' import { Dispatch, FC, SetStateAction, useState } from 'react' interface MantecaReviewStepProps { setCurrentStep: Dispatch> - withdrawDetails: MantecaWithdrawResponseData | undefined claimLink: string + destinationAddress: string + amount: string } -const MantecaReviewStep: FC = ({ setCurrentStep, withdrawDetails, claimLink }) => { +const MantecaReviewStep: FC = ({ setCurrentStep, claimLink, destinationAddress, amount }) => { const [isSubmitting, setIsSubmitting] = useState(false) const [error, setError] = useState(null) + const { price, isLoading } = useCurrency('ARS') // TODO: change to the currency of the selected Method const detailsCardRows: MantecaCardRow[] = [ { key: 'alias', label: 'Alias', - value: withdrawDetails?.stages[2].destination.address, + value: destinationAddress, allowCopy: true, }, { key: 'exchangeRate', label: 'Exchange Rate', - value: `1 USD = ${withdrawDetails?.details.price} ARS`, + value: `1 USD = ${price?.buy} ARS`, }, { key: 'fee', @@ -34,13 +40,17 @@ const MantecaReviewStep: FC = ({ setCurrentStep, withdra hideBottomBorder: true, }, ] - const handleWithdraw = async () => { - if (withdrawDetails) { + if (destinationAddress) { try { setIsSubmitting(true) - const destinationAddress = withdrawDetails.stages[2].destination.address - await sendLinksApi.claim(destinationAddress, claimLink, destinationAddress) + const claimResponse = await sendLinksApi.claim(MANTECA_DEPOSIT_ADDRESS, claimLink) + await mantecaWithdraw({ + amount, + destinationAddress, + txHash: claimResponse.claim?.txHash ?? '', + currency: 'ARS', + }) setCurrentStep(MercadoPagoStep.SUCCESS) } catch (error) { setError(error instanceof Error ? error.message : 'Something went wrong') @@ -51,6 +61,10 @@ const MantecaReviewStep: FC = ({ setCurrentStep, withdra } } + if (isLoading) { + return + } + return ( <> diff --git a/src/constants/index.ts b/src/constants/index.ts index 5b2a7124c..3214ac1ff 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -4,3 +4,4 @@ export * from './general.consts' export * from './loadingStates.consts' export * from './query.consts' export * from './zerodev.consts' +export * from './manteca.consts' diff --git a/src/constants/manteca.consts.ts b/src/constants/manteca.consts.ts new file mode 100644 index 000000000..9330c63dd --- /dev/null +++ b/src/constants/manteca.consts.ts @@ -0,0 +1 @@ +export const MANTECA_DEPOSIT_ADDRESS = '0x959e088a09f61aB01cb83b0eBCc74b2CF6d62053' diff --git a/src/services/sendLinks.ts b/src/services/sendLinks.ts index 5b8d1b9ce..97c4f9aed 100644 --- a/src/services/sendLinks.ts +++ b/src/services/sendLinks.ts @@ -200,10 +200,10 @@ export const sendLinksApi = { * @param destinationAddress - The destination address to claim the link to (for manteca claims) * @returns The claim link data */ - claim: async (recipient: string, link: string, destinationAddress?: string): Promise => { + claim: async (recipient: string, link: string): Promise => { const params = getParamsFromLink(link) const pubKey = generateKeysFromString(params.password).address - return await claimSendLink(pubKey, recipient, params.password, destinationAddress) + return await claimSendLink(pubKey, recipient, params.password) }, /** diff --git a/src/types/manteca.types.ts b/src/types/manteca.types.ts index d60fca1e1..a9c57006d 100644 --- a/src/types/manteca.types.ts +++ b/src/types/manteca.types.ts @@ -13,6 +13,8 @@ export enum MercadoPagoStep { export type MantecaWithdrawData = { amount: string destinationAddress: string + txHash: string + currency: string } export type MantecaWithdrawResponseData = { From 11ed3ecb495764d423e2dd19a83ca60a0f6b61b8 Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Tue, 16 Sep 2025 19:59:00 +0530 Subject: [PATCH 10/13] move manteca APIs to service --- src/app/actions/onramp.ts | 52 --------------- .../RegionalMethods/MercadoPago/index.tsx | 4 +- .../Claim/Link/views/MantecaReviewStep.tsx | 4 +- .../Views/MantecaFulfuillment.view.tsx | 4 +- src/services/manteca.ts | 66 +++++++++++++++++++ src/types/manteca.types.ts | 6 ++ 6 files changed, 78 insertions(+), 58 deletions(-) diff --git a/src/app/actions/onramp.ts b/src/app/actions/onramp.ts index 693376bed..053e3f2e3 100644 --- a/src/app/actions/onramp.ts +++ b/src/app/actions/onramp.ts @@ -3,7 +3,6 @@ import { cookies } from 'next/headers' import { fetchWithSentry } from '@/utils' import { CountryData } from '@/components/AddMoney/consts' -import { MantecaDepositDetails } from '@/types/manteca.types' import { getCurrencyConfig } from '@/utils/bridge.utils' import { getCurrencyPrice } from '@/app/actions/currency' @@ -113,54 +112,3 @@ export async function createOnrampForGuest( return { error: 'An unexpected error occurred.' } } } - -interface CreateMantecaOnrampParams { - usdAmount: string - currency: string - chargeId?: string -} - -export async function createMantecaOnramp( - params: CreateMantecaOnrampParams -): Promise<{ data?: MantecaDepositDetails; error?: string }> { - const apiUrl = process.env.PEANUT_API_URL - const cookieStore = cookies() - const jwtToken = (await cookieStore).get('jwt-token')?.value - - if (!apiUrl || !API_KEY) { - console.error('API URL or API Key is not configured.') - return { error: 'Server configuration error.' } - } - - try { - const response = await fetchWithSentry(`${apiUrl}/manteca/deposit`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${jwtToken}`, - 'api-key': API_KEY, - }, - body: JSON.stringify({ - usdAmount: params.usdAmount, - currency: params.currency, - chargeId: params.chargeId, - }), - }) - - const data = await response.json() - - if (!response.ok) { - console.log('error', response) - return { error: data.error || 'Failed to create on-ramp transfer for guest.' } - } - - return { data } - } catch (error) { - console.log('error', error) - console.error('Error calling create manteca on-ramp API:', error) - if (error instanceof Error) { - return { error: error.message } - } - return { error: 'An unexpected error occurred.' } - } -} diff --git a/src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx b/src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx index d7e0ff9ee..d5e2e0493 100644 --- a/src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx +++ b/src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx @@ -1,10 +1,10 @@ import React, { FC, useMemo, useState } from 'react' import MercadoPagoDepositDetails from './MercadoPagoDepositDetails' import InputAmountStep from '../../InputAmountStep' -import { createMantecaOnramp } from '@/app/actions/onramp' import { useParams } from 'next/navigation' import { countryData } from '@/components/AddMoney/consts' import { MantecaDepositDetails } from '@/types/manteca.types' +import { mantecaApi } from '@/services/manteca' interface MercadoPagoProps { source: 'bank' | 'regionalMethod' @@ -33,7 +33,7 @@ const MercadoPago: FC = ({ source }) => { try { setError(null) setIsCreatingDeposit(true) - const depositData = await createMantecaOnramp({ + const depositData = await mantecaApi.deposit({ usdAmount: tokenUSDAmount.replace(/,/g, ''), currency: selectedCountry.currency, }) diff --git a/src/components/Claim/Link/views/MantecaReviewStep.tsx b/src/components/Claim/Link/views/MantecaReviewStep.tsx index b330ed7bc..566324fdb 100644 --- a/src/components/Claim/Link/views/MantecaReviewStep.tsx +++ b/src/components/Claim/Link/views/MantecaReviewStep.tsx @@ -1,10 +1,10 @@ -import { mantecaWithdraw } from '@/app/actions/manteca' import { Button } from '@/components/0_Bruddle' import ErrorAlert from '@/components/Global/ErrorAlert' import MantecaDetailsCard, { MantecaCardRow } from '@/components/Global/MantecaDetailsCard' import PeanutLoading from '@/components/Global/PeanutLoading' import { MANTECA_DEPOSIT_ADDRESS } from '@/constants' import { useCurrency } from '@/hooks/useCurrency' +import { mantecaApi } from '@/services/manteca' import { sendLinksApi } from '@/services/sendLinks' import { MercadoPagoStep } from '@/types/manteca.types' import { Dispatch, FC, SetStateAction, useState } from 'react' @@ -45,7 +45,7 @@ const MantecaReviewStep: FC = ({ setCurrentStep, claimLi try { setIsSubmitting(true) const claimResponse = await sendLinksApi.claim(MANTECA_DEPOSIT_ADDRESS, claimLink) - await mantecaWithdraw({ + await mantecaApi.withdraw({ amount, destinationAddress, txHash: claimResponse.claim?.txHash ?? '', diff --git a/src/components/Payment/Views/MantecaFulfuillment.view.tsx b/src/components/Payment/Views/MantecaFulfuillment.view.tsx index 2fcb25d23..00e19583d 100644 --- a/src/components/Payment/Views/MantecaFulfuillment.view.tsx +++ b/src/components/Payment/Views/MantecaFulfuillment.view.tsx @@ -1,4 +1,3 @@ -import { createMantecaOnramp } from '@/app/actions/onramp' import { MERCADO_PAGO } from '@/assets' import ErrorAlert from '@/components/Global/ErrorAlert' import MantecaDetailsCard from '@/components/Global/MantecaDetailsCard' @@ -8,6 +7,7 @@ import PeanutLoading from '@/components/Global/PeanutLoading' import ShareButton from '@/components/Global/ShareButton' import { useRequestFulfillmentFlow } from '@/context/RequestFulfillmentFlowContext' import { usePaymentStore } from '@/redux/hooks' +import { mantecaApi } from '@/services/manteca' import { useQuery } from '@tanstack/react-query' import React from 'react' @@ -17,7 +17,7 @@ const MantecaFulfuillment = () => { const { data: depositData, isLoading: isLoadingDeposit } = useQuery({ queryKey: ['manteca-deposit', chargeDetails?.uuid], queryFn: () => - createMantecaOnramp({ + mantecaApi.deposit({ usdAmount: requestDetails?.tokenAmount || chargeDetails?.tokenAmount || '0', currency: 'ARS', chargeId: chargeDetails?.uuid, diff --git a/src/services/manteca.ts b/src/services/manteca.ts index 4b18f254c..7a6204a4b 100644 --- a/src/services/manteca.ts +++ b/src/services/manteca.ts @@ -1,4 +1,10 @@ import { PEANUT_API_URL, PEANUT_API_KEY } from '@/constants' +import { + MantecaDepositDetails, + MantecaWithdrawData, + MantecaWithdrawResponse, + CreateMantecaOnrampParams, +} from '@/types/manteca.types' import { fetchWithSentry } from '@/utils' import Cookies from 'js-cookie' import type { Address, Hash } from 'viem' @@ -142,4 +148,64 @@ export const mantecaApi = { return response.json() }, + + deposit: async (params: CreateMantecaOnrampParams): Promise<{ data?: MantecaDepositDetails; error?: string }> => { + try { + const response = await fetchWithSentry(`${PEANUT_API_URL}/manteca/deposit`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${Cookies.get('jwt-token')}`, + }, + body: JSON.stringify({ + usdAmount: params.usdAmount, + currency: params.currency, + chargeId: params.chargeId, + }), + }) + + const data = await response.json() + + if (!response.ok) { + console.log('error', response) + return { error: data.error || 'Failed to create on-ramp transfer for guest.' } + } + + return { data } + } catch (error) { + console.log('error', error) + console.error('Error calling create manteca on-ramp API:', error) + if (error instanceof Error) { + return { error: error.message } + } + return { error: 'An unexpected error occurred.' } + } + }, + + withdraw: async (data: MantecaWithdrawData): Promise => { + try { + const response = await fetchWithSentry(`${PEANUT_API_URL}/manteca/withdraw`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${Cookies.get('jwt-token')}`, + }, + body: JSON.stringify(data), + }) + + const result = await response.json() + if (!response.ok) { + return { error: result.error || 'Failed to create manteca withdraw.' } + } + + return { data: result } + } catch (error) { + console.log('error', error) + console.error('Error calling create manteca withdraw API:', error) + if (error instanceof Error) { + return { error: error.message } + } + return { error: 'An unexpected error occurred.' } + } + }, } diff --git a/src/types/manteca.types.ts b/src/types/manteca.types.ts index a9c57006d..7ac881617 100644 --- a/src/types/manteca.types.ts +++ b/src/types/manteca.types.ts @@ -67,3 +67,9 @@ export type MantecaWithdrawResponse = { data?: MantecaWithdrawResponseData error?: string } + +export interface CreateMantecaOnrampParams { + usdAmount: string + currency: string + chargeId?: string +} From b71d7db0cd0464c875069da982867531fb50486f Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Tue, 16 Sep 2025 20:02:22 +0530 Subject: [PATCH 11/13] chore: add 'use client' directive to MantecaFlowManager component --- src/components/Claim/Link/MantecaFlowManager.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Claim/Link/MantecaFlowManager.tsx b/src/components/Claim/Link/MantecaFlowManager.tsx index 8bd526fc4..ead46215f 100644 --- a/src/components/Claim/Link/MantecaFlowManager.tsx +++ b/src/components/Claim/Link/MantecaFlowManager.tsx @@ -1,3 +1,5 @@ +'use client' + import { MERCADO_PAGO } from '@/assets' import NavHeader from '@/components/Global/NavHeader' import PeanutActionDetailsCard from '@/components/Global/PeanutActionDetailsCard' From d6dd05ff8bc3ed89a7e58348436a8407c4c1dc6a Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Tue, 16 Sep 2025 20:16:03 +0530 Subject: [PATCH 12/13] fix: improve error handling and transaction hash validation in MantecaReviewStep --- .../Claim/Link/views/MantecaReviewStep.tsx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/components/Claim/Link/views/MantecaReviewStep.tsx b/src/components/Claim/Link/views/MantecaReviewStep.tsx index 566324fdb..2ea5bb388 100644 --- a/src/components/Claim/Link/views/MantecaReviewStep.tsx +++ b/src/components/Claim/Link/views/MantecaReviewStep.tsx @@ -43,17 +43,27 @@ const MantecaReviewStep: FC = ({ setCurrentStep, claimLi const handleWithdraw = async () => { if (destinationAddress) { try { + setError(null) setIsSubmitting(true) const claimResponse = await sendLinksApi.claim(MANTECA_DEPOSIT_ADDRESS, claimLink) - await mantecaApi.withdraw({ - amount, + const txHash = claimResponse?.claim?.txHash + if (!txHash) { + setError('Claim failed: missing transaction hash.') + return + } + const { data, error: withdrawError } = await mantecaApi.withdraw({ + amount: amount.replace(/,/g, ''), destinationAddress, - txHash: claimResponse.claim?.txHash ?? '', - currency: 'ARS', + txHash, + currency: 'ARS', // TODO: source-selected currency }) + if (withdrawError || !data) { + setError(withdrawError || 'Something went wrong. Please contact Support') + return + } setCurrentStep(MercadoPagoStep.SUCCESS) } catch (error) { - setError(error instanceof Error ? error.message : 'Something went wrong') + setError(error instanceof Error ? error.message : 'Something went wrong. Please contact Support') console.error('Error claiming link:', error) } finally { setIsSubmitting(false) From 4a749911fc9e2379b2f23b5cd3223481d00b19a0 Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Tue, 16 Sep 2025 22:47:53 +0530 Subject: [PATCH 13/13] fix: typo --- src/components/Payment/PaymentForm/index.tsx | 4 ++-- ...ntecaFulfuillment.view.tsx => MantecaFulfillment.view.tsx} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/components/Payment/Views/{MantecaFulfuillment.view.tsx => MantecaFulfillment.view.tsx} (98%) diff --git a/src/components/Payment/PaymentForm/index.tsx b/src/components/Payment/PaymentForm/index.tsx index b35515830..b1163a147 100644 --- a/src/components/Payment/PaymentForm/index.tsx +++ b/src/components/Payment/PaymentForm/index.tsx @@ -36,7 +36,7 @@ import { useAccount } from 'wagmi' import { useUserInteractions } from '@/hooks/useUserInteractions' import { useUserByUsername } from '@/hooks/useUserByUsername' import { PaymentFlow } from '@/app/[...recipient]/client' -import MantecaFulfuillment from '../Views/MantecaFulfuillment.view' +import MantecaFulfillment from '../Views/MantecaFulfillment.view' export type PaymentFlowProps = { isPintaReq?: boolean @@ -642,7 +642,7 @@ export const PaymentForm = ({ } if (fulfillUsingManteca) { - return + return } return ( diff --git a/src/components/Payment/Views/MantecaFulfuillment.view.tsx b/src/components/Payment/Views/MantecaFulfillment.view.tsx similarity index 98% rename from src/components/Payment/Views/MantecaFulfuillment.view.tsx rename to src/components/Payment/Views/MantecaFulfillment.view.tsx index 00e19583d..7f5618669 100644 --- a/src/components/Payment/Views/MantecaFulfuillment.view.tsx +++ b/src/components/Payment/Views/MantecaFulfillment.view.tsx @@ -11,7 +11,7 @@ import { mantecaApi } from '@/services/manteca' import { useQuery } from '@tanstack/react-query' import React from 'react' -const MantecaFulfuillment = () => { +const MantecaFulfillment = () => { const { setFulfillUsingManteca } = useRequestFulfillmentFlow() const { requestDetails, chargeDetails } = usePaymentStore() const { data: depositData, isLoading: isLoadingDeposit } = useQuery({ @@ -107,4 +107,4 @@ const MantecaFulfuillment = () => { ) } -export default MantecaFulfuillment +export default MantecaFulfillment