Як підключити магазин
Підключення складається з чотирьох кроків: реєстрація, токен monobank, відправник Нової Пошти та інтеграція з вашим сайтом. Усі налаштування — у вкладках кабінету.
1. Реєстрація
Створіть акаунт на сторінці реєстрації: ім’я або назва компанії, email, пароль і адреса магазину. Ми перевіряємо, що сайт відкривається з інтернету й не закритий від індексації. Назву магазину беремо з сайту, її можна змінити у вкладці «Основне».
Один акаунт — один магазин. У вкладці «Основне» також задаються логотип і акцентний колір чекаута.
2. Токен monobank
- Відкрийте web.monobank.ua → Еквайринг → API-токен і скопіюйте токен. Для тестів підійде тестовий токен з api.monobank.ua.
- У кабінеті Harno Pay перейдіть у вкладку «Оплата» і вставте токен. Він зберігається зашифрованим.
- У тій самій вкладці показано адресу webhook для monobank виду
https://pay.harnopay.online/api/webhooks/mono/<id магазину>. Через неї monobank повідомляє нас про статус оплати.
Гроші надходять напряму на ваш рахунок monobank. Harno Pay не є еквайром і не тримає кошти.
3. Доставка
У вкладці «Доставка» задайте ціну доставки Новою Поштою та суму, від якої вона безкоштовна. Доставка за кордон налаштовується зонами: назва, список країн кодами ISO (наприклад, DE, PL) і ціна. Знак * означає всі інші доступні країни. Ціни доставки рахуються на сервері, покупець не може їх змінити.
4. Нова Пошта
У вкладці «Нова Пошта» вкажіть дані відправника:
- ключ API з кабінету Нової Пошти;
- місто й номер відділення відправки текстом;
- телефон, вагу посилки за замовчуванням і опис вантажу;
- хто платить за доставку і форму оплати.
Контрагента, контактну особу й довідники міста та відділення сервіс знаходить сам. У списку замовлень з доставкою на відділення з’явиться кнопка «Створити ТТН»: вона створює одержувача й накладну, показує номер і вартість. «Друк етикетки» відкриває PDF 100×100, ключ API при цьому не потрапляє в браузер. Статуси незавершених накладних оновлюються автоматично раз на 30 хвилин або кнопкою «Оновити статус».
Обмеження: одна вага на всю посилку, накладні лише «відділення → відділення», без накладеного платежу.
5. Shopify
Умови Shopify не дозволяють публічним застосункам з App Store обходити Shopify Checkout, тому підключення йде через custom app у вашому магазині.
- Shopify admin → Settings → Apps → Develop apps → Create app. Admin API scopes:
read_products,write_draft_orders,write_orders,read_inventory. Встановіть застосунок і скопіюйте токенshpat_…. - У кабінеті Harno Pay, вкладка «Інтеграція»: оберіть Shopify, вкажіть домен my-store.myshopify.com і Admin API токен.
- Вставте скрипт у theme.liquid перед
</body>:
<script src="https://pay.harnopay.online/harno-shopify.js"
data-shop="{{ shop.permanent_domain }}" defer></script>Скрипт перехоплює кнопки оформлення й відправляє покупця на чекаут Harno Pay. Після оплати замовлення створюється у Shopify, а кошик очищується.
6. Універсальний API
Для будь-якого сайту. Сесію створює сервер магазину за секретним ключем: згенеруйте його у вкладці «Інтеграція» (тип «Універсальний API»). Ключ показується один раз, у базі зберігається лише його хеш. Створювати сесії з браузера не можна: CORS вимкнений, бо інакше ціни не перевірити.
curl -X POST https://pay.harnopay.online/api/v1/sessions \
-H "Authorization: Bearer gp_..." \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"title": "Худі", "variantTitle": "Графіт / L",
"sku": "HD-01-L", "price": 189900, "quantity": 1,
"image": "https://..."
}
],
"reference": "order-1001",
"returnUrl": "https://shop.example/thanks",
"cancelUrl": "https://shop.example/cart"
}'201 Created
{ "id": "...", "url": "https://pay.harnopay.online/c/..." }Перенаправте покупця на url. Валюта береться з налаштувань магазину (поки лише UAH).
| Поле | Опис |
|---|---|
items[].title | Назва позиції. Обов’язково. |
items[].variantTitle, sku, image | Варіант, артикул і посилання на зображення. Необов’язково. |
items[].price | Ціна за одиницю в копійках, ціле число. 1 899,00 ₴ → 189900. |
items[].quantity | Кількість, від 1 до 999. |
reference | Ваш ідентифікатор замовлення. Повернеться у webhook. |
returnUrl | Куди повернути покупця після оплати. |
cancelUrl | Посилання «до кошика» в шапці чекаута. |
7. Webhook order.paid
Вкажіть адресу webhook у вкладці «Інтеграція» і створіть секрет підпису. Після оплати на цю адресу приходить POST з даними замовлення. Суми — цілі копійки.
POST /your/webhook
Content-Type: application/json
X-Harno-Timestamp: 1793793600
X-Harno-Signature: v1=5f2c…
X-Harno-Delivery: <id замовлення>
X-Harno-Attempt: 1{
"event": "order.paid",
"id": "...",
"number": 12,
"reference": "order-1001",
"currency": "UAH",
"total": 259700,
"shippingPrice": 7000,
"lines": [
{ "title": "Худі", "variantTitle": "Графіт / L",
"sku": "HD-01-L", "price": 189900, "quantity": 1 }
],
"customer": { "firstName": "...", "lastName": "...",
"phone": "+380...", "email": "..." },
"delivery": { "method": "nova_poshta_warehouse",
"price": 7000, "...": "..." },
"paidAt": "2026-11-04T12:00:00.000Z"
}| Заголовок | Значення |
|---|---|
X-Harno-Timestamp | Час відправки в секундах (Unix). Відхиляйте запити, старші за кілька хвилин. |
X-Harno-Signature | v1=<hex>: HMAC-SHA256 від рядка timestamp.body, ключ — секрет підпису з кабінету. |
X-Harno-Delivery | id замовлення. За ним дедуплікуйте повторні доставки. |
X-Harno-Attempt | Номер спроби, починаючи з 1. |
Повтори
Відповідь не 2xx, тайм-аут 10 секунд або мережева помилка — і ми повторюємо доставку з паузами 1 хв, 5 хв, 30 хв, 2 год, 6 год, 12 год і 24 год: усього 8 спроб, після останньої статус «Помилка». Статус, кількість спроб і час наступної видно в кабінеті, там же кнопка «Повторити webhook», яка скидає лічильник.
Доставка «щонайменше один раз»: той самий webhook може прийти повторно. Дедуплікуйте за id замовлення (він же в X-Harno-Delivery).
8. Перевірка підпису
Перевіряйте підпис за сирим тілом запиту, до парсингу JSON, і відхиляйте старі timestamp. Відповідайте 2xx, щойно прийняли замовлення.
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, headers, secret) {
const ts = headers["x-harno-timestamp"];
const sig = (headers["x-harno-signature"] ?? "").replace(/^v1=/, "");
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${ts}.${rawBody}`)
.digest("hex");
return sig.length === expected.length &&
timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}Залишились питання? Напишіть на [email protected].