Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.x402r.org/llms.txt

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.

Refund request queries

refund.has

Check whether a refund request exists for a payment.
const hasRequest = await merchant.refund?.has(paymentInfo)
Parameters
NameTypeDescription
paymentInfoPaymentInfoFull struct identifying the payment
Returns Promise<boolean>.

refund.getStatus

Retrieve the current status of a refund request.
import { RefundRequestStatus } from '@x402r/sdk'

const status = await merchant.refund?.getStatus(paymentInfo)
Parameters
NameTypeDescription
paymentInfoPaymentInfoFull struct identifying the payment
Returns Promise<RefundRequestStatus>, Pending | Approved | Denied | Cancelled | Refused.

refund.get

Retrieve the complete refund request data.
const request = await merchant.refund?.get(paymentInfo)
Parameters
NameTypeDescription
paymentInfoPaymentInfoFull struct identifying the payment
Returns Promise<RefundRequestData>:
FieldTypeDescription
paymentInfoHashHexkeccak256 of the payment info struct
amountbigintAmount the payer requested
approvedAmountbigintAmount actually executed (0 until approved)
statusRefundRequestStatusLifecycle state

refund.getByKey

Look up a refund request directly by its payment info hash.
const request = await merchant.refund?.getByKey(paymentInfoHash)
Parameters
NameTypeDescription
paymentInfoHashHexkeccak256 of the payment info struct
Returns Promise<RefundRequestData>.

Paginated refund request listing

refund.getReceiverRequests

Retrieve paginated refund request keys for this merchant (the receiver).
const { keys, total } = await merchant.refund?.getReceiverRequests(
  receiverAddress,
  0n,
  10n,
) ?? { keys: [], total: 0n }

for (const hash of keys) {
  const request = await merchant.refund?.getByKey(hash)
  // ... inspect request.amount, request.status
}
Parameters
NameTypeDescription
receiverAddressReceiver address to query (typically the merchant)
offsetbigintIndex offset
countbigintMax entries to return
Returns Promise<{ keys: readonly Hex[]; total: bigint }>. To hydrate each entry, call refund.getByKey(hash) per key.
getOperatorRequests (paginated across all payments under an operator) lives on createArbiterClient, not on the merchant client.

Refund request actions

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.

payment.voidPayment

To approve and execute a refund, call payment.voidPayment(). The operator’s VOID_POST_ACTION_HOOK (RefundRequest) flips the request status to Approved.
const tx = await merchant.payment.voidPayment(paymentInfo)
Parameters
NameTypeDescription
paymentInfoPaymentInfoFull struct identifying the payment
dataHex (optional)Pass-through data for pre/post action plugins
Returns Promise<Hash>.
voidPayment() flips the pending RefundRequest to Approved. This action cannot be undone.

Freeze management

freeze.isFrozen

Check whether a freeze currently holds a payment. The escrow blocks capture on a frozen payment until the arbiter unfreezes it.
const frozen = await merchant.freeze?.isFrozen(paymentInfo)
Parameters
NameTypeDescription
paymentInfoPaymentInfoFull struct identifying the payment
Returns Promise<boolean>.
The merchant client exposes freeze.isFrozen only. Lifting a freeze (unfreeze) is an arbiter-role action; use createArbiterClient or createX402r().

Complete refund workflow

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')
  }
}

Refund request lifecycle

Method reference

MethodParametersReturns
refund.haspaymentInfoboolean
refund.getStatuspaymentInfoRefundRequestStatus
refund.getpaymentInfoRefundRequestData
refund.getByKeypaymentInfoHashRefundRequestData
refund.getStoredPaymentInfopaymentInfoHashPaymentInfo
refund.getReceiverRequestsreceiver, offset, count{ keys: readonly Hex[]; total: bigint }
refund.getCancelCountpaymentInfobigint (number of cancellations on this RefundRequest)
refund.getCancelledAmountpaymentInfo, cancelIndexbigint (amount cancelled at the given index)
freeze.isFrozenpaymentInfoboolean
payment.voidPaymentpaymentInfo, data?Hash (flips the pending RefundRequest to Approved)

Next steps

Merchant SDK

Capture funds, charge, and query escrow state.

RefundRequest

RefundRequest contract details and state machine.