В данном примере рассматривается процесс формирования ссылки на оплату, исходя из предоставленных параметров. При переходе по данной ссылке плательщик будет перенаправлен на страницу оплаты для завершения транзакции.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
const https = require('https'); const querystring = require('querystring'); // Доступы к Paykeeper const server = 'https://xxxxxxxx.server.paykeeper.ru'; const user = 'admin'; const password = 'xxxxxxxx'; // Данные клиента const email = ''; const phone = ''; // Номер заказа const orderId = 9999; // Массив товаров в заказе const basket = [ { name: 'Тортилья, 20 см, 756 г', count: 5, price: 381.0, }, ]; // Собираем массив товаров для Paykeeper let total = 0; const prods = basket.map((row) => { const sum = row.price * row.count; total += sum; return { name: row.name, price: row.price, quantity: row.count, sum: sum, tax: 'none', item_type: 'goods', }; }); // Добавляем доставку const delivery = { name: 'Доставка', price: 300, quantity: 1, sum: 300, tax: 'none', item_type: 'service', }; prods.push(delivery); total += delivery.sum; // Авторизация в Paykeeper const credentials = Buffer.from(`${user}:${password}`).toString('base64'); const headers = { 'Content-Type': 'application/x-www-form-urlencoded', Authorization: `Basic {credentials}`, }; // Получение токена const getToken = () => { const options = { hostname: new URL(server).hostname, path: '/info/settings/token/', method: 'GET', headers, }; return new Promise((resolve, reject) => { const req = https.request(options, (res) => { let data = ''; res.on('data', (chunk) => (data += chunk)); res.on('end', () => resolve(JSON.parse(data))); }); req.on('error', reject); req.end(); }); }; // Создание счета const createInvoice = (token) => { const paymentData = { pay_amount: total, orderid: orderId, service_name: `;PKC|${JSON.stringify(prods)}|`, client_email: email, client_phone: phone, token: token, }; const requestBody = querystring.stringify(paymentData); const options = { hostname: new URL(server).hostname, path: '/change/invoice/preview/', method: 'POST', headers: { ...headers, 'Content-Length': Buffer.byteLength(requestBody), }, }; return new Promise((resolve, reject) => { const req = https.request(options, (res) => { let data = ''; res.on('data', (chunk) => (data += chunk)); res.on('end', () => resolve(JSON.parse(data))); }); req.on('error', reject); req.write(requestBody); req.end(); }); }; // Основной процесс (async () => { try { const tokenResponse = await getToken(); if (tokenResponse.token) { const token = tokenResponse.token; const invoiceResponse = await createInvoice(token); if (invoiceResponse.invoice_id) { const invoiceId = invoiceResponse.invoice_id; const link = `${server}/bill/${invoiceId}/`; console.log(`Перейдите по ссылке: ${link}`); } else { console.error('Ошибка при создании счета:', invoiceResponse); } } else { console.error('Ошибка авторизации:', tokenResponse); } } catch (error) { console.error('Ошибка:', error.message); } })(); |
После перехода по ссылке ${link} пользователь будет направлен на страницу оплаты.
На языке Node.js обработку запросов от PayKeeper об успешных платежах можно реализовать следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
const crypto = require("crypto"); const http = require("http"); // Секретное слово const SECRET_SEED = "verysecretseed"; // Утилита для создания MD5-хэша const createHash = (str) => crypto.createHash("md5").update(str).digest("hex"); // Функция обработки запроса const handlePaymentNotification = (req, res) => { let body = ""; // Считываем тело запроса req.on("data", (chunk) => { body += chunk.toString(); }); req.on("end", () => { const params = new URLSearchParams(body); // Извлекаем параметры const id = params.get("id"); // например 123 const sum = params.get("sum"); // например 1500.00 const clientid = params.get("clientid") || ""; // Параметр может быть необязательным, например Иванов Иван const orderid = params.get("orderid") || ""; // Параметр может быть необязательным, например 456 const key = params.get("key"); // например 2ba28d39e3836f4cc1bd96559417b4fb // Проверяем обязательные параметры if (!id || !sum || !key) { res.writeHead(400, { "Content-Type": "text/plain" }); res.end(); // Никакого дополнительного вывода return; } // Вычисляем ожидаемый хэш const expectedKey = createHash(`${id}${Number(sum).toFixed(2)}{clientid}${orderid}${SECRET_SEED}`); // Проверяем хэш if (key !== expectedKey) { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("Error! Hash mismatch"); // Никакого дополнительного вывода return; } // Вычисляем ответный хэш const responseHash = createHash(`${id}${SECRET_SEED}`); // Формируем ответ строго в требуемом формате res.writeHead(200, { "Content-Type": "text/plain" }); res.end(`OK ${responseHash}`); }); }; // Создаём сервер http.createServer(handlePaymentNotification).listen(8080, () => { console.log("Сервер запущен на порту 8080"); }); |
Полный перечень параметров в POST-оповещении можно посмотреть здесь.
В ответ на данный запрос должна быть отправлена строка вида:
Важно отметить, что ответ данного скрипта должен быть исключительно в указанном формате и ни байтом больше, в противном случае PayKeeper будет считать, что ваш сайт неправильно обработал оповещение об успешном платеже.
В PayKeeper возможен полный возврат платежа с помощью метода /change/payment/reverse/. Возврат могут делать только пользователи с включённой функцией возврата. Подробнее можно ознакомиться в документации JSON API. Пример запроса на полный возврат:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
const https = require('https'); const querystring = require('querystring'); const Buffer = require('buffer').Buffer; // Номер платежа const invoiceId = '1234567890'; // Доступы к Paykeeper const server = 'https://xxxxxxx.paykeeper.ru'; const user = 'admin'; const password = 'xxxxxxx'; // Авторизация в Paykeeper const base64Credentials = Buffer.from(`${user}:${password}`).toString('base64'); const headers = { 'Content-Type': 'application/x-www-form-urlencoded', Authorization: `Basic ${base64Credentials}`, }; // Функция для выполнения HTTP-запроса function httpRequest(url, method, headers, data = null) { return new Promise((resolve, reject) => { const uri = new URL(url); const options = { hostname: uri.hostname, path: uri.pathname + uri.search, method, headers, }; const req = https.request(options, (res) => { let body = ''; res.on('data', (chunk) => (body += chunk)); res.on('end', () => resolve({ statusCode: res.statusCode, body })); }); req.on('error', reject); if (data) req.write(data); req.end(); }); } // Получение информации о платеже httpRequest(`${server}/info/invoice/byid/?id={invoiceId}`, 'GET', headers) .then((response) => { const info = JSON.parse(response.body); if (info.paymentid) { // Запрос токена return httpRequest(`${server}/info/settings/token/`, 'GET', headers).then((tokenResponse) => { const tokenData = JSON.parse(tokenResponse.body); if (tokenData.token) { // Запрос на возврат платежа const requestData = querystring.stringify({ id: info.paymentid, amount: 500, partial: false, token: tokenData.token, }); return httpRequest( `${server}/change/payment/reverse/`, 'POST', headers, requestData ).then((reverseResponse) => { const reverseData = JSON.parse(reverseResponse.body); console.log(reverseData); }); } else { console.log('Не удалось получить токен'); } }); } else { console.log('Платеж не найден'); } }) .catch((error) => { console.error('Ошибка:', error); }); |
Менеджер перезвонит вам и расскажет про детали подключения