{
  "info": {
    "name": "i-Free Money Mock Service — Subscription Service (sandbox)",
    "description": "Песочница подписок.\n\nПодпись здесь **не такая**, как у разовых платежей: один проход HMAC-SHA256, ключ — SecretKey, сообщение — `SubscriptionProjectID + Now`. Логин в подпись не входит, секрет отдельный. Pre-request script считает её перед каждым запросом и кладёт в `{{now}}` и `{{signature}}`.\n\nДоступы по умолчанию — общие демо-доступы песочницы.\n\nВажно: песочница не хранит состояние подписок и возвращает новый случайный `SubscriptionID` в каждом ответе. Коллекция сохраняет тот, что пришёл из CreateSubscription, и подставляет его дальше — в бою так и надо, но здесь ответ ApproveSubscription придёт с другим id, и это нормально.\n\nИмпортируется в Insomnia как есть.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    {
      "key": "base_url",
      "value": "https://subs-sandbox.digitalspb.com",
      "type": "string"
    },
    {
      "key": "project_id",
      "value": "2001",
      "type": "string"
    },
    {
      "key": "secret",
      "value": "subs_secret_key",
      "type": "string"
    },
    {
      "key": "msisdn",
      "value": "79600000000",
      "type": "string"
    },
    {
      "key": "now",
      "value": "",
      "type": "string"
    },
    {
      "key": "signature",
      "value": "",
      "type": "string"
    },
    {
      "key": "subscription_id",
      "value": "",
      "type": "string"
    },
    {
      "key": "billing_operation_id",
      "value": ""
    },
    {
      "key": "widget_token",
      "value": ""
    }
  ],
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "// Подпись Subscription Service — ОДИН проход HMAC-SHA256:",
          "//   signature = HMAC_SHA256(SecretKey, SubscriptionProjectID + Now)",
          "// У разовых платежей схема другая (два прохода, с логином) — не перепутайте.",
          "const secret    = pm.collectionVariables.get('secret');",
          "const projectId = pm.collectionVariables.get('project_id');",
          "",
          "// Now — UTC в формате yyyyMMddHHmmss",
          "const now = new Date().toISOString().replace(/\\.\\d{3}Z$/, 'Z').replace(/[-:TZ]/g, '');",
          "",
          "// Внимание на порядок аргументов: у CryptoJS сначала сообщение, потом ключ.",
          "const signature = CryptoJS.HmacSHA256(projectId + now, secret).toString();",
          "",
          "pm.collectionVariables.set('now', now);",
          "pm.collectionVariables.set('signature', signature);"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "1. CreateSubscription",
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "const body = pm.response.text();",
              "pm.test('REQUEST_ACCEPTED', () => pm.expect(body).to.include('<StatusName>REQUEST_ACCEPTED</StatusName>'));",
              "const m = body.match(/<SubscriptionID>([^<]+)<\\/SubscriptionID>/);",
              "if (m) { pm.collectionVariables.set('subscription_id', m[1]); }"
            ]
          }
        }
      ],
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "text/xml; charset=utf-8"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "<CreateSubscriptionRequest>\n  <SubscriptionProjectID>{{project_id}}</SubscriptionProjectID>\n  <Subscriber_MSISDN>{{msisdn}}</Subscriber_MSISDN>\n  <Now>{{now}}</Now>\n  <Signature>{{signature}}</Signature>\n</CreateSubscriptionRequest>"
        },
        "url": {
          "raw": "{{base_url}}/Http/Services/CreateSubscription.aspx",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "Http",
            "Services",
            "CreateSubscription.aspx"
          ]
        },
        "description": "Через 15 секунд после этого запроса песочница сама пришлёт коллбек SubscriptionStatusNotification с EventType=APPROVED — независимо от того, вызовете ли вы ApproveSubscription."
      }
    },
    {
      "name": "2. ApproveSubscription",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "text/xml; charset=utf-8"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "<ApproveSubscriptionRequest>\n  <SubscriptionProjectID>{{project_id}}</SubscriptionProjectID>\n  <SubscriptionID>{{subscription_id}}</SubscriptionID>\n  <PinCode>1234</PinCode>\n  <Now>{{now}}</Now>\n  <Signature>{{signature}}</Signature>\n</ApproveSubscriptionRequest>"
        },
        "url": {
          "raw": "{{base_url}}/Http/Services/ApproveSubscription.aspx",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "Http",
            "Services",
            "ApproveSubscription.aspx"
          ]
        },
        "description": "PinCode песочница не проверяет — подойдёт любой. В ответе придёт другой SubscriptionID: состояние подписок здесь не хранится."
      }
    },
    {
      "name": "3. GetSubscriptionStatus",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "text/xml; charset=utf-8"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "<GetSubscriptionStatusRequest>\n  <SubscriptionProjectID>{{project_id}}</SubscriptionProjectID>\n  <SubscriptionID>{{subscription_id}}</SubscriptionID>\n  <Now>{{now}}</Now>\n  <Signature>{{signature}}</Signature>\n</GetSubscriptionStatusRequest>"
        },
        "url": {
          "raw": "{{base_url}}/Http/Services/GetSubscriptionStatus.aspx",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "Http",
            "Services",
            "GetSubscriptionStatus.aspx"
          ]
        },
        "description": "В песочнице всегда Active, PaidUntil — «сейчас + 30 дней», независимо от созданной подписки."
      }
    },
    {
      "name": "CreateSubscription — карта / СБП",
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "const body = pm.response.text();",
              "pm.test('REQUEST_ACCEPTED', () => pm.expect(body).to.include('<StatusName>REQUEST_ACCEPTED</StatusName>'));",
              "pm.test('AuthorizeUrl выдан', () => pm.expect(body).to.include('<AuthorizeUrl>'));",
              "const m = body.match(/<AuthorizeUrl>([^<]+)<\\/AuthorizeUrl>/);",
              "if (m) { pm.collectionVariables.set('authorize_url', m[1].trim()); console.log('Откройте: ' + m[1].trim()); }"
            ]
          }
        }
      ],
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "text/xml; charset=utf-8"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "<CreateSubscriptionRequest>\n  <SubscriptionProjectID>{{project_id}}</SubscriptionProjectID>\n  <CustomerType>Card</CustomerType>\n  <CustomerId>cust-{{$timestamp}}</CustomerId>\n  <Now>{{now}}</Now>\n  <Signature>{{signature}}</Signature>\n</CreateSubscriptionRequest>"
        },
        "url": {
          "raw": "{{base_url}}/Http/Services/CreateSubscription.aspx",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "Http",
            "Services",
            "CreateSubscription.aspx"
          ]
        },
        "description": "Подписка с оплатой банковской картой или через СБП. В ответе AuthorizeUrl — страница оплаты; для QR СБП добавьте к ней ?useSbpQr=True. ApproveSubscription в этом сценарии не вызывается: подписка активируется по факту оплаты. Отказ воспроизводится картой с последними 4 цифрами 0001…0005."
      }
    },
    {
      "name": "CreateSubscription — WAP (по ClientIP)",
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "const body = pm.response.text();",
              "pm.test('AUTH_PAGE', () => pm.expect(body).to.include('AUTH_PAGE'));",
              "const m = body.match(/<AuthorizeUrl>([^<]+)<\\/AuthorizeUrl>/);",
              "if (m) { pm.collectionVariables.set('authorize_url', m[1].trim()); }"
            ]
          }
        }
      ],
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "text/xml; charset=utf-8"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "<CreateSubscriptionRequest>\n  <SubscriptionProjectID>{{project_id}}</SubscriptionProjectID>\n  <ClientIP>80.70.60.50</ClientIP>\n  <Localization>ru-RU</Localization>\n  <Now>{{now}}</Now>\n  <Signature>{{signature}}</Signature>\n</CreateSubscriptionRequest>"
        },
        "url": {
          "raw": "{{base_url}}/Http/Services/CreateSubscription.aspx",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "Http",
            "Services",
            "CreateSubscription.aspx"
          ]
        },
        "description": "WAP-активация: номер абонента определяет шлюз оператора, партнёр его не передаёт. В ответе AuthorizeUrl — страница подтверждения; к ней можно дописать ?ReturnUrl=result.php. Номер выводится из ClientIP детерминированно, поэтому повторный запрос с тем же IP даёт ту же подписку."
      }
    },
    {
      "name": "CreateSubscription — цикл списаний",
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "const body = pm.response.text();",
              "pm.test('REQUEST_ACCEPTED', () => pm.expect(body).to.include('<StatusName>REQUEST_ACCEPTED</StatusName>'));",
              "const m = body.match(/<SubscriptionID>([^<]+)<\\/SubscriptionID>/);",
              "if (m) { pm.collectionVariables.set('subscription_id', m[1]); }"
            ]
          }
        }
      ],
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "text/xml; charset=utf-8"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "<CreateSubscriptionRequest>\n  <SubscriptionProjectID>{{project_id}}</SubscriptionProjectID>\n  <Subscriber_MSISDN>{{msisdn}}</Subscriber_MSISDN>\n  <Now>{{now}}</Now>\n  <Signature>{{signature}}</Signature>\n  <TestInfo>\n    <BillingPeriodSeconds>10</BillingPeriodSeconds>\n    <MaxIterations>3</MaxIterations>\n    <ExpectedBillingResult>Success</ExpectedBillingResult>\n    <BillingAmount>49.00</BillingAmount>\n  </TestInfo>\n</CreateSubscriptionRequest>"
        },
        "url": {
          "raw": "{{base_url}}/Http/Services/CreateSubscription.aspx",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "Http",
            "Services",
            "CreateSubscription.aspx"
          ]
        },
        "description": "Песочные ручки биллингового цикла: BillingPeriodSeconds включает регулярные списания и сжимает период до секунд, MaxIterations задаёт их число, ExpectedBillingResult (Success | Failed) — исход. После ApproveSubscription пойдут уведомления APPROVED_BILLING, последнее — FINISHED со статусом Closed. Проверяйте статус запросом GetSubscriptionStatus."
      }
    },
    {
      "name": "CancelSubscription",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "text/xml; charset=utf-8"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "<CancelSubscriptionRequest>\n  <SubscriptionProjectID>{{project_id}}</SubscriptionProjectID>\n  <SubscriptionID>{{subscription_id}}</SubscriptionID>\n  <Now>{{now}}</Now>\n  <Signature>{{signature}}</Signature>\n</CancelSubscriptionRequest>"
        },
        "url": {
          "raw": "{{base_url}}/Http/Services/CancelSubscription.aspx",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "Http",
            "Services",
            "CancelSubscription.aspx"
          ]
        }
      }
    },
    {
      "name": "ResendPinCode",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "text/xml; charset=utf-8"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "<ResendPinCodeRequest>\n  <SubscriptionProjectID>{{project_id}}</SubscriptionProjectID>\n  <SubscriptionID>{{subscription_id}}</SubscriptionID>\n  <Now>{{now}}</Now>\n  <Signature>{{signature}}</Signature>\n</ResendPinCodeRequest>"
        },
        "url": {
          "raw": "{{base_url}}/Http/Services/ResendPinCode.aspx",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "Http",
            "Services",
            "ResendPinCode.aspx"
          ]
        },
        "description": "SMS песочница не отправляет — метод отвечает заготовленным REQUEST_ACCEPTED."
      }
    },
    {
      "name": "GetSubscriptions",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "text/xml; charset=utf-8"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "<GetSubscriptionsRequest>\n  <SubscriptionProjectID>{{project_id}}</SubscriptionProjectID>\n  <Subscriber_MSISDN>{{msisdn}}</Subscriber_MSISDN>\n  <Now>{{now}}</Now>\n  <Signature>{{signature}}</Signature>\n</GetSubscriptionsRequest>"
        },
        "url": {
          "raw": "{{base_url}}/Http/Services/GetSubscriptions",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "Http",
            "Services",
            "GetSubscriptions"
          ]
        },
        "description": "Единственный метод сервиса **без** `.aspx` — так он объявлен платформой; адрес с `.aspx` отклоняется. Фильтр по абоненту необязателен, выдача ограничена 50 записями."
      }
    },
    {
      "name": "RefundBillingOperation",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "text/xml; charset=utf-8"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "<RefundBillingOperationRequest>\n  <SubscriptionProjectID>{{project_id}}</SubscriptionProjectID>\n  <BillingOperationID>{{billing_operation_id}}</BillingOperationID>\n  <Now>{{now}}</Now>\n  <Signature>{{signature}}</Signature>\n</RefundBillingOperationRequest>"
        },
        "url": {
          "raw": "{{base_url}}/Http/Services/RefundBillingOperation.aspx",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "Http",
            "Services",
            "RefundBillingOperation.aspx"
          ]
        },
        "description": "BillingOperationID приходит в уведомлении о списании. Состояния у операций песочница не хранит: проверяется форма идентификатора, возврат мгновенный."
      }
    },
    {
      "name": "Виджет: sbpWidgetCreateQr",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Authorization",
            "value": "Bearer {{widget_token}}"
          }
        ],
        "url": {
          "raw": "{{base_url}}/Cards/Sbp/create",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "Cards",
            "Sbp",
            "create"
          ]
        },
        "description": "Токен — значение из `AuthorizeUrl` ответа CreateSubscription (карта/СБП): `.../Cards/TB/{session}/Init`. `paid` у этого виджета означает привязку счёта, а не списание."
      }
    },
    {
      "name": "Виджет: sbpWidgetStatus",
      "request": {
        "method": "GET",
        "header": [
          {
            "key": "Authorization",
            "value": "Bearer {{widget_token}}"
          }
        ],
        "url": {
          "raw": "{{base_url}}/Cards/Sbp/status",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "Cards",
            "Sbp",
            "status"
          ]
        },
        "description": "Опрос статуса привязки счёта."
      }
    }
  ]
}
