A payment callback (payment notification) will be sent to the merchant’s callback_url whenever the order status, refund status, or billing status changes due to a request. The callback_url parameter is defined when creating the object.
TestingFor payment callback handling you can use requestcatcher.com tool.
Callback Format
Callback data is sent in POST method.
By creating new API App you can choose in which format you want to receive callbacks:
- Form Encoding (application/x-www-form-urlencoded)
- JSON (application/json)

Change callback format for existing API App go to Merchant » API » Apps » Edit.
Callback Retry Schedule
CoinGate sends payment notification while your application returns response 200 (OK) HTTP or 204 (No Content) status code.
Retry Policy for Payment Notifications
Payment notifications are sent according to the following schedule based on the retry count:
Retry Count Range | Interval Between Retries |
---|---|
1 – 5 | Every 1 minute |
6 – 10 | Every 5 minutes |
11 – 15 | Every 10 minutes |
16 – 20 | Every 20 minutes |
21 – 25 | Every 30 minutes |
26 – 30 | Every 1 hour |
31 – 35 | Every 5 hours |
36 – 40 | Every 1 day |
41 and above | Callback is terminated and no more will be sent |
Notification Response Timeout
After sending a payment notification, CoinGate waits for a response for 20 seconds.
Conditions for Canceling and Terminating Payment Notifications
A payment notification will be canceled and terminated under the following conditions:
Retry limit reached | Description |
---|---|
Retry limit reached | Callback was attempted 40 times without success. |
Response status: 301 or 302 | Redirect received — typically happens when using an http URL that redirects to https. Use the correct protocol in your callback URL. |
Response status: 401 Unauthorized | Server requires authentication — usually occurs if your site is password-protected. Ensure your callback URL is publicly accessible. |
Response status: 403 Forbidden | Callback is explicitly blocked — check your firewall, IP whitelist, or server restrictions to ensure CoinGate is not being denied access. |
Callback to TOR network | Notifications to addresses within the TOR network are not allowed. |
Callback to private network | Notifications sent to private networks (e.g., localhost, 127.0.0.1, or internal IPs) are not permitted. |
IP Addresses
Payment Callbacks are sent from the servers listed in the following public API endpoints:
These endpoints are public and do not require authentication. Each IP address is listed on a new line.
Ensure that your server and any third-party security services (e.g., Cloudflare, Incapsula) allow traffic from these IP addresses. Blocking them may prevent successful callback delivery.
We recommend updating your IP whitelist regularly using the information provided by these endpoints.
Private Nework & Localhost
CoinGate payment callback will not send notifications to private networks (for example: localhost).
In localhost, you can send test payment notification with cURL library:
# https://httpie.io/
http POST "https://coingate.requestcatcher.com/payment" \
id==343 \
order_id=="ORDER-1415020039" \
status=="paid" \
price_amount==1050.99 \
price_currency=="USD" \
receive_currency=="EUR" \
receive_amount==926.73 \
pay_amount==4.81849315 \
pay_currency=="BTC" \
created_at=="2014-11-03T13:07:28+00:00"
curl --request POST \
--data-urlencode "id=343" \
--data-urlencode "order_id=ORDER-1415020039" \
--data-urlencode "status=paid" \
--data-urlencode "price_amount=1050.99" \
--data-urlencode "price_currency=USD" \
--data-urlencode "receive_currency=EUR" \
--data-urlencode "receive_amount=926.73" \
--data-urlencode "pay_amount=4.81849315" \
--data-urlencode "pay_currency=BTC" \
--data-urlencode "created_at=2014-11-03T13:07:28+00:00" \
"https://coingate.requestcatcher.com/payment"
Payment Callback Logs
You can review payment callbacks and your server response to callback in your account panel. Login to your account and locate API » Payment Callbacks.
- Live: https://account.coingate.com/merchant-tools/api-integrations?tab=callbacks
- Sandbox: https://dashboard-sandbox.coingate.com/account/apps/api-payment-callbacks
Accepting Payment Callback
Example: save the code below as accept-coingate-callback.php
and execute cURL command in your command line tool:
# https://httpie.io/
http POST "http://localhost/accept-coingate-callback.php?token=5d02161be9bfb6192a33" \
id==343 \
order_id=="ORDER-1415020039" \
status=="paid" \
price_amount==1050.99 \
price_currency=="USD" \
receive_currency=="EUR" \
receive_amount==926.73 \
pay_amount==4.81849315 \
pay_currency=="BTC" \
created_at=="2014-11-03T13:07:28+00:00"
curl --request POST \
--data-urlencode "id=343" \
--data-urlencode "order_id=ORDER-1415020039" \
--data-urlencode "status=paid" \
--data-urlencode "price_amount=1050.99" \
--data-urlencode "price_currency=USD" \
--data-urlencode "receive_currency=EUR" \
--data-urlencode "receive_amount=926.73" \
--data-urlencode "pay_amount=4.81849315" \
--data-urlencode "pay_currency=BTC" \
--data-urlencode "created_at=2014-11-03T13:07:28+00:00" \
"http://localhost/accept-coingate-callback.php?token=5d02161be9bfb6192a33"
<?php
// Database connection
$mysqli = new mysqli('hostname', 'username', 'password', 'database');
// Check connection
if ($mysqli->connect_error) {
die("Connection failed: " . $mysqli->connect_error);
}
// Prepare and bind to securely fetch the order
$order_id = $_POST['order_id'];
$stmt = $mysqli->prepare("SELECT * FROM orders WHERE id = ?");
$stmt->bind_param("i", $order_id);
$stmt->execute();
$result = $stmt->get_result();
$order = $result->fetch_assoc();
$stmt->close();
// Securely compare the tokens
$received_token = $_POST['token'];
$stored_hashed_token = $order['token'];
if (hash_equals($stored_hashed_token, hash_hmac('sha256', $received_token, 'your_secret_key'))) {
// Handle CoinGate order status: https://developer.coingate.com/docs/order-statuses
$status = NULL;
// Verify payment status
if ($_POST['status'] === 'paid') {
if ($_POST['price_amount'] == $order['amount']) {
$status = 'paid';
}
} else {
$status = $_POST['status'];
}
// Update order status if necessary
if (!is_null($status)) {
$stmt = $mysqli->prepare("UPDATE orders SET status = ? WHERE id = ?");
$stmt->bind_param("si", $status, $order_id);
$stmt->execute();
$stmt->close();
}
}
// Close the database connection
$mysqli->close();
?>
class CoingateCallbackController < ApplicationController
skip_before_action :verify_authenticity_token, only: :create
def create
order = Order.find_by(id: params[:order_id])
if order.present?
if params[:token].present? && order.token == params[:token]
status = nil
if params[:status] == 'paid'
if params[:price].to_d >= order.amount # in addition you can check currency (params[:currency] == order.currency)
status = 'paid'
end
else
status = params[:status]
end
if status.present?
order.update_attribute(:status, status)
end
end
end
end
end