Skip to main content

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:

  1. the payment exists,
  2. the recipient is your merchant address,
  3. the amount equals expectedAmount (if provided),
  4. 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' });

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.