iPintiPint Docs
Guides

Check Payment Status

How to verify payment status and handle the invoice callback.

Check Payment Status

After creating a payment, you need to know when it's completed. iPint uses webhooks + polling to keep you informed.

The flow

  1. iPint sends a callback to your invoice_callback_url when status changes
  2. You call GET /invoice to verify the actual status (don't trust the callback body)
  3. You mark the payment based on the transaction_status in the response

Calling GET /invoice

const response = await fetch(
  `https://api.ipint.io:8003/invoice?invoice_id=${invoiceId}`
);
const invoice = await response.json();

// Check transaction_status
if (invoice.transaction_status === "COMPLETED") {
  // Payment successful — fulfill the order
} else if (invoice.transaction_status === "FAILED") {
  // Payment failed — cancel the order
} else {
  // Still processing — wait for the next callback
}

transaction_status values

StatusMeaningAction
CHECKINGChecking for transaction on blockchainWait
PROCESSINGTransaction hit blockchain, confirmations < 3Wait
COMPLETEDTransaction completedMark successful. Stop querying.
FAILEDCustomer didn't pay, or transaction not included in a blockMark failed. Stop querying.
CANCELLEDCustomer closed before QR was generatedMark cancelled. Stop querying.

If you get COMPLETED, FAILED, or CANCELLED, do not query further. These are terminal states.

Verifying full payment

Sometimes invoices are underpaid or overpaid. Always compare the invoice amounts with the received amounts before giving credit:

const isFullyPaid =
  invoice.received_amount_in_usd >= invoice.invoice_amount_in_usd &&
  invoice.received_crypto_amount >= invoice.invoice_crypto_amount;
CompareWith
invoice_amount_in_local_currencyreceived_amount_in_local_currency
invoice_amount_in_usdreceived_amount_in_usd
invoice_crypto_amountreceived_crypto_amount

Handling the callback

app.post("/webhook", (req, res) => {
  const { invoice_id } = req.body;

  // 1. Respond 200 immediately
  res.status(200).send();

  // 2. Verify via GET /invoice (don't trust the callback body)
  verifyInvoice(invoice_id).then((invoice) => {
    switch (invoice.transaction_status) {
      case "COMPLETED":
        fulfillOrder(invoice_id);
        break;
      case "FAILED":
        cancelOrder(invoice_id);
        break;
      case "CANCELLED":
        markCancelled(invoice_id);
        break;
    }
  });
});

Callback security notes

  • iPint does not send signed requests — do not trust the callback body.
  • Use it only as a trigger to call GET /invoice.
  • Do not whitelist iPint's IP addresses (they change without notice).
  • Use HTTPS for your callback URL.
  • Respond with HTTP 200 + empty body. Any other response = failed delivery.

Next steps

On this page