CoinGate - это сервис онлайн оплаты, который работает с криптовалютой (Bitcoin, Litecoin, Ethereum и другие). При этом плательщик может использовать любую разрешенную сервисом криптовалюту, а вы получите оплату например в долларах или биткоинах по своему желанию.


Регистрация на сервисе.

Сначала нужно создать учетную запись CoinGate на https://coingate.com для production и https://sandbox.coingate.com для тестирования.
Для режима тестирования не потребуется вводить никакую информацию касающуюся продавца/получателя средств. Этот режим и будет использован в примере.
Каждый сайт/интернет магазин необходимо зарегистрировать отдельно на странице https://sandbox.coingate.com/account/apps
После регистрации нужно будет сохранить полученный от API токен.


Описание API CoinGate.

Обзор API - общая схема и описание процесса https://developer.coingate.com/docs/api-overview
Указаны возможные среды (для тестирования и рабочая, ограничения)

Create Order https://developer.coingate.com/docs/create-order
Создание заказа в CoinGate и перенаправление покупателя на сервис для оплаты (payment_url).
Тут указаны какие параметры передавать.

Payment Callback https://developer.coingate.com/v2/docs/payment-callback
Описание страницы на которую переадресует пользователя API после изменения статуса оплаты. Тут указана параметры которые будут переданы POST запросом назад и пример как обработать - нужно установить в БД статус оплаты.
CoinGate отправляет уведомление об оплате, пока ваше приложение возвращает ответ 200 (OK) код состояния HTTP .

Get Order https://developer.coingate.com/docs/get-order
Получение информаци об оплате с помощью идентификатора заказа CoinGate

List Orders https://developer.coingate.com/docs/list-orders
Получение информации обо всех размещенных заказах. Можно получить массив с данными о всех заказах за указанный период.

Список возможных статусов заказа https://developer.coingate.com/docs/order-statuses



Пример использования.

В коде примера используется фреймворк Yii2, но его легко модифицировать под любой другой.

После того как пользователь выберет в качестве способа оплаты на вашем сайте CoinGate, нужно сформировать и отправить POST запрос с данными платежа для создания заказа на оплату и дальнейшего перенаправления пользователя на данный сервис.

Этот процесс можно разбить на 2 метода которые вызываются в контроллере после получения и проверки данных из формы.
Первый refill - создает новую транзакцию в БД куда сохраняется (из формы) сумма которую пользователь собирается оплатить, статус оплаты (в ожидании), id пользователя и др. нужная информация. Пример:
public function refill()
{
    $user = Yii::$app->user->identity;
    if ($this->validate()) {
        $transaction = new Transaction();
        $transaction->user_id = $user->id;
        $transaction->type = Transaction::TYPE_REFILL;
        $transaction->method = $this->method;
        $transaction->amount = $this->amount;
        $transaction->created_at = time();
        $transaction->status = Transaction::STATUS_PENDING;

        if ($transaction->save()) {
            $url = $this->formUrl($transaction);
            return $url;
        }
    }
    return false;
}

Далее нужно сформировать POST запрос к API CoinGate, поэтому там же (или в контроллере) вызывается метод который формирует запрос, отправляет его, получает ответ и извлекает ссылку для перенаправления пользователя:
/**
 * @param $transaction \app\models\Transaction
 * @return bool
 */
private function formUrl(&$transaction)
{

    $params = Yii::$app->params;
    $environment = $params['coingateEnvironment'];
    $coingateSecret = $params['coingateSecret'];
    $auth_token = $environment === 'sandbox' ? $params['coingateTestToken'] : $params['coingateToken'];

    \CoinGate\CoinGate::config(array(
        'environment'               => $environment, // sandbox OR live
        'auth_token'                => $auth_token,
        'curlopt_ssl_verifypeer'    => TRUE // default is false
    ));


    $transactionId = $transaction->id;
    $amount = $transaction->amount;
    $hashData = $coingateSecret.'_'.$transactionId.'_'.$amount.'_'.$transaction->created_at;

    $post_params = array(
        'order_id'          => $transactionId,
        'price_amount'      => $amount,
        'price_currency'    => 'USD', //в какой валюте указана сумма платежа
        'receive_currency'  => 'BTC', //конвертировать в биткоины для продавца
        //Адрес для сообщения (POST) от API CoinGate когда меняется статус оплаты.
        'callback_url'      => Url::toRoute('/balance/coingate-interaction', true),
        //Перенаправить если покупатель отменяет оплату
        'cancel_url'        => Url::toRoute('/balance/coingate-failed', true),
        //Перенаправить при успешной оплате
        'success_url'       => Url::toRoute('/balance/coingate-success', true),
        'title'             => Yii::t('main', 'Payment MySite.org'),
        'description'       => Yii::t('main', 'Recharge'),
        'token'             => hash('sha256', $hashData),
    );

    $order = \CoinGate\Merchant\Order::create($post_params);

    if ($order) {
        $url = $order->payment_url;
        return $url;
    } else {
        # Order Is Not Valid
        return false;
    }
}

Для работы с API используется специальное php расширение coingate/coingate-php которое необходимо установить с помощью Composer. Его страница:
https://github.com/coingate/coingate-php
Оно не привязано к какому либо фреймворку.

В методе formUrl указывается конфигурация, т.е. режим в котором будет работать API - тестовом (sandbox) или реальном (live), так же указывается токен полученный при регистрации.
\CoinGate\CoinGate::config(array(
    'environment'               => $environment, // sandbox OR live
    'auth_token'                => $auth_token,
    'curlopt_ssl_verifypeer'    => TRUE // default is false
));

