Прием платежей на сайте через Юкассу
Самое подробное описание процесса оплаты через сервис ЮKassa (ЮМани) с помощью прямых запросов к API через PHP cURLПрием платежей на сайте через Юкассу
Самое подробное описание процесса оплаты через сервис ЮKassa (ЮМани) с помощью прямых запросов к API через PHP cURL
📅 02 апреля 2023
👁🗨 5198
Данное решение по приёму платежей через юкассу было выполнено на системе CMS 1С-Битрикс, поэтому подключаемые файлы и используемые методы взяты оттуда, но понимая принцип работы и зная язык программирования, его можно кастомизировать без проблем. К тому же для удобства я оставил максимальное количество комментариев в коде.
Принцип работы
- Пользователь заходит на раздел /pay/, вводит данные в форму, нажимает оплатить
- Пользователя редиректит на страницу оплаты, он проводит оплату, нажимает "вернуться в магазин"
- Пользователя возвращает на /pay/?paid и под формой он получает уведомление "Если возникли вопросы, свяжитесь с техподдержкой."
- После совершения оплаты, Юкасса отправляет объект с данными о текущем платеже на адрес который указывается в личном кабинете Юкассы в разделе Интеграция -> HTTP-уведомления. В нашем примере на https://stanislav-web.com/pay/paid.php
- Мы проводим обработку полученных данных в файле paid.php и отправляем данные из описания платежа на почту
Последовательность настройки
- Получаем shop_id и secret_key в личном кабинете магазина
- Добавляем форму HTML и код PHP для отправки запроса в Юкассу на создание платежа
- Настраиваем в личном кабинете HTTP-уведомления (на скрине ползунок установлен только на получение информации об успешном платеже)
- Настраиваем код PHP на получение запроса об успешно созданном платеже и отправление на почту
<? // index.php
require ($_SERVER['DOCUMENT_ROOT'] . '/bitrix/header.php');
$APPLICATION->SetTitle('Оплата');
$domain = ((!empty($_SERVER['HTTPS'])) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST']; // Получаем адрес сайта
$curPage = $APPLICATION->GetCurPage(); // Получаем текущий раздел
?>
<div class="pay">
<form action="/pay/" method="POST" class="pay-form" autocomplete="on"><!-- action="/pay/" - значит POST-запрос придёт на раздел /pay/ -->
<input type="hidden" name="url" value="<?= $domain . $curPage . '?paid'; ?>"><!-- В ключ url массива $_POST передадим текущий раздел с GET-параметром ?paid, чтобы потом его передать в return_url, этот адрес будет установлен на кнопке возврата в магазин после совершения оплаты -->
<input type="text" name="fio" class="pay-form__input" required placeholder="ФИО">
<input type="text" name="dogovor" inputmode="tel" minlength="9" maxlength="9" class="pay-form__input" required placeholder="Номер договора">
<input type="text" name="telefon" inputmode="tel" class="pay-form__input" required placeholder="Контактный номер телефона">
<input type="email" name="email" class="pay-form__input" required placeholder="Email">
<input type="number" name="summa" inputmode="tel" class="pay-form__input" required placeholder="Сумма оплаты">
<input type="submit" class="pay-form__btn" value="Оплатить">
</form>
<? if (isset($_GET['paid'])) { ?><!-- Проверка на вышеустановленный GET-параметр, он будет установлен после возврата со страницы оплаты, тогда выведется блок ниже с классом pay__support -->
<div class="pay__support">Если возникли вопросы, свяжитесь с техподдержкой.</div>
<? } ?>
</div>
<?
if ( // Если в массив $_POST пришли все нижеперечисленные ключи
!empty($_POST['fio'])
&&
!empty($_POST['dogovor'])
&&
!empty($_POST['telefon'])
&&
!empty($_POST['email'])
&&
!empty($_POST['summa'])
) {
// Функция для генерации ключа идемпотентности, уникального значения операции на стороне сайта
function gen_uuid() {
return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
mt_rand( 0, 0xffff ),
mt_rand( 0, 0x0fff ) | 0x4000,
mt_rand( 0, 0x3fff ) | 0x8000,
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
);
};
// Функция для отправки запроса в Юкассу
function gateway($data) {
$ch = curl_init('https://api.yookassa.ru/v3/payments');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_USERPWD, 'shop_id:secret_key'); // Здесь указывается тестовый либо боевой shop_id и secret_key вашего магазина
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Idempotence-Key: ' . gen_uuid()));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$res = curl_exec($ch);
curl_close($ch);
$response = json_decode($res, true);
return $response;
};
// Собираем строку из данных в описание платежа, чтобы потом разбить и отправить на почту, больше у Юкассы нет полей для текста
$description = $_POST['fio'] . ', ' . $_POST['dogovor'] . ', ' . $_POST['telefon'] . ', ' . $_POST['email'];
// Генерируем рандомное значение от 0 до 1000000 млн для идентификатора заказа order_id
$rand = rand(0, 1000000);
// Собираем массив из данных
$data = [
'amount' => [
'value' => $_POST['summa'],
'currency' => 'RUB',
],
'capture' => true,
'confirmation' => [
'type' => 'redirect',
'return_url' => $_POST['url'],
],
'description' => $description,
'metadata' => [
'order_id' => $rand,
]
];
// Преобразовываем массив $data в JSON
$data = json_encode($data, JSON_UNESCAPED_UNICODE);
// Передаём аргументом массив $data в формате JSON в функцию gateway для отправки запроса в Юкассу
$response = gateway($data);
// Если в ответе от Юкассы мы получим какую-либо ошибку, в $response вернётся ключ type со значением error
if (!empty($response['type'])) {
// И если будет ошибка, выводим строку в виде блока с классом pay__support
echo '<div class="pay__support">Произошла ошибка! Попробуйте снова или свяжитесь с техподдержкой.</div>';
} else {
// Если ошибок нет, производим редирект пользователя на страницу оплату, адрес которой придёт в ответе
header('Location: ' . $response['confirmation']['confirmation_url'], true, 301);
exit();
};
};
?>
<? require ($_SERVER['DOCUMENT_ROOT'] . '/bitrix/footer.php'); ?>
<? // /pay/paid.php
require ($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php');
// Получаем объект от Юкассы
$source = file_get_contents('php://input');
$requestBody = json_decode($source, true);
// Если получаем ответ об успешной оплате
if ($requestBody['event'] == 'payment.succeeded') {
// Проводим манипуляции с полученными данными, формируем новый массив и отправляем на почту
$descriptionExplode = explode(', ', $requestBody['object']['description']); // Здесь разбиваем ту самую строку, которую формировали и передавали в $description
$fio = $descriptionExplode[0];
$dogovor = $descriptionExplode[1];
$telefon = $descriptionExplode[2];
$email = $descriptionExplode[3];
$summa = $requestBody['object']['amount']['value'] . ' ' . $requestBody['object']['amount']['currency'];
$subject = 'Оплата на сайте';
$message = '';
$message .= '<b>ФИО: </b>' . $fio . '<br>';
$message .= '<b>Номер договора: </b>' . $dogovor . '<br>';
$message .= '<b>Контактный номер телефона: </b>' . $telefon . '<br>';
$message .= '<b>Email: </b>' . $email . '<br>';
$message .= '<b>Сумма оплаты: </b>' . $summa;
$mail = 'iam@stanislav-web.com'; // Получатель письма
$headers = "Content-type: text/html; charset=utf-8\r\n";
$headers .= "From:stanislav-web.com <from@stanislav-web.com>\r\n";
$headers .= "Reply-To: from@stanislav-web.com\r\n";
mail($mail, $subject, $message, $headers, '-f from@stanislav-web.com');
// Такое тело будет у письма которое придёт на почту:
//
// ФИО: Иванов Иван Иванович
// Номер договора: 123456789
// Контактный номер телефона: +7 (999) 888-77-66
// Email: test@test.ru
// Сумма оплаты: 777 RUB
}
?>
Если в ответе возвращается ошибка "Receipt is missing or illegal", значит отсутствует информации о чеке. Скорее всего в "боевом" магазине включена онлайн-касса.
В таком случае, массив из данных будет выглядеть вот так:
<?
// Собираем массив из данных
$data = [
'amount' => [
'value' => $post['summa'],
'currency' => 'RUB',
],
'capture' => true,
'confirmation' => [
'type' => 'redirect',
'return_url' => $post['url'],
],
'description' => $post['description'],
'metadata' => [
'order_id' => $post['id'],
],
'receipt' => [
'customer' => [
'email' => $post['email'],
],
'items' => [
[
'description' => $post['description'],
'quantity' => '1.00',
'amount' => [
'value' => $post['summa'],
'currency' => 'RUB'
],
'tax_system_code' => '1',
'vat_code' => '1',
'payment_mode' => 'full_payment',
'payment_subject' => 'service'
]
]
]
];
?>