
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']; }
Для простоты примера я не привожу логи, которые должны писаться при ошибках и прочее.
Нужные страницы продавца (обязательны при разработке и тестировании)
- Список созданных приложений https://sandbox.coingate.com/account/apps
Список отправленных запросов к API с данными платежа (в models/CoingateRefillForm.php - formUrl). Можно посмотреть какие данные пришли.
- API Payment Callbacks https://sandbox.coingate.com/account/apps/api-payment-callbacks
При разработке на локальном домене, данные от API не дойдут, поэтому можно имитировать запрос с помощью curl отправив массив аналогичных данных.