Skip to main content

Add cash-out and buy to your site

The fastest way to let your users cash out to a bank account and buy crypto with a bank transfer is the same drop-in UI that does swaps. You don't build any fiat screens. This guide gets it working, then covers the three things that are yours to get right: bringing users back to a trade, your Content-Security-Policy, and knowing when a trade is done.

What you need: a website (any stack for the widget, React for the component), a Hugging Face token for plain-English input, and about 15 minutes.


1. Put the UI on the page​

Widget: any website
<script type="module" src="https://cdn.jsdelivr.net/npm/@recoilpay/intent-widget@0.7/dist/recoilpay-intent-widget.js"></script>

<recoilpay-intent
hf-access-token="YOUR_HUGGING_FACE_TOKEN"
walletconnect-project-id="YOUR_REOWN_PROJECT_ID"
></recoilpay-intent>
React component
npm install @recoilpay/intent-react @recoilpay/intent-core
import { RecoilIntent } from '@recoilpay/intent-react';
import { useWagmiIntentWallet } from '@recoilpay/intent-react/wagmi';
import '@recoilpay/intent-react/styles.css';
import { useConnectModal } from '@rainbow-me/rainbowkit';

export function Pay() {
const wallet = useWagmiIntentWallet();
const { openConnectModal } = useConnectModal();
return <RecoilIntent hfAccessToken={HF_TOKEN} wallet={wallet} onConnectWallet={openConnectModal} />;
}

That's enough for fiat to work. Type either sentence:

  • cash out 10 USDC on Base Sepolia to NGN: the offer, a bank details form, then the payout.
  • buy ₦1,000 of USDC on Base Sepolia: the offer, the one-time identity check, then where to pay.

Suggest them to your users with examples:

document.querySelector('recoilpay-intent').examples = [
{ label: 'Cash out 10 USDC', sentence: 'cash out 10 USDC on Base Sepolia to NGN' },
{ label: 'Buy ₦5,000 of USDC', sentence: 'buy ₦5,000 of USDC on Base Sepolia' },
];

In React, pass the same array as the examples prop.

2. Bring users back to a trade​

A cash-out waits for the user to confirm the money arrived, and a purchase waits for them to pay. Both can outlast a page visit.

Widget: nothing to do. It remembers a trade that's under way in localStorage and shows it again on the next visit.

React: store the id when a trade starts and pass it back:

import { useState } from 'react';

const read = (key: string) => localStorage.getItem(key);
const save = (key: string, id: string | null) => (id ? localStorage.setItem(key, id) : localStorage.removeItem(key));

export function Pay() {
const wallet = useWagmiIntentWallet();
const { openConnectModal } = useConnectModal();
const [cashOut] = useState(() => read('recoilpay:cash-out'));
const [purchase] = useState(() => read('recoilpay:purchase'));

return (
<RecoilIntent
hfAccessToken={HF_TOKEN}
wallet={wallet}
onConnectWallet={openConnectModal}
resumeCashOut={cashOut}
onCashOutStarted={(id) => save('recoilpay:cash-out', id)}
onCashOutEnded={() => save('recoilpay:cash-out', null)}
resumePurchase={purchase}
onPurchaseStarted={(id) => save('recoilpay:purchase', id)}
onPurchaseEnded={() => save('recoilpay:purchase', null)}
/>
);
}

Save the id somewhere that follows the user if you can, such as their account on your side, so they can finish on another device too. The id isn't a secret: every action on the trade needs their wallet's signature.

3. Allow the identity check through your CSP​

If your site sends a Content-Security-Policy, add these on top of the ones swaps need:

DirectiveAllowWhy
script-srchttps://widget.dojah.ioThe identity check, loaded only when a user starts their first purchase
frame-srchttps://identity.dojah.ioThe identity check's window

Without them, a user's first purchase stops at "Could not load the identity check".

4. Know when a trade is done​

Use the events (widget) or callbacks (React) to update balances, send a receipt or record a conversion:

document.addEventListener('recoilpay-purchase-ended', (e) => {
const { tradeId, state } = e.detail;
if (state === 'settled_to_fiat_payer') {
// Bought: the USDC is released to the user's wallet within about a minute.
}
});

document.addEventListener('recoilpay-cash-out-ended', (e) => {
const { tradeId, state } = e.detail;
// settled_to_fiat_payer: cashed out · refunded_to_funder: USDC returned · failed: nothing moved
});
Widget eventReact callbackdetail
recoilpay-cash-out-startedonCashOutStarted(tradeId){ tradeId }
recoilpay-cash-out-endedonCashOutEnded(trade){ tradeId, state }
recoilpay-purchase-startedonPurchaseStarted(tradeId){ tradeId }
recoilpay-purchase-endedonPurchaseEnded(trade){ tradeId, state }

Want the full trade? GET /api/v1/fiat/trades/{tradeId} returns it, including release_tx_hash once the escrow's release has landed.


Check before you ship​

  • A cash-out and a purchase both reach the end on testnet, from your page.
  • Reloading during a cash-out brings back the "Did ₦… arrive?" step.
  • Reloading during a purchase brings back where to pay.
  • Your CSP lets the identity check open, if you have one.
  • Your users know the USDC is testnet USDC, but a bank transfer can be real naira.

Next: want your own screens? Build a cash-out flow · Build a purchase flow