В современном мире электронной коммерции гибкость и контроль над процессом оплаты стали ключевыми преимуществами для онлайн‑магазинов. Если вы работаете с 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‑режим.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *