From 0ba4c37dbd4526bd4b284f7ee311a643e8d2c9dc Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Wed, 17 Sep 2025 20:35:47 +0530 Subject: [PATCH 1/4] Enable claim to bank in claim flow --- .../Claim/Link/MantecaFlowManager.tsx | 8 ++++++-- .../Claim/Link/views/MantecaReviewStep.tsx | 15 +++++++++++---- src/components/Common/CountryList.tsx | 2 +- src/components/Common/CountryListRouter.tsx | 17 ++++++++++++++--- .../Global/PeanutActionDetailsCard/index.tsx | 17 +++++++++++------ 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/components/Claim/Link/MantecaFlowManager.tsx b/src/components/Claim/Link/MantecaFlowManager.tsx index ead46215f..5e122af53 100644 --- a/src/components/Claim/Link/MantecaFlowManager.tsx +++ b/src/components/Claim/Link/MantecaFlowManager.tsx @@ -19,12 +19,14 @@ interface MantecaFlowManagerProps { } const MantecaFlowManager: FC = ({ claimLinkData, amount, attachment }) => { - const { setClaimToMercadoPago } = useClaimBankFlow() + const { setClaimToMercadoPago, selectedCountry } = useClaimBankFlow() const [currentStep, setCurrentStep] = useState(MercadoPagoStep.DETAILS) const router = useRouter() const [destinationAddress, setDestinationAddress] = useState('') const isSuccess = currentStep === MercadoPagoStep.SUCCESS + const selectedCurrency = selectedCountry?.currency || 'ARS' + const logo = selectedCountry?.id ? undefined : MERCADO_PAGO const renderStepDetails = () => { if (currentStep === MercadoPagoStep.DETAILS) { @@ -43,6 +45,7 @@ const MantecaFlowManager: FC = ({ claimLinkData, amount claimLink={claimLinkData.link} destinationAddress={destinationAddress} amount={amount} + currency={selectedCurrency} /> ) } @@ -88,7 +91,8 @@ const MantecaFlowManager: FC = ({ claimLinkData, amount tokenSymbol={claimLinkData.tokenSymbol} message={attachment.message} fileUrl={attachment.attachmentUrl} - logo={isSuccess ? undefined : MERCADO_PAGO} + logo={isSuccess ? undefined : logo} + countryCodeForFlag={selectedCountry?.id.toLowerCase()} /> {renderStepDetails()} diff --git a/src/components/Claim/Link/views/MantecaReviewStep.tsx b/src/components/Claim/Link/views/MantecaReviewStep.tsx index 2ea5bb388..fe1c882a0 100644 --- a/src/components/Claim/Link/views/MantecaReviewStep.tsx +++ b/src/components/Claim/Link/views/MantecaReviewStep.tsx @@ -14,12 +14,19 @@ interface MantecaReviewStepProps { claimLink: string destinationAddress: string amount: string + currency: string } -const MantecaReviewStep: FC = ({ setCurrentStep, claimLink, destinationAddress, amount }) => { +const MantecaReviewStep: FC = ({ + setCurrentStep, + claimLink, + destinationAddress, + amount, + currency, +}) => { 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 { price, isLoading } = useCurrency(currency) const detailsCardRows: MantecaCardRow[] = [ { @@ -31,7 +38,7 @@ const MantecaReviewStep: FC = ({ setCurrentStep, claimLi { key: 'exchangeRate', label: 'Exchange Rate', - value: `1 USD = ${price?.buy} ARS`, + value: `1 USD = ${price?.buy} ${currency}`, }, { key: 'fee', @@ -55,7 +62,7 @@ const MantecaReviewStep: FC = ({ setCurrentStep, claimLi amount: amount.replace(/,/g, ''), destinationAddress, txHash, - currency: 'ARS', // TODO: source-selected currency + currency, }) if (withdrawError || !data) { setError(withdrawError || 'Something went wrong. Please contact Support') diff --git a/src/components/Common/CountryList.tsx b/src/components/Common/CountryList.tsx index 22ab13a5e..f79635851 100644 --- a/src/components/Common/CountryList.tsx +++ b/src/components/Common/CountryList.tsx @@ -135,7 +135,7 @@ export const CountryList = ({ isSupported = isBridgeSupportedCountry || isMantecaSupportedCountry } else if (viewMode === 'claim-request') { // support all countries - isSupported = isBridgeSupportedCountry + isSupported = isBridgeSupportedCountry || isMantecaSupportedCountry } else { // support all countries isSupported = true diff --git a/src/components/Common/CountryListRouter.tsx b/src/components/Common/CountryListRouter.tsx index e11b4ae85..c4b584759 100644 --- a/src/components/Common/CountryListRouter.tsx +++ b/src/components/Common/CountryListRouter.tsx @@ -9,7 +9,7 @@ import { formatUnits } from 'viem' import { formatTokenAmount, printableAddress } from '@/utils/general.utils' import { CountryList } from '@/components/Common/CountryList' import { ClaimLinkData } from '@/services/sendLinks' -import { CountryData } from '@/components/AddMoney/consts' +import { CountryData, MantecaSupportedExchanges } from '@/components/AddMoney/consts' import useSavedAccounts from '@/hooks/useSavedAccounts' import { ParsedURL } from '@/lib/url-parser/types/payment' import { useCallback, useMemo } from 'react' @@ -41,7 +41,12 @@ export const CountryListRouter = ({ requestLinkData, inputTitle, }: ICountryListRouterViewProps) => { - const { setFlowStep: setClaimBankFlowStep, setSelectedCountry } = useClaimBankFlow() + const { + setFlowStep: setClaimBankFlowStep, + setSelectedCountry, + setClaimToMercadoPago, + setFlowStep, + } = useClaimBankFlow() const { setFlowStep: setRequestFulfilmentBankFlowStep, setShowRequestFulfilmentBankFlowManager, @@ -56,7 +61,13 @@ export const CountryListRouter = ({ const handleCountryClick = (country: CountryData) => { if (flow === 'claim') { setSelectedCountry(country) - setClaimBankFlowStep(ClaimBankFlowStep.BankDetailsForm) + const isMantecaSupportedCountry = Object.keys(MantecaSupportedExchanges).includes(country.id) + if (isMantecaSupportedCountry) { + setFlowStep(null) // reset the flow step to initial view first + setClaimToMercadoPago(true) + } else { + setClaimBankFlowStep(ClaimBankFlowStep.BankDetailsForm) + } } else if (flow === 'request') { setSelectedCountryForRequest(country) diff --git a/src/components/Global/PeanutActionDetailsCard/index.tsx b/src/components/Global/PeanutActionDetailsCard/index.tsx index 3b1e875c7..594441e7f 100644 --- a/src/components/Global/PeanutActionDetailsCard/index.tsx +++ b/src/components/Global/PeanutActionDetailsCard/index.tsx @@ -165,13 +165,15 @@ export default function PeanutActionDetailsCard({ const isWithdrawBankAccount = transactionType === 'WITHDRAW_BANK_ACCOUNT' && recipientType === 'BANK_ACCOUNT' const isAddBankAccount = transactionType === 'ADD_MONEY_BANK_ACCOUNT' const isClaimLinkBankAccount = transactionType === 'CLAIM_LINK_BANK_ACCOUNT' && recipientType === 'BANK_ACCOUNT' + const isRegionalMethodClaim = transactionType === 'REGIONAL_METHOD_CLAIM' const withdrawBankIcon = () => { const imgSrc = logo ? logo : `https://flagcdn.com/w320/${countryCodeForFlag}.png` - if (isWithdrawBankAccount || isAddBankAccount || isClaimLinkBankAccount) + + if (isWithdrawBankAccount || isAddBankAccount || isClaimLinkBankAccount || isRegionalMethodClaim) return (
- {countryCodeForFlag && ( + {(countryCodeForFlag || logo) && ( {`${countryCodeForFlag} )} -
- -
+ {!isRegionalMethodClaim && ( +
+ +
+ )}
) return undefined @@ -192,7 +196,8 @@ export default function PeanutActionDetailsCard({
- {viewType !== 'SUCCESS' && (isWithdrawBankAccount || isAddBankAccount || isClaimLinkBankAccount) ? ( + {viewType !== 'SUCCESS' && + (isWithdrawBankAccount || isAddBankAccount || isClaimLinkBankAccount || isRegionalMethodClaim) ? ( withdrawBankIcon() ) : ( Date: Wed, 17 Sep 2025 23:42:20 +0530 Subject: [PATCH 2/4] Enable Req fulfilment through bank using manteca --- src/app/[...recipient]/client.tsx | 3 +-- src/components/Common/CountryListRouter.tsx | 7 ++++++- .../Payment/Views/MantecaFulfillment.view.tsx | 12 +++++++++--- src/hooks/useCurrency.ts | 2 +- src/services/manteca.ts | 1 + 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/app/[...recipient]/client.tsx b/src/app/[...recipient]/client.tsx index aef205f4e..b65cfbb49 100644 --- a/src/app/[...recipient]/client.tsx +++ b/src/app/[...recipient]/client.tsx @@ -394,8 +394,7 @@ export default function PaymentPage({ recipient, flow = 'request_pay' }: Props) }, [transactionForDrawer, currentView, dispatch, openTransactionDetails, isExternalWalletFlow, chargeId]) 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 + (flow !== 'direct_pay' || (flow === 'direct_pay' && !user)) && // Show for direct-pay when user is not logged in !fulfillUsingManteca // Show when not fulfilling using Manteca // Send to bank step if its mentioned in the URL and guest KYC is not needed useEffect(() => { diff --git a/src/components/Common/CountryListRouter.tsx b/src/components/Common/CountryListRouter.tsx index c4b584759..9f66128d7 100644 --- a/src/components/Common/CountryListRouter.tsx +++ b/src/components/Common/CountryListRouter.tsx @@ -52,6 +52,7 @@ export const CountryListRouter = ({ setShowRequestFulfilmentBankFlowManager, setSelectedCountry: setSelectedCountryForRequest, setShowVerificationModal, + setFulfillUsingManteca, } = useRequestFulfillmentFlow() const savedAccounts = useSavedAccounts() const { chargeDetails } = usePaymentStore() @@ -59,9 +60,9 @@ export const CountryListRouter = ({ const { user } = useAuth() const handleCountryClick = (country: CountryData) => { + const isMantecaSupportedCountry = Object.keys(MantecaSupportedExchanges).includes(country.id) if (flow === 'claim') { setSelectedCountry(country) - const isMantecaSupportedCountry = Object.keys(MantecaSupportedExchanges).includes(country.id) if (isMantecaSupportedCountry) { setFlowStep(null) // reset the flow step to initial view first setClaimToMercadoPago(true) @@ -69,6 +70,10 @@ export const CountryListRouter = ({ setClaimBankFlowStep(ClaimBankFlowStep.BankDetailsForm) } } else if (flow === 'request') { + if (isMantecaSupportedCountry) { + setShowRequestFulfilmentBankFlowManager(false) + setFulfillUsingManteca(true) + } setSelectedCountryForRequest(country) if (requestType === BankRequestType.PayerKycNeeded) { diff --git a/src/components/Payment/Views/MantecaFulfillment.view.tsx b/src/components/Payment/Views/MantecaFulfillment.view.tsx index 7f5618669..972c578d9 100644 --- a/src/components/Payment/Views/MantecaFulfillment.view.tsx +++ b/src/components/Payment/Views/MantecaFulfillment.view.tsx @@ -12,14 +12,14 @@ import { useQuery } from '@tanstack/react-query' import React from 'react' const MantecaFulfillment = () => { - const { setFulfillUsingManteca } = useRequestFulfillmentFlow() + const { setFulfillUsingManteca, selectedCountry, setSelectedCountry } = useRequestFulfillmentFlow() const { requestDetails, chargeDetails } = usePaymentStore() const { data: depositData, isLoading: isLoadingDeposit } = useQuery({ queryKey: ['manteca-deposit', chargeDetails?.uuid], queryFn: () => mantecaApi.deposit({ usdAmount: requestDetails?.tokenAmount || chargeDetails?.tokenAmount || '0', - currency: 'ARS', + currency: selectedCountry?.currency || 'ARS', chargeId: chargeDetails?.uuid, }), refetchOnWindowFocus: false, @@ -27,6 +27,10 @@ const MantecaFulfillment = () => { enabled: Boolean(chargeDetails?.uuid), }) + const actionCardLogo = selectedCountry?.id + ? `https://flagcdn.com/w320/${selectedCountry?.id.toLowerCase()}.png` + : MERCADO_PAGO + const generateShareText = () => { const textParts = [] textParts.push(`CBU: ${depositData?.data?.depositAddress}`) @@ -44,6 +48,7 @@ const MantecaFulfillment = () => { { + setSelectedCountry(null) setFulfillUsingManteca(false) }} /> @@ -58,7 +63,8 @@ const MantecaFulfillment = () => { tokenSymbol={requestDetails?.tokenSymbol || 'USDC'} message={requestDetails?.reference || chargeDetails?.requestLink?.reference || ''} fileUrl={requestDetails?.attachmentUrl || chargeDetails?.requestLink?.attachmentUrl || ''} - logo={MERCADO_PAGO} + logo={actionCardLogo} + countryCodeForFlag={selectedCountry?.id.toLowerCase()} /> {depositData?.error && } diff --git a/src/hooks/useCurrency.ts b/src/hooks/useCurrency.ts index a103fa8dd..d200d7bc4 100644 --- a/src/hooks/useCurrency.ts +++ b/src/hooks/useCurrency.ts @@ -22,7 +22,7 @@ export const useCurrency = (currencyCode: string | null) => { const [code, setCode] = useState(currencyCode?.toUpperCase() ?? null) const [symbol, setSymbol] = useState(null) const [price, setPrice] = useState<{ buy: number; sell: number } | null>(null) - const [isLoading, setIsLoading] = useState(false) + const [isLoading, setIsLoading] = useState(true) useEffect(() => { if (!code) { diff --git a/src/services/manteca.ts b/src/services/manteca.ts index 1b5cbd522..f0f9aaab7 100644 --- a/src/services/manteca.ts +++ b/src/services/manteca.ts @@ -177,6 +177,7 @@ export const mantecaApi = { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${Cookies.get('jwt-token')}`, + 'api-key': 'DRdzuEiRvTtbFYKGahIdFUHuwhYzl1vo', }, body: JSON.stringify({ usdAmount: params.usdAmount, From 1d6f106bb4f3215382b5f19ddc07f290977570fc Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Wed, 17 Sep 2025 23:50:53 +0530 Subject: [PATCH 3/4] fix: kush's leftovers --- src/app/(mobile-ui)/qr-pay/page.tsx | 32 +++++++++---------- ...ag-And-Name.tsx => CountryFlagAndName.tsx} | 0 src/components/Kyc/CountryRegionRow.tsx | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) rename src/components/Kyc/{Country-Flag-And-Name.tsx => CountryFlagAndName.tsx} (100%) diff --git a/src/app/(mobile-ui)/qr-pay/page.tsx b/src/app/(mobile-ui)/qr-pay/page.tsx index aad1f6bf2..6d90fbe77 100644 --- a/src/app/(mobile-ui)/qr-pay/page.tsx +++ b/src/app/(mobile-ui)/qr-pay/page.tsx @@ -2,7 +2,7 @@ import { useSearchParams, useRouter } from 'next/navigation' import { useState, useCallback, useMemo, useEffect, useContext } from 'react' -import { Card } from '@/components/0_Bruddle/Card' + import { Button } from '@/components/0_Bruddle/Button' import { Icon } from '@/components/Global/Icons/Icon' import { mantecaApi } from '@/services/manteca' @@ -29,6 +29,8 @@ import { QrKycState, useQrKycGate } from '@/hooks/useQrKycGate' import ActionModal from '@/components/Global/ActionModal' import { saveRedirectUrl } from '@/utils/general.utils' import { MantecaGeoSpecificKycModal } from '@/components/Kyc/InitiateMantecaKYCModal' +import { PeanutDoesntStoreAnyPersonalInformation } from '@/components/Kyc/KycVerificationInProgressModal' +import Card from '@/components/Global/Card' const MANTECA_DEPOSIT_ADDRESS = '0x959e088a09f61aB01cb83b0eBCc74b2CF6d62053' const MAX_QR_PAYMENT_AMOUNT = '200' @@ -216,18 +218,18 @@ export default function QRPayPage() { if (!!errorInitiatingPayment) { return (
- - - Unable to get QR details - + +
+

Unable to get QR details

+

{errorInitiatingPayment || 'An error occurred while getting the QR details.'} - - - - - +

+
+
+ +
) @@ -273,11 +275,7 @@ export default function QRPayPage() { icon: 'check-circle', }, ]} - footer={ -

- Peanut doesn't store any personal information -

- } + footer={} />
) diff --git a/src/components/Kyc/Country-Flag-And-Name.tsx b/src/components/Kyc/CountryFlagAndName.tsx similarity index 100% rename from src/components/Kyc/Country-Flag-And-Name.tsx rename to src/components/Kyc/CountryFlagAndName.tsx diff --git a/src/components/Kyc/CountryRegionRow.tsx b/src/components/Kyc/CountryRegionRow.tsx index 64cdb13ea..00519a908 100644 --- a/src/components/Kyc/CountryRegionRow.tsx +++ b/src/components/Kyc/CountryRegionRow.tsx @@ -1,5 +1,5 @@ import { PaymentInfoRow } from '@/components/Payment/PaymentInfoRow' -import { CountryFlagAndName } from './Country-Flag-And-Name' +import { CountryFlagAndName } from './CountryFlagAndName' interface CountryRegionRowProps { countryCode?: string | null From 12c8ba2859aaf1052d72d7ec4a3ea052216d5f31 Mon Sep 17 00:00:00 2001 From: Zishan Mohd Date: Thu, 18 Sep 2025 13:08:53 +0530 Subject: [PATCH 4/4] add KYC to links flow --- .../Claim/Link/MantecaFlowManager.tsx | 44 +++++++++++++++++- .../Payment/Views/MantecaFulfillment.view.tsx | 46 ++++++++++++++++++- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/src/components/Claim/Link/MantecaFlowManager.tsx b/src/components/Claim/Link/MantecaFlowManager.tsx index 5e122af53..d32d6556f 100644 --- a/src/components/Claim/Link/MantecaFlowManager.tsx +++ b/src/components/Claim/Link/MantecaFlowManager.tsx @@ -5,12 +5,16 @@ 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 { FC, useEffect, 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' +import useKycStatus from '@/hooks/useKycStatus' +import { InitiateMantecaKYCModal } from '@/components/Kyc/InitiateMantecaKYCModal' +import { useAuth } from '@/context/authContext' +import { CountryData } from '@/components/AddMoney/consts' interface MantecaFlowManagerProps { claimLinkData: ClaimLinkData @@ -23,11 +27,35 @@ const MantecaFlowManager: FC = ({ claimLinkData, amount const [currentStep, setCurrentStep] = useState(MercadoPagoStep.DETAILS) const router = useRouter() const [destinationAddress, setDestinationAddress] = useState('') + const [isKYCModalOpen, setIsKYCModalOpen] = useState(false) + const argentinaCountryData = { + id: 'AR', + type: 'country', + title: 'Argentina', + currency: 'ARS', + path: 'argentina', + iso2: 'AR', + iso3: 'ARG', + } as CountryData + + const { isUserMantecaKycApproved } = useKycStatus() + const { fetchUser } = useAuth() const isSuccess = currentStep === MercadoPagoStep.SUCCESS const selectedCurrency = selectedCountry?.currency || 'ARS' const logo = selectedCountry?.id ? undefined : MERCADO_PAGO + const handleKycCancel = () => { + setIsKYCModalOpen(false) + onPrev() + } + + useEffect(() => { + if (!isUserMantecaKycApproved) { + setIsKYCModalOpen(true) + } + }, [isUserMantecaKycApproved]) + const renderStepDetails = () => { if (currentStep === MercadoPagoStep.DETAILS) { return ( @@ -96,6 +124,20 @@ const MantecaFlowManager: FC = ({ claimLinkData, amount /> {renderStepDetails()} + + {isKYCModalOpen && ( + { + // close the modal and let the user continue with amount input + setIsKYCModalOpen(false) + fetchUser() + }} + country={selectedCountry || argentinaCountryData} + /> + )}
) diff --git a/src/components/Payment/Views/MantecaFulfillment.view.tsx b/src/components/Payment/Views/MantecaFulfillment.view.tsx index 972c578d9..cc9fce0bf 100644 --- a/src/components/Payment/Views/MantecaFulfillment.view.tsx +++ b/src/components/Payment/Views/MantecaFulfillment.view.tsx @@ -1,19 +1,26 @@ import { MERCADO_PAGO } from '@/assets' +import { CountryData } from '@/components/AddMoney/consts' 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 { InitiateMantecaKYCModal } from '@/components/Kyc/InitiateMantecaKYCModal' +import { useAuth } from '@/context/authContext' import { useRequestFulfillmentFlow } from '@/context/RequestFulfillmentFlowContext' +import useKycStatus from '@/hooks/useKycStatus' import { usePaymentStore } from '@/redux/hooks' import { mantecaApi } from '@/services/manteca' import { useQuery } from '@tanstack/react-query' -import React from 'react' +import React, { useEffect, useState } from 'react' const MantecaFulfillment = () => { const { setFulfillUsingManteca, selectedCountry, setSelectedCountry } = useRequestFulfillmentFlow() const { requestDetails, chargeDetails } = usePaymentStore() + const [isKYCModalOpen, setIsKYCModalOpen] = useState(false) + const { isUserMantecaKycApproved } = useKycStatus() + const { fetchUser } = useAuth() const { data: depositData, isLoading: isLoadingDeposit } = useQuery({ queryKey: ['manteca-deposit', chargeDetails?.uuid], queryFn: () => @@ -24,9 +31,19 @@ const MantecaFulfillment = () => { }), refetchOnWindowFocus: false, staleTime: Infinity, // don't refetch the data - enabled: Boolean(chargeDetails?.uuid), + enabled: Boolean(chargeDetails?.uuid) && isUserMantecaKycApproved, }) + const argentinaCountryData = { + id: 'AR', + type: 'country', + title: 'Argentina', + currency: 'ARS', + path: 'argentina', + iso2: 'AR', + iso3: 'ARG', + } as CountryData + const actionCardLogo = selectedCountry?.id ? `https://flagcdn.com/w320/${selectedCountry?.id.toLowerCase()}.png` : MERCADO_PAGO @@ -39,6 +56,18 @@ const MantecaFulfillment = () => { return textParts.join('\n') } + const handleKycCancel = () => { + setIsKYCModalOpen(false) + setSelectedCountry(null) + setFulfillUsingManteca(false) + } + + useEffect(() => { + if (!isUserMantecaKycApproved) { + setIsKYCModalOpen(true) + } + }, [isUserMantecaKycApproved]) + if (isLoadingDeposit) { return } @@ -109,6 +138,19 @@ const MantecaFulfillment = () => { )} + {isKYCModalOpen && ( + { + // close the modal and let the user continue with amount input + setIsKYCModalOpen(false) + fetchUser() + }} + country={selectedCountry || argentinaCountryData} + /> + )} ) }