Server-Side Integration
Browser callbacks can be spoofed. Before fulfilling an order, verify the payment on-chain - on Arc this is trivial because amounts are public and finality is instant.
Verify by payment ID
import { verifyPayment } from 'aruvi-arc';
const result = await verifyPayment({
paymentId: req.body.paymentId, // from your frontend callback
merchantAddress: '0xYourWallet',
expectedAmount: '49.99', // USDC - checked against the chain
gatewayAddress: process.env.ARUVI_GATEWAY,
});
if (result.verified) {
// ✅ recipient is you, amount matches, not refunded - ship the order
console.log(result.payment.customerAddress, result.payment.amount);
} else {
console.warn('Rejected:', result.error);
}
verifyPayment performs an eth_call to getPaymentInfo(paymentId) and checks:
- the payment exists,
- the recipient is your merchant address,
- the amount equals
expectedAmount(if provided), - it hasn't been refunded.
Verify by transaction hash
import { verifyPaymentByTxHash } from 'aruvi-arc';
const result = await verifyPaymentByTxHash(txHash, {
merchantAddress: '0xYourWallet',
gatewayAddress: process.env.ARUVI_GATEWAY,
});
Reads the receipt, finds the gateway's PaymentSent log, and validates recipient and amount from it.
Zero-dependency verification
Both helpers use plain fetch against Arc's JSON-RPC - no web3 library required, so they run in any Node, Deno, Bun, or edge runtime:
// Custom RPC (e.g. a dedicated provider)
await verifyPayment({ …, rpcUrl: 'https://rpc.testnet.arc.network' });
Recommended flow
Because Arc finality is deterministic, there's no waiting period - verify immediately after the callback.
Idempotency
Store fulfilled paymentIds and reject duplicates. Payment IDs are unique per payment and can only be created by the gateway, so they're a safe idempotency key.