Charges Webhooks

On-chain transactions

  • status = underpaid - Transaction is seen but the charge is only partially paid. Waiting for the user to send the remainder.
  • status = refunded- Transaction was underpaid but the user canceled it, asking for a refund.
  • status = processing - Transaction is seen for the first time on the mempool.
  • status = paid - Transaction is confirmed on the Bitcoin blockchain.
  • status = expired - Checkout has expired (default : 24h)

Lighting Network transactions

  • status = paid - Transaction is settled.
  • status = expired - Checkout has expired (default : 24h)
POST callback_url | application/x-www-form-urlencoded
{
    id: id,
    callback_url: callback_url,
    success_url: success_url,
    status: status,
    order_id: order_id,
    description: description,
    price: price,
    fee: fee,
    auto_settle: auto_settle,
    hashed_order: hashed_order
}

When a charge is paid via an on-chain transaction, there are extra fields available, such as the bitcoin transactions and, if the transaction was underpaid, the missing_amt, that specifies the missing amount, in satoshis.

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

Validating webhooks

OpenNode signs all charge related events it sends to your endpoints with a hashed_order field on the event payload. This allows you to validate that the events were sent by OpenNode and not by a third party.

You can verify the signatures by computing an HMAC with the SHA256 hash function. Use the api-key used on the charge creation as the key, and the charge id as the message.

const crypto = require('crypto');

const received = charge.hashed_order;
const calculated = crypto.createHmac('sha256', MY_API_KEY).update(charge.id).digest('hex');

if (received === calculated) {
    //Signature is valid
}
else {
    //Signature is invalid. Ignore.
}