{
  "name": "Повторы безопасных запросов: 502/503/504 и обрыв после отправки",
  "description": "GET после 502 (не JSON) повторяется через 500 мс. Безопасный POST (lookupMember, x-safe-to-retry) после 503 с Retry-After: 1 — через 1000 мс. POST с ключом идемпотентности после таймаута ответа — через 500 мс с тем же ключом. GET, который всё время отвечает 504, после двух повторов (по умолчанию) — ошибка server: пауза min(8000, 500 × 2^n) при n = 0, 1.",
  "client": {
    "api": "runtime",
    "credential": {"terminalKey": "lk_test_k7m2p9q4r8s3.RqTvNAJirupwDOPZ8O48PZpsbGlZGfecp04FD7w06kw"},
    "baseUrl": "https://api.example.com"
  },
  "calls": [
    {
      "operation": "getReceipt",
      "args": {"receipt": "01929c1e-0050-7050-8050-000000000050"},
      "expect": {
        "result": {
          "id": "01929c1e-0050-7050-8050-000000000050",
          "status": "confirmed",
          "number": "1042",
          "business_date": "2026-10-11",
          "purchased_at": "2026-10-11T14:59:58+03:00",
          "member": {
            "id": "01929c1e-0040-7040-8040-000000000040",
            "masked_phone": "+7 *** ***-00-01",
            "status": "active"
          },
          "points": {"point_type_id": "01929c1e-0011-7011-8011-000000000011", "precision": 0},
          "rules": {"snapshot_id": "01929c1e-0012-7012-8012-000000000012", "version": 3},
          "total": 100000,
          "discount": 0,
          "redeemed_money": 30000,
          "redeemed_points": 300,
          "points_earned": 35,
          "payable": 70000,
          "max_redeemable_points": 1000,
          "redemption_confirmation_required": false,
          "lines": [
            {
              "id": "1",
              "amount": 100000,
              "discount": 0,
              "redeemed_money": 30000,
              "redeemed_points": 300,
              "points_earned": 35,
              "payable": 70000,
              "returned_quantity": 0
            }
          ],
          "campaigns": [],
          "stamps": [],
          "explanation": [{"code": "accrual", "data": {"percent_bp": 500, "base": 70000, "points": 35}}],
          "reserved_until": null,
          "confirmed_at": "2026-10-11T12:00:01.250000Z"
        }
      }
    },
    {
      "operation": "lookupMember",
      "args": {"body": {"phone": "+79000000001"}},
      "expect": {
        "result": {
          "id": "01929c1e-0040-7040-8040-000000000040",
          "status": "active",
          "masked_phone": "+7 *** ***-00-01",
          "first_name": "Анна",
          "registered_at": "2026-10-11T11:58:30.000000Z",
          "referral_code": "ANNA-7K2P",
          "balance": {
            "point_type_id": "01929c1e-0011-7011-8011-000000000011",
            "precision": 0,
            "available": 1035,
            "pending": 0,
            "held": 0,
            "spendable": 1035,
            "next_expiration": {"amount": 35, "at": "2027-10-11T12:00:01.250000Z"}
          },
          "tier": null,
          "stamps": []
        }
      }
    },
    {
      "operation": "registerReceipt",
      "args": {
        "body": {
          "number": "1043",
          "business_date": "2026-10-11",
          "purchased_at": "2026-10-11T14:59:58+03:00",
          "member": {"phone": "+79000000001"},
          "lines": [{"id": "1", "sku": "LATTE-M", "name": "Латте 300 мл", "quantity": 2000, "amount": 50000}],
          "cashier": "Иванова"
        }
      },
      "expect": {
        "result": {
          "id": "01929c1e-0051-7051-8051-000000000051",
          "status": "reserved",
          "number": "1043",
          "business_date": "2026-10-11",
          "purchased_at": "2026-10-11T14:59:58+03:00",
          "member": {
            "id": "01929c1e-0040-7040-8040-000000000040",
            "masked_phone": "+7 *** ***-00-01",
            "status": "active"
          },
          "points": {"point_type_id": "01929c1e-0011-7011-8011-000000000011", "precision": 0},
          "rules": {"snapshot_id": "01929c1e-0012-7012-8012-000000000012", "version": 3},
          "total": 50000,
          "discount": 0,
          "redeemed_money": 0,
          "redeemed_points": 0,
          "points_earned": 25,
          "payable": 50000,
          "max_redeemable_points": 1000,
          "redemption_confirmation_required": false,
          "lines": [
            {
              "id": "1",
              "amount": 50000,
              "discount": 0,
              "redeemed_money": 0,
              "redeemed_points": 0,
              "points_earned": 25,
              "payable": 50000,
              "returned_quantity": 0
            }
          ],
          "campaigns": [],
          "stamps": [],
          "explanation": [{"code": "accrual", "data": {"percent_bp": 500, "base": 50000, "points": 25}}],
          "reserved_until": "2026-10-11T12:29:58.000000Z",
          "confirmed_at": null
        }
      }
    },
    {
      "operation": "getBalance",
      "args": {"member": "01929c1e-0040-7040-8040-000000000040"},
      "expect": {"error": {"class": "server", "status": 504, "code": null, "requestId": null}}
    }
  ],
  "exchanges": [
    {
      "request": {
        "method": "GET",
        "url": "https://api.example.com/api/v1/receipts/01929c1e-0050-7050-8050-000000000050",
        "headers": {
          "authorization": "Bearer lk_test_k7m2p9q4r8s3.RqTvNAJirupwDOPZ8O48PZpsbGlZGfecp04FD7w06kw",
          "accept": "application/json",
          "content-type": null,
          "idempotency-key": null,
          "loyal-merchant": null
        }
      },
      "response": {
        "status": 502,
        "headers": {"Content-Type": "text/html"},
        "text": "<html><head><title>502 Bad Gateway</title></head><body><h1>502 Bad Gateway</h1></body></html>"
      }
    },
    {
      "request": {
        "method": "GET",
        "url": "https://api.example.com/api/v1/receipts/01929c1e-0050-7050-8050-000000000050",
        "headers": {
          "authorization": "Bearer lk_test_k7m2p9q4r8s3.RqTvNAJirupwDOPZ8O48PZpsbGlZGfecp04FD7w06kw",
          "accept": "application/json",
          "content-type": null,
          "idempotency-key": null,
          "loyal-merchant": null
        }
      },
      "response": {
        "status": 200,
        "headers": {
          "Content-Type": "application/json",
          "X-Request-Id": "01929c20-0001-7000-8000-000000000001",
          "Loyal-Environment": "sandbox"
        },
        "json": {
          "id": "01929c1e-0050-7050-8050-000000000050",
          "status": "confirmed",
          "number": "1042",
          "business_date": "2026-10-11",
          "purchased_at": "2026-10-11T14:59:58+03:00",
          "member": {
            "id": "01929c1e-0040-7040-8040-000000000040",
            "masked_phone": "+7 *** ***-00-01",
            "status": "active"
          },
          "points": {"point_type_id": "01929c1e-0011-7011-8011-000000000011", "precision": 0},
          "rules": {"snapshot_id": "01929c1e-0012-7012-8012-000000000012", "version": 3},
          "total": 100000,
          "discount": 0,
          "redeemed_money": 30000,
          "redeemed_points": 300,
          "points_earned": 35,
          "payable": 70000,
          "max_redeemable_points": 1000,
          "redemption_confirmation_required": false,
          "lines": [
            {
              "id": "1",
              "amount": 100000,
              "discount": 0,
              "redeemed_money": 30000,
              "redeemed_points": 300,
              "points_earned": 35,
              "payable": 70000,
              "returned_quantity": 0
            }
          ],
          "campaigns": [],
          "stamps": [],
          "explanation": [{"code": "accrual", "data": {"percent_bp": 500, "base": 70000, "points": 35}}],
          "reserved_until": null,
          "confirmed_at": "2026-10-11T12:00:01.250000Z"
        }
      }
    },
    {
      "request": {
        "method": "POST",
        "url": "https://api.example.com/api/v1/members:lookup",
        "headers": {
          "authorization": "Bearer lk_test_k7m2p9q4r8s3.RqTvNAJirupwDOPZ8O48PZpsbGlZGfecp04FD7w06kw",
          "accept": "application/json",
          "content-type": "application/json",
          "idempotency-key": null,
          "loyal-merchant": null
        },
        "json": {"phone": "+79000000001"}
      },
      "response": {
        "status": 503,
        "headers": {
          "Content-Type": "application/problem+json",
          "X-Request-Id": "01929c20-0002-7000-8000-000000000002",
          "Loyal-Environment": "sandbox",
          "Retry-After": "1"
        },
        "json": {
          "type": "urn:loyal:problem:http_503",
          "title": "Service Unavailable",
          "status": 503,
          "code": "http_503",
          "request_id": "01929c20-0002-7000-8000-000000000002"
        }
      }
    },
    {
      "request": {
        "method": "POST",
        "url": "https://api.example.com/api/v1/members:lookup",
        "headers": {
          "authorization": "Bearer lk_test_k7m2p9q4r8s3.RqTvNAJirupwDOPZ8O48PZpsbGlZGfecp04FD7w06kw",
          "accept": "application/json",
          "content-type": "application/json",
          "idempotency-key": null,
          "loyal-merchant": null
        },
        "json": {"phone": "+79000000001"}
      },
      "response": {
        "status": 200,
        "headers": {
          "Content-Type": "application/json",
          "X-Request-Id": "01929c20-0003-7000-8000-000000000003",
          "Loyal-Environment": "sandbox"
        },
        "json": {
          "id": "01929c1e-0040-7040-8040-000000000040",
          "status": "active",
          "masked_phone": "+7 *** ***-00-01",
          "first_name": "Анна",
          "registered_at": "2026-10-11T11:58:30.000000Z",
          "referral_code": "ANNA-7K2P",
          "balance": {
            "point_type_id": "01929c1e-0011-7011-8011-000000000011",
            "precision": 0,
            "available": 1035,
            "pending": 0,
            "held": 0,
            "spendable": 1035,
            "next_expiration": {"amount": 35, "at": "2027-10-11T12:00:01.250000Z"}
          },
          "tier": null,
          "stamps": []
        }
      }
    },
    {
      "request": {
        "method": "POST",
        "url": "https://api.example.com/api/v1/receipts",
        "headers": {
          "authorization": "Bearer lk_test_k7m2p9q4r8s3.RqTvNAJirupwDOPZ8O48PZpsbGlZGfecp04FD7w06kw",
          "accept": "application/json",
          "content-type": "application/json",
          "idempotency-key": "{{idempotency-key}}",
          "loyal-merchant": null
        },
        "json": {
          "number": "1043",
          "business_date": "2026-10-11",
          "purchased_at": "2026-10-11T14:59:58+03:00",
          "member": {"phone": "+79000000001"},
          "lines": [{"id": "1", "sku": "LATTE-M", "name": "Латте 300 мл", "quantity": 2000, "amount": 50000}],
          "cashier": "Иванова"
        }
      },
      "failure": "after_send"
    },
    {
      "request": {
        "method": "POST",
        "url": "https://api.example.com/api/v1/receipts",
        "headers": {
          "authorization": "Bearer lk_test_k7m2p9q4r8s3.RqTvNAJirupwDOPZ8O48PZpsbGlZGfecp04FD7w06kw",
          "accept": "application/json",
          "content-type": "application/json",
          "idempotency-key": "{{idempotency-key}}",
          "loyal-merchant": null
        },
        "json": {
          "number": "1043",
          "business_date": "2026-10-11",
          "purchased_at": "2026-10-11T14:59:58+03:00",
          "member": {"phone": "+79000000001"},
          "lines": [{"id": "1", "sku": "LATTE-M", "name": "Латте 300 мл", "quantity": 2000, "amount": 50000}],
          "cashier": "Иванова"
        }
      },
      "response": {
        "status": 201,
        "headers": {
          "Content-Type": "application/json",
          "X-Request-Id": "01929c20-0004-7000-8000-000000000004",
          "Loyal-Environment": "sandbox"
        },
        "json": {
          "id": "01929c1e-0051-7051-8051-000000000051",
          "status": "reserved",
          "number": "1043",
          "business_date": "2026-10-11",
          "purchased_at": "2026-10-11T14:59:58+03:00",
          "member": {
            "id": "01929c1e-0040-7040-8040-000000000040",
            "masked_phone": "+7 *** ***-00-01",
            "status": "active"
          },
          "points": {"point_type_id": "01929c1e-0011-7011-8011-000000000011", "precision": 0},
          "rules": {"snapshot_id": "01929c1e-0012-7012-8012-000000000012", "version": 3},
          "total": 50000,
          "discount": 0,
          "redeemed_money": 0,
          "redeemed_points": 0,
          "points_earned": 25,
          "payable": 50000,
          "max_redeemable_points": 1000,
          "redemption_confirmation_required": false,
          "lines": [
            {
              "id": "1",
              "amount": 50000,
              "discount": 0,
              "redeemed_money": 0,
              "redeemed_points": 0,
              "points_earned": 25,
              "payable": 50000,
              "returned_quantity": 0
            }
          ],
          "campaigns": [],
          "stamps": [],
          "explanation": [{"code": "accrual", "data": {"percent_bp": 500, "base": 50000, "points": 25}}],
          "reserved_until": "2026-10-11T12:29:58.000000Z",
          "confirmed_at": null
        }
      }
    },
    {
      "request": {
        "method": "GET",
        "url": "https://api.example.com/api/v1/members/01929c1e-0040-7040-8040-000000000040/balance",
        "headers": {
          "authorization": "Bearer lk_test_k7m2p9q4r8s3.RqTvNAJirupwDOPZ8O48PZpsbGlZGfecp04FD7w06kw",
          "accept": "application/json",
          "content-type": null,
          "idempotency-key": null,
          "loyal-merchant": null
        }
      },
      "response": {
        "status": 504,
        "headers": {"Content-Type": "text/html"},
        "text": "<html><head><title>504 Gateway Timeout</title></head><body><h1>504 Gateway Timeout</h1></body></html>"
      }
    },
    {
      "request": {
        "method": "GET",
        "url": "https://api.example.com/api/v1/members/01929c1e-0040-7040-8040-000000000040/balance",
        "headers": {
          "authorization": "Bearer lk_test_k7m2p9q4r8s3.RqTvNAJirupwDOPZ8O48PZpsbGlZGfecp04FD7w06kw",
          "accept": "application/json",
          "content-type": null,
          "idempotency-key": null,
          "loyal-merchant": null
        }
      },
      "response": {
        "status": 504,
        "headers": {"Content-Type": "text/html"},
        "text": "<html><head><title>504 Gateway Timeout</title></head><body><h1>504 Gateway Timeout</h1></body></html>"
      }
    },
    {
      "request": {
        "method": "GET",
        "url": "https://api.example.com/api/v1/members/01929c1e-0040-7040-8040-000000000040/balance",
        "headers": {
          "authorization": "Bearer lk_test_k7m2p9q4r8s3.RqTvNAJirupwDOPZ8O48PZpsbGlZGfecp04FD7w06kw",
          "accept": "application/json",
          "content-type": null,
          "idempotency-key": null,
          "loyal-merchant": null
        }
      },
      "response": {
        "status": 504,
        "headers": {"Content-Type": "text/html"},
        "text": "<html><head><title>504 Gateway Timeout</title></head><body><h1>504 Gateway Timeout</h1></body></html>"
      }
    }
  ],
  "sleeps": [500, 1000, 500, 500, 1000]
}
