Многие платёжные системы приучили нас к следующему формату подключения приёма платежей: на своей стороне мы генерируем форму с набором input'ов type="hidden" и кнопкой отправки данных формы на некий url платёжной системы. Соответственно, кликая по этой кнопке, мы переходим на страницу платёжной системы и уже там нам предлагается заполнить данные карты. По завершении платёжная система делает запрос на указанный нами url, в котором содержится информация о состоянии платежа и его деталях.
Stripe предлагает более универсальное решение — вы сами принимаете и отправляете платёжные данные в Stripe. В свою очередь Stripe сообщает нам результаты о каждой операции. В этом есть свои преимущества: клиент не уходит на сторонний сайт, а процесс оплаты происходит значительно быстрее.
Нужно сказать, что, конечно, в Stripe позаботились о том, чтобы разработчики могли по-минимуму вникать в подробности и подготовили для нас как клиентские готовые решения в виде javascripts, генерирующих готовую форму оплаты, так и серверные бибилиотеки для обработки данных, поступившей из формы.
Мы же попробуем обойтись без готовых решений и попробуем максимально нативно решить задачу.
Логика работы
Всё действие состоим минимум из 2-х запросов:
- Запрос на получение токена;
- Запрос на оплату.
1. Запрос на получение токена (сущность TOKENS)
<?
$publicKey = 'pk_test_....';
$arrayPost = array(
'card' => array(
'number' => '4242424242424242',
'exp_month' => '12',
'exp_year' => '2020',
'cvc' => '123',
),
);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.stripe.com/v1/tokens',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => http_build_query($arrayPost),
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer '.$publicKey,
'Content-Type: application/x-www-form-urlencoded',
'Cache-Control: no-cache',
),
));
$response = curl_exec($curl);
$arrayInfo = curl_getinfo($curl);
curl_close($curl);
Обратите внимание на заголовок Content-Type, который должен быть строго application/x-www-form-urlencoded, а также на то как мы передаём публичный ключ.
Теперь проверяем $arrayInfo['http_code']. Документация Stripe говорит, что:
- 200 — всё, хорошо, можно продолжать;
- 4xx — мы передаём что-то не то. Неправильный номер карты, даты, CVC и т.д.
- 5xx — косяк на сервере Stripe.
Про ошибки инфа тут.
Если мы получаем код 4xx, то в $response будет JSON следующего вида:
{
"error": {
"code": "invalid_cvc",
"doc_url": "https://stripe.com/docs/error-codes/invalid-cvc",
"message": "Your card's security code is invalid.",
"param": "cvc",
"type": "card_error"
}
}
Если мы получаем код 200, то в $response будет такой JSON:
{
"id": "tok_1Chh58AGwED8fJ9ZZ66a5N1V",
"object": "token",
"card": {
"id": "card_1Chh58AGwED8fJ9ZkT7ccBph",
"object": "card",
"address_city": null,
"address_country": null,
"address_line1": null,
"address_line1_check": null,
"address_line2": null,
"address_state": null,
"address_zip": null,
"address_zip_check": null,
"brand": "Visa",
"country": "US",
"cvc_check": "unchecked",
"dynamic_last4": null,
"exp_month": 12,
"exp_year": 2019,
"fingerprint": "IKhrSWlsg0WBp9N1",
"funding": "credit",
"last4": "4242",
"metadata": {},
"name": null,
"tokenization_method": null
},
"client_ip": "178.68.54.197",
"created": 1530119126,
"livemode": false,
"type": "card",
"used": false
}
То, что хранится в начале этого JSON, а именно в "id" — это и есть наш искомый токен.
Документация по сущности TOKENS тут.
2. Запрос на оплату (сущность CHARGES)
<?
$publicKey = 'pk_test_....';
$curl = curl_init();
$arrayPost = array(
'source' => $token,
'amount' => 1000,
'currency' => 'usd',
'description' => 'описание заказа',
);
curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.stripe.com/v1/charges",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => http_build_query($arrayPost),
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer '.$publicKey,
'Content-Type: application/x-www-form-urlencoded',
'Cache-Control: no-cache',
),
));
$response = curl_exec($curl);
$arrayInfo = curl_getinfo($curl);
curl_close($curl);
Обратите внимание, что значение amount в POST, т.е. сумма платежа — это положительное целое число, указывающее сумму в наименьшей единице валюты (центы, евроценты, пенни, копейки и пр.). В примере amount = 1000 означает 10USD.
Что касается принимаемых валют, то в июне 2018 Stripe поддерживал более 130 валют (список тут).
При успешном запросе $response будет хранить JSON вида:
{
"id": "ch_1ChdO6AGwED8fJ9ZkX607gb9",
"object": "charge",
"amount": 1000,
"amount_refunded": 0,
"application": null,
"application_fee": null,
"balance_transaction": "txn_1ChdO6AGwED8fJ9ZxlRfB8Rr",
"captured": true,
"created": 1530104926,
"currency": "usd",
"customer": null,
"description": "Order #21",
"destination": null,
"dispute": null,
"failure_code": null,
"failure_message": null,
"fraud_details": {},
"invoice": null,
"livemode": false,
"metadata": {},
"on_behalf_of": null,
"order": null,
"outcome": {
"network_status": "approved_by_network",
"reason": null,
"risk_level": "normal",
"seller_message": "Payment complete.",
"type": "authorized"
},
"paid": true,
"receipt_email": null,
"receipt_number": null,
"refunded": false,
"refunds": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/ch_1ChdO6AGwED8fJ9ZkX607gb9/refunds"
},
"review": null,
"shipping": null,
"source": {
"id": "card_1ChdNuAGwED8fJ9ZzBWSjSrT",
"object": "card",
"address_city": null,
"address_country": null,
"address_line1": null,
"address_line1_check": null,
"address_line2": null,
"address_state": null,
"address_zip": null,
"address_zip_check": null,
"brand": "Visa",
"country": "US",
"customer": null,
"cvc_check": "pass",
"dynamic_last4": null,
"exp_month": 12,
"exp_year": 2019,
"fingerprint": "IKhrSWlsg0WBp9N1",
"funding": "credit",
"last4": "4242",
"metadata": {},
"name": null,
"tokenization_method": null
},
"source_transfer": null,
"statement_descriptor": null,
"status": "succeeded",
"transfer_group": null
}
В случае неуспеха — что-то типа:
{
"error": {
"code": "token_already_used",
"doc_url": "https://stripe.com/docs/error-codes/token-already-used",
"message": "You cannot use a Stripe token more than once: tok_1ChdNvAGwED8fJ9ZWGT8BEcy.",
"type": "invalid_request_error"
}
}
Это и есть тот нативный минимум, позволяющий принять отплату через банковские карты.
Документация по сущности CHARGES тут.
И это ваш хвалёный Stripe?!
На самом деле возможности платформы значительно шире. Выше мы рассмотрели всего две сущности (TOKEN и CHARGES), в действительности есть возможность работать с балансом (сущность BALANCE), покупателями (CUSTOMERS), спорами (DISPUTES), событиями (EVENTS), файлами (FILE UPLOADS), выводом средств (PAYOUTS), товарами (PRODUCTS) и возвратами (REFUNDS).
И всё это только по продукту Stripe Payments. А есть ещё Stripe Billing, позволяющий принимать регулярные платежи и Stripe Connect, позволяющий делать выплаты третьим лицам.