Перейти к содержанию

Сценарии работы с POS системой

Описание пошаговых сценариев работы кассовой системы с API Sagi для интеграции бонусной программы.


Визуальные схемы интеграции

Ниже представлены ключевые сценарии интеграции в виде диаграмм последовательности с указанием конкретных API-вызовов.

Начисление бонусов (без списания)

sequenceDiagram
    participant POS as Касса (POS)
    participant API as Sagi API

    POS->>API: GET /api/v1/promoters/find?phone={phone}&branch_id={id}
    API-->>POS: { id, first_name, tags, ... }

    POS->>API: GET /api/v1/branches/{id}/private/balance?user_id={id}&group_id={id}
    API-->>POS: 2500.50

    POS->>API: GET /api/v1/branches/{id}/cashback?user_id={id}
    API-->>POS: 5.0

    Note over POS: Кассир завершает оплату

    POS->>API: POST /api/v1/branches/{id}/cash/order
    Note right of POS: { user_id, amount, bonus_amount: 0 }
    API-->>POS: { id: order_id, status: "CREATED" }

    POS->>API: POST /api/v1/orders/{order_id}/cash/complete
    Note right of POS: { use_balance: false, payment_method: "card" }
    API-->>POS: 200 OK

    Note over API: Кэшбек начислен на баланс клиента

Списание бонусов с кодом подтверждения

sequenceDiagram
    participant POS as Касса (POS)
    participant API as Sagi API
    participant Кл as Клиент (SMS)

    POS->>API: GET /api/v1/promoters/find?phone={phone}&branch_id={id}
    API-->>POS: { id, send_code_for_use_cashback: true, ... }

    POS->>API: GET /api/v1/branches/{id}/private/balance?user_id={id}&group_id={id}
    API-->>POS: 2500.50

    Note over POS: Кассир выбирает "списать 1000 бонусов"

    POS->>API: POST /api/v1/customers/{user_id}/send-use-cashback-code
    Note right of POS: { branch_id }
    API->>Кл: SMS: "Ваш код: 1234"
    API-->>POS: "1234"

    Note over POS: Кассир вводит код от клиента

    POS->>API: POST /api/v1/branches/{id}/cash/order
    Note right of POS: { user_id, amount: 2000,<br/>bonus_amount: 1000,<br/>use_balance_code: "1234" }
    API-->>POS: { id: order_id }

    POS->>API: POST /api/v1/orders/{order_id}/cash/complete
    Note right of POS: { use_balance: true, amount: 1000,<br/>payment_method: "mixed" }
    API-->>POS: 200 OK

Выдача награды (штампы)

sequenceDiagram
    participant POS as Касса (POS)
    participant API as Sagi API

    POS->>API: GET /api/v1/branches/{id}/customers/{user_id}/award
    API-->>POS: { stamp_count: 10, received_stamp_count: 10, is_active: true }

    Note over POS: Штампов достаточно — показать "Выдать награду"

    POS->>API: POST /api/v1/branches/{id}/cash/order
    Note right of POS: { user_id, amount: 2000,<br/>give_award: true }
    API-->>POS: { id: order_id }

    POS->>API: POST /api/v1/branches/{id}/awards/{award_id}/give
    Note right of POS: { user_id, stamp_count: 10 }
    API-->>POS: { received_stamp_count: 0, total: 4 }

    POS->>API: POST /api/v1/orders/{order_id}/cash/complete
    Note right of POS: { use_balance: false, payment_method: "card" }
    API-->>POS: 200 OK

    Note over API: Штампы сброшены, награда выдана

Первоначальная настройка (выполняется один раз)

1. Подключение приложения к Sagi

  1. Авторизация: Запросите логин и пароль у бизнеса, выполните запрос к /api/v1/auth/business
  2. Получение филиала: Из ответа авторизации сохраните первый элемент массива branches как branch_id
  3. Получение Group ID: Вызовите /api/v1/branches/{branch_id} для получения group_id филиала
  4. Настройка товара-подарка: Для каждого заведения настройте товар-подарок (нулевой стоимости) для программы наград
  5. Автоматическое обновление токенов: Настройте cron-задачу для вызова /api/v1/auth/refresh_token

Что сохранить в памяти: - access_token - JWT токен - branch_id - ID филиала - group_id - Group ID филиала
- gift_product_id - ID товара-подарка - role - роль пользователя (для определения прав доступа)


Сценарий 1: Начисление бонусов (без списания)

Начало работы смены

При запуске кассы проверьте: 1. Наличие действительного токена (если истек - обновите) 2. Статус программы наград в филиале (проверьте configuration.award.enabled)

При переходе к оплате чека

Шаг 1: Запрос телефона клиента

Кассир вводит номер телефона клиента в формате +77XXXXXXXXX

Шаг 2: Поиск клиента и получение информации

# 1. Найти клиента
GET /api/v1/promoters/find?phone={phone}&branch_id={branch_id}

# 2. Получить баланс в филиале  
GET /api/v1/branches/{branch_id}/private/balance?user_id={user_id}&group_id={group_id}

# 3. Получить процент кешбека
GET /api/v1/branches/{branch_id}/cashback?user_id={user_id}

# 4. Получить информацию о штампах (если награды включены)
GET /api/v1/branches/{branch_id}/customers/{user_id}/award

Шаг 3: Отображение информации кассиру

╔══════════════════════════════════════╗
║ Клиент: Иван Иванов                 ║
║ Телефон: +77071234567                ║
║ Баланс бонусов: 2,500 тенге          ║
║ Кешбек: 5% (макс. 100 тенге)         ║
║ Штампы: 7 из 10 для награды          ║
║                                      ║
║ □ Начислить штампики [1] шт          ║
║                                      ║
║ Покупка: 2,000 тенге                 ║
║ Будет начислено: 100 тенге           ║
╚══════════════════════════════════════╝

Шаг 4: Обработка штампиков

  • Если received_stamp_count == stamp_count → показать кнопку "Выдать награду"
  • Иначе → показать checkbox "начислить штампики" с полем ввода количества
  • При выборе начисления штампиков проверить: текущие + начисляемые >= требуемых → показать кнопку "Выдать награду"

После завершения оплаты

Шаг 5: Создание транзакции

POST /api/v1/branches/{branch_id}/cash/order
{
  "user_id": 1460220481801031680,
  "amount": 2000,
  "bonus_amount": 0,
  "comment": "Покупка в кассе #1", 
  "user_phone": "+77077000087",
  "add_stamp": true,
  "stamp_count": 1,
  "give_award": false
}

Шаг 6: Завершение транзакции

POST /api/v1/orders/{order_id}/cash/complete
{
  "use_balance": false,
  "amount": 0,
  "payment_method": "card"
}

Информация в чеке

════════════════════════════════════════
           КАФЕ "АРОМАТ"
════════════════════════════════════════
Товары:                        2,000 тг
────────────────────────────────────────
ИТОГО К ОПЛАТЕ:                2,000 тг
Оплачено картой:               2,000 тг
════════════════════════════════════════
БОНУСНАЯ ПРОГРАММА SAGI:
• Начислены бонусы:              100 тг  
• Добавлено штампиков:               1
• Штампов накоплено:               8/10
════════════════════════════════════════

Сценарий 2: Списание бонусов с кодом подтверждения

Шаги 1-3: Аналогично сценарию 1

Шаг 4: Отображение с возможностью списания

╔══════════════════════════════════════╗
║ Клиент: Иван Иванов                 ║
║ Баланс бонусов: 2,500 тенге          ║
║                                      ║
║ Списать бонусы: [1000] тенге         ║
║ Максимально: 2,500 тенге             ║
║                                      ║
║ □ Начислить штампики [1] шт          ║
║                                      ║
║ К доплате: 1,000 тенге               ║
╚══════════════════════════════════════╝

Шаг 5: Отправка кода подтверждения (если требуется)

# Если send_code_for_use_cashback = true и bonus_amount > 0
POST /api/v1/customers/{user_id}/send-use-cashback-code
{
  "branch_id": 1234567890123456  
}
╔══════════════════════════════════════╗
║ Код подтверждения отправлен клиенту  ║
║ на номер +77071234567                ║
║                                      ║
║ Введите код: [______]                ║
╚══════════════════════════════════════╝

Шаг 6: Создание транзакции с кодом

POST /api/v1/branches/{branch_id}/cash/order
{
  "user_id": 1460220481801031680,
  "amount": 2000,
  "bonus_amount": 1000,
  "comment": "Покупка в кассе #1",
  "user_phone": "+77077000087", 
  "add_stamp": true,
  "stamp_count": 1,
  "give_award": false,
  "use_balance_code": "123456"
}

Шаг 7: Завершение со списанием

POST /api/v1/orders/{order_id}/cash/complete
{
  "use_balance": true,
  "amount": 1000,
  "payment_method": "mixed"
}

Информация в чеке при списании

════════════════════════════════════════
           КАФЕ "АРОМАТ"  
════════════════════════════════════════
Товары:                        2,000 тг
────────────────────────────────────────
Списано бонусов:              -1,000 тг
К доплате:                     1,000 тг
Оплачено картой:               1,000 тг
════════════════════════════════════════
БОНУСНАЯ ПРОГРАММА SAGI:
• Списано бонусов:             1,000 тг
• Начислены бонусы:               50 тг
• Добавлено штампиков:               1
════════════════════════════════════════

Сценарий 3: Выдача награды

При достаточном количестве штампов

Отображение кнопки "Выдать награду"

╔══════════════════════════════════════╗
║ Клиент: Иван Иванов                 ║
║ Штампы: 10 из 10 (готов)             ║
║                                      ║
║ [ВЫДАТЬ НАГРАДУ]                     ║
║    "Бесплатный кофе"                 ║
║                                      ║
║ Покупка: 2,000 тенге                 ║
╚══════════════════════════════════════╝

При выдаче награды

# 1. Добавить товар-подарок в чек автоматически
# Товар с нулевой стоимостью

# 2. Создать транзакцию с выдачей награды
POST /api/v1/branches/{branch_id}/cash/order
{
  "user_id": 1460220481801031680,
  "amount": 2000,
  "bonus_amount": 0,
  "comment": "Покупка + награда",
  "user_phone": "+77077000087",
  "add_stamp": false,
  "stamp_count": 0,
  "give_award": true
}

# 3. Выдать награду
POST /api/v1/branches/{branch_id}/awards/{award_id}/give
{
  "user_id": 1460220481801031680,
  "stamp_count": 10
}

# 4. Завершить транзакцию  
POST /api/v1/orders/{order_id}/cash/complete
{
  "use_balance": false,
  "amount": 0,
  "payment_method": "card"
}

Чек с выдачей награды

════════════════════════════════════════
           КАФЕ "АРОМАТ"
════════════════════════════════════════
Товары:                        2,000 тг
ПОДАРОК: Кофе американо            0 тг  
────────────────────────────────────────
ИТОГО К ОПЛАТЕ:                2,000 тг
Оплачено картой:               2,000 тг
════════════════════════════════════════
БОНУСНАЯ ПРОГРАММА SAGI:
• Начислены бонусы:              100 тг
• Выдана НАГРАДА за 10 штампов!
• Штампов осталось:              0/10
════════════════════════════════════════

Сценарий 4: Обработка ошибок

При неверном коде подтверждения

HTTP 400 Bad Request

{
  "code": 3025,
  "message": "code for using private balance is wrong"
}
Действие: Показать сообщение "Неверный код. Попробуйте еще раз" и повторить ввод.

При недоступности API

╔══════════════════════════════════════╗
║ ВНИМАНИЕ                             ║
║                                      ║
║ Сервис бонусов временно недоступен   ║
║ Транзакция проведена БЕЗ начисления  ║
║ бонусов                              ║  
║                                      ║
║ Обратитесь в службу поддержки        ║
╚══════════════════════════════════════╝

При ошибке настройки подарка

╔══════════════════════════════════════╗
║ Товар-подарок не настроен            ║
║                                      ║
║ Обратитесь к администратору          ║
║ для настройки программы наград       ║
╚══════════════════════════════════════╝

Сценарий 5: Отмена и возвраты

Полная отмена транзакции

POST /api/v1/orders/{order_id}/revert-transaction
# Возвращает все начисленные бонусы и штампы

Частичный возврат

PATCH /api/v1/orders/{order_id}/refund
{
  "amount": 500    # Возврат 500 тенге из 2000
}
# Возвращает пропорциональные бонусы, штампы остаются

Особенности реализации

Проверки перед началом работы

  1. Токен валиден: Проверять срок действия токена при запуске смены
  2. Настройки филиала: Загружать настройки наград (configuration.award.enabled)
  3. Товар-подарок: Проверять наличие настроенного товара для наград

Логирование для аудита

[2024-03-15 14:30:00] USER_SEARCH: phone=+77071234567, found=true, user_id=1460220481801031680
[2024-03-15 14:30:15] BONUS_TRANSACTION: order_id=60f1b2b3c4d5e6f7g8h9i0j1, amount=2000, bonus_used=0, cashback=100
[2024-03-15 14:30:20] STAMP_ADDED: user_id=1460220481801031680, stamps=1, total=8/10
[2024-03-15 14:30:25] TRANSACTION_COMPLETE: order_id=60f1b2b3c4d5e6f7g8h9i0j1, status=completed

Безопасность

  • Никогда не логировать коды подтверждения
  • Хранить токены в безопасном месте
  • Очищать поля ввода кода после использования
  • Ограничить время ожидания кода подтверждения (5 минут)

