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

03 merchant

MerchantEndpoint — инвойсы, NFC pull-charge, топап для merchant/agent.

Доступ

requireLogin = true (задекларировано на классе). Каждый метод дополнительно вызывает requireAccountType(session, [...]) — см. таблицу ниже.

Методы

Метод accountType HMAC ключ Возвращает Особенность
createInvoice(amount, currency, idempotencyKey, {note?, appScope?, posTerminalId?, ttlSeconds?}) merchant auth-center (default) InvoiceQrPayload Создаёт интент INVOICE_PAYMENT в PM + кодирует QR через InvoiceQrEncoder. QR-строка в поле qrPayload.
cancelInvoice(intentId) merchant auth-center IntentDto PM-вызов cancelIntent с reason='merchant_canceled'. Только свои инвойсы (actingUserId = merchantId).
getInvoiceStatus(intentId) merchant auth-center IntentDto Прямой PspHmacClient.getIntent без callPm().
chargeByNfc(token, counter, amount, currency, idempotencyKey, {lat?, lon?}) merchant PmKey.merchant (привилегированный) NfcChargeResult Отдельный HMAC-secret auth-center-merchant. Pull-charge с кошелька клиента на кошелёк мерчанта. Расчёт синхронный — ответ содержит терминальный статус.
createTopup(consumerPhone, amount, currency, idempotencyKey, {comment?}) agent auth-center IntentDto Агент пополняет баланс клиента-consumer наличными. Резолвит consumerPhone через хеш PII.
requestSettlement(agentUserId, amount, currency, idempotencyKey) merchant auth-center IntentDto Мерчант инициирует вывод баланса через агента (operationType=WITHDRAWAL). Агент должен иметь accountType=agent.

createInvoice — детали

  • appScope: 'a' (all apps) / 'w' (wallet only) / 'c' (closeloop only). Default 'a'.
  • InvoiceQrPayload содержит: intentId, merchantDisplayName (businessName → firstName → email), merchantTbAccountId, amountSatang, currency, expiresAt, appScope, signature, qrPayload.
  • Если кошелёк мерчанта не найден в v_user_tb_accountsCloseloopException(MERCHANT_WALLET_NOT_FOUND).

chargeByNfc — детали

Последовательность выполнения (6 шагов до PM-вызова):

  1. amount > 0 и currency непустой — fast-fail ДО списания счётчика метки.
  2. NfcTagService.resolveAndConsume(token, counter, idempotencyKey) — атомарное потребление касания.
  3. NfcTagService.assertNfcEnabled(customerUserId) — gate C8: NFC включён у владельца метки.
  4. NfcTagService.checkTagLimits(tag, amount) — per-tag лимиты. При превышении → FCM клиенту (nfcChargeRejectedLimit).
  5. Загрузка кошельков клиента и мерчанта из v_user_tb_accounts (обоих, по userId + currency).
  6. PM-вызов с PmKey.merchant, actingUserId = customerUserId (не merchantId!).

При успешном SETTLEDNfcTag.lastUsedAt обновляется best-effort (ошибка логируется, не прерывает ответ). При LIMIT_EXCEEDED от PM → FCM клиенту best-effort.

createTopup — детали

  • Резолвит consumerPhone через PiiHashService → хеш → user_profiles.phone.
  • Проверяет accountType == 'consumer' у получателя.
  • operationType = 'TOPUP', source = 'closeloop', flow = 'topup'.

Error codes (специфичные для endpoint)

Код Источник Когда
MERCHANT_WALLET_NOT_FOUND CloseloopException кошелёк мерчанта/агента не найден в v_user_tb_accounts
VALIDATION_ERROR CloseloopException amount <= 0 или пустой currency (fast-fail в chargeByNfc)
NFC_TAG_LIMIT_EXCEEDED CloseloopException per-tag лимит превышен
NFC_DISABLED CloseloopException NFC выключен оператором для клиента (gate C8)
LIMIT_EXCEEDED CloseloopException (от PM) глобальный PM-лимит