Payment callback (Payment notification) will be sent to merchant's callback_url when order status or refund status or billing status is changed. Parameter callback_url is defined by creating object:

๐Ÿ“˜

Testing

For 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 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