Subscribe to real-time payment, refund, and freeze events as a payer
The Client SDK provides methods to subscribe to blockchain events in real-time using viem’s watchContractEvent under the hood. All subscription methods return an object with an unsubscribe function that you should call when you no longer need the watcher.
Watch for state changes on a specific payment. This subscribes to ReleaseExecuted, RefundInEscrowExecuted, and RefundPostEscrowExecuted events on the PaymentOperator contract.
Copy
const { unsubscribe } = client.watchPaymentState( paymentInfoHash, (event) => { console.log(`Payment event: ${event.eventName}`); switch (event.eventName) { case 'ReleaseExecuted': console.log('Funds released to merchant'); console.log('Amount:', event.args.amount); break; case 'RefundInEscrowExecuted': console.log('Funds refunded from escrow'); break; case 'RefundPostEscrowExecuted': console.log('Funds refunded after escrow period'); break; } });// Stop watching when doneunsubscribe();
Watch for refund request lifecycle events. This subscribes to RefundRequested, RefundRequestStatusUpdated, and RefundRequestCancelled events on the RefundRequest contract.
Copy
const { unsubscribe } = client.watchRefundRequests((event) => { switch (event.eventName) { case 'RefundRequested': console.log('New refund request submitted'); console.log('Payment:', event.args.paymentInfoHash); console.log('Amount:', event.args.amount); break; case 'RefundRequestStatusUpdated': console.log('Refund status changed:', event.args.status); // 1 = Approved, 2 = Denied break; case 'RefundRequestCancelled': console.log('Refund request cancelled'); break; }});// Stop watching when doneunsubscribe();
Watch for new payment authorizations where the connected wallet is the payer. This subscribes to AuthorizationCreated events on the PaymentOperator contract, filtered by the wallet’s address.
Store the unsubscribe function and call it when you no longer need the watcher. Failing to unsubscribe causes memory leaks and orphaned WebSocket connections.
Copy
const { unsubscribe } = client.watchPaymentState(hash, callback);// In your cleanup / shutdown logic:unsubscribe();
Handle WebSocket reconnection
Event subscriptions use WebSocket connections under the hood. If you are running a long-lived process, consider implementing reconnection logic:
Copy
function watchWithReconnect( hash: `0x${string}`, callback: (event: PaymentOperatorEventLog) => void) { let sub = client.watchPaymentState(hash, callback); // Reconnect on error (simplified example) const reconnect = () => { sub.unsubscribe(); sub = client.watchPaymentState(hash, callback); }; return { unsubscribe: () => sub.unsubscribe(), reconnect, };}
Use a WebSocket transport for real-time events
For reliable real-time events, configure your viem PublicClient with a WebSocket transport instead of HTTP polling:
Copy
import { createPublicClient, webSocket } from 'viem';import { baseSepolia } from 'viem/chains';const publicClient = createPublicClient({ chain: baseSepolia, transport: webSocket('wss://base-sepolia.your-rpc-provider.com'),});
Debounce rapid events
Multiple events can fire in quick succession (e.g., batch releases). Consider debouncing your UI update handlers:
Copy
function debounce<T extends (...args: unknown[]) => void>( fn: T, ms: number): T { let timer: NodeJS.Timeout; return ((...args: unknown[]) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), ms); }) as T;}const debouncedHandler = debounce((event) => { console.log('Processing event:', event);}, 500);client.watchMyPayments(debouncedHandler);