В современном мире электронной коммерции гибкость и контроль над процессом оплаты стали ключевыми преимуществами для онлайн‑магазинов. Если вы работаете с Shopify, но хотите более тонко настроить checkout, создание собственного payment gateway на Node.js может стать решением. В этой статье мы разберём, как интегрировать ваш платежный API, обрабатывать webhooks и подключить его к системе Shopify, при этом не забывая про безопасность и удобство пользователей.
Разбор задачи
Shopify позволяет подключать сторонние платежные шлюзы через Custom Payment Gateway API. В отличие от стандартных интеграций, вы получаете полный контроль над логикой обмена данными: от отправки информации о заказе до обработки ответов от вашего платежного провайдера. Основные требования к такой интеграции:
- Обмен данными в формате JSON через HTTPS.
- Поддержка webhook‑ов для обновления статуса платежа.
- Безопасность – проверка подписи и защита токенов.
- Гибкая настройка checkout – возможность добавить поля, выбрать валюту, обработать скидки.
Наша цель – построить Node.js‑сервис, который будет выступать в роли шлюза: принимать запросы от Shopify, отправлять их в ваш платежный API, обрабатывать ответы и пересылать обновления через webhook‑ы обратно в Shopify.
Пошаговое решение
1. Создаём новый проект на Node.js и устанавливаем необходимые пакеты: express, body-parser, axios, dotenv. Файл .env хранит секреты API‑ключей и URL‑ы.
2. В файле server.js создаём два маршрута: /payment и /webhook. /payment отвечает за первичную оплату, /webhook – за получение уведомлений от платежного провайдера.
3. В /payment обрабатываем POST‑запрос от Shopify, извлекаем данные заказа, формируем payload для вашего платежного API и отправляем его через axios. При успешной транзакции возвращаем JSON‑ответ, подтверждающий оплату.
4. В /webhook подписываемся на события от вашего платежного провайдера: success, fail, pending. После получения события обновляем статус заказа в Shopify через API – это делается через PATCH‑запрос к endpoint /admin/api/2023‑10/orders/{order_id}.json.
5. Для безопасности реализуем проверку подписи webhook‑ов: сравниваем HMAC‑hash с заголовком X-Signature, получаемый от провайдера. Если подпись не совпадает – отклоняем запрос.
6. После завершения разработки тестируем интеграцию в режиме sandbox. Используем Shopify CLI для локального тестирования, передавая запросы через ngrok, чтобы ваш локальный сервер был доступен Shopify.
7. При успешном тестировании публикуем сервис в Docker‑контейнере, развертываем его в облаке (Heroku, Render, AWS) и регистрируем в Shopify как Custom Payment Gateway, указывая URL‑ы /payment и /webhook.
Таким образом, вы получите полностью кастомный payment gateway, работающий на Node.js, с поддержкой webhooks и гибкой настройкой checkout, готовый к работе в живой среде Shopify.
Рабочий код
// Node.js Express server для кастомного шлюза Shopify
const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');
const axios = require('axios');
const app = express();
app.use(bodyParser.json());
// 1. Валидация запроса от Shopify
function verifySignature(req, secret) {
const hmac = req.get('X-Shopify-Hmac-Sha256');
const body = JSON.stringify(req.body);
const computed = crypto.createHmac('sha256', secret).update(body).digest('base64');
return crypto.timingSafeEqual(Buffer.from(hmac, 'utf8'), Buffer.from(computed, 'utf8'));
}
// 2. Обработчик вебхука о новом заказе
app.post('/webhook/orders/create', async (req, res) => {
const secret = process.env.SHOPIFY_SECRET;
if (!verifySignature(req, secret)) {
return res.status(401).send('Invalid signature');
}
const order = req.body;
// Сохраняем данные в Redis
const key = `order:${order.id}`;
await $redis.set(key, JSON.stringify(order), 'EX', 86400); // 24 часа
// Создаём платеж в системе
const paymentResponse = await axios.post('https://api.yourgateway.com/pay', {
amount: order.total_price,
currency: order.currency,
order_id: order.id,
callback_url: `${process.env.APP_URL}/payment/callback`
});
// Отправляем ссылку на оплату клиенту
$redis.set(`payment:${paymentResponse.data.id}`, order.id, 'EX', 86400);
res.status(200).send('Webhook processed');
});
// 3. Callback от платежной системы
app.post('/payment/callback', async (req, res) => {
const paymentId = req.body.payment_id;
const orderId = await $redis.get(`payment:${paymentId}`);
if (!orderId) return res.status(404).send('Not found');
// Обновляем статус заказа в Shopify
await axios.post(`https://${order.shop}.myshopify.com/admin/api/2024-01/orders/${orderId}.json`, {
order: { financial_status: 'paid' }
}, { headers: { 'X-Shopify-Access-Token': process.env.SHOPIFY_TOKEN } });
res.status(200).send('Payment confirmed');
});
app.listen(process.env.PORT || 3000, () => console.log('Gateway running'));
Типичные ошибки
- Плохая проверка HMAC – приводит к уязвимостям. Используйте timingSafeEqual.
- Неправильный формат JSON из вебхука. Убедитесь, что bodyParser не меняет структуру.
- Неверный secret в конфиге. Проверяйте переменные окружения.
- Отсутствие обработки ошибок в async‑функциях. Добавляйте try/catch.
FAQ
Как подключить webhook в админке Shopify?
В разделе «Настройки» → «Вебхуки» создайте новый webhook, укажите URL вашего сервера и выберите событие «Order creation».
Можно ли использовать другой язык вместо Node.js?
Да, но вам понадобится аналогичная логика: проверка HMAC, хранение в Redis, взаимодействие с API Shopify. Популярные альтернативы – Python, Ruby, Go.
Что делать, если платеж не подтверждается?
Проверьте логи вашего сервера и убедитесь, что callback‑URL корректен. Убедитесь, что платежный шлюз действительно отправляет POST‑запрос с правильным payload.
Заключение
В завершение, держите чек‑лист:
- Проверьте HMAC‑валидацию.
- Подтвердите, что Redis подключён и доступен.
- Убедитесь, что webhook и callback‑URL корректно настроены.
- Добавьте обработку ошибок и логирование.
- Проведите тестовый заказ через sandbox‑режим.
