feat: add PayKilla payment provider

This commit is contained in:
3252a8
2026-06-04 13:32:15 +03:00
parent 1ccda9ebd1
commit c46aaad7e3
20 changed files with 1974 additions and 8 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ Remnawave Minishop - Telegram-бот и Web App (Mini App) для продажи
- Web App / Mini App с входом через Telegram или email; - Web App / Mini App с входом через Telegram или email;
- встроенные инструкции установки в Mini App: личный экран `/install` и публичная ссылка `/s/<token>` для передачи инструкции; - встроенные инструкции установки в Mini App: личный экран `/install` и публичная ссылка `/s/<token>` для передачи инструкции;
- пробный период, промокоды и реферальная программа; - пробный период, промокоды и реферальная программа;
- оплата через YooKassa, FreeKassa, Platega, SeverPay, Wata, CryptoPay, Heleket и Telegram Stars; - оплата через YooKassa, FreeKassa, Platega, SeverPay, Wata, CryptoPay, Heleket, PayKilla и Telegram Stars;
- тикеты поддержки в Web App и внешняя ссылка на поддержку; - тикеты поддержки в Web App и внешняя ссылка на поддержку;
- раздел "Мои устройства" при включенном `MY_DEVICES_SECTION_ENABLED`. - раздел "Мои устройства" при включенном `MY_DEVICES_SECTION_ENABLED`.
@@ -369,7 +369,7 @@ SETTINGS_MANIFEST: List[SettingField] = [
"string", "string",
"payments", "payments",
"Порядок методов оплаты", "Порядок методов оплаты",
"Через запятую: severpay,freekassa,yookassa,platega,stars,cryptopay,heleket", "Через запятую: severpay,freekassa,yookassa,platega,stars,cryptopay,heleket,paykilla",
subsection="common", subsection="common",
), ),
# ─── Trial ───────────────────────────────────────────────────── # ─── Trial ─────────────────────────────────────────────────────
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
from typing import Any, Dict, Iterable, List, Mapping, Optional from typing import Any, Dict, Iterable, List, Mapping, Optional
from . import cryptopay, freekassa, heleket, platega, severpay, stars, wata, yookassa from . import cryptopay, freekassa, heleket, paykilla, platega, severpay, stars, wata, yookassa
from .base import ( from .base import (
PaymentProviderPresentation, PaymentProviderPresentation,
PaymentProviderSpec, PaymentProviderSpec,
@@ -21,6 +21,7 @@ PAYMENT_PROVIDER_SPECS: tuple[PaymentProviderSpec, ...] = (
stars.SPEC, stars.SPEC,
cryptopay.SPEC, cryptopay.SPEC,
heleket.SPEC, heleket.SPEC,
paykilla.SPEC,
) )
@@ -14,6 +14,7 @@ class PaymentContextMixin:
"severpay": "SeverPay", "severpay": "SeverPay",
"wata": "Wata", "wata": "Wata",
"cryptopay": "Crypto Pay", "cryptopay": "Crypto Pay",
"paykilla": "PayKilla",
"telegram_stars": "Telegram Stars", "telegram_stars": "Telegram Stars",
} }
+2 -1
View File
@@ -305,7 +305,7 @@ class Settings(BaseSettings):
STARS_ADMIN_ONLY_ENABLED: bool = Field(default=False) STARS_ADMIN_ONLY_ENABLED: bool = Field(default=False)
PAYMENT_METHODS_ORDER: Optional[str] = Field( PAYMENT_METHODS_ORDER: Optional[str] = Field(
default=None, default=None,
description="Comma-separated list of payment methods to show (e.g., severpay,wata,freekassa,yookassa,platega,stars,cryptopay)", # noqa: E501 description="Comma-separated list of payment methods to show (e.g., severpay,wata,freekassa,yookassa,platega,stars,cryptopay,heleket,paykilla)", # noqa: E501
) )
SUBSCRIPTION_PURCHASE_DESCRIPTION_ENABLED: bool = Field( SUBSCRIPTION_PURCHASE_DESCRIPTION_ENABLED: bool = Field(
default=True, default=True,
@@ -1026,6 +1026,7 @@ class Settings(BaseSettings):
"stars", "stars",
"cryptopay", "cryptopay",
"heleket", "heleket",
"paykilla",
] ]
# Make sure default_order itself includes every registered spec. # Make sure default_order itself includes every registered spec.
for sid in spec_ids: for sid in spec_ids:
+23
View File
@@ -75,6 +75,7 @@ SUPPORTED_REMNASHOP_PROVIDER_TYPES = {
"TELEGRAM_STARS", "TELEGRAM_STARS",
"YOOKASSA", "YOOKASSA",
"HELEKET", "HELEKET",
"PAYKILLA",
"CRYPTOPAY", "CRYPTOPAY",
"FREEKASSA", "FREEKASSA",
"PLATEGA", "PLATEGA",
@@ -93,6 +94,7 @@ PAYMENT_WEBHOOK_PATHS = {
"wata": "/webhook/wata", "wata": "/webhook/wata",
"cryptopay": "/webhook/cryptopay", "cryptopay": "/webhook/cryptopay",
"heleket": "/webhook/heleket", "heleket": "/webhook/heleket",
"paykilla": "/webhook/paykilla",
"freekassa": "/webhook/freekassa", "freekassa": "/webhook/freekassa",
"platega": "/webhook/platega", "platega": "/webhook/platega",
} }
@@ -477,6 +479,26 @@ def remnashop_payment_gateway_overrides(
) )
return _provider_mapping_result(gateway_type, ["heleket"], overrides, warnings) return _provider_mapping_result(gateway_type, ["heleket"], overrides, warnings)
if gateway_type == "PAYKILLA":
_add_override(overrides, "PAYKILLA_ENABLED", active)
_add_override(
overrides,
"PAYKILLA_API_KEY",
settings.get("api_key") or settings.get("public_key") or settings.get("publicKey"),
)
_add_override(
overrides,
"PAYKILLA_SECRET_KEY",
settings.get("secret_key") or settings.get("secretKey"),
)
if currency and currency != "RUB":
warnings.append(
f"PayKilla source currency was {currency}; Minishop keeps payment currency "
"controlled by tariffs/default currency. Configure PAYKILLA_CURRENCY and "
"PAYKILLA_PAYMENT_CURRENCIES manually if this instance needs a different default."
)
return _provider_mapping_result(gateway_type, ["paykilla"], overrides, warnings)
if gateway_type == "FREEKASSA": if gateway_type == "FREEKASSA":
_add_override(overrides, "FREEKASSA_ENABLED", active) _add_override(overrides, "FREEKASSA_ENABLED", active)
_add_override(overrides, "FREEKASSA_MERCHANT_ID", settings.get("shop_id")) _add_override(overrides, "FREEKASSA_MERCHANT_ID", settings.get("shop_id"))
@@ -539,6 +561,7 @@ def remnashop_post_migration_actions(
"wata": "WATA merchant dashboard -> webhook/callback URL", "wata": "WATA merchant dashboard -> webhook/callback URL",
"cryptopay": "CryptoBot/Crypto Pay app -> webhook URL", "cryptopay": "CryptoBot/Crypto Pay app -> webhook URL",
"heleket": "Heleket merchant dashboard -> payment webhook/callback URL", "heleket": "Heleket merchant dashboard -> payment webhook/callback URL",
"paykilla": "PayKilla Dashboard -> Settings -> Webhooks",
"freekassa": "FreeKassa shop settings -> notification/result URL", "freekassa": "FreeKassa shop settings -> notification/result URL",
"platega": "Platega merchant/project settings -> webhook URL", "platega": "Platega merchant/project settings -> webhook URL",
}.get(provider_id, "Payment provider dashboard -> webhook/callback URL"), }.get(provider_id, "Payment provider dashboard -> webhook/callback URL"),
+35 -1
View File
@@ -197,7 +197,7 @@
| Переменная | Назначение | | Переменная | Назначение |
| --- | --- | | --- | --- |
| `PAYMENT_METHODS_ORDER` | Порядок кнопок оплаты: `severpay,wata,freekassa,platega,yookassa,stars,cryptopay,heleket`. | | `PAYMENT_METHODS_ORDER` | Порядок кнопок оплаты: `severpay,wata,freekassa,platega,yookassa,stars,cryptopay,heleket,paykilla`. |
| `SUBSCRIPTION_PURCHASE_DESCRIPTION_ENABLED` | Показывать описание подписки перед выбором срока. | | `SUBSCRIPTION_PURCHASE_DESCRIPTION_ENABLED` | Показывать описание подписки перед выбором срока. |
| `SUBSCRIPTION_PURCHASE_DESCRIPTION_RU` / `SUBSCRIPTION_PURCHASE_DESCRIPTION_EN` | Локализованное описание подписки. | | `SUBSCRIPTION_PURCHASE_DESCRIPTION_RU` / `SUBSCRIPTION_PURCHASE_DESCRIPTION_EN` | Локализованное описание подписки. |
| `PAYMENT_<METHOD>_WEBAPP_LABEL_RU` / `PAYMENT_<METHOD>_WEBAPP_LABEL_EN` | Текст кнопки провайдера в Web App. | | `PAYMENT_<METHOD>_WEBAPP_LABEL_RU` / `PAYMENT_<METHOD>_WEBAPP_LABEL_EN` | Текст кнопки провайдера в Web App. |
@@ -213,6 +213,7 @@
| `WATA_ENABLED` | Включает Wata. | | `WATA_ENABLED` | Включает Wata. |
| `CRYPTOPAY_ENABLED` | Включает CryptoPay. | | `CRYPTOPAY_ENABLED` | Включает CryptoPay. |
| `HELEKET_ENABLED` | Включает Heleket. | | `HELEKET_ENABLED` | Включает Heleket. |
| `PAYKILLA_ENABLED` | Включает PayKilla. |
Конкретные ключи отображения: Конкретные ключи отображения:
@@ -271,6 +272,12 @@ PAYMENT_HELEKET_WEBAPP_ICON
PAYMENT_HELEKET_TELEGRAM_LABEL_RU PAYMENT_HELEKET_TELEGRAM_LABEL_RU
PAYMENT_HELEKET_TELEGRAM_LABEL_EN PAYMENT_HELEKET_TELEGRAM_LABEL_EN
PAYMENT_HELEKET_TELEGRAM_EMOJI PAYMENT_HELEKET_TELEGRAM_EMOJI
PAYMENT_PAYKILLA_WEBAPP_LABEL_RU
PAYMENT_PAYKILLA_WEBAPP_LABEL_EN
PAYMENT_PAYKILLA_WEBAPP_ICON
PAYMENT_PAYKILLA_TELEGRAM_LABEL_RU
PAYMENT_PAYKILLA_TELEGRAM_LABEL_EN
PAYMENT_PAYKILLA_TELEGRAM_EMOJI
``` ```
### YooKassa ### YooKassa
@@ -357,6 +364,33 @@ PAYMENT_HELEKET_TELEGRAM_EMOJI
| `HELEKET_VERIFY_WEBHOOK_SIGNATURE` | Проверять подпись webhook. | | `HELEKET_VERIFY_WEBHOOK_SIGNATURE` | Проверять подпись webhook. |
| `HELEKET_TRUSTED_IPS` | Список доверенных IP webhook-источников. | | `HELEKET_TRUSTED_IPS` | Список доверенных IP webhook-источников. |
### PayKilla
Для приема оплат нужен API key типа **HMAC** с правом **INVOICE**. Право **WITHDRAWAL** для оплаты подписок не требуется; включайте его только для отдельной интеграции выплат.
Webhook настраивается в PayKilla Dashboard: **Settings -> Webhooks**. Укажите `WEBHOOK_BASE_URL` + `/webhook/paykilla`, например `https://bot.example.com/webhook/paykilla`. Включите события `INVOICE_PAID` и `INVOICE_EXPIRED` как минимум. Рекомендуемый набор галочек: `INVOICE_PAID`, `PAYMENT_COMPLETED`, `PAYMENT_FAILED`, `PAYMENT_OVERPAID`, `PAYMENT_UNDERPAID`, `PAYMENT_PARTIAL`, `INVOICE_EXPIRED`, `COMPLIANCE_FAILED`. Если хотите видеть промежуточные статусы в логах PayKilla, дополнительно включите `INVOICE_CREATED`, `PAYMENT_PENDING`, `TRANSACTION_CONFIRMED` и `TRANSACTION_FINAL`.
| Переменная | Назначение |
| --- | --- |
| `PAYKILLA_BASE_URL` | Базовый URL API, по умолчанию `https://account-api.paykilla.com`. |
| `PAYKILLA_WIDGET_URL` | URL hosted checkout, по умолчанию `https://gopay.paykilla.com`. |
| `PAYKILLA_API_KEY` / `PAYKILLA_V2_API_KEY` | Public HMAC key с правом `INVOICE`. |
| `PAYKILLA_SECRET_KEY` / `PAYKILLA_V2_SECRET_KEY` | Secret HMAC key для подписи API-запросов и проверки webhook. |
| `PAYKILLA_CURRENCY` | Валюта инвойса. Для RUB/USD/EUR/AED/GBP используется `FIAT_BASED`, для остальных - `FIXED_AMOUNT`. |
| `PAYKILLA_PAYMENT_CURRENCIES` | Crypto tickers для оплаты, например `USDTTRC,BTC,ETH`. |
| `PAYKILLA_SUPPORTED_CURRENCIES` | Валюты инвойса, разрешенные в этом магазине. |
| `PAYKILLA_INVOICE_TYPE` | Необязательный override: `FIAT_BASED`, `FIXED_AMOUNT` или `OPEN_AMOUNT`. |
| `PAYKILLA_RETURN_URL` | URL кнопки возврата на checkout-странице. |
| `PAYKILLA_SUCCESS_URL` | URL после успешной оплаты; используется с auto-redirect. |
| `PAYKILLA_CANCEL_URL` | URL после отмены оплаты. |
| `PAYKILLA_LIFETIME_SECONDS` | TTL инвойса, отправляется как `expiredAt`. |
| `PAYKILLA_RECV_WINDOW_MS` | `recvWindow` для подписанных API-запросов. |
| `PAYKILLA_USER_PAYS_SERVICE_FEE` | `true`, если пользователь оплачивает service fee. |
| `PAYKILLA_USER_PAYS_NETWORK_FEE` | `true`, если пользователь оплачивает network fee. |
| `PAYKILLA_VERIFY_WEBHOOK_SIGNATURE` | Проверять `X-API-SIGN` по raw body webhook. |
| `PAYKILLA_WEBHOOK_URL` | Точный публичный webhook URL для проверки подписи, если он отличается от `WEBHOOK_BASE_URL` + `/webhook/paykilla`. |
| `PAYKILLA_TRUSTED_IPS` | Необязательный список доверенных IP webhook-источников. |
## Тарифы и legacy-цены ## Тарифы и legacy-цены
Рекомендуемый способ настройки тарифов - раздел **Система -> Тарифы** в админке. Он сохраняет JSON в `TARIFFS_CONFIG_PATH`. Рекомендуемый способ настройки тарифов - раздел **Система -> Тарифы** в админке. Он сохраняет JSON в `TARIFFS_CONFIG_PATH`.
+32
View File
@@ -31,6 +31,7 @@
| Wata | `WEBHOOK_BASE_URL` + `/webhook/wata` | Если включена проверка подписи, настройте `WATA_WEBHOOK_VERIFY_SIGNATURE` и `WATA_PUBLIC_KEY`. | | Wata | `WEBHOOK_BASE_URL` + `/webhook/wata` | Если включена проверка подписи, настройте `WATA_WEBHOOK_VERIFY_SIGNATURE` и `WATA_PUBLIC_KEY`. |
| CryptoPay | `WEBHOOK_BASE_URL` + `/webhook/cryptopay` | Указывается в настройках Crypto Bot / CryptoPay webhook. | | CryptoPay | `WEBHOOK_BASE_URL` + `/webhook/cryptopay` | Указывается в настройках Crypto Bot / CryptoPay webhook. |
| Heleket | `WEBHOOK_BASE_URL` + `/webhook/heleket` | При необходимости включите `HELEKET_VERIFY_WEBHOOK_SIGNATURE` и `HELEKET_TRUSTED_IPS`. | | Heleket | `WEBHOOK_BASE_URL` + `/webhook/heleket` | При необходимости включите `HELEKET_VERIFY_WEBHOOK_SIGNATURE` и `HELEKET_TRUSTED_IPS`. |
| PayKilla | `WEBHOOK_BASE_URL` + `/webhook/paykilla` | Указывается в PayKilla Dashboard -> Settings -> Webhooks; включите события оплаты инвойсов. |
| Telegram Stars | Отдельный платежный webhook не нужен | Stars-события приходят через webhook Telegram-бота: `WEBHOOK_BASE_URL` + `/tg/webhook`. | | Telegram Stars | Отдельный платежный webhook не нужен | Stars-события приходят через webhook Telegram-бота: `WEBHOOK_BASE_URL` + `/tg/webhook`. |
После настройки сделайте тестовый платеж и проверьте, что в логах `backend` видно входящий `POST` на нужный путь. Если провайдер сообщает, что адрес недоступен, сначала проверьте DNS/HTTPS и reverse proxy для `WEBHOOK_BASE_URL`, затем убедитесь, что путь начинается ровно с `/webhook/...` без `/api`, `/auth` и frontend-домена. После настройки сделайте тестовый платеж и проверьте, что в логах `backend` видно входящий `POST` на нужный путь. Если провайдер сообщает, что адрес недоступен, сначала проверьте DNS/HTTPS и reverse proxy для `WEBHOOK_BASE_URL`, затем убедитесь, что путь начинается ровно с `/webhook/...` без `/api`, `/auth` и frontend-домена.
@@ -153,6 +154,37 @@ Heleket используется для крипто-инвойсов с отд
Справочник переменных: [Heleket](../configuration/env-vars.md#heleket). Справочник переменных: [Heleket](../configuration/env-vars.md#heleket).
## PayKilla
PayKilla используется для крипто-инвойсов V2 через hosted checkout `https://gopay.paykilla.com/{invoice_id}`. API-запросы подписываются HMAC-SHA256, webhook проверяется по заголовку `X-API-SIGN` и raw body.
Какие полномочия нужны API key:
1. В PayKilla Dashboard откройте **Settings -> API keys**.
2. Создайте ключ типа **HMAC**.
3. Для приема оплат включите permission **INVOICE**.
4. Permission **WITHDRAWAL** не нужен для Minishop-платежей; не включайте его без отдельной необходимости выплат.
5. Сохраните `publicKey` в `PAYKILLA_API_KEY`, а `secretKey` в `PAYKILLA_SECRET_KEY`.
Как настроить webhook в PayKilla:
1. Откройте **Settings -> Webhooks**.
2. В URL укажите `WEBHOOK_BASE_URL` + `/webhook/paykilla`, например `https://bot.example.com/webhook/paykilla`.
3. Минимальные галочки: `INVOICE_PAID`, `INVOICE_EXPIRED`.
4. Рекомендуемые галочки для production: `INVOICE_PAID`, `PAYMENT_COMPLETED`, `PAYMENT_FAILED`, `PAYMENT_OVERPAID`, `PAYMENT_UNDERPAID`, `PAYMENT_PARTIAL`, `INVOICE_EXPIRED`, `COMPLIANCE_FAILED`.
5. Опционально включите `INVOICE_CREATED`, `PAYMENT_PENDING`, `TRANSACTION_CONFIRMED` и `TRANSACTION_FINAL`, если нужны промежуточные события в логах.
6. Оставьте `PAYKILLA_VERIFY_WEBHOOK_SIGNATURE=True`. Если публичный URL у PayKilla отличается от `WEBHOOK_BASE_URL` + `/webhook/paykilla`, задайте точное значение в `PAYKILLA_WEBHOOK_URL`.
Что настроить в Minishop:
1. Включите `PAYKILLA_ENABLED`.
2. Укажите `PAYKILLA_API_KEY` и `PAYKILLA_SECRET_KEY`.
3. Проверьте `PAYKILLA_CURRENCY` и `PAYKILLA_PAYMENT_CURRENCIES`, например `USDTTRC,BTC,ETH`.
4. При необходимости задайте `PAYKILLA_SUCCESS_URL`, `PAYKILLA_RETURN_URL` и `PAYKILLA_CANCEL_URL`.
5. Добавьте `paykilla` в `PAYMENT_METHODS_ORDER`, если хотите задать явный порядок кнопок.
Справочник переменных: [PayKilla](../configuration/env-vars.md#paykilla).
## Telegram Stars ## Telegram Stars
Telegram Stars используются напрямую и поддерживаются в legacy-ценах и JSON-каталоге тарифов. Telegram Stars используются напрямую и поддерживаются в legacy-ценах и JSON-каталоге тарифов.
+4 -2
View File
@@ -46,7 +46,7 @@ App, а не веб-кабинетом Remnashop. В Minishop `SUBSCRIPTION_MINI
Платежные провайдеры берутся из таблицы Remnashop `payment_gateways`. Платежные провайдеры берутся из таблицы Remnashop `payment_gateways`.
Поддерживаются и автоматически маппятся: Telegram Stars, YooKassa, WATA, Поддерживаются и автоматически маппятся: Telegram Stars, YooKassa, WATA,
CryptoPay, Heleket, FreeKassa и Platega. Для них importer переносит флаги CryptoPay, Heleket, PayKilla, FreeKassa и Platega. Для них importer переносит флаги
включения, API-ключи/merchant IDs и прямые технические параметры, без которых включения, API-ключи/merchant IDs и прямые технические параметры, без которых
провайдер не сможет работать: YooKassa receipt email/VAT, FreeKassa second провайдер не сможет работать: YooKassa receipt email/VAT, FreeKassa second
secret/payment method/server IP и Platega payment method. secret/payment method/server IP и Platega payment method.
@@ -55,7 +55,8 @@ Provider currency и supported-currency ограничения не перено
в Minishop валюта платежа управляется тарифами и `DEFAULT_CURRENCY_SYMBOL`. в Minishop валюта платежа управляется тарифами и `DEFAULT_CURRENCY_SYMBOL`.
Если старый gateway Remnashop был настроен на нестандартную валюту, importer Если старый gateway Remnashop был настроен на нестандартную валюту, importer
оставит предупреждение в JSON-сводке; проверьте `CRYPTOPAY_ASSET`, оставит предупреждение в JSON-сводке; проверьте `CRYPTOPAY_ASSET`,
`HELEKET_CURRENCY`, `HELEKET_SUPPORTED_CURRENCIES` или `HELEKET_CURRENCY`, `HELEKET_SUPPORTED_CURRENCIES`, `PAYKILLA_CURRENCY`,
`PAYKILLA_PAYMENT_CURRENCIES` или
`PLATEGA_SUPPORTED_CURRENCIES` вручную. `PLATEGA_SUPPORTED_CURRENCIES` вручную.
Провайдеры YooMoney, Cryptomus, MulenPay, PayMaster, RoboKassa и UrlPay сейчас Провайдеры YooMoney, Cryptomus, MulenPay, PayMaster, RoboKassa и UrlPay сейчас
@@ -77,6 +78,7 @@ Remnashop может хранить секреты в формате `enc_...`.
- WATA webhook/callback URL: `WEBHOOK_BASE_URL` + `/webhook/wata`; - WATA webhook/callback URL: `WEBHOOK_BASE_URL` + `/webhook/wata`;
- CryptoBot/Crypto Pay webhook URL: `WEBHOOK_BASE_URL` + `/webhook/cryptopay`; - CryptoBot/Crypto Pay webhook URL: `WEBHOOK_BASE_URL` + `/webhook/cryptopay`;
- Heleket payment webhook/callback URL: `WEBHOOK_BASE_URL` + `/webhook/heleket`; - Heleket payment webhook/callback URL: `WEBHOOK_BASE_URL` + `/webhook/heleket`;
- PayKilla webhook URL: `WEBHOOK_BASE_URL` + `/webhook/paykilla`;
- FreeKassa notification/result URL: `WEBHOOK_BASE_URL` + `/webhook/freekassa`; - FreeKassa notification/result URL: `WEBHOOK_BASE_URL` + `/webhook/freekassa`;
- Platega webhook URL: `WEBHOOK_BASE_URL` + `/webhook/platega`; - Platega webhook URL: `WEBHOOK_BASE_URL` + `/webhook/platega`;
- Telegram webhook `WEBHOOK_BASE_URL` + `/tg/webhook` выставляется ботом - Telegram webhook `WEBHOOK_BASE_URL` + `/tg/webhook` выставляется ботом
+5
View File
@@ -479,6 +479,11 @@ export const DEMO_DATASET = {
name: "Heleket", name: "Heleket",
icon: "CreditCard", icon: "CreditCard",
}, },
{
id: "paykilla",
name: "PayKilla",
icon: "Bitcoin",
},
], ],
referral: { referral: {
code: "DEMO0001", code: "DEMO0001",
@@ -650,7 +650,7 @@
"section_order": 4, "section_order": 4,
"subsection": "common", "subsection": "common",
"label": "Порядок методов оплаты", "label": "Порядок методов оплаты",
"description": "Через запятую: severpay,freekassa,yookassa,platega,stars,cryptopay,heleket", "description": "Через запятую: severpay,freekassa,yookassa,platega,stars,cryptopay,heleket,paykilla",
"i18n_label_key": "admin_settings_field_payment_methods_order_label", "i18n_label_key": "admin_settings_field_payment_methods_order_label",
"i18n_description_key": "admin_settings_field_payment_methods_order_description", "i18n_description_key": "admin_settings_field_payment_methods_order_description",
"i18n_subsection_key": "admin_settings_subsection_common", "i18n_subsection_key": "admin_settings_subsection_common",
@@ -3839,6 +3839,692 @@
"overridden": false, "overridden": false,
"updated_at": null, "updated_at": null,
"webhook_base_url_configured": false "webhook_base_url_configured": false
},
{
"key": "PAYKILLA_ENABLED",
"type": "bool",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Enabled",
"description": "",
"i18n_label_key": "admin_settings_field_paykilla_enabled_label",
"i18n_description_key": null,
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"mutually_exclusive_key": "PAYKILLA_ADMIN_ONLY_ENABLED",
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_API_KEY",
"type": "string",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "API key",
"description": "PayKilla public HMAC key with INVOICE permission.",
"i18n_label_key": "admin_settings_field_paykilla_api_key_label",
"i18n_description_key": "admin_settings_field_paykilla_api_key_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": true,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"has_value": false,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_SECRET_KEY",
"type": "string",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Secret key",
"description": "PayKilla HMAC secret key. Never expose it client-side.",
"i18n_label_key": "admin_settings_field_paykilla_secret_key_label",
"i18n_description_key": "admin_settings_field_paykilla_secret_key_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": true,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"has_value": false,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_BASE_URL",
"type": "url",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Base URL",
"description": "",
"i18n_label_key": "admin_settings_field_paykilla_base_url_label",
"i18n_description_key": null,
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": "admin_settings_field_paykilla_base_url_placeholder",
"placeholder": "https://account-api.paykilla.com",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_WIDGET_URL",
"type": "url",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Widget URL",
"description": "",
"i18n_label_key": "admin_settings_field_paykilla_widget_url_label",
"i18n_description_key": null,
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": "admin_settings_field_paykilla_widget_url_placeholder",
"placeholder": "https://gopay.paykilla.com",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_CURRENCY",
"type": "string",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Invoice currency",
"description": "Fiat or crypto invoice currency. FIAT_BASED is selected for RUB/USD/EUR/AED/GBP.",
"i18n_label_key": "admin_settings_field_paykilla_currency_label",
"i18n_description_key": "admin_settings_field_paykilla_currency_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": "admin_settings_field_paykilla_currency_placeholder",
"placeholder": "RUB",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_SUPPORTED_CURRENCIES",
"type": "string",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Supported invoice currencies",
"description": "Comma-separated invoice currencies allowed for PayKilla in this shop.",
"i18n_label_key": "admin_settings_field_paykilla_supported_currencies_label",
"i18n_description_key": "admin_settings_field_paykilla_supported_currencies_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": "admin_settings_field_paykilla_supported_currencies_placeholder",
"placeholder": "RUB,USD,EUR,AED,GBP,BTC,ETH,TRX,TON,USDTTRC,USDTETH,USDTBSC,USDCETH,USDCBSC,DAIETH,DAIBSC,BNBBSC,ETHBSC,LINKETH,LINKBSC,USDTTON,AAVEETH,MANAETH,SHIBETH",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_PAYMENT_CURRENCIES",
"type": "string",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Accepted crypto tickers",
"description": "Comma-separated PayKilla tickers sent as paymentCurrencies, e.g. USDTTRC,BTC,ETH.",
"i18n_label_key": "admin_settings_field_paykilla_payment_currencies_label",
"i18n_description_key": "admin_settings_field_paykilla_payment_currencies_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": "admin_settings_field_paykilla_payment_currencies_placeholder",
"placeholder": "USDTTRC,BTC,ETH",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_INVOICE_TYPE",
"type": "string",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Invoice type",
"description": "Optional override: FIAT_BASED, FIXED_AMOUNT, or OPEN_AMOUNT.",
"i18n_label_key": "admin_settings_field_paykilla_invoice_type_label",
"i18n_description_key": "admin_settings_field_paykilla_invoice_type_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"choices": [
{
"value": "",
"label": "Auto",
"i18n_label_key": "admin_settings_field_paykilla_invoice_type_choice_default"
},
{
"value": "FIAT_BASED",
"label": "FIAT_BASED",
"i18n_label_key": "admin_settings_field_paykilla_invoice_type_choice_fiat_based"
},
{
"value": "FIXED_AMOUNT",
"label": "FIXED_AMOUNT",
"i18n_label_key": "admin_settings_field_paykilla_invoice_type_choice_fixed_amount"
},
{
"value": "OPEN_AMOUNT",
"label": "OPEN_AMOUNT",
"i18n_label_key": "admin_settings_field_paykilla_invoice_type_choice_open_amount"
}
],
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_RETURN_URL",
"type": "url",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Return URL",
"description": "",
"i18n_label_key": "admin_settings_field_paykilla_return_url_label",
"i18n_description_key": null,
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_SUCCESS_URL",
"type": "url",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Success URL",
"description": "",
"i18n_label_key": "admin_settings_field_paykilla_success_url_label",
"i18n_description_key": null,
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_CANCEL_URL",
"type": "url",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Cancel URL",
"description": "",
"i18n_label_key": "admin_settings_field_paykilla_cancel_url_label",
"i18n_description_key": null,
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_LIFETIME_SECONDS",
"type": "int",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Invoice lifetime (seconds)",
"description": "Used to send expiredAt to PayKilla.",
"i18n_label_key": "admin_settings_field_paykilla_lifetime_seconds_label",
"i18n_description_key": "admin_settings_field_paykilla_lifetime_seconds_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"min": 300,
"max": 2592000,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_RECV_WINDOW_MS",
"type": "int",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Request recvWindow (ms)",
"description": "Validity window for signed PayKilla API requests.",
"i18n_label_key": "admin_settings_field_paykilla_recv_window_ms_label",
"i18n_description_key": "admin_settings_field_paykilla_recv_window_ms_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"min": 1000,
"max": 60000,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_USER_PAYS_SERVICE_FEE",
"type": "bool",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "User pays service fee",
"description": "",
"i18n_label_key": "admin_settings_field_paykilla_user_pays_service_fee_label",
"i18n_description_key": null,
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_USER_PAYS_NETWORK_FEE",
"type": "bool",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "User pays network fee",
"description": "",
"i18n_label_key": "admin_settings_field_paykilla_user_pays_network_fee_label",
"i18n_description_key": null,
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_VERIFY_WEBHOOK_SIGNATURE",
"type": "bool",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Verify webhook signature",
"description": "",
"i18n_label_key": "admin_settings_field_paykilla_verify_webhook_signature_label",
"i18n_description_key": null,
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_WEBHOOK_URL",
"type": "url",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Exact webhook URL",
"description": "Optional override for signature verification. Leave empty to use WEBHOOK_BASE_URL + /webhook/paykilla.",
"i18n_label_key": "admin_settings_field_paykilla_webhook_url_label",
"i18n_description_key": "admin_settings_field_paykilla_webhook_url_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_TRUSTED_IPS",
"type": "string",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Trusted IPs",
"description": "Optional comma-separated IP addresses accepted for PayKilla webhooks.",
"i18n_label_key": "admin_settings_field_paykilla_trusted_ips_label",
"i18n_description_key": "admin_settings_field_paykilla_trusted_ips_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYMENT_PAYKILLA_WEBAPP_LABEL_RU",
"type": "string",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "WebApp button text (RU)",
"description": "Custom Russian text shown in the Web App payment method button.",
"i18n_label_key": "admin_settings_field_payment_paykilla_webapp_label_ru_label",
"i18n_description_key": "admin_settings_field_payment_paykilla_webapp_label_ru_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": "admin_settings_field_payment_paykilla_webapp_label_ru_placeholder",
"placeholder": "PayKilla",
"optional": true,
"secret": false,
"default": "PayKilla",
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYMENT_PAYKILLA_WEBAPP_LABEL_EN",
"type": "string",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "WebApp button text (EN)",
"description": "Custom English text shown in the Web App payment method button.",
"i18n_label_key": "admin_settings_field_payment_paykilla_webapp_label_en_label",
"i18n_description_key": "admin_settings_field_payment_paykilla_webapp_label_en_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": "admin_settings_field_payment_paykilla_webapp_label_en_placeholder",
"placeholder": "PayKilla",
"optional": true,
"secret": false,
"default": "PayKilla",
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYMENT_PAYKILLA_WEBAPP_ICON",
"type": "icon",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "WebApp button icon",
"description": "Lucide icon name rendered inside the Web App payment method button.",
"i18n_label_key": "admin_settings_field_payment_paykilla_webapp_icon_label",
"i18n_description_key": "admin_settings_field_payment_paykilla_webapp_icon_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": "admin_settings_field_payment_paykilla_webapp_icon_placeholder",
"placeholder": "Bitcoin",
"optional": true,
"secret": false,
"default": "Bitcoin",
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYMENT_PAYKILLA_TELEGRAM_LABEL_RU",
"type": "string",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Telegram button text (RU)",
"description": "Custom Russian text shown in Telegram bot payment buttons.",
"i18n_label_key": "admin_settings_field_payment_paykilla_telegram_label_ru_label",
"i18n_description_key": "admin_settings_field_payment_paykilla_telegram_label_ru_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": "admin_settings_field_payment_paykilla_telegram_label_ru_placeholder",
"placeholder": "PayKilla",
"optional": true,
"secret": false,
"default": "PayKilla",
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYMENT_PAYKILLA_TELEGRAM_LABEL_EN",
"type": "string",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Telegram button text (EN)",
"description": "Custom English text shown in Telegram bot payment buttons.",
"i18n_label_key": "admin_settings_field_payment_paykilla_telegram_label_en_label",
"i18n_description_key": "admin_settings_field_payment_paykilla_telegram_label_en_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": "admin_settings_field_payment_paykilla_telegram_label_en_placeholder",
"placeholder": "PayKilla",
"optional": true,
"secret": false,
"default": "PayKilla",
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYMENT_PAYKILLA_TELEGRAM_EMOJI",
"type": "string",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Telegram button emoji",
"description": "Emoji prepended to the Telegram bot payment button when customized.",
"i18n_label_key": "admin_settings_field_payment_paykilla_telegram_emoji_label",
"i18n_description_key": "admin_settings_field_payment_paykilla_telegram_emoji_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"default": "",
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
},
{
"key": "PAYKILLA_ADMIN_ONLY_ENABLED",
"type": "bool",
"section": "payments",
"section_order": 4,
"subsection": "PayKilla",
"label": "Only for admins",
"description": "Shows this payment method only to users from ADMIN_IDS. Webhooks and provider services remain active for admin test payments.",
"i18n_label_key": "admin_settings_provider_admin_only_label",
"i18n_description_key": "admin_settings_provider_admin_only_description",
"i18n_subsection_key": "admin_settings_subsection_paykilla",
"i18n_placeholder_key": null,
"placeholder": "",
"optional": true,
"secret": false,
"mutually_exclusive_key": "PAYKILLA_ENABLED",
"provider_id": "paykilla",
"provider_label": "PayKilla",
"webhook_provider_id": "paykilla",
"webhook_path": "/webhook/paykilla",
"webhook_requires_base_url": false,
"value": "",
"overridden": false,
"updated_at": null,
"webhook_base_url_configured": false
} }
] ]
}, },
+1
View File
@@ -874,6 +874,7 @@ remnashop_webhook_checklist() {
printf ' WATA merchant dashboard -> webhook/callback URL: %s/webhook/wata\n' "$base_url" printf ' WATA merchant dashboard -> webhook/callback URL: %s/webhook/wata\n' "$base_url"
printf ' CryptoBot/Crypto Pay app -> webhook URL: %s/webhook/cryptopay\n' "$base_url" printf ' CryptoBot/Crypto Pay app -> webhook URL: %s/webhook/cryptopay\n' "$base_url"
printf ' Heleket merchant dashboard -> payment webhook/callback URL: %s/webhook/heleket\n' "$base_url" printf ' Heleket merchant dashboard -> payment webhook/callback URL: %s/webhook/heleket\n' "$base_url"
printf ' PayKilla Dashboard -> Settings -> Webhooks URL: %s/webhook/paykilla\n' "$base_url"
printf ' FreeKassa shop settings -> notification/result URL: %s/webhook/freekassa\n' "$base_url" printf ' FreeKassa shop settings -> notification/result URL: %s/webhook/freekassa\n' "$base_url"
printf ' Platega merchant/project settings -> webhook URL: %s/webhook/platega\n' "$base_url" printf ' Platega merchant/project settings -> webhook URL: %s/webhook/platega\n' "$base_url"
printf ' Telegram webhook: %s/tg/webhook (configured automatically on bot startup)\n' "$base_url" printf ' Telegram webhook: %s/tg/webhook (configured automatically on bot startup)\n' "$base_url"
+1
View File
@@ -175,6 +175,7 @@ class BuildServicesWiringTests(unittest.TestCase):
"severpay_service", "severpay_service",
"wata_service", "wata_service",
"heleket_service", "heleket_service",
"paykilla_service",
} }
self.assertEqual(set(services), expected_keys) self.assertEqual(set(services), expected_keys)
+1
View File
@@ -76,6 +76,7 @@ def test_shell_installer_prints_remnashop_webhook_checklist():
assert "/webhook/wata" in script assert "/webhook/wata" in script
assert "/webhook/cryptopay" in script assert "/webhook/cryptopay" in script
assert "/webhook/heleket" in script assert "/webhook/heleket" in script
assert "/webhook/paykilla" in script
assert "/webhook/freekassa" in script assert "/webhook/freekassa" in script
assert "/webhook/platega" in script assert "/webhook/platega" in script
assert "/tg/webhook" in script assert "/tg/webhook" in script
+1
View File
@@ -203,6 +203,7 @@ class RemnashopMigrationDocumentationFactsTests(unittest.TestCase):
"/webhook/wata", "/webhook/wata",
"/webhook/cryptopay", "/webhook/cryptopay",
"/webhook/heleket", "/webhook/heleket",
"/webhook/paykilla",
"/webhook/freekassa", "/webhook/freekassa",
"/webhook/platega", "/webhook/platega",
"/tg/webhook", "/tg/webhook",
+4
View File
@@ -64,6 +64,7 @@ _PROVIDER_MODULES = {
"stars": "StarsService", "stars": "StarsService",
"wata": "WataService", "wata": "WataService",
"heleket": "HeleketService", "heleket": "HeleketService",
"paykilla": "PaykillaService",
} }
@@ -125,6 +126,7 @@ def test_service_keys_and_statuses_come_from_provider_specs():
"stars_service", "stars_service",
"cryptopay_service", "cryptopay_service",
"heleket_service", "heleket_service",
"paykilla_service",
} }
assert set(pending_statuses()) >= { assert set(pending_statuses()) >= {
"pending", "pending",
@@ -136,6 +138,7 @@ def test_service_keys_and_statuses_come_from_provider_specs():
"pending_cryptopay", "pending_cryptopay",
"pending_stars", "pending_stars",
"pending_heleket", "pending_heleket",
"pending_paykilla",
} }
@@ -152,6 +155,7 @@ def test_provider_labels_and_emojis_include_storage_keys_and_method_aliases():
assert emojis["stars"] == get_provider_spec("stars").default_telegram_emoji assert emojis["stars"] == get_provider_spec("stars").default_telegram_emoji
assert emojis["telegram_stars"] == get_provider_spec("stars").default_telegram_emoji assert emojis["telegram_stars"] == get_provider_spec("stars").default_telegram_emoji
assert emojis["cryptopay"] == get_provider_spec("cryptopay").default_telegram_emoji assert emojis["cryptopay"] == get_provider_spec("cryptopay").default_telegram_emoji
assert labels["paykilla"] == "PayKilla"
def test_provider_presentation_resolves_defaults_and_overrides(): def test_provider_presentation_resolves_defaults_and_overrides():
+1
View File
@@ -27,6 +27,7 @@ _PROVIDERS_FROM_CALLERS = {
"severpay", "severpay",
"wata", "wata",
"cryptopay", "cryptopay",
"paykilla",
"telegram_stars", "telegram_stars",
} }
+16
View File
@@ -185,6 +185,22 @@ def test_remnashop_crypto_provider_currency_is_not_imported_as_setting_override(
} }
assert "HELEKET_CURRENCY" not in heleket["overrides"] assert "HELEKET_CURRENCY" not in heleket["overrides"]
paykilla = remnashop_payment_gateway_overrides(
{
"type": "PAYKILLA",
"currency": "USD",
"is_active": True,
"settings": {"public_key": "public", "secret_key": "secret"},
}
)
assert paykilla["overrides"] == {
"PAYKILLA_ENABLED": True,
"PAYKILLA_API_KEY": "public",
"PAYKILLA_SECRET_KEY": "secret",
}
assert "PAYKILLA_CURRENCY" not in paykilla["overrides"]
assert any("source currency was USD" in warning for warning in paykilla["warnings"])
def test_remnashop_unsupported_gateway_is_reported_without_overrides(): def test_remnashop_unsupported_gateway_is_reported_without_overrides():
result = remnashop_payment_gateway_overrides( result = remnashop_payment_gateway_overrides(
+80
View File
@@ -1,6 +1,7 @@
import hashlib import hashlib
import hmac import hmac
import json import json
import time
import unittest import unittest
from types import SimpleNamespace from types import SimpleNamespace
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
@@ -19,6 +20,12 @@ from bot.app.web.webapp_auth import (
from bot.payment_providers.cryptopay import CryptoPayService from bot.payment_providers.cryptopay import CryptoPayService
from bot.payment_providers.freekassa import FreeKassaService from bot.payment_providers.freekassa import FreeKassaService
from bot.payment_providers.heleket import HeleketConfig, HeleketService, _compute_signature from bot.payment_providers.heleket import HeleketConfig, HeleketService, _compute_signature
from bot.payment_providers.paykilla import (
PaykillaConfig,
PaykillaService,
_sign_query,
_webhook_signature,
)
from bot.payment_providers.yookassa import yookassa_webhook_route from bot.payment_providers.yookassa import yookassa_webhook_route
from bot.services.email_templates import render_login_code from bot.services.email_templates import render_login_code
from bot.utils.request_security import request_client_ip from bot.utils.request_security import request_client_ip
@@ -199,6 +206,79 @@ class HeleketServiceTests(unittest.TestCase):
self.assertFalse(service._verify_signature({"order_id": "435", "sign": "bad"})) self.assertFalse(service._verify_signature({"order_id": "435", "sign": "bad"}))
class PaykillaServiceTests(unittest.TestCase):
def _make_service(self, *, webhook_url: str = "https://shop.example/webhook/paykilla"):
service = PaykillaService.__new__(PaykillaService)
service.config = PaykillaConfig(
ENABLED=True,
API_KEY="public-key",
SECRET_KEY="secret-key",
WEBHOOK_URL=webhook_url,
)
service.settings = SimpleNamespace(
WEBHOOK_BASE_URL="https://shop.example",
trusted_proxies=["127.0.0.1"],
)
return service
def test_sign_query_uses_timestamp_and_recv_window_only(self):
query, signature = _sign_query(1738800000000, 5000, "secret-key")
expected = hmac.new(
b"secret-key",
b"timestamp=1738800000000&recvWindow=5000",
hashlib.sha256,
).hexdigest()
self.assertEqual(query, "timestamp=1738800000000&recvWindow=5000")
self.assertEqual(signature, expected)
def test_verify_webhook_signature_accepts_raw_body_signature(self):
service = self._make_service()
raw_body = (
b'{"id":"evt_1","priority":"HIGH","eventType":"INVOICE_PAID",'
b'"data":{"id":"inv_1","clientOrderId":"42"}}'
)
timestamp = str(int(time.time() * 1000))
signature = _webhook_signature(
timestamp=timestamp,
method="POST",
url="https://shop.example/webhook/paykilla",
raw_body=raw_body,
secret_key="secret-key",
)
request = SimpleNamespace(
headers={
"X-API-KEY": "public-key",
"X-API-TIMESTAMP": timestamp,
"X-API-RECV-WINDOW": "5000",
"X-API-SIGN": signature,
},
method="POST",
scheme="https",
host="shop.example",
path_qs="/webhook/paykilla",
)
self.assertTrue(service._verify_webhook_signature(request, raw_body))
def test_verify_webhook_signature_rejects_invalid_signature(self):
service = self._make_service()
request = SimpleNamespace(
headers={
"X-API-KEY": "public-key",
"X-API-TIMESTAMP": str(int(time.time() * 1000)),
"X-API-RECV-WINDOW": "5000",
"X-API-SIGN": "bad",
},
method="POST",
scheme="https",
host="shop.example",
path_qs="/webhook/paykilla",
)
self.assertFalse(service._verify_webhook_signature(request, b"{}"))
class WebAppSecurityTests(unittest.IsolatedAsyncioTestCase): class WebAppSecurityTests(unittest.IsolatedAsyncioTestCase):
class _AsyncSessionFactory: class _AsyncSessionFactory:
def __call__(self): def __call__(self):