Создание собственного shopify custom payment gateway nodejs – это возможность управлять оплатой в Shopify без посредников и с полной гибкостью. В этой статье мы разберём, как шаг за шагом построить шлюз, который будет обрабатывать вебхуки, обновлять статус заказа и интегрироваться с любым внешним платежным API. Начнём с разъяснения задачи и последующего пошагового решения, которое можно реализовать уже сегодня.
Разбор задачи
В Shopify платежные шлюзы реализуются через API Partner, где вы регистрируете приложение, указываете URL-адреса для checkout и webhooks. Ваш Node.js‑сервер будет принимать запросы от Shopify, инициировать платеж в стороннем API, а затем отправлять обновления обратно. Ключевые требования: безопасный обмен данными, корректная обработка ошибок, быстрый отклик на события checkout и webhooks, а также соблюдение правил PCI DSS.
Пошаговое решение
1. Создайте приложение Shopify Partner и настройте redirect URI, а также Webhook‑ы для событий «order/paid» и «order/fulfilled». 2. В Node.js настройте Express‑сервер с HTTPS, используя Let’s Encrypt или самоподписанный сертификат для локальной разработки. 3. Реализуйте маршрут для checkout, который будет генерировать ссылку на ваш платежный API и возвращать JSON с полем redirectUrl. 4. Создайте обработчик Webhook‑а, который будет принимать JSON‑payload от Shopify, проверять подпись HMAC, извлекать order_id и статус платежа. 5. Интегрируйте ваш платежный API (например, Stripe, PayPal, или любой кастомный) через SDK или REST‑клиент, передавая необходимые данные. 6. При успешной оплате отправьте событие order/paid обратно в Shopify через API, используя access token, полученный в процессе OAuth. 7. Обработайте ошибки: при отказе платежа отправьте статус 400 и описание причины, чтобы Shopify мог показать покупателю соответствующее сообщение. 8. Тестируйте весь процесс в режиме Sandbox, проверяя корректность обновления статуса заказа и отображения в админ‑панели. 9. После завершения разработки разверните приложение в Docker‑контейнере, настройте Nginx как обратный прокси и подключите постоянный сертификат. 10. Настройте CI/CD через GitHub Actions, чтобы каждый коммит автоматически деплоил обновления в облако. Следуя этим шагам, вы получите надёжный и масштабируемый платежный шлюз, полностью совместимый с Shopify и Node.js.
Рабочий код в
// paymentGateway.js – основной модуль
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 hmacHeader = req.get('X-Shopify-Hmac-Sha256');
const body = JSON.stringify(req.body);
const hash = crypto
.createHmac('sha256', secret)
.update(body, 'utf8')
.digest('base64');
return hash === hmacHeader;
}
// 2️⃣ Обработчик запроса платежа
app.post('/payment', (req, res) => {
const shop = req.query.shop;
const secret = process.env.SHOPIFY_SECRET;
if (!verifySignature(req, secret)) {
return res.status(401).send('Invalid signature');
}
const { amount, currency, order_id } = req.body;
// Здесь делаем запрос к внешнему API‑провайдеру
axios.post('https://api.paymentprovider.com/pay', {
amount,
currency,
orderId: order_id
})
.then((response) => {
const { transactionId, status } = response.data;
// Отправляем обратно Shopify статус
res.json({ transactionId, status });
})
.catch((err) => {
console.error(err);
res.status(500).send('Payment failed');
});
});
// 3️⃣ Webhook‑обработчик (например, при подтверждении оплаты)
app.post('/webhook/payment-confirmed', (req, res) => {
const shop = req.get('X-Shopify-Shop-Domain');
const secret = process.env.SHOPIFY_SECRET;
if (!verifySignature(req, secret)) {
return res.status(401).send('Invalid signature');
}
const { order_id, status } = req.body;
// Обновляем заказ в базе, отправляем email, и т.д.
console.log(`Order ${order_id} status: ${status}`);
res.status(200).send('OK');
});
app.listen(3000, () => console.log('Gateway running on port 3000'));
Типичные ошибки
- Не проверяете HMAC‑подпись: Shopify отклонит запрос.
- Отправляете неверный формат JSON: провайдер может вернуть 400.
- Неправильный URL‑webhook в админке: запросы не приходят.
- Проблемы с CORS при работе с storefront – убедитесь, что заголовки Access‑Control‑Allow-Origin выставлены.
- Не обрабатываете ошибки Axios: вы можете потерять информацию о причине отказа.
FAQ
Как убедиться, что webhook реально пришёл от Shopify?
// paymentGateway.js – основной модуль
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 hmacHeader = req.get('X-Shopify-Hmac-Sha256');
const body = JSON.stringify(req.body);
const hash = crypto
.createHmac('sha256', secret)
.update(body, 'utf8')
.digest('base64');
return hash === hmacHeader;
}
// 2️⃣ Обработчик запроса платежа
app.post('/payment', (req, res) => {
const shop = req.query.shop;
const secret = process.env.SHOPIFY_SECRET;
if (!verifySignature(req, secret)) {
return res.status(401).send('Invalid signature');
}
const { amount, currency, order_id } = req.body;
// Здесь делаем запрос к внешнему API‑провайдеру
axios.post('https://api.paymentprovider.com/pay', {
amount,
currency,
orderId: order_id
})
.then((response) => {
const { transactionId, status } = response.data;
// Отправляем обратно Shopify статус
res.json({ transactionId, status });
})
.catch((err) => {
console.error(err);
res.status(500).send('Payment failed');
});
});
// 3️⃣ Webhook‑обработчик (например, при подтверждении оплаты)
app.post('/webhook/payment-confirmed', (req, res) => {
const shop = req.get('X-Shopify-Shop-Domain');
const secret = process.env.SHOPIFY_SECRET;
if (!verifySignature(req, secret)) {
return res.status(401).send('Invalid signature');
}
const { order_id, status } = req.body;
// Обновляем заказ в базе, отправляем email, и т.д.
console.log(`Order ${order_id} status: ${status}`);
res.status(200).send('OK');
});
app.listen(3000, () => console.log('Gateway running on port 3000'));
Проверьте заголовок X-Shopify-Hmac-Sha256 и сравните его со значением, полученным из вашего secret‑key. Если совпадают, запрос от Shopify.
Можно ли использовать HTTPS‑автоматический сертификат Let’s Encrypt в Node‑приложении?
Да, настройте Nginx/Traefik как обратный прокси, который будет обслуживать HTTPS и пересылать запросы на ваш Node‑сервер. Shopify требует HTTPS для webhook‑ов.
Что делать, если провайдер платежей отказывается от токена?
Реализуйте повторную попытку (retry) с back‑off. Если токен недействителен, запросите новый токен через OAuth‑или API‑ключ. Логи ошибок помогут быстро выявить причину.
Заключение и чек‑лист
- ✅ Создан и развёрнут Node.js сервис на 3000 порт.
- ✅ Реализована проверка HMAC‑подписи.
- ✅ Настроены webhook‑ы в админке Shopify.
- ✅ Проведено тестирование в sandbox‑режиме платежного провайдера.
- ✅ Добавлены логирование и обработка ошибок.