Эти параметры нужно предварительно сохранить в предназначенный для этого файл, например config/params.php
//CoinGate
'coingateEnvironment' => 'sandbox', //sandbox(test) OR live
'coingateTestToken' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', //for testing
'coingateToken' => '',
'coingateSecret' => 'xxxxxxxxxxxxxxxxxxxxxxx', //random
'coingateSecret' - произвольная строка которая будет служить токеном - будет отправлена к API, чтобы затем он использовал ее при отправке запросов назад. Таким образом можно будет проверить, что запрос пришел именно от API.

Сформировав массив данных передаем его с помощью вызова метода create расширения:
$order = \CoinGate\Merchant\Order::create($post_params);

Используя curl будет отправлен POST запрос к API и получен ответ вида:
Order {#209 ▼
-order: array:12 [▼
"id" => 109420
"status" => "new"
"do_not_convert" => true
"price_currency" => "USD"
"price_amount" => "10.0"
"lightning_network" => false
"receive_currency" => ""
"receive_amount" => ""
"created_at" => "2018-01-04T08:35:35+00:00"
"order_id" => "2295"
"payment_url" => "https://sandbox.coingate.com/invoice/f7d40a28-fe1e-47c4-a632-c2f5ed9ba0e3"
"token" => "xxxxxxxxxxxxxxxxxxxxxxx"
]
}

В элементе payment_url содержится ссылка на которую нужно перенаправить пользователя для оплаты. В данном случает используется sandbox, т.е. режим тестирования. Платежи фактически не будут проведены.
Получаем ссылку и возвращаем в контроллер для перенаправления.


Далее пользователь совершает оплату на странице CoinGate и перенаправляется в случае успеха на одно действие контроллера, а в случае ошибки/отмены платежа на другое. Достаточно просто вывести сообщение о том, что платеж успешно прошел или наоборот. А можно дополнительно к ссылкам передать в GET параметрах какие-то данные, например id транзакции и пользователя, чтобы перепроверить действительно ли оплата прошла (статус «paid»).

После того как пользователь сделает оплату, от API придет POST запрос на маршрут указанный в callback_url о смене статуса платежа такого вида:
POST вида {
"id"=>109420,
"order_id"=>"295", //id транзакции
"status"=>"paid",
"pay_amount"=>"0.001263",
"pay_currency"=>"BTC",
"price_amount"=>"10.0",
"price_currency"=>"USD",
"receive_currency"=>"BTC",
"receive_amount"=>"0.00125",
"created_at"=>"2018-01-04T08:35:35+00:00",
"token"=>"xxxxxxxxxxxxxxxxxxxxxxxxxx"
}



Пример такого действия контроллера:
//Перенаправление от API CoinGate (callback_url) при изменении статуса платежа
public function actionCoingateInteraction()
{
    if (Yii::$app->request->isPost && $statusCode = CoingateRefillForm::validateInteractionRequest()) {
        return Yii::$app->response->setStatusCode($statusCode[0], $statusCode[1]);
    }

    throw new HttpException(404, Yii::t('main', 'Page not found'));
}

В нем вызывается метод validateInteractionRequest который обрабатывает эти данные, меняет статус транзакции, может вызывать другие методы и возвращает нужный HTTP код ответа для API. На основании ответа API определяет получил ли сервер его запрос или отправлять повторно.

public static function validateInteractionRequest()
{
    $token = Yii::$app->request->post('token');
    $transactionId = Yii::$app->request->post('order_id');
    $statusApi = Yii::$app->request->post('status'); //https://developer.coingate.com/docs/order-statuses
    $price_amount = Yii::$app->request->post('price_amount'); //сколько оплачено в $
    $receive_amount = Yii::$app->request->post('receive_amount'); //сколько получено в BTC (0.00125)

    $transaction = Transaction::findOne(['id' => $transactionId]);

    if ($transaction && $statusApi && $price_amount && $receive_amount) {

        $params = Yii::$app->params;
        $coingateSecret = $params['coingateSecret'];
        $hashData = $coingateSecret.'_'.$transaction->id.'_'.$transaction->amount.'_'.$transaction->created_at;
        if(hash('sha256', $hashData) != $token){
            return ['400', 'Bad Request'];
        }

        $info['transaction'] = $transaction->toArray();

        if ($transaction->status != Transaction::STATUS_SUCCESS) {
            $transaction->status = self::getStatusTransaction($statusApi);
            $transaction->amount_btc = $receive_amount;

            if ($transaction->save()) {
                if ($transaction->status === Transaction::STATUS_SUCCESS &&
                    $transaction->amount >= $price_amount) {

                    //вызов какого-то метода в случае успешной оплаты (например продажа товара)
                }
                return ['200', 'OK'];
            } 
        }
    } 

    return ['406', 'Not Acceptable'];
}

Для простоты примера я не привожу логи, которые должны писаться при ошибках и прочее.


Нужные страницы продавца (обязательны при разработке и тестировании)
Для каждого сайта создается отдельно. Указываются разрешенные IP.
Список отправленных запросов к API с данными платежа (в models/CoingateRefillForm.php - formUrl). Можно посмотреть какие данные пришли.
Список запросов от API после изменения статуса платежа. Можно увидеть какие данные были высланы на адрес сайта указанный в callback_url (методом POST).
При разработке на локальном домене, данные от API не дойдут, поэтому можно имитировать запрос с помощью curl отправив массив аналогичных данных.