How To: Implement Seller Payouts on Marketplaces (API)
-
Implement Metakeep wallet
The below implementation demonstrates how you can create a wallet for every seller on your platform with their email.import { Connection, PublicKey, Transaction, VersionedTransaction } from '@solana/web3.js'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { MetaKeep } from 'metakeep'; // Create seller's metakeep wallet with sign and send transaction functionalities export function useMetakeepWallet({ appId, email }: { appId: string; email: string }) { const metakeep = useMemo(() => { if (!email || !appId) return null; return new MetaKeep({ appId, user: { email }, }); }, [appId, email]); const [publicKey, setPublicKey] = useState<PublicKey | null>(null); useEffect(() => { metakeep ?.getWallet() .then(res => setPublicKey(new PublicKey(res.wallet.solAddress))); }, [metakeep]); const signTransaction = useCallback( async <T extends Transaction | VersionedTransaction>(transaction: T): Promise<T> => { if (!metakeep) throw new Error('metakeep is null'); const metakeepRes: { signature: string } = await metakeep.signTransaction( transaction, 'Withdraw USD' ); const signature = Buffer.from(metakeepRes.signature.replace('0x', ''), 'hex'); if (transaction instanceof Transaction) { transaction.signatures.push({ publicKey: publicKey!, signature, }); transaction.signatures = transaction.signatures.filter(sig => !!sig.signature); } else { transaction.signatures.push(signature); } return transaction; }, [metakeep, publicKey] ); const signMessage = useCallback( async (message: Uint8Array) => { if (!metakeep) throw new Error('metakeep is null'); const { signature } = await metakeep.signMessage( Buffer.from(message).toString('utf8'), 'Sign in' ); return Uint8Array.from(Buffer.from(signature.slice(2), 'hex')); }, [metakeep] ); return { publicKey, signMessage, signTransaction, }; } -
curl --request GET \ --url https://api-sandbox.coinflow.cash/api/seller/verifyTransaction \ --header 'accept: application/json' \ --header 'x-coinflow-auth-blockchain: solana' \ --header 'x-coinflow-auth-wallet: CVrsREanmfBAeZH14PXrBzw4wF8Eh12PJDHYq2feoxUq' OK -
Get Wallet balance
This endpoint will show you how much is in the seller’s wallet balance.Textcurl --request GET \ --url https://api-sandbox.coinflow.cash/api/withdraw/balances \ --header 'accept: application/json' \ --header 'x-coinflow-auth-blockchain: solana' \ --header 'x-coinflow-auth-wallet: CVrsREanmfBAeZH14PXrBzw4wF8Eh12PJDHYq2feoxUq' { "balances": [ { "mint": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU", "balance": 107.02, "decimals": 6, "icon": "https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU/logo.png", "name": "USD Coin", "symbol": "USDC", "isStablecoin": true, "stablecoinCurrency": "USD" } ] } -
Add Payout Destination
Debit Card (US Only)curl --location 'https://api-sandbox.coinflow.cash/api/withdraw/debit-card' \ --header 'Authorization: YOUR_MARKETPLACE_API_KEY' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --header 'x-coinflow-auth-wallet: CVrsREanmfBAeZH14PXrBzw4wF8Eh12PJDHYq2feoxUq' \ --header 'x-coinflow-auth-blockchain: solana' \ --data '{ "cardToken": "411111YJM5TX1111", "expMonth": "10", "expYear": "29" }' Bank Account (US Only)curl --location 'https://api-sandbox.coinflow.cash/api/withdraw/account' \ --header 'Authorization: <YOUR_API_KEY>' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --header 'x-coinflow-auth-user-id: usher' \ --data ' { "type": "checking", "alias": "Ushers Savings 1", "accountNumber": "1111222233330000", "routingNumber": "333333334" } ' IBAN (EU/UK Only)curl --location 'https://api-sandbox.coinflow.cash/api/withdraw/iban' \ --header 'Authorization: <YOUR_API_KEY>' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --header 'x-coinflow-auth-user-id: usher' \ --data ' { "alias": "Ushers Savings 1", "number": "<Iban number>", "sortCode": "<UK Sort Code>" // Pass only for UK Residents } ' PIX (BRL Only)curl --request POST \ --url https://api-sandbox.coinflow.cash/api/withdraw/pix \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data '{"pixKey":"1234"}' -
Get withdrawer
Calling get withdrawer will allow you to fetch any linked payout destinations.curl --request GET \ --url https://api-sandbox.coinflow.cash/api/withdraw \ --header 'accept: application/json' \ --header 'x-coinflow-auth-blockchain: solana' \ --header 'x-coinflow-auth-wallet: CVrsREanmfBAeZH14PXrBzw4wF8Eh12PJDHYq2feoxUq' { "withdrawer": { "dwolla": { "customerId": "67cec202-ff55-4abe-90af-7b4b849c3b2e", "status": "verified", "acceptedTerms": "2024-08-12T21:01:10.854Z" }, "_id": "66567eabcff2bb5ac89534f1", "__v": 0, "currency": "USD", "email": "yankay.tl+3@gmail.com", "merchant": "66314a51a26b3cb28fab9bd0", "user": true, "verification": { "hash": "e5c00e045519199bf0d74d67ea4ca90dbe141ce8", "vendor": "persona", "reference": "ver_7E4mUCXhPM3XkD9PeXqeRtcdBJ5A", "status": "approved" }, "wallets": [ { "wallet": "J3es35bqeTg9qXTt7zyx1znX9oESSUEq3G2xyjJSu3yY", "blockchain": "solana" } ], "originalCurrency": "USD", "riskScoreOverride": false, "watchlistExempt": "Unknown", "availability": { "status": "Functional", "reason": "Conversion", "editor": "system", "updatedAt": "2025-01-30T15:17:06.892Z" }, "bankAccounts": [ { "alias": "Plaid Checking 0000", "token": "ebb19596-3edf-47a2-8f7e-360ffc78893c", "routingNumber": "011401533", "last4": "0000", "accountHash": "a66621183ad216e4543f9004426a263ad58385f9", "rtpEligible": true, "reference": "66a7d6fa70df7319adef25d6", "isDeleted": false }, { "alias": "Plaid Saving 1111", "token": "efe0f2b5-6350-4ab3-ac0e-886045003310", "routingNumber": "011401533", "last4": "1111", "accountHash": "171e7a6868b9c2b1b3ae64ab39e2660252dc90b4", "rtpEligible": true, "reference": "66a7d6fa70df7319adef25d6", "isDeleted": false } ], "cards": [], "ibans": [], "pixes": [], "efts": [], "rtpDisabled": false, "cardDisabled": false } } -
Get withdraw Tx
This generates a solana transaction record which subsequently needs to be signed by the seller’s wallet (confirming they authorize the transaction).curl --request POST \ --url https://api-sandbox.coinflow.cash/api/withdraw/transaction \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --header 'x-coinflow-auth-blockchain: solana' \ --header 'x-coinflow-auth-wallet: CVrsREanmfBAeZH14PXrBzw4wF8Eh12PJDHYq2feoxUq' \ --data ' { "speed": "asap", "amount": 5, "merchantId": "sellerAA", "account": "ebb19596-3edf-47a2-8f7e-360ffc78893c" } ' { "transactions": [ "3g672MB8XcuVweWHcFjvapToy3G5oiPhMiHTt1oHtDHEfnVjPwjqV5vT1NSqjtW2fD361zQxPGjZz5f55mP39vvayVcbogaapK4ys1ek5uk2JHJth9ZwkdZKS9geWsqXSF8Hbvtj2B5fNj13h5E6jj8wdm5Qvo44NYM1Vwijp2GoXCW1B5UMG8LDqh9j2pNTXqEhqeAtrkrUNuDku2Tryx72V2f1XoRUUnK9SauqzmEjrzdrrgS84F137dz5VaWKfCgKxAekBEUtR2gWU4sLqqUggLrLpaVkAUidjPYy3wfEcdEibUZwAyt6otSphZK8CCPmECHtRKgeXUmr9R7sypUBX2kz8BxUnHYsasVETxoVFyeXECnyGZajrTHhfEmwk73RCNfzeu5Ss5JYWdwp22kVW2XapAMLLmhTaXwEmeY2zdXmTjA8e3ojksE9gFoKUVTpSqPyAkYz3deBYpoBdmdeUi4mTKnhbLUGa9X9baW2qeopMxurRsy6xJvD6knT3wtUNaJygJLhHDTkATejwP7hiqmoF8BYbvmdaW6eAtbQheYwupwYFw46fUUihSSAWsR1F3u4yenngrii2NsrVJJkkw697VMYCpAXjjjz3EjBYvHAPfH7JGhdZxHENJj6VK5WvsS7zDRG7mn96Tqi2Gyvci2rbvqgXEzXEH7Ncdr1qvUj2AyepNHNChxzwPoXHQEiWWpBMJbRZyYvjutKZzMdL3Gu6MWVww3YUTFPAGpt" ] } -
Sign and Send Tx
The below shows how you can fetch the transaction from step 5, enable the seller to sign the transaction and then send the signed transaction to the blockchain. Once this is sent, Coinflow will initiate the payout.Sign and Send Tximport { Connection, PublicKey, Transaction, VersionedTransaction } from '@solana/web3.js'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { MetaKeep } from 'metakeep'; import { Buffer } from 'buffer'; import base58 from 'bs58'; const handleSignAndSend = useCallback(async () => { if (!wallet?.signTransaction || !wallet?.publicKey) { console.error('Wallet not ready'); return; } //ensure wallet is set up and ready to sign + send transaction const base58Tx ='3g67...'; // Transaction returned from Step 5 try { const buffer = base58.decode(base58Tx); // Deserialize the transaction let transaction; try { transaction = Transaction.from(buffer); } catch { transaction = VersionedTransaction.deserialize(buffer); } const signedTx = await wallet.signTransaction(transaction); console.log('Signed transaction:', signedTx); // wallet signing tx const txId = await connection.sendRawTransaction(signedTx.serialize()); console.log('Transaction sent! Signature:', txId); // sends wallet to blockchain } catch (err) { console.error('Error signing or sending transaction:', err); } }, [wallet, connection]); Implementation w/ MetaKeep Walletfunction App() { // Initiate Metakeep Wallet const wallet = useMetakeepWallet({ appId: 'YOUR_METAKEEP_APP_ID', // Get this from calling Get Merchant: https://docs.coinflow.cash/reference/getmerchantv2 email: 'SELLERS_EMAIL_ADDRESS', }); // Initiate connection to solana blockchain. Note: Change to mainnet on prod: https://api.mainnet-beta.solana.com const connection = useMemo( () => new Connection('https://api.devnet.solana.com'), [] ); useEffect(() => { if (wallet?.publicKey) { console.log('Metakeep Wallet Address:', wallet.publicKey.toBase58()); // The sellers wallet address } }, [wallet]); // Triggers signing the transaction returned from Coinflow and sending to the blockchain to initiate the payout const handleSignAndSend = useCallback(async () => { if (!wallet?.signTransaction || !wallet?.publicKey) { console.error('Wallet not ready'); return; } const base58Tx = 'YOUR_BASE58_TX_FROM_STEP_5'; // Get this from Step 5: https://docs.coinflow.cash/reference/gettransaction try { const buffer = base58.decode(base58Tx); // Deserialize the transaction let transaction; try { transaction = Transaction.from(buffer); } catch { transaction = VersionedTransaction.deserialize(buffer); } const signedTx = await wallet.signTransaction(transaction); console.log('Signed transaction:', signedTx); const txId = await connection.sendRawTransaction(signedTx.serialize()); console.log('Transaction sent! Signature:', txId); } catch (err) { console.error('Error signing or sending transaction:', err); } }, [wallet, connection]); return ( <div> <h1>Initiate payout from wallet</h1> <button onClick={handleSignAndSend}>Sign & Send Transaction</button> </div> ); } -
curl --request GET \ --url https://api-sandbox.coinflow.cash/api/withdraw/history \ --header 'accept: application/json' \ --header 'x-coinflow-auth-blockchain: solana' \ --header 'x-coinflow-auth-wallet: CVrsREanmfBAeZH14PXrBzw4wF8Eh12PJDHYq2feoxUq' { "withdraws": [ { "amount": { "cents": 500, "currency": "USD" }, "userPaidFees": { "fees": { "cents": 200, "currency": "USD" }, "gasFees": { "cents": 0, "currency": "USD" }, "swapFees": { "cents": 0, "currency": "USD" }, "customFees": { "cents": 0, "currency": "USD" } }, "merchantPaidFees": { "fees": { "cents": 0, "currency": "USD" }, "gasFees": { "cents": 0, "currency": "USD" } }, "_id": "685314cb57cf19cf5054ace9", "withdrawer": "66567eabcff2bb5ac89534f1", "transferId": "f87fd108-7081-409b-9684-ebd8418abfd0", "wallet": "J3es35bqeTg9qXTt7zyx1znX9oESSUEq3G2xyjJSu3yY", "blockchain": "solana", "transaction": "2zPjUSz2wkbiXByFRnqn26VmRDcs2DCmLeTjwxariBXexd2nST2NnHTWi6n2NowjiWa13qxXJrfRvmP54NEmoJbd", "accountId": "ebb19596-3edf-47a2-8f7e-360ffc78893c", "usdToForeignExchangeRate": 1, "status": "pending", "merchant": "66567eabb9e401ada5513234", "speed": "asap", "expectedDeliveryDate": "2025-06-18T19:49:35.240Z", "provider": "dwolla", "createdAt": "2025-06-18T19:34:35.243Z", "updatedAt": "2025-06-18T19:34:37.319Z", "__v": 0 } ] }

