Handling underpayments

When a customer pays via standard on-chain transaction, sometimes a payment request is underpaid by a few satoshis. This is most common when the buyer pays via an exchange or custodial wallet and these services take a fee on top of the payment.

You can choose to accept payments that are just slightly underpaid. To enable that feature, access Settings - Payments and Invoices and select a percentage that you feel comfortable with.

When a payment falls bellow the chosen threshold your payment request status will be marked as underpaid.

If you're using OpenNode's checkout experience (checkout.opennode.co), the buyer will have the option to send the remaining amount or cancel payment request underpayment be returned. By choosing to cancel the payment, the buyer will pay 200 satoshis (0.00000200 BTC) which goes toward paying the on-chain network fees associated with the refund.

982

Buyers have the option to complete payment or ask for underpayment to be returned.

904

Here, the payer is requesting return of underpayment and canceling purchase.

Even if you are not using our payment page, we recommend your payers be given the option to pay remaining amount or request underpayment to be returned.

Pay remaining amount

A webhook event is sent whenever a charge status is underpaid.

The buyer can complete their payment by sending the remainder (missing_amt) to the given bitcoin address (address).

POST callback_url | application/x-www-form-urlencoded
{
  id: 'ba57e419-a6c9-41b2-a54c-b870d073d899',
  callback_url: 'https://5eaacde7.ngrok.io/',
  success_url: 'https://opennode.co',
  status: 'underpaid',
  order_id: 'N/A',
  description: 'N/A',
  price: '250413',
  fee: '0',
  auto_settle: '0',
  address: '2Mz4Sx2fmKpop4Hmi4jEtJhiuDCF9VVu2ds',
  missing_amt: '125366',
  transactions: [
    {
      address: '2Mz4Sx2fmKpop4Hmi4jEtJhiuDCF9VVu2ds',
      created_at: '1559657208',
      settled_at: '',
      tx: 'e1e6e522386948daeabfb5b017aa87a695a823c9f561e88f03b6f467f55ba735',
      status: 'pending',
      amount: '125047'
    }
  ],
  hashed_order: 'c2a3896d4c8bfdcc25bbff0f3f15278fd948b96035f0438372eee9d4898f53b7'
}

Returning underpayment

When a charge has been underpaid, you can optionally cancel the payment and send funds back to your buyer.

You can initiate return of an underpayment using the Refunds API.

You will need to submit the checkout's ID (id) along with a bitcoin address and email (optional) that your buyer should provide. OpenNode will return funds to your buyer on your behalf and update them on their refund status via email.

const request = new XMLHttpRequest();

request.open('POST', 'https://api.opennode.co/v1/refunds');

request.setRequestHeader('Content-Type', 'application/json');
request.setRequestHeader('Authorization', 'API-Key');

let body = {
  checkout_id: 'ba57e419-a6c9-41b2-a54c-b870d073d899',
  address: '2MvPEjUGgE144amswWNxZh7634H42Lqzt6W',
  email: '[email protected]'
};

request.send(JSON.stringify(body));

Once a refund is initiated, the charge's new status is refunded. If you initially passed a callback_url on the charge creation a webhook will be sent with the new status and the refund information:

POST callback_url | application/x-www-form-urlencoded
{
  id: 'ba57e419-a6c9-41b2-a54c-b870d073d899',
  callback_url: 'https://5eaacde7.ngrok.io/',
  success_url: 'https://opennode.co',
  status: 'refunded',
  order_id: 'N/A',
  description: 'N/A',
  price: '250413',
  fee: '0',
  auto_settle: '0',
  address: '2Mz4Sx2fmKpop4Hmi4jEtJhiuDCF9VVu2ds',
  missing_amt: '125366',
  transactions: [
    {
      address: '2Mz4Sx2fmKpop4Hmi4jEtJhiuDCF9VVu2ds',
      created_at: '1559657208',
      settled_at: '',
      tx: 'e1e6e522386948daeabfb5b017aa87a695a823c9f561e88f03b6f467f55ba735',
      status: 'pending',
      amount: '125047'
    }
  ],
  refund: {
        "id": "97f6bee9-c36b-4b1f-a7e5-f9ab6e695c44",
        "email": "[email protected]",
        "address": "tb1qm9jn6l66eeap72e5meu3jwl5wnv4lg6x79e3zu",
        "amount": '125047',
        "fee": '200',
        "tx": null,
        "status": 'pending',
        "created_at": 1560349045,
        "processed_at": null,
        "checkout_id": 'ba57e419-a6c9-41b2-a54c-b870d073d899'
  }
  hashed_order: 'c2a3896d4c8bfdcc25bbff0f3f15278fd948b96035f0438372eee9d4898f53b7'
}