Pay.

Документация API

Платёжный микросервис: CRM создаёт заказ, получает ссылку на оплату, принимает server-callback после успеха. Деньги и статусы хранит Pay.

Base URL https://pay.potap.io

Authentication

Все методы (кроме webhook банка) требуют заголовки:

Header Description
X-Service Имя вызывающего сервиса, например crm
X-Secret Секрет Pay (SECRET)
X-Owner Публичный owner_id
X-Owner-Secret Пароль владельца
owner_id в JSON тела не передаётся — берётся из X-Owner.

Payment flow

CRM → POST /api/order/create → payment.url Клиент оплачивает у Точки Точка → webhook Pay → SuccessPayment Pay → POST callback_url (CRM) Браузер → success_url / fail_url

callback_url

Server → CRM. JSON об успешной оплате. Не редирект браузера.

success_url / fail_url

Браузер пользователя после формы Точки. У каждой CRM — свои URL.

Create Order + Payment

POST /api/order/create

Создаёт заказ и сразу платёж. В ответе — order, опционально subscription, и payment со ссылкой url.

Суммы в копейках (1 ₽ = 100).

Required body fields

Field Type Description
product_ids string[] ID версий продуктов
price int Сумма до скидок (коп.)
amount int Итог к оплате (коп.)
client_id string ID клиента в CRM
client_email string Email клиента
success_url string Redirect после успеха (обязателен для Tochka)
fail_url string Redirect после ошибки (обязателен для Tochka)

Optional

Field Default Description
currency RUB Валюта
provider Tochka Tochka | Robo
callback_url — Куда Pay шлёт результат оплаты
client_name / phone / address / comment — Данные клиента
utm, manager_id, parent_id, meta — Метаданные CRM
is_recurring false Подписка
subscription — Обязателен при is_recurring=true

subscription

Field Description
uuid ID подписки/сделки в CRM (уникален в рамках owner)
period 1 day | 2 week | 3 month | 4 quart | 5 year
amount Сумма регулярного списания (коп.)
next Дата следующего списания, RFC3339
Если subscription.uuid уже есть → subscription already exists, заказ не создаётся.

Example request

cURL
curl -s -X POST 'https://pay.potap.io/api/order/create' \
  -H 'Content-Type: application/json' \
  -H 'X-Service: crm' \
  -H 'X-Secret: YOUR_PAY_SECRET' \
  -H 'X-Owner: your_owner_id' \
  -H 'X-Owner-Secret: your_owner_secret' \
  -d '{
    "product_ids": ["prod_ver_1"],
    "price": 100,
    "amount": 100,
    "currency": "RUB",
    "client_id": "42",
    "client_email": "user@example.com",
    "callback_url": "https://crm.example.com/hooks/pay",
    "success_url": "https://crm.example.com/pay/success",
    "fail_url": "https://crm.example.com/pay/fail",
    "provider": "Tochka"
  }'

Example response

JSON
{
  "order": {
    "id": 15,
    "uuid": "ord_abc123",
    "owner_id": "your_owner_id",
    "client_id": "42",
    "amount": 100,
    "status": 1,
    "is_recurring": false
  },
  "payment": {
    "id": 3,
    "provider": "Tochka",
    "url": "https://merch.tochka.com/order/?uuid=...",
    "order_uuid": "ord_abc123",
    "amount": 100,
    "operation_id": "op_example_uuid",
    "status": 1
  }
}

CRM редиректит пользователя на payment.url.

Order status

status Meaning
1 создан
5 частично оплачен
7 оплачен
8 отменён
9 возврат

Callback

После успешной оплаты Pay делает POST на callback_url. До 5 попыток с backoff. Ошибка callback не откатывает оплату. CRM отвечает HTTP 2xx.

JSON body
{
  "event": "payment.succeeded",
  "owner_id": "your_owner_id",
  "order_uuid": "ord_abc123",
  "payment_uuid": "op_example_uuid",
  "client_id": "42",
  "amount": 100,
  "currency": "RUB",
  "provider": "Tochka",
  "is_recurring": true,
  "status": 7
}

Browser redirects

Field When
success_url Успешная оплата на стороне Точки
fail_url Ошибка / отмена

Передаются в CreateOrder каждой CRM отдельно. Pay прокидывает их в Точку как redirectUrl / failRedirectUrl.

List Orders

GET /api/orders
Query Default Description
limit 50 (max 100) Размер страницы
offset 0 Сдвиг
cURL
curl -s 'https://pay.potap.io/api/orders?limit=20&offset=0' \
  -H 'X-Service: crm' \
  -H 'X-Secret: YOUR_PAY_SECRET' \
  -H 'X-Owner: your_owner_id' \
  -H 'X-Owner-Secret: your_owner_secret'

Get Order + Payments

GET /api/order?uuid={order_uuid}
cURL
curl -s 'https://pay.potap.io/api/order?uuid=ord_abc123' \
  -H 'X-Service: crm' \
  -H 'X-Secret: YOUR_PAY_SECRET' \
  -H 'X-Owner: your_owner_id' \
  -H 'X-Owner-Secret: your_owner_secret'

Ответ: { "order": {...}, "payments": [...] }

Client Orders

GET /api/orders/client?client_id={client_id}
cURL
curl -s 'https://pay.potap.io/api/orders/client?client_id=42' \
  -H 'X-Service: crm' \
  -H 'X-Secret: YOUR_PAY_SECRET' \
  -H 'X-Owner: your_owner_id' \
  -H 'X-Owner-Secret: your_owner_secret'

Go client

Единый CallAPI + отдельные структуры запросов и ответов.

Go
func CallAPI(method, path string, req any, dest any) error {
  var body io.Reader
  if req != nil {
    b, err := json.Marshal(req)
    if err != nil { return err }
    body = bytes.NewReader(b)
  }
  httpReq, err := http.NewRequest(method, "https://pay.potap.io"+path, body)
  if err != nil { return err }
  if req != nil { httpReq.Header.Set("Content-Type", "application/json") }
  httpReq.Header.Set("X-Service", "crm")
  httpReq.Header.Set("X-Secret", paySecret)
  httpReq.Header.Set("X-Owner", ownerID)
  httpReq.Header.Set("X-Owner-Secret", ownerSecret)

  resp, err := (&http.Client{Timeout: 15 * time.Second}).Do(httpReq)
  if err != nil { return err }
  defer resp.Body.Close()
  raw, _ := io.ReadAll(resp.Body)
  if resp.StatusCode < 200 || resp.StatusCode >= 300 {
    return fmt.Errorf("pay status %d: %s", resp.StatusCode, raw)
  }
  if dest == nil || len(raw) == 0 { return nil }
  return json.Unmarshal(raw, dest)
}

type CreateOrderReq struct {
  ProductIDs  []string `json:"product_ids"`
  Price       int64    `json:"price"`
  Amount      int64    `json:"amount"`
  ClientID    string   `json:"client_id"`
  ClientEmail string   `json:"client_email"`
  CallbackURL string   `json:"callback_url"`
  SuccessURL  string   `json:"success_url"`
  FailURL     string   `json:"fail_url"`
  Provider    string   `json:"provider"`
}

type CreateOrderResp struct {
  Order   Order       `json:"order"`
  Payment PaymentInfo `json:"payment"`
}

func CreateOrder(req CreateOrderReq) (*CreateOrderResp, error) {
  var resp CreateOrderResp
  if err := CallAPI(http.MethodPost, "/api/order/create", req, &resp); err != nil {
    return nil, err
  }
  return &resp, nil
}

Полные структуры и CRM-методы — в readme/API.md.

Pay · API docs pay.potap.io