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