Payment callback (Payment notification) will be sent to merchant's callback_url when order status is changed to pending confirming, paid, invalid, canceled, refunded or expired.

🚧

Callback data is sent in POST method.

📘

Testing

For payment callback handling you can use requestcatcher.com tool.

CoinGate callback sends data below:

NameValue
idCoinGate order (invoice) ID.
order_idCustom order ID of the merchant. Should be used to identify order or invoice number.
statusCoinGate payment status.
price_amountThe price set by the merchant; for example, 499.95.
price_currencyThe currency code which defines the currency in which the merchant's goods/services are priced; for example, USD, CHF, BTC (see supported currencies).
receive_currencyThe currency code which defines the currency in which the merchant's settlements will be paid. Currency conversions are done by CoinGate automatically. For example: EUR, USD, BTC, USDT, etc.
receive_amountThe amount which will be credited to the merchant when the invoice is paid. It is calculated by taking the price amount (converted to currency units set in receive_currency) and subtracting CoinGate processing fee from it.
pay_amountThe amount of cryptocurrency (defined by pay_currency) paid by the shopper.
pay_currencyThe cryptocurrency in which the payment was made; for example, BTC, LTC, ETH.
underpaid_amountThe amount of cryptocurrency (defined by pay_currency) underpaid by the shopper; for example, if pay_amount => 0.123, pay_currency => BTC, and the shopper paid 0.12 BTC, then underpaid_amount => 0.003. Changes in underpaid_amount will not trigger additional callbacks, but when order information is retrieved using GET or LIST, latest value will be shown.
overpaid_amountThe amount of cryptocurrency (defined by pay_currency) overpaid by the shopper; for example, if pay_amount => 0.123, pay_currency => BTC, and the shopper paid 0.15 BTC, then overpaid_amount => 0.027. Changes in overpaid_amount will not trigger additional callbacks, but when order information is retrieved using GET or LIST, latest value will be shown.
is_refundablePossible values: true, false. Indicates whether or not the shopper can request a refund on the invoice. Changes in is_refundable will not trigger additional callbacks, but when order information is retrieved using GET or LIST, latest value will be shown.
created_atInvoice creation time.
tokenYour custom token (or generated by CoinGate) to validate payment callback.

Fields pay_currency, pay_amount, expire_at, payment_address are only sent when the customer chooses the currency with which he is going to pay for the invoice.

Content-Type: application/x-www-form-urlencoded

// print_r($_POST)

Array
(
    [id] => 343
    [order_id] => 14037
    [status] => paid
    [price_amount] => 1050.99
    [price_currency] => USD
    [receive_amount] => 926.73
    [receive_currency] => EUR
    [pay_amount] => 4.81849315
    [pay_currency] => BTC
    [created_at] => 2014-11-03T13:07:28+00:00
        [token] => ff7a7343-93bf-42b7-b82c-b38687081a4e
)

See the code below how to accept callback.

Callback Retry Time

📘

CoinGate sends payment notification while your application returns response 200 (OK) HTTP or 204 (No Content) status code.

  • Sends every 1 minute if retry count is <= 5
  • Sends every 5 minutes if retry count is > 5 and <= 10
  • Sends every 10 minutes if retry count is > 10 and <= 15
  • Sends every 20 minutes if retry count is > 15 and <= 20
  • Sends every 30 minutes if retry count is > 20 and <= 25
  • Sends every 1 hour if retry count is > 25 and <= 30
  • Sends every 5 hours if retry count is > 30 and <= 35
  • Sends every 1 day if try count is > 35 and <= 40
  • Callback will be canceled if retry count is >= 41

After sending payment notification, CoinGate waits for a response for 20 seconds.

Payment notification will be canceled and terminated if one of the following conditions occur:

  • Payment notification is sent 40 times.
  • After sending payment notification, response status received is 301, 302 (Redirect). This commonly happens if you use "http" in your URL and it gets redirected to "https".
  • After sending payment notification, response status received is 401 (Unauthorized). This commonly happens when your website is password-protected (Basic access authentication). Ensure your website is publicly accessible.
  • Payment notification is sent to TOR network.
  • Payment notification is sent to a private network, such as localhost.

IP Addresses

Payment Callback is sent from servers described in API endpoint. This API endpoint is public, authentication is not required.
Live: https://api.coingate.com/v2/ips-v4
Sandbox: https://api-sandbox.coingate.com/v2/ips-v4

Each IP is separated by a new line. Make sure that your server, as well as third-party security services (cloudflare, incapsula, etc.), are not blocking these IP addresses.

We recommend update your IP whitelist from this API endpoint regularly.

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:

curl -X POST -d "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%2B00: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.

Accepting Payment Callback

Example: save the code below as accept-coingate-callback.php and execute cURL command in your command line tool:

curl -X POST -d "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%2B00:00" http://localhost/accept-coingate-callback.php?token=5d02161be9bfb6192a33
<?php

// Your custom order_id is defined when you creating new order: https://developer.coingate.com/docs/create-order
// Also don't forget to prevent SQL injection
$result = mysql_query("SELECT * FROM orders WHERE id = " . $_POST['order_id']);
$order = mysql_fetch_assoc($result);

// token is your random secure string (for example: 5d02161be9bfb6192a33) for each order
if ($_POST['token'] == $order['token']) {
  // Handle CoinGate order status: https://developer.coingate.com/docs/order-statuses
  $status = NULL;
  if ($_POST['status'] == 'paid') {
    if ($_POST['price_amount'] >= $order['amount']) {
      $status = 'paid';
    }
  }
  else {
    $status = $_POST['status'];
  }

  if (!is_null($status)) {
      mysql_query("UPDATE orders SET status = '".$status."' WHERE id = ".$_POST['order_id']);
  }
}
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