Merchant API
version: 1.0.0
Merchant API bilan ishlash uchun bekendning ochiq manzili talab qilinadi. Dasturchi sahifasidagi Yandex Split konsolidagi Callback URL maydonida manzilni kiriting.
Bekend so‘rovni qayta ishlashdan oldin uning haqiqiyligini tekshirishi kerak.
Bu muhim
Mantiqni aniq IP-manzillarga bog‘lamang – ular o‘zgarishi mumkin. Haqiqiylikni tekshirish uchun ochiq kalitlar yordamida JWT token tasdiqlashdan foydalaning.
Yandex so‘rovi autentifikatsiyasi
Soʻrov tanasida Yandex ES256 algoritmi boʻyicha imzolangan JWT tokenini yuboradi.
Token uch qismdan iborat: sarlavha (header), foydali yuk (payload) va imzo (signature),
nuqtasi orqali birlashtiriladi: <base64-encoded headers JSON>.<base64-encoded payload JSON>.<signature>.
Base64'dan dekod qilingan sarlavha quyidagi tuzilishga ega JSON obyektidan iborat:
{
"alg": "ES256", // imzo algoritmi
"kid": "key-id", // kalit ID raqami, ochiq kalitni aniqlashda ishlatiladi
"iat": "1639408318", // token chiqarilgan vaqt, UNIX vaqt formatida
"exp": "1639429918", // token amal qilish muddati tugaydigan vaqt, shu vaqtdan keyin token yaroqsiz hisoblanadi
"typ": "JWT" // token turi
}
Foydali yuklama ham JSON boʻlib, uning maydonlarining aniq tuzilishi chaqirilayotgan metod bilan aniqlanadi. JWT tokenining haqiqiyligini tekshirish uchun imzo talab qilinadi.
Bu muhim
Tokenni deserializatsiya qilishdan oldin uning yaroqliligini tekshiring.
Token yaroqliligini tekshirish algoritmi
JWT tokenining yaroqliligini tekshirish uchun roʻyxatdagi standart kutubxonalardan dan foydalanish qulayroq: https://jwt.io/libraries. Imkon qadar tekshiruvlarni qo‘lda amalga oshirishdan saqlaning.
Umuman olganda, kutubxona yordamida JWT tokenni tekshirish algoritmi quyidagilarni o‘z ichiga oladi:
- JWT-token imzosining haqiqiyligini quyidagi manzillarda joylashtirilgan ochiq JWK kalitlari yordamida tekshiring:
https://sandbox.pay.yandex.uz/api/jwkstest muhiti uchun,https://pay.yandex.uz/api/jwks
ishchi muhiti uchun. Muayyan JWT-token imzosini tekshirish uchun ishlatiladigan ochiq kalit token sarlavhasidagi
algvakidtalablariga asoslanib tanlanadi. - JWT-token sarlavhasidagi standart talablarni tekshirish:
alg,typ,iat,expva boshqalar.
Faqat ikkala tekshiruv ham muvaffaqiyatli o‘tgandan keyingina qabul qiluvchi JWT-tokenning JSON payload qismini deserializatsiya qilishi mumkin. Shundan so‘ng qabul qiluvchi qo‘shimcha ravishda token payload qismidagi merchantId qiymati Yandex Splitdagi sotuvchi ID raqami bilan mos kelishini tekshirishi kerak. Aks holda tokenni ishlatib bo‘lmaydi.
Agar tekshiruvlardan biri muvaffaqiyatsiz tugasa, token yaroqsiz deb hisoblanadi, so‘rov esa autentifikatsiyadan o‘tmagan deb qaraladi. Bunday so‘rov rad etilishi kerak. Xatolik kodi reasonCode sababga bog‘liq:
TOKEN_EXPIRED– tokenning amal qilish muddati tugagan;UNAUTHORIZED– token imzosini tekshirib bo‘lmadi;OTHER– boshqa tasdiqlash xatoliklari.
Javob namunalari:
{
"status": "fail",
"reasonCode": "TOKEN_EXPIRED",
"reason": "JWT token has expired"
}
{
"status": "fail",
"reasonCode": "UNAUTHORIZED",
"reason": "Invalid token signature"
}
{
"status": "fail",
"reasonCode": "OTHER",
"reason": "Invalid merchantId"
}
Ochiq JWK kalitlarini keshlash
JWT-tokenlarni tekshirishda foydalaniladigan JWK kalitlarini do‘kon bekendida keshlashga ruxsat etiladi. Bunday keshning amal qilish muddati shunday belgilanilishi kerakki, Yandex Split tomonida JWK kalitlari rotatsiya qilinganda tokenlarni tasdiqlashda xatoliklar yuzaga kelmasin. Amaliyotda kabi ayrim kutubxonalar bunday kesh uchun standart holatda amal qilish muddatini 10 daqiqa qilib belgilaydi. JWK kalitlari keshlangan holatda quyidagi tekshiruv ssenariylarini amalga oshirish zarur.
A ssenariy: Keshlangan JWK kalitlari bilan token muvaffaqiyatli tasdiqlandi.
- Keshlangan JWK kalitlari orasida
kidtopilgan va keshning amal qilish muddati tugamagan token bilan so‘rov keladi. - Token imzosi muvaffaqiyatli tekshiriladi.
- Token haqiqiy deb hisoblanadi.
B ssenariy: Keshlangan JWK kalitlari bilan tokenni tekshirish muvaffaqiyatsiz.
- Keshlangan JWK kalitlari orasida
kidtopilgan va keshning amal qilish muddati tugamagan token bilan so‘rov keladi. - Token imzosini tekshirish muvaffaqiyatsiz yakunlanadi.
- Token yaroqsiz deb hisoblanadi va so‘rov rad etiladi.
C ssenariy: JWK kalitlari keshini yangilash orqali tokenni tekshirish.
- Keshlangan JWK kalitlari orasida
kidtopilmagan yoki keshning amal qilish muddati tugagan token bilan so‘rov keladi. - Sotuvchi JWK kalitlari keshini tozalashi va tegishli muhit manzilidan barcha faol JWK kalitlari ro‘yxatini qayta yuklab olishi kerak.
- Shundan so‘ng tekshirish A yoki B ssenariysi bo‘yicha davom etadi.
Token validatsiyasidagi kabi, ushbu ssenariylarni qo‘lda amalga oshirishdan ko‘ra standart kutubxonalardan foydalanish tavsiya etiladi.
JWT-tokenni tekshirish uchun kod namunasi
Quyida test muhitida chiqarilgan JWT-tokenni muvaffaqiyatli tekshirish namunasi keltirilgan.
Ishchi muhitida muhitida tokenlarni validatsiya qilish uchun quyidagi manzilda mavjud bo‘lgan ochiq kalitlardan foydalanish kerak: https://pay.yandex.uz/api/jwks.
import json
from urllib.request import urlopen
from jose import jwt
YANDEX_JWK_ENDPOINT = "https://sandbox.pay.yandex.uz/api/jwks"
JWT_TOKEN = (
"eyJhbGciOiJFUzI1NiIsImlhdCI6MTY1MDE5Njc1OCwia2lkIjoidGVzdC1rZXkiLCJ0eXAiOiJKV1QifQ.eyJjYXJ0Ijp7Iml0ZW1zIjpbeyJwcm9kdWN"
"0SWQiOiJwMSIsInF1YW50aXR5Ijp7ImNvdW50IjoiMSJ9fV19LCJjdXJyZW5jeUNvZGUiOiJSVUIiLCJtZXJjaGFudElkIjoiMjc2Y2YxZjEtZjhlZC00N"
"GZlLTg5ZTMtNWU0MTEzNDZkYThkIn0.YmQjHlh3ddLWgBexQ3QrwtbgAA3u1TVnBl1qnfMIvToBwinH3uH92KGB15m4NAQXdz5nhkjPZZu7RUStJt40PQ"
)
with urlopen(YANDEX_JWK_ENDPOINT) as response:
public_jwks = json.load(response)
payload = jwt.decode(JWT_TOKEN, public_jwks, algorithms=["ES256"])
print(json.dumps(payload, indent=2))
use Firebase\JWT\JWT;
use Firebase\JWT\JWK;
$sandboxJwk = 'https://sandbox.pay.yandex.uz/api/jwks';
$JWT_TOKEN =
"eyJhbGciOiJFUzI1NiIsImlhdCI6MTY1MDE5Njc1OCwia2lkIjoidGVzdC1rZXkiLCJ0eXAiOiJKV1QifQ.eyJjYXJ0Ijp7Iml0ZW1zIjpbeyJwcm9kdWN"
. "0SWQiOiJwMSIsInF1YW50aXR5Ijp7ImNvdW50IjoiMSJ9fV19LCJjdXJyZW5jeUNvZGUiOiJSVUIiLCJtZXJjaGFudElkIjoiMjc2Y2YxZjEtZjhlZC00N"
. "GZlLTg5ZTMtNWU0MTEzNDZkYThkIn0.YmQjHlh3ddLWgBexQ3QrwtbgAA3u1TVnBl1qnfMIvToBwinH3uH92KGB15m4NAQXdz5nhkjPZZu7RUStJt40PQ";
$client = new GuzzleHttp\Client();
$keysJson = $client->get($sandboxJwk)->getBody();
$keysData = json_decode($keysJson, JSON_OBJECT_AS_ARRAY);
$keys = JWK::parseKeySet($keysData);
$payload = JWT::decode($JWT_TOKEN, $keys);
print_r($payload);
Namunani ishga tushirish uchun quyidagi bog‘liqliklarni o‘rnatish kerak:
composer require firebase/php-jwt
composer require guzzlehttp/guzzle
Webhooklar bilan bog‘liq muammolarni hal qilish
Agar bildirishnomalar kelmayotgan bo‘lsa, quyidagi holatlarni tekshiring:
-
Bekend manzili noto‘g‘ri
Bildirishnomalar siz kutgan manzilga emas, boshqa joyga yuborilayotgan bo‘lishi mumkin. Callback URLni
/v1/webhookqismini qo‘shmasdan kiriting – bu yo‘l avtomatik ravishda qo‘shiladi.Callback URL
Soʻrov qayerga yuboriladi
https://example.merchant.uzhttps://example.merchant.uz/v1/webhookhttps://example.merchant.uz/v1/webhookhttps://example.merchant.uz/v1/webhook/v1/webhook -
Content-Typeni qayta ishlashDo‘kon bekendi
Content-Type: application/octet-streamsarlavhasi bilan keladigan xabarlarni qabul qila olishiga ishonch hosil qiling. -
SSL sertifikat
Tizim o‘z-o‘zini imzolagan SSL sertifikatlarni tan olmaydi. Ishonchli sertifikatlash markazi tomonidan berilgan SSL sertifikatdan foydalaning.
-
Brandmauer sozlamalari
Brandmauer kiruvchi so‘rovlarni bloklamayotganini va so‘rov tanasini kesib tashlamayotganini tekshiring.
Endpoints
Specification
Open API
{
"components": {
"schemas": {
"MerchantErrorResponse": {
"properties": {
"reason": {
"description": "Xatolik sababining tavsifi.",
"type": "string"
},
"reasonCode": {
"description": "Xatolik kodi:\n\n- `FORBIDDEN` – buyurtma mavjud, ammo to‘lov Yandex Split orqali amalga oshirilmagan;\n- `ORDER_NOT_FOUND` – buyurtma sotuvchi tizimida topilmadi;\n- `ORDER_AMOUNT_MISMATCH` – buyurtma summasi sotuvchi tizimidagi summa bilan mos kelmaydi;\n- `ORDER_DETAILS_MISMATCH` – buyurtma tafsilotlari sotuvchi tizimidagi ma’lumotlardan farq qiladi;\n- `OTHER` – umumiy xatolik;\n- `UNAUTHORIZED` – JWT-token imzosini tekshirish muvaffaqiyatsiz tugadi;\n- `TOKEN_EXPIRED` – JWT-token amal qilish muddati tugagan;\n- `CONFLICT` – bildirishnomadagi ma’lumotlar sotuvchi tizimidagi buyurtma holati bilan mos kelmaydi. Masalan, bekor qilingan buyurtma uchun to‘lov haqida bildirishnoma kelib tushdi.",
"enum": [
"FORBIDDEN",
"ITEM_NOT_FOUND",
"ORDER_NOT_FOUND",
"ORDER_AMOUNT_MISMATCH",
"ORDER_DETAILS_MISMATCH",
"OUT_OF_INVENTORY",
"PICKUP_POINT_NOT_FOUND",
"SHIPPING_DETAILS_MISMATCH",
"OTHER",
"UNAUTHORIZED",
"TOKEN_EXPIRED",
"CONFLICT"
],
"type": "string"
},
"status": {
"default": "fail",
"type": "string"
}
},
"required": [
"reasonCode"
],
"type": "object"
},
"MerchantSuccessResponse": {
"properties": {
"status": {
"default": "success",
"type": "string"
}
},
"type": "object"
},
"MerchantWebhookV1Request": {
"properties": {
"event": {
"description": "Hodisa turi:\n- `ORDER_STATUS_UPDATED` – buyurtma holati yangilash;\n- `OPERATION_STATUS_UPDATED` – mablag‘ yechish, qaytarish yoki to‘lovni bekor qilish operatsiyasi holatni yangilash.",
"enum": [
"TRANSACTION_STATUS_UPDATE",
"ORDER_STATUS_UPDATED",
"OPERATION_STATUS_UPDATED",
"SUBSCRIPTION_STATUS_UPDATED"
],
"type": "string"
},
"eventTime": {
"description": "Hodisa vaqti `RFC 3339` formatida: `YYYY-MM-DDThh:mm:ssTZD`.",
"example": "2025-05-26T21:00:36.08847+00:00",
"format": "date-time",
"type": "string"
},
"merchantId": {
"description": "Sotuvchining ID (identifikatori) raqami.",
"format": "uuid",
"type": "string"
},
"operation": {
"allOf": [
{
"properties": {
"externalOperationId": {
"description": "Sotuvchi tizimidagi operatsiya identifikatori. U noyob bo‘lishi kerak.\n\nMuayyan operatsiyani [v1/operations/{external_operation_id}](../yandex-pay-api/operation/merchant_v1_operations-get) metodi orqali kuzatish uchun ushbu parametrni yuboring.",
"type": "string"
},
"operationId": {
"description": "Operatsiya identifikatori.",
"example": "5d32f295-8723-457d-81f9-ab13f17b7bd6",
"format": "uuid",
"type": "string"
},
"operationType": {
"description": "Operatsiya turi. Operatsiya turlari haqida batafsil ma’lumotni [Operatsiya holatlari](../../../payments/statuses) bo‘limida o‘qing.",
"enum": [
"AUTHORIZE",
"BIND_CARD",
"REFUND",
"CAPTURE",
"VOID",
"RECURRING",
"PREPAYMENT",
"SUBMIT"
],
"type": "string"
},
"orderId": {
"description": "Buyurtma yaratilganda [v1/orders](../yandex-pay-api/order/merchant_v1_orders-post.md) metodiga yuborilgan buyurtma ID raqami.",
"type": "string"
},
"status": {
"description": "Operatsiya holati. Operatsiya holatlari haqida batafsil ma’lumotni [Operatsiya holatlari](../../../payments/statuses) bo‘limida o‘qing.",
"enum": [
"PENDING",
"SUCCESS",
"FAIL"
],
"type": "string"
}
},
"required": [
"operationId",
"operationType",
"orderId",
"status"
],
"type": "object"
}
],
"description": "Operatsiya haqida ma’lumot. Ma’lumot `OPERATION_STATUS_UPDATED` hodisasi bilan birga keladi"
},
"order": {
"allOf": [
{
"properties": {
"cartUpdated": {
"description": "Savatcha yangilangan yoki yo‘qligi. Ballar orqali to‘lov amalga oshirilganda qaytariladi. Agar flag qiymati `true` bo‘lsa, yangilangan savatchani oling.",
"type": "boolean"
},
"orderId": {
"description": "Buyurtma yaratilganda [v1/orders](../yandex-pay-api/order/merchant_v1_orders-post.md) metodiga yuborilgan buyurtma ID raqami.",
"type": "string"
},
"paymentStatus": {
"description": "Buyurtma holati. Batafsil ma’lumotni [Buyurtma holati](../../../payments/statuses.md) bo‘limida o‘qing.",
"enum": [
"PENDING",
"AUTHORIZED",
"CAPTURED",
"VOIDED",
"REFUNDED",
"CONFIRMED",
"PARTIALLY_REFUNDED",
"FAILED"
],
"type": "string",
"x-enumDescriptions": {
"AUTHORIZED": "Платеж за заказ авторизован. Средства заблокированы на счету плательщика",
"CAPTURED": "Заказ успешно оплачен. Средства списаны со счета плательщика",
"CONFIRMED": "Заказ успешно оформлен",
"FAILED": "Заказ не был успешно оплачен",
"PARTIALLY_REFUNDED": "Совершён частичный возврат средств за заказ",
"PENDING": "Ожидается оплата",
"REFUNDED": "Совершён возврат средств за заказ",
"VOIDED": "Оплата отменена (voided). Списание средств не производилось"
}
}
},
"required": [
"orderId",
"paymentStatus"
],
"type": "object"
}
],
"description": "Buyurtma haqida ma’lumot. Ma’lumot `ORDER_STATUS_UPDATED` hodisasi bilan birga keladi."
},
"subscription": {
"allOf": [
{
"properties": {
"customerSubscriptionId": {
"description": "Obuna ID raqami. Obuna muvaffaqiyatli yaratilganda SDK tomonidan qaytariladi. Shuningdek, obunani u bo‘yicha kelgan birinchi bildirishnoma orqali ham saqlab qo‘yish mumkin. Ushbu obuna bo‘yicha keyingi yangilanishlar aynan shu maydon qiymati bilan yuboriladi.",
"format": "uuid",
"type": "string"
},
"nextWriteOff": {
"description": "Obuna bo‘yicha keyingi pul yechish urinishining sanasi",
"format": "date-time",
"type": "string"
},
"status": {
"description": "Obuna holati",
"enum": [
"NEW",
"ACTIVE",
"CANCELLED",
"EXPIRED"
],
"type": "string"
},
"subscriptionPlanId": {
"description": "Shaxsiy kabinetda yoki API orqali yaratilgan obuna rejasi ID raqami.",
"format": "uuid",
"type": "string"
}
},
"required": [
"customerSubscriptionId",
"status",
"subscriptionPlanId"
],
"type": "object"
}
],
"description": "Obuna holati."
}
},
"required": [
"event",
"eventTime",
"merchantId"
],
"type": "object"
},
"OperationWebhookData": {
"properties": {
"externalOperationId": {
"description": "Sotuvchi tizimidagi operatsiya identifikatori. U noyob bo‘lishi kerak.\n\nMuayyan operatsiyani [v1/operations/{external_operation_id}](../yandex-pay-api/operation/merchant_v1_operations-get) metodi orqali kuzatish uchun ushbu parametrni yuboring.",
"type": "string"
},
"operationId": {
"description": "Operatsiya identifikatori.",
"example": "5d32f295-8723-457d-81f9-ab13f17b7bd6",
"format": "uuid",
"type": "string"
},
"operationType": {
"description": "Operatsiya turi. Operatsiya turlari haqida batafsil ma’lumotni [Operatsiya holatlari](../../../payments/statuses) bo‘limida o‘qing.",
"enum": [
"AUTHORIZE",
"BIND_CARD",
"REFUND",
"CAPTURE",
"VOID",
"RECURRING",
"PREPAYMENT",
"SUBMIT"
],
"type": "string"
},
"orderId": {
"description": "Buyurtma yaratilganda [v1/orders](../yandex-pay-api/order/merchant_v1_orders-post.md) metodiga yuborilgan buyurtma ID raqami.",
"type": "string"
},
"status": {
"description": "Operatsiya holati. Operatsiya holatlari haqida batafsil ma’lumotni [Operatsiya holatlari](../../../payments/statuses) bo‘limida o‘qing.",
"enum": [
"PENDING",
"SUCCESS",
"FAIL"
],
"type": "string"
}
},
"required": [
"operationId",
"operationType",
"orderId",
"status"
],
"type": "object"
},
"OrderWebhookData": {
"properties": {
"cartUpdated": {
"description": "Savatcha yangilangan yoki yo‘qligi. Ballar orqali to‘lov amalga oshirilganda qaytariladi. Agar flag qiymati `true` bo‘lsa, yangilangan savatchani oling.",
"type": "boolean"
},
"orderId": {
"description": "Buyurtma yaratilganda [v1/orders](../yandex-pay-api/order/merchant_v1_orders-post.md) metodiga yuborilgan buyurtma ID raqami.",
"type": "string"
},
"paymentStatus": {
"description": "Buyurtma holati. Batafsil ma’lumotni [Buyurtma holati](../../../payments/statuses.md) bo‘limida o‘qing.",
"enum": [
"PENDING",
"AUTHORIZED",
"CAPTURED",
"VOIDED",
"REFUNDED",
"CONFIRMED",
"PARTIALLY_REFUNDED",
"FAILED"
],
"type": "string",
"x-enumDescriptions": {
"AUTHORIZED": "Платеж за заказ авторизован. Средства заблокированы на счету плательщика",
"CAPTURED": "Заказ успешно оплачен. Средства списаны со счета плательщика",
"CONFIRMED": "Заказ успешно оформлен",
"FAILED": "Заказ не был успешно оплачен",
"PARTIALLY_REFUNDED": "Совершён частичный возврат средств за заказ",
"PENDING": "Ожидается оплата",
"REFUNDED": "Совершён возврат средств за заказ",
"VOIDED": "Оплата отменена (voided). Списание средств не производилось"
}
}
},
"required": [
"orderId",
"paymentStatus"
],
"type": "object"
},
"SubscriptionWebhookData": {
"properties": {
"customerSubscriptionId": {
"description": "Obuna ID raqami. Obuna muvaffaqiyatli yaratilganda SDK tomonidan qaytariladi. Shuningdek, obunani u bo‘yicha kelgan birinchi bildirishnoma orqali ham saqlab qo‘yish mumkin. Ushbu obuna bo‘yicha keyingi yangilanishlar aynan shu maydon qiymati bilan yuboriladi.",
"format": "uuid",
"type": "string"
},
"nextWriteOff": {
"description": "Obuna bo‘yicha keyingi pul yechish urinishining sanasi",
"format": "date-time",
"type": "string"
},
"status": {
"description": "Obuna holati",
"enum": [
"NEW",
"ACTIVE",
"CANCELLED",
"EXPIRED"
],
"type": "string"
},
"subscriptionPlanId": {
"description": "Shaxsiy kabinetda yoki API orqali yaratilgan obuna rejasi ID raqami.",
"format": "uuid",
"type": "string"
}
},
"required": [
"customerSubscriptionId",
"status",
"subscriptionPlanId"
],
"type": "object"
}
}
},
"info": {
"description": "Merchant API bilan ishlash uchun bekendning ochiq manzili talab qilinadi. [Dasturchi sahifasidagi](https://console.pay.yandex.uz/settings) Yandex Split konsolidagi **Callback URL** maydonida [manzilni kiriting](../../../console/settings-pay-split.md#callback-settings).\n\nBekend so‘rovni qayta ishlashdan oldin uning haqiqiyligini tekshirishi kerak.\n\n{% note warning %}\n\nMantiqni aniq IP-manzillarga bog‘lamang – ular o‘zgarishi mumkin. Haqiqiylikni tekshirish uchun ochiq kalitlar yordamida [JWT token tasdiqlashdan](#validate) foydalaning.\n\n{% endnote %}\n\n## Yandex so‘rovi autentifikatsiyasi {#auth}\n\nSoʻrov tanasida Yandex ES256 algoritmi boʻyicha imzolangan JWT tokenini yuboradi.\nToken uch qismdan iborat: sarlavha (header), foydali yuk (payload) va imzo (signature), \n nuqtasi orqali birlashtiriladi: `<base64-encoded headers JSON>.<base64-encoded payload JSON>.<signature>`.\nBase64'dan dekod qilingan sarlavha quyidagi tuzilishga ega JSON obyektidan iborat:\n\n```json\n{\n \"alg\": \"ES256\", // imzo algoritmi\n \"kid\": \"key-id\", // kalit ID raqami, ochiq kalitni aniqlashda ishlatiladi\n \"iat\": \"1639408318\", // token chiqarilgan vaqt, UNIX vaqt formatida\n \"exp\": \"1639429918\", // token amal qilish muddati tugaydigan vaqt, shu vaqtdan keyin token yaroqsiz hisoblanadi\n \"typ\": \"JWT\" // token turi\n}\n```\n\nFoydali yuklama ham JSON boʻlib, uning maydonlarining aniq tuzilishi \n chaqirilayotgan metod bilan aniqlanadi. JWT tokenining haqiqiyligini tekshirish uchun imzo talab qilinadi.\n\n{% note warning %}\n\nTokenni deserializatsiya qilishdan oldin uning yaroqliligini tekshiring.\n\n{% endnote %}\n\n### Token yaroqliligini tekshirish algoritmi {#validate}\n\nJWT tokenining yaroqliligini tekshirish uchun roʻyxatdagi standart kutubxonalardan \ndan foydalanish qulayroq: <https://jwt.io/libraries>. Imkon qadar tekshiruvlarni qo‘lda amalga oshirishdan saqlaning.\n\nUmuman olganda, kutubxona yordamida JWT tokenni tekshirish algoritmi quyidagilarni o‘z ichiga oladi:\n\n1. JWT-token imzosining haqiqiyligini quyidagi manzillarda joylashtirilgan ochiq JWK kalitlari yordamida tekshiring: \n<https://sandbox.pay.yandex.uz/api/jwks>test muhiti uchun,<https://pay.yandex.uz/api/jwks>\n ishchi muhiti uchun. Muayyan JWT-token imzosini tekshirish uchun ishlatiladigan ochiq kalit token sarlavhasidagi `alg` va `kid` talablariga asoslanib \n tanlanadi.\n1. JWT-token sarlavhasidagi standart talablarni tekshirish: `alg`, `typ`, `iat`, `exp` va boshqalar.\n\nFaqat ikkala tekshiruv ham muvaffaqiyatli o‘tgandan keyingina qabul qiluvchi JWT-tokenning JSON payload qismini deserializatsiya qilishi mumkin. Shundan so‘ng qabul qiluvchi qo‘shimcha ravishda token payload qismidagi `merchantId` qiymati Yandex Splitdagi sotuvchi ID raqami bilan mos kelishini tekshirishi kerak. Aks holda tokenni ishlatib bo‘lmaydi.\n\nAgar tekshiruvlardan biri muvaffaqiyatsiz tugasa, token yaroqsiz deb hisoblanadi, so‘rov esa autentifikatsiyadan o‘tmagan deb qaraladi. Bunday so‘rov rad etilishi kerak. Xatolik kodi `reasonCode` sababga bog‘liq:\n\n- `TOKEN_EXPIRED` – tokenning amal qilish muddati tugagan;\n- `UNAUTHORIZED` – token imzosini tekshirib bo‘lmadi;\n- `OTHER` – boshqa tasdiqlash xatoliklari.\n\nJavob namunalari:\n\n```json\n{\n \"status\": \"fail\",\n \"reasonCode\": \"TOKEN_EXPIRED\",\n \"reason\": \"JWT token has expired\"\n}\n```\n\n```json\n{\n \"status\": \"fail\",\n \"reasonCode\": \"UNAUTHORIZED\",\n \"reason\": \"Invalid token signature\"\n}\n```\n\n```json\n{\n \"status\": \"fail\",\n \"reasonCode\": \"OTHER\",\n \"reason\": \"Invalid merchantId\"\n}\n```\n\n#### Ochiq JWK kalitlarini keshlash {#jwk-cache}\n\nJWT-tokenlarni tekshirishda foydalaniladigan JWK kalitlarini do‘kon bekendida keshlashga ruxsat etiladi.\nBunday keshning amal qilish muddati shunday belgilanilishi kerakki, Yandex Split tomonida JWK kalitlari rotatsiya qilinganda tokenlarni \n tasdiqlashda xatoliklar yuzaga kelmasin. Amaliyotda \n kabi [ayrim kutubxonalar](https://github.com/auth0/node-jwks-rsa#caching) bunday kesh uchun standart holatda\namal qilish muddatini 10 daqiqa qilib belgilaydi. JWK kalitlari keshlangan holatda quyidagi\ntekshiruv ssenariylarini amalga oshirish zarur.\n\n**A ssenariy:** Keshlangan JWK kalitlari bilan token muvaffaqiyatli tasdiqlandi.\n\n1. Keshlangan JWK kalitlari orasida `kid` topilgan va keshning amal qilish muddati tugamagan token bilan so‘rov keladi.\n1. Token imzosi muvaffaqiyatli tekshiriladi.\n1. Token haqiqiy deb hisoblanadi.\n\n**B ssenariy:** Keshlangan JWK kalitlari bilan tokenni tekshirish muvaffaqiyatsiz.\n\n1. Keshlangan JWK kalitlari orasida `kid` topilgan va keshning amal qilish muddati tugamagan token bilan so‘rov keladi.\n1. Token imzosini tekshirish muvaffaqiyatsiz yakunlanadi.\n1. Token yaroqsiz deb hisoblanadi va so‘rov rad etiladi.\n\n**C ssenariy:** JWK kalitlari keshini yangilash orqali tokenni tekshirish.\n\n1. Keshlangan JWK kalitlari orasida `kid` topilmagan yoki keshning amal qilish muddati tugagan token bilan so‘rov keladi.\n1. Sotuvchi JWK kalitlari keshini tozalashi va tegishli muhit manzilidan barcha faol JWK kalitlari ro‘yxatini qayta yuklab olishi kerak.\n1. Shundan so‘ng tekshirish A yoki B ssenariysi bo‘yicha davom etadi.\n\nToken validatsiyasidagi kabi, ushbu ssenariylarni\nqo‘lda amalga oshirishdan ko‘ra standart kutubxonalardan foydalanish tavsiya etiladi.\n\n### JWT-tokenni tekshirish uchun kod namunasi {#jwk-verify-example}\n\nQuyida test muhitida chiqarilgan JWT-tokenni muvaffaqiyatli tekshirish namunasi keltirilgan.\nIshchi muhitida muhitida tokenlarni validatsiya qilish uchun quyidagi manzilda mavjud bo‘lgan ochiq kalitlardan foydalanish kerak: `https://pay.yandex.uz/api/jwks`.\n\n{% list tabs %}\n\n- Python\n\n ```python\n import json\n from urllib.request import urlopen\n\n from jose import jwt\n\n YANDEX_JWK_ENDPOINT = \"https://sandbox.pay.yandex.uz/api/jwks\"\n\n JWT_TOKEN = (\n \"eyJhbGciOiJFUzI1NiIsImlhdCI6MTY1MDE5Njc1OCwia2lkIjoidGVzdC1rZXkiLCJ0eXAiOiJKV1QifQ.eyJjYXJ0Ijp7Iml0ZW1zIjpbeyJwcm9kdWN\"\n \"0SWQiOiJwMSIsInF1YW50aXR5Ijp7ImNvdW50IjoiMSJ9fV19LCJjdXJyZW5jeUNvZGUiOiJSVUIiLCJtZXJjaGFudElkIjoiMjc2Y2YxZjEtZjhlZC00N\"\n \"GZlLTg5ZTMtNWU0MTEzNDZkYThkIn0.YmQjHlh3ddLWgBexQ3QrwtbgAA3u1TVnBl1qnfMIvToBwinH3uH92KGB15m4NAQXdz5nhkjPZZu7RUStJt40PQ\"\n )\n\n with urlopen(YANDEX_JWK_ENDPOINT) as response:\n public_jwks = json.load(response)\n\n payload = jwt.decode(JWT_TOKEN, public_jwks, algorithms=[\"ES256\"])\n print(json.dumps(payload, indent=2))\n ```\n\n- PHP\n\n ```php\n use Firebase\\JWT\\JWT;\n use Firebase\\JWT\\JWK;\n\n $sandboxJwk = 'https://sandbox.pay.yandex.uz/api/jwks';\n\n $JWT_TOKEN =\n \"eyJhbGciOiJFUzI1NiIsImlhdCI6MTY1MDE5Njc1OCwia2lkIjoidGVzdC1rZXkiLCJ0eXAiOiJKV1QifQ.eyJjYXJ0Ijp7Iml0ZW1zIjpbeyJwcm9kdWN\"\n . \"0SWQiOiJwMSIsInF1YW50aXR5Ijp7ImNvdW50IjoiMSJ9fV19LCJjdXJyZW5jeUNvZGUiOiJSVUIiLCJtZXJjaGFudElkIjoiMjc2Y2YxZjEtZjhlZC00N\"\n . \"GZlLTg5ZTMtNWU0MTEzNDZkYThkIn0.YmQjHlh3ddLWgBexQ3QrwtbgAA3u1TVnBl1qnfMIvToBwinH3uH92KGB15m4NAQXdz5nhkjPZZu7RUStJt40PQ\";\n\n $client = new GuzzleHttp\\Client();\n $keysJson = $client->get($sandboxJwk)->getBody();\n $keysData = json_decode($keysJson, JSON_OBJECT_AS_ARRAY);\n\n $keys = JWK::parseKeySet($keysData);\n $payload = JWT::decode($JWT_TOKEN, $keys);\n\n print_r($payload);\n ```\n\n Namunani ishga tushirish uchun quyidagi bog‘liqliklarni o‘rnatish kerak:\n\n ```bash\n composer require firebase/php-jwt\n composer require guzzlehttp/guzzle\n ```\n\n{% endlist %}\n\n## Webhooklar bilan bog‘liq muammolarni hal qilish {#troubleshooting}\n\nAgar bildirishnomalar kelmayotgan bo‘lsa, quyidagi holatlarni tekshiring:\n\n- Bekend manzili noto‘g‘ri\n\n Bildirishnomalar siz kutgan manzilga emas, boshqa joyga yuborilayotgan bo‘lishi mumkin. [Callback URL](../../../console/settings-pay-split.md#callback-settings)ni `/v1/webhook` qismini qo‘shmasdan kiriting – bu yo‘l avtomatik ravishda qo‘shiladi.\n\n #|\n || **Callback URL** | **Soʻrov qayerga yuboriladi** ||\n || `https://example.merchant.uz` | `https://example.merchant.uz/v1/webhook` ||\n || `https://example.merchant.uz/v1/webhook` | `https://example.merchant.uz/v1/webhook/v1/webhook` ||\n |#\n\n- `Content-Type`ni qayta ishlash\n\n Do‘kon bekendi `Content-Type: application/octet-stream` sarlavhasi bilan keladigan xabarlarni qabul qila olishiga ishonch hosil qiling.\n\n- SSL sertifikat\n\n Tizim o‘z-o‘zini imzolagan SSL sertifikatlarni tan olmaydi. Ishonchli sertifikatlash markazi tomonidan berilgan SSL sertifikatdan foydalaning.\n\n- Brandmauer sozlamalari\n\n Brandmauer kiruvchi so‘rovlarni bloklamayotganini va so‘rov tanasini kesib tashlamayotganini tekshiring.",
"title": "Merchant API",
"version": "1.0.0"
},
"openapi": "3.0.3",
"paths": {
"/v1/webhook": {
"post": {
"description": "Status o‘zgarishi haqida bildirishnomalar.\n\nBuyurtma statusi yoki buyurtma bo‘yicha operatsiya statusi o‘zgarganda so‘rov yuboriladi.\n\nDastaklanadigan hodisalar:\n\n- `ORDER_STATUS_UPDATED` – buyurtma holati yangilash;\n- `OPERATION_STATUS_UPDATED` – mablag‘ yechish, qaytarish yoki to‘lovni bekor qilish operatsiyasi holatni yangilash.\n<!-- - `SUBSCRIPTION_STATUS_UPDATED` — сейчас этот статус не отдаем? -->\n\n## So‘rov formati {#webhook-format}\n\nSo‘rov `application/octet-stream` formatida yuboriladi va ES256 algoritmi bilan imzolangan JWT-token ko‘rinishida bo‘ladi. So‘rovni qayta ishlashdan oldin uning haqiqiyligini tekshiring. Buni qanday qilish haqida [Autentifikatsiya](../merchant-api/index.md)bo‘limida o‘qing.\n\nTekshirilib dekod qilingan JWT-tokenning payload qismi hodisa ma’lumotlarini o‘z ichiga olgan JSON'dan iborat bo‘ladi. [Hodisalar misollarini](#webhook-examples) ko‘ring.\n\n{% note warning \"Agar token ichida so‘rov tanasi mavjud bo‘lmasa\" %}\n\n- Do‘kon bekendi `Content-Type: application/octet-stream` sarlavhasi bilan keladigan xabarlarni qabul qila olishiga ishonch hosil qiling.\n- Brandmauer kiruvchi so‘rovlarni bloklamayotganini va so‘rov tanasini kesib tashlamayotganini tekshiring.\n\nBoshqa xatoliklarni [Webhooklar bilan bog‘liq muammolarni hal qilish](../merchant-api/index.md#troubleshooting)bo‘limida ko‘ring.\n\n{% endnote %}\n\n## Operatsiyalar idempotentligi {#idempotency}\n\nBuyurtma bilan bog‘liq amallarni bajarishda, masalan, [/v2/orders/{order_id}/refund](../yandex-pay-api/order/merchant_v2_refund-post.md) metodi orqali mablag‘larni qaytarishda, operatsiyaning noyob identifikatori – `externalOperationId`ni uzating.\n\nU yordamida siz:\n- qaysi operatsiya bo‘yicha bildirishnoma kelganini aniqlashingiz;\n- [v1/operations/{external_operation_id}](../yandex-pay-api/operation/merchant_v1_operations-get.md) metodi orqali operatsiya holatini tekshirishingiz;\n- operatsiya takrorlanishidan himoyalanishingiz mumkin.\n\nAgar siz bir xil `externalOperationId` bilan so‘rovni qayta yuborsangiz, quyidagilardan birini olasiz:\n- operatsiya jarayonda bo‘lsa, joriy operatsiya haqida ma’lumot;\n- operatsiya yakunlangan bo‘lsa, `reasonCode: \"DUPLICATE_EXTERNAL_OPERATION_ID\"` xatosi.\n\n## Hodisalar misollari {#webhook-examples}\n\n### Buyurtma to‘lovi\n\n{% list tabs %}\n\n- Muvaffaqiyatli\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2023-11-26T08:11:09.359370+00:00\",\n \"order\":{\n \"orderId\": \"700aa3f04df64b3b8712d6b51f752e8b\",\n \"paymentStatus\": \"CAPTURED\"\n }\n }\n ```\n\n JWT-token namunasi bilan [jwt.io](https://jwt.io/#debugger-io?token=eyJhbGciOiJFUzI1NiIsImV4cCI6MTcwMDk4NzYwMCwiaWF0IjoxNzAwOTg3MzAwLCJraWQiOiIxLW1lcmNoYW50LWFwaSIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudElkIjoieHh4eHh4eHh4LXh4eC01eHh4LXh4eHh4LXh4eHh4eHh4IiwiZXZlbnQiOiJPUkRFUl9TVEFUVVNfVVBEQVRFRCIsImV2ZW50VGltZSI6IjIwMjMtMTEtMjZUMDg6MTE6MDkuMzU5MzcwKzAwOjAwIiwib3JkZXIiOnsib3JkZXJJZCI6IjcwMGFhM2YwNGRmNjRiM2I4NzEyZDZiNTFmNzUyZThiIiwicGF5bWVudFN0YXR1cyI6IkNBUFRVUkVEIn19.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) saytida tanishishingiz mumkin.\n\n **Yandex tomonidan do‘kon bekendiga yuboriladigan so‘rov namunasi:**\n\n ```(bash)\n curl -X POST https://test.uz/some/prefix/v1/webhook \\\n --header 'User-Agent: YandexPay/1.0' \\\n --header 'Accept: \\*/\\*' \\\n --header 'Content-Type: application/octet-stream' \\\n --header 'X-Request-Id: ff2a54885c4e45309853d2e33af1d63b\\\\_3a70f3062db640fcb2f3c34de1a27bd5' \\\n --header 'X-Request-Timeout: 13970' \\\n --compressed \\\n -d eyJhbGciOiJFUzI1NiIsImV4cCI6MTcwMDk4NzYwMCwiaWF0IjoxNzAwOTg3MzAwLCJraWQiOiIxLW1lcmNoYW50LWFwaSIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudElkIjoieHh4eHh4eHh4LXh4eC01eHh4LXh4eHh4LXh4eHh4eHh4IiwiZXZlbnQiOiJPUkRFUl9TVEFUVVNfVVBEQVRFRCIsImV2ZW50VGltZSI6IjIwMjMtMTEtMjZUMDg6MTE6MDkuMzU5MzcwKzAwOjAwIiwib3JkZXIiOnsib3JkZXJJZCI6IjcwMGFhM2YwNGRmNjRiM2I4NzEyZDZiNTFmNzUyZThiIiwicGF5bWVudFN0YXR1cyI6IkNBUFRVUkVEIn19.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n ```\n\n- Omadsiz\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-25T07:56:29.974810+00:00\",\n \"order\":{\n \"orderId\": \"253222_1714029088\",\n \"paymentStatus\": \"FAILED\"\n }\n }\n ```\n\n JWT-token namunasi bilan [jwt.io](https://jwt.io/#debugger-io?token=eyJhbGciOiJFUzI1NiIsImV4cCI6MTcxNDAzMjIyMSwiaWF0IjoxNzE0MDMxOTIxLCJraWQiOiIxLW1lcmNoYW50LWFwaSIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudElkIjoieHh4eHh4eHh4LXh4eC01eHh4LXh4eHh4LXh4eHh4eHh4IiwiZXZlbnQiOiJPUkRFUl9TVEFUVVNfVVBEQVRFRCIsImV2ZW50VGltZSI6IjIwMjQtMDQtMjVUMDc6NTY6MjkuOTc0ODEwKzAwOjAwIiwib3JkZXIiOnsib3JkZXJJZCI6IjI1MzIyMl8xNzE0MDI5MDg4IiwicGF5bWVudFN0YXR1cyI6IkZBSUxFRCJ9fQ.v9dw_cR3_b4R5v0D8WRisrSPABxhegSSpEq4kz9s10fr5cUK150yWnwJREYCGQCm5BZK1Yydsquh-WE6OyRR2APOST) saytida tanishishingiz mumkin.\n\n{% endlist %}\n\n### Qaytarish\n\n{% note tip %}\n\nAvval qaytarishlar qanday ishlashini [/v2/orders/{order_id}/refund](../yandex-pay-api/order/merchant_v2_refund-post.md) bo‘limida o‘rganing.\n\n{% endnote %}\n\n#### To‘liq qaytarish\n\nBuyurtma holati o‘zgargan-o‘zgarmaganidan qat’i nazar, 2 ta notifikatsiya yuboriladi: operatsiya bo‘yicha va buyurtma bo‘yicha.\n\n{% list tabs %}\n\n- Muvaffaqiyatli\n\n 1. `OPERATION_STATUS_UPDATED` – qaytarish operatsiyasi muvaffaqiyatli yakunlandi:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"OPERATION_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T10:27:53.323878+00:00\",\n \"operation\":{\n \"operationId\": \"73dec2cd-db5c-4386-be6d-10c5b5a2ee09\",\n \"orderId\": \"86283\",\n \"status\": \"SUCCESS\",\n \"operationType\": \"REFUND\"\n }\n }\n ```\n\n 2. `ORDER_STATUS_UPDATED` – buyurtma yakuniy `REFUNDED` holatiga o‘tdi. Endi qaytarishlarni chaqirish mumkin emas.\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T12:16:28.766392+00:00\",\n \"order\":{\n \"orderId\": \"86283\",\n \"paymentStatus\": \"REFUNDED\"\n }\n }\n ```\n\n JWT-token namunasi bilan [jwt.io](https://jwt.io/#debugger-io?token=eyJhbGciOiJFUzI1NiIsImV4cCI6MTcxMzUyOTI4OSwiaWF0IjoxNzEzNTI4OTg5LCJraWQiOiIxLW1lcmNoYW50LWFwaSIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudElkIjoieHh4eHh4eHh4LXh4eC01eHh4LXh4eHh4LXh4eHh4eHh4IiwiZXZlbnQiOiJPUkRFUl9TVEFUVVNfVVBEQVRFRCIsImV2ZW50VGltZSI6IjIwMjQtMDQtMTlUMTI6MTY6MjguNzY2MzkyKzAwOjAwIiwib3JkZXIiOnsib3JkZXJJZCI6Ijg2MjgzIiwicGF5bWVudFN0YXR1cyI6IlJFRlVOREVEIn19.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) saytida tanishishingiz mumkin.\n\n- Omadsiz\n\n 1. `OPERATION_STATUS_UPDATED` – qaytarish operatsiyasi muvaffaqiyatsiz yakunlandi:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"OPERATION_STATUS_UPDATED\",\n \"eventTime\": \"2024-06-13T22:27:53.323878+00:00\",\n \"operation\":{\n \"operationId\": \"73dec2cd-db5c-4386-be6d-10c5b5a2ee08\",\n \"orderId\": \"9c8aed6d-a8e5-4c6a-acd8-645538173f66\",\n \"status\": \"FAIL\",\n \"operationType\": \"REFUND\"\n }\n }\n ```\n\n JWT-token namunasi bilan [jwt.io](https://jwt.io/#debugger-io?token=eyJhbGciOiJFUzI1NiIsImV4cCI6MTcxODMxNzk3NCwiaWF0IjoxNzE4MzE3Njc0LCJraWQiOiIxLW1lcmNoYW50LWFwaSIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudElkIjoieHh4eHh4eHh4LXh4eC01eHh4LXh4eHh4LXh4eHh4eHh4IiwiZXZlbnQiOiJPUEVSQVRJT05fU1RBVFVTX1VQREFURUQiLCJldmVudFRpbWUiOiIyMDI0LTA2LTEzVDIyOjI3OjUzLjMyMzg3OCswMDowMCIsIm9wZXJhdGlvbiI6eyJvcGVyYXRpb25JZCI6IjczZGVjMmNkLWRiNWMtNDM4Ni1iZTZkLTEwYzViNWEyZWUwOCIsIm9yZGVySWQiOiI5YzhhZWQ2ZC1hOGU1LTRjNmEtYWNkOC02NDU1MzgxNzNmNjYiLCJzdGF0dXMiOiJGQUlMIiwib3BlcmF0aW9uVHlwZSI6IlJFRlVORCJ9fQ.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) saytida tanishishingiz mumkin.\n\n 2. `ORDER_STATUS_UPDATED` – buyurtma avvalgi `CAPTURED` holatida qoldi:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2024-06-13T22:27:54.323878+00:00\",\n \"order\":{\n \"orderId\": \"9c8aed6d-a8e5-4c6a-acd8-645538173f66\",\n \"paymentStatus\": \"CAPTURED\"\n }\n }\n ```\n\n{% endlist %}\n\n#### Qisman qaytarish\n\nSiz buyurtmaning to‘liq summasini bir nechta qisman qaytarishlar orqali qaytarishingiz mumkin. Barcha qaytarishlar summasi buyurtmaning to‘liq qiymatiga yetganda, buyurtma yakuniy `REFUNDED` holatiga o‘tadi. Undan keyin qaytarishlarni amalga oshirib bo‘lmaydi.\n\nBuyurtma holati o‘zgargan-o‘zgarmaganidan qat’i nazar, 2 ta notifikatsiya yuboriladi: operatsiya bo‘yicha va buyurtma bo‘yicha.\n\nMisol sifatida uchta sharbatdan iborat buyurtmani ko‘rib chiqamiz.\n\n1. Bitta sharbat uchun qisman qaytarish amalga oshirildi. Sizga 2 ta bildirishnoma keladi:\n\n 1. `OPERATION_STATUS_UPDATED` – qaytarish operatsiyasi muvaffaqiyatli yakunlandi:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"OPERATION_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T10:27:53.323878+00:00\",\n \"operation\":{\n \"operationId\": \"73dec3cs-sd5t-4356-ne6d-10c79b5d2ee09\",\n \"externalOperationId\": \"123-partial-refund-1\",\n \"orderId\": \"123\",\n \"status\": \"SUCCESS\",\n \"operationType\": \"REFUND\"\n }\n }\n ```\n\n 2. `ORDER_STATUS_UPDATED` – buyurtma `PARTIALLY_REFUNDED` holatiga o‘tdi:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T12:16:28.766392+00:00\",\n \"order\":{\n \"orderId\": \"123\",\n \"paymentStatus\": \"PARTIALLY_REFUNDED\"\n }\n }\n ```\n\n2. Ikkinchi marta bir dona sharbat uchun qisman qaytarish amalga oshirildi. Sizga 2 ta bildirishnoma keladi:\n\n 1. `OPERATION_STATUS_UPDATED` – qaytarish operatsiyasi muvaffaqiyatli yakunlandi:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"OPERATION_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T13:27:53.323878+00:00\",\n \"operation\":{\n \"operationId\": \"28fba9ds-kl2m-7891-qw3r-45e82c7f1bb12\",\n \"externalOperationId\": \"123-partial-refund-2\",\n \"orderId\": \"123\",\n \"status\": \"SUCCESS\",\n \"operationType\": \"REFUND\"\n }\n }\n ```\n\n 2. `ORDER_STATUS_UPDATED` – buyurtma `PARTIALLY_REFUNDED` holatida qoldi:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T13:27:54.321878+00:00\",\n \"order\":{\n \"orderId\": \"123\",\n \"paymentStatus\": \"PARTIALLY_REFUNDED\"\n }\n }\n ```\n\n3. Uchinchi qisman qaytarish amalga oshirildi. Barcha qaytarishlar summasi buyurtmaning to‘liq qiymatiga yetdi. Sizga 2 ta bildirishnoma keladi:\n\n 1. `OPERATION_STATUS_UPDATED` – qaytarish operatsiyasi muvaffaqiyatli yakunlandi:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"OPERATION_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T13:40:51.323878+00:00\",\n \"operation\":{\n \"operationId\": \"64abc2ts-rj4y-3187-mf5g-56b71e9a4dd67\",\n \"externalOperationId\": \"123-partial-refund-3\",\n \"orderId\": \"123\",\n \"status\": \"SUCCESS\",\n \"operationType\": \"REFUND\"\n }\n }\n ```\n\n 2. `ORDER_STATUS_UPDATED` – buyurtma yakuniy `REFUNDED` holatiga o‘tdi. Endi qaytarishlarni chaqirish mumkin emas.\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T14:16:28.766392+00:00\",\n \"order\":{\n \"orderId\": \"123\",\n \"paymentStatus\": \"REFUNDED\"\n }\n }\n ```",
"operationId": "webhook",
"requestBody": {
"content": {
"application/json": {
"schema": {
"properties": {
"event": {
"description": "Hodisa turi:\n- `ORDER_STATUS_UPDATED` – buyurtma holati yangilash;\n- `OPERATION_STATUS_UPDATED` – mablag‘ yechish, qaytarish yoki to‘lovni bekor qilish operatsiyasi holatni yangilash.",
"enum": [
"TRANSACTION_STATUS_UPDATE",
"ORDER_STATUS_UPDATED",
"OPERATION_STATUS_UPDATED",
"SUBSCRIPTION_STATUS_UPDATED"
],
"type": "string"
},
"eventTime": {
"description": "Hodisa vaqti `RFC 3339` formatida: `YYYY-MM-DDThh:mm:ssTZD`.",
"example": "2025-05-26T21:00:36.08847+00:00",
"format": "date-time",
"type": "string"
},
"merchantId": {
"description": "Sotuvchining ID (identifikatori) raqami.",
"format": "uuid",
"type": "string"
},
"operation": {
"allOf": [
{
"properties": {
"externalOperationId": {
"description": "Sotuvchi tizimidagi operatsiya identifikatori. U noyob bo‘lishi kerak.\n\nMuayyan operatsiyani [v1/operations/{external_operation_id}](../yandex-pay-api/operation/merchant_v1_operations-get) metodi orqali kuzatish uchun ushbu parametrni yuboring.",
"type": "string"
},
"operationId": {
"description": "Operatsiya identifikatori.",
"example": "5d32f295-8723-457d-81f9-ab13f17b7bd6",
"format": "uuid",
"type": "string"
},
"operationType": {
"description": "Operatsiya turi. Operatsiya turlari haqida batafsil ma’lumotni [Operatsiya holatlari](../../../payments/statuses) bo‘limida o‘qing.",
"enum": [
"AUTHORIZE",
"BIND_CARD",
"REFUND",
"CAPTURE",
"VOID",
"RECURRING",
"PREPAYMENT",
"SUBMIT"
],
"type": "string"
},
"orderId": {
"description": "Buyurtma yaratilganda [v1/orders](../yandex-pay-api/order/merchant_v1_orders-post.md) metodiga yuborilgan buyurtma ID raqami.",
"type": "string"
},
"status": {
"description": "Operatsiya holati. Operatsiya holatlari haqida batafsil ma’lumotni [Operatsiya holatlari](../../../payments/statuses) bo‘limida o‘qing.",
"enum": [
"PENDING",
"SUCCESS",
"FAIL"
],
"type": "string"
}
},
"required": [
"operationId",
"operationType",
"orderId",
"status"
],
"type": "object"
}
],
"description": "Operatsiya haqida ma’lumot. Ma’lumot `OPERATION_STATUS_UPDATED` hodisasi bilan birga keladi"
},
"order": {
"allOf": [
{
"properties": {
"cartUpdated": {
"description": "Savatcha yangilangan yoki yo‘qligi. Ballar orqali to‘lov amalga oshirilganda qaytariladi. Agar flag qiymati `true` bo‘lsa, yangilangan savatchani oling.",
"type": "boolean"
},
"orderId": {
"description": "Buyurtma yaratilganda [v1/orders](../yandex-pay-api/order/merchant_v1_orders-post.md) metodiga yuborilgan buyurtma ID raqami.",
"type": "string"
},
"paymentStatus": {
"description": "Buyurtma holati. Batafsil ma’lumotni [Buyurtma holati](../../../payments/statuses.md) bo‘limida o‘qing.",
"enum": [
"PENDING",
"AUTHORIZED",
"CAPTURED",
"VOIDED",
"REFUNDED",
"CONFIRMED",
"PARTIALLY_REFUNDED",
"FAILED"
],
"type": "string",
"x-enumDescriptions": {
"AUTHORIZED": "Платеж за заказ авторизован. Средства заблокированы на счету плательщика",
"CAPTURED": "Заказ успешно оплачен. Средства списаны со счета плательщика",
"CONFIRMED": "Заказ успешно оформлен",
"FAILED": "Заказ не был успешно оплачен",
"PARTIALLY_REFUNDED": "Совершён частичный возврат средств за заказ",
"PENDING": "Ожидается оплата",
"REFUNDED": "Совершён возврат средств за заказ",
"VOIDED": "Оплата отменена (voided). Списание средств не производилось"
}
}
},
"required": [
"orderId",
"paymentStatus"
],
"type": "object"
}
],
"description": "Buyurtma haqida ma’lumot. Ma’lumot `ORDER_STATUS_UPDATED` hodisasi bilan birga keladi."
},
"subscription": {
"allOf": [
{
"properties": {
"customerSubscriptionId": {
"description": "Obuna ID raqami. Obuna muvaffaqiyatli yaratilganda SDK tomonidan qaytariladi. Shuningdek, obunani u bo‘yicha kelgan birinchi bildirishnoma orqali ham saqlab qo‘yish mumkin. Ushbu obuna bo‘yicha keyingi yangilanishlar aynan shu maydon qiymati bilan yuboriladi.",
"format": "uuid",
"type": "string"
},
"nextWriteOff": {
"description": "Obuna bo‘yicha keyingi pul yechish urinishining sanasi",
"format": "date-time",
"type": "string"
},
"status": {
"description": "Obuna holati",
"enum": [
"NEW",
"ACTIVE",
"CANCELLED",
"EXPIRED"
],
"type": "string"
},
"subscriptionPlanId": {
"description": "Shaxsiy kabinetda yoki API orqali yaratilgan obuna rejasi ID raqami.",
"format": "uuid",
"type": "string"
}
},
"required": [
"customerSubscriptionId",
"status",
"subscriptionPlanId"
],
"type": "object"
}
],
"description": "Obuna holati."
}
},
"required": [
"event",
"eventTime",
"merchantId"
],
"type": "object"
}
}
}
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"properties": {
"status": {
"default": "success",
"type": "string"
}
},
"type": "object"
}
}
},
"description": "Vebhuk muvaffaqiyatli qabul qilindi va qayta ishlangan.\nJavob tanasi ixtiyoriy bo‘lishi mumkin, ammo quyidagini yuborish tavsiya etiladi: `{\"status\": \"success\"}`.\nAgar `200` status kodi qaytarilsa, Yandex vebhuklarni qayta yuborishni to‘xtatadi."
},
"400": {
"content": {
"application/json": {
"schema": {
"properties": {
"reason": {
"description": "Xatolik sababining tavsifi.",
"type": "string"
},
"reasonCode": {
"description": "Xatolik kodi:\n\n- `FORBIDDEN` – buyurtma mavjud, ammo to‘lov Yandex Split orqali amalga oshirilmagan;\n- `ORDER_NOT_FOUND` – buyurtma sotuvchi tizimida topilmadi;\n- `ORDER_AMOUNT_MISMATCH` – buyurtma summasi sotuvchi tizimidagi summa bilan mos kelmaydi;\n- `ORDER_DETAILS_MISMATCH` – buyurtma tafsilotlari sotuvchi tizimidagi ma’lumotlardan farq qiladi;\n- `OTHER` – umumiy xatolik;\n- `UNAUTHORIZED` – JWT-token imzosini tekshirish muvaffaqiyatsiz tugadi;\n- `TOKEN_EXPIRED` – JWT-token amal qilish muddati tugagan;\n- `CONFLICT` – bildirishnomadagi ma’lumotlar sotuvchi tizimidagi buyurtma holati bilan mos kelmaydi. Masalan, bekor qilingan buyurtma uchun to‘lov haqida bildirishnoma kelib tushdi.",
"enum": [
"FORBIDDEN",
"ITEM_NOT_FOUND",
"ORDER_NOT_FOUND",
"ORDER_AMOUNT_MISMATCH",
"ORDER_DETAILS_MISMATCH",
"OUT_OF_INVENTORY",
"PICKUP_POINT_NOT_FOUND",
"SHIPPING_DETAILS_MISMATCH",
"OTHER",
"UNAUTHORIZED",
"TOKEN_EXPIRED",
"CONFLICT"
],
"type": "string"
},
"status": {
"default": "fail",
"type": "string"
}
},
"required": [
"reasonCode"
],
"type": "object"
}
}
},
"description": "Vebhukni qayta ishlashda xatolik.\nAgar javob bo‘lmasa yoki `200`dan boshqa status qaytarilsa, Yandex yangi JWT-token yaratadi va vebhukni qayta yuboradi:\n- dastlabki 10 ta urinish har 5 ms dan keyin;\n- keyin interval eksponentsial ravishda oshib boradi va 15 daqiqagacha yetadi;\n- shundan so‘ng 24 soat davomida har 15 daqiqada yuboriladi.\nQayta yuborishlarning umumiy davomiyligi – 24 soat. Shundan keyin vebhuk yetkazilmagan deb hisoblanadi."
}
},
"summary": "/v1/webhook"
}
}
},
"servers": [
{
"description": "Production",
"url": "https://example.merchant.uz"
},
{
"description": "Sandbox",
"url": "https://sandbox.example.merchant.uz"
}
]
}