Производительность

  • Кешировать информацию о филиале (group_id, настройки)
  • Использовать локальное хранение для токенов
  • Реализовать повторные попытки для критичных операций
  • Показывать индикаторы загрузки для API запросов

Сценарий 6: Управление токенами авторизации

Проактивное обновление токенов

Настройка автоматического обновления

# Пример cron-задачи (каждые 30 минут)
*/30 * * * * /path/to/refresh_token_script.sh

Скрипт обновления токена

#!/bin/bash
CURRENT_TOKEN="your_current_token"
API_BASE="https://gateway.sagi.kz"

# Попытка обновления токена
RESPONSE=$(curl -s -w "%{http_code}" -X POST "$API_BASE/api/v1/auth/refresh_token" \
  -H "Authorization: Bearer $CURRENT_TOKEN" \
  -o /tmp/refresh_response.json)

HTTP_CODE="${RESPONSE: -3}"

if [ "$HTTP_CODE" -eq 200 ]; then
    # Успешное обновление - сохранить новый токен
    NEW_TOKEN=$(jq -r '.token' /tmp/refresh_response.json)
    echo "$NEW_TOKEN" > /secure/path/token_storage
    logger "Token refreshed successfully"
else
    # Ошибка обновления - требуется повторная авторизация
    logger "Token refresh failed with code $HTTP_CODE - manual re-authentication required"
    # Уведомить администратора
fi

Обработка истечения токена в POS

Проверка токена перед критическими операциями

╔══════════════════════════════════════╗
║ Проверка авторизации...               ║
║                                      ║
║ Токен истек или недействителен        ║
║                                      ║
║ [Обновить токен]  [Войти заново]      ║
╚══════════════════════════════════════╝

Автоматическое обновление при ошибке 401

# При получении HTTP 401 от любого API:
POST /api/v1/auth/refresh_token
# Если успешно → повторить исходный запрос
# Если ошибка → показать форму авторизации

Безопасность токенов

Хранение токенов

  • Зашифрованное хранилище: Не сохранять токены в открытом виде
  • Ограниченное время жизни: Срок действия токена — 365 дней, рекомендуется обновлять раз в сутки
  • Ротация: Автоматическое обновление предотвращает компрометацию

Мониторинг токенов

[2024-03-15 09:00:00] TOKEN_REFRESH: success, expires_at=2024-03-16T09:00:00Z
[2024-03-15 09:30:00] TOKEN_REFRESH: success, expires_at=2024-03-16T09:30:00Z
[2024-03-15 10:00:00] TOKEN_REFRESH: failed, code=401, action=re-auth_required

Сценарий 7: Работа с процентами кешбека

Динамический расчет кешбека

Получение актуального процента

# Перед каждой транзакцией проверять актуальный процент
GET /api/v1/branches/{branch_id}/cashback?user_id={user_id}&type=direct

Отображение информации о кешбеке

╔══════════════════════════════════════╗
║ Клиент: Иван Иванов                 ║
║ Кешбек: 5% (макс. 500 тенге за день) ║
║                                      ║
║ Условия кешбека:                     ║
║ • Мин. сумма покупки: 1000 тенге     ║
║ • Лимит транзакций: 5 в день         ║
║ • Использовано сегодня: 2/5           ║
╚══════════════════════════════════════╝

Обработка ограничений кешбека

Превышение дневного лимита

╔══════════════════════════════════════╗
║ ВНИМАНИЕ                             ║
║                                      ║
║ Клиент превысил дневной лимит         ║
║ транзакций с кешбеком                ║
║                                      ║
║ Кешбек по этой покупке: 0%           ║
║ Лимит восстановится завтра           ║
╚══════════════════════════════════════╝

Клиент в черном списке

# Ответ API при клиенте в черном списке
HTTP 400 Bad Request
{
  "code": 3010,
  "message": "phone number is in blacklist"
}

Градационный кешбек

Расчет по сумме покупок

  • 0-5,000 тенге/месяц: 3% кешбек
  • 5,001-15,000 тенге/месяц: 5% кешбек
  • 15,001+ тенге/месяц: 7% кешбек

Расчет по количеству транзакций

  • 1-10 покупок/месяц: базовый процент
  • 11-25 покупок/месяц: +1% к базовому
  • 26+ покупок/месяц: +2% к базовому

ПРИМЕЧАНИЕ: Приведенные выше суммы и проценты являются примерами. Все параметры градационного кешбека (пороги сумм, проценты, периоды) настраиваются индивидуально для каждого бизнеса в личном кабинете администратора.