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:
| Directive | Allow | Why |
|---|---|---|
script-src | https://widget.dojah.io | The identity check, loaded only when a user starts their first purchase |
frame-src | https://identity.dojah.io | The 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 event | React callback | detail |
|---|---|---|
recoilpay-cash-out-started | onCashOutStarted(tradeId) | { tradeId } |
recoilpay-cash-out-ended | onCashOutEnded(trade) | { tradeId, state } |
recoilpay-purchase-started | onPurchaseStarted(tradeId) | { tradeId } |
recoilpay-purchase-ended | onPurchaseEnded(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