Use this file to discover all available pages before exploring further.
The merchant client exposes a read-heavy slice of refund actions plus freeze.isFrozen. Writes that change refund-request status (deny, refuse) and writes that lift a freeze (unfreeze) live on createArbiterClient or on the full createX402r() client.Use createMerchantClient for queries below; for executing a refund, see Capture vs refund decision flow. The merchant client’s payment.voidPayment() flips the request to Approved through the VOID_POST_ACTION_HOOK.
Approving or denying a request through the operator hook is what the merchant does. Terminal deny and refuse calls on the RefundRequest contract belong to the arbiter role; from a merchant, execute the refund through payment.voidPayment() (which flips the request to Approved) or signal a refusal off-chain and let the arbiter terminalize it.
To approve and execute a refund, call payment.voidPayment(). The operator’s VOID_POST_ACTION_HOOK (RefundRequest) flips the request status to Approved.
A full workflow that detects a refund request, reviews it, makes a decision, and executes the refund when approved.
import { createMerchantClient, RefundRequestStatus } from '@x402r/sdk'import type { PaymentInfo } from '@x402r/sdk'async function handleRefundWorkflow( merchant: ReturnType<typeof createMerchantClient>, paymentInfo: PaymentInfo,) { // Step 1: Check if a refund request exists const hasRequest = await merchant.refund?.has(paymentInfo) if (!hasRequest) { console.log('No refund request for this payment') return } // Step 2: Get the full request data const request = await merchant.refund?.get(paymentInfo) console.log('Refund request:', request?.amount, 'status:', request?.status) // Step 3: Only process pending requests if (request?.status !== RefundRequestStatus.Pending) { console.log('Request already processed') return } // Step 4: Check if the payment is frozen const frozen = await merchant.freeze?.isFrozen(paymentInfo) if (frozen) { console.log('Payment is frozen, resolve dispute first') return } // Step 5: Check available amounts const amounts = await merchant.payment.getAmounts(paymentInfo) console.log('Available to refund:', amounts.refundableAmount) // Step 6: Make a decision const shouldApprove = request.amount <= amounts.refundableAmount if (shouldApprove) { // Execute the refund (the VOID_POST_ACTION_HOOK flips the request to Approved) const tx = await merchant.payment.voidPayment(paymentInfo) console.log('Refund executed:', tx) } else { // The arbiter can terminalize the request via refund.deny / refund.refuse. // The merchant can simply leave the request Pending and capture as usual, // or escalate off-chain to the arbiter. console.log('Declining; arbiter may deny if escalated') }}