Accept a Payment
Accept stablecoin payments in your application. Learn how to receive payments, verify transactions, and reconcile payments using memos.
Receiving Payments
Payments are automatically credited to the recipient's address when a transfer is executed. You don't need to do anything special to "accept" a payment, it happens automatically onchain.
In this basic receiving demo you can see the balances update after you add funds to your account, using the getBalance and watchEvent calls documented below.
Receive a Payment
demoVerifying Payments
Check if a payment has been received by querying the token balance or listening for transfer events:
Check Balance
import { client } from './viem.config'
const balance = await client.token.getBalance({
token: '0x20c0000000000000000000000000000000000001', // AlphaUSD
address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb',
})
console.log('Balance:', balance)Listen for Transfer Events
import { watchEvent } from 'viem'
// Watch for incoming transfers
const unwatch = watchEvent(client, {
address: '0x20c0000000000000000000000000000000000001',
event: {
type: 'event',
name: 'Transfer',
inputs: [
{ name: 'from', type: 'address', indexed: true },
{ name: 'to', type: 'address', indexed: true },
{ name: 'value', type: 'uint256' },
],
},
onLogs: (logs) => {
logs.forEach((log) => {
if (log.args.to === yourAddress) {
console.log('Received payment:', {
from: log.args.from,
amount: log.args.value,
})
}
})
},
})Payment Reconciliation with Memos
If payments include memos (invoice IDs, order numbers, etc.), you can reconcile them automatically:
// Watch for TransferWithMemo events
const unwatch = watchEvent(client, {
address: tokenAddress,
event: {
type: 'event',
name: 'TransferWithMemo',
inputs: [
{ name: 'from', type: 'address', indexed: true },
{ name: 'to', type: 'address', indexed: true },
{ name: 'value', type: 'uint256' },
{ name: 'memo', type: 'bytes32', indexed: true },
],
},
onLogs: (logs) => {
logs.forEach((log) => {
if (log.args.to === yourAddress) {
const invoiceId = log.args.memo
// Mark invoice as paid in your database
markInvoiceAsPaid(invoiceId, log.args.value)
}
})
},
})Smart Contract Integration
If you're building a smart contract that accepts payments:
contract PaymentReceiver {
ITIP20 public token;
mapping(bytes32 => bool) public paidInvoices;
event PaymentReceived(
address indexed payer,
uint256 amount,
bytes32 indexed invoiceId
);
function receivePayment(
address payer,
uint256 amount,
bytes32 invoiceId
) external {
require(!paidInvoices[invoiceId], "Invoice already paid");
// Transfer tokens from payer to this contract
token.transferFrom(payer, address(this), amount);
paidInvoices[invoiceId] = true;
emit PaymentReceived(payer, amount, invoiceId);
}
}Payment Verification Best Practices
- Verify onchain: Always verify payments onchain before marking orders as paid
- Use memos: Request memos from payers to link payments to invoices or orders
- Check confirmations: Wait for transaction finality (~1 second on Tempo) before processing
- Handle edge cases: Account for partial payments, refunds, and failed transactions
Cross-Stablecoin Payments
If you need to accept payments in a specific stablecoin but receive a different one, use the exchange to swap:
// User sends USDC, but you need USDT
// Swap USDC to USDT using the exchange
const { receipt } = await client.dex.sellSync({
tokenIn: usdcAddress,
tokenOut: usdtAddress,
amountIn: receivedAmount,
minAmountOut: receivedAmount * 99n / 100n, // 1% slippage
})Next Steps
- Send a payment to learn how to send payments
- Learn more about Exchange for cross-stablecoin payments