Skip to content

API Events

1. Inbound — IPN (handled by @nx/mq-pay)

Payment provider IPN endpoints are owned by @nx/mq-pay (registered as controllers when running in FULL or API mode). Payment package itself does not declare IPN routes.

ProviderIPN endpointAuth
VNPAY QR MMS(provider-specific)Provider signature verification (in MQ-Pay PaymentVerificationService)
VNPAY PhonePOS(provider-specific)Same
SYSTEMn/a (in-platform)n/a

Exact IPN paths are defined inside @nx/mq-pay provider registry; consult that package for routes.

2. Outbound — HTTP Webhooks

Subscribers register their endpoints via WebhookConfigController (REST). On each MQ-Pay state change, WebhookEventHandlerHelper queries active webhooks subscribed to the event type and dispatches via WebhookDispatcherService.

Filtering

WebhookEventHandlerHelper.handle() (src/helpers/webhook-event-handler/helper.ts:54-96):

  1. Skip dispatch for non-actionable events (TRANSACTION_CREATED, ATTEMPT_CREATED, ATTEMPT_SENT) — those are WS-only.
  2. Query WebhookConfigRepository.find({ status=ACTIVATED, eventTypes contains eventType }).
  3. For each matching webhook → WebhookDispatcherService.dispatch({ webhookConfig, eventType, payload }).

Event types

From @nx/mq-pay/src/common/events/index.ts:14-28.

EventPayload typeTriggered byWebhook dispatched
mq-pay:transaction.createdITransactionCreatedPayloadTransaction insert✗ (WS only)
mq-pay:transaction.settledITransactionSettledPayloadFull settlement
mq-pay:transaction.cancelledITransactionCancelledPayloadAuto/admin cancel
mq-pay:attempt.createdIAttemptCreatedPayloadAttempt insert✗ (WS only)
mq-pay:attempt.sentIAttemptSentPayloadProvider call success (QR available)✗ (WS only)
mq-pay:attempt.successIAttemptSuccessPayloadIPN confirmed
mq-pay:attempt.failedIAttemptFailedPayloadProvider call failed
mq-pay:attempt.expiredIAttemptExpiredPayloadQR expired without payment
mq-pay:attempt.cancelledIAttemptCancelledPayloadCancel cascade

Webhook payload shape

ts
{
  event: string,             // e.g. 'mq-pay:attempt.success'
  transaction?: TTransaction,
  attempt?: TPaymentAttempt,
  timestamp: ISO,
  source?: { id: string, type: string }, // typically SaleOrder/SaleCheck
}

Subscriber side (sale)

Sale's PaymentWebhookController receives these at POST /v1/api/sale/webhooks/payment. See sale/payments.md for the full handler logic.

3. Outbound — WebSocket

PaymentSocketEventService (src/services/payment-socket-event.service.ts).

TopicConstantTrigger
observation/payment/transactionPaymentWebSocketTopics.TRANSACTIONAny transaction event (*.created, *.settled, *.cancelled)
observation/payment/payment-attemptPaymentWebSocketTopics.ATTEMPTAny attempt event (*.created, *.sent, *.success, *.failed, *.expired, *.cancelled)

Room fanout

ts
// Transaction (PaymentWebSocketRooms.getTransactionRooms)
[
  'merchants/<merchantId>',
  'merchants/<merchantId>/transactions',
  'transactions/<transactionId>',
]

// Attempt (PaymentWebSocketRooms.getPaymentAttemptRooms)
[
  'merchants/<merchantId>',
  'merchants/<merchantId>/payment-attempts',
  'payment-attempts/<attemptId>',
  'transactions/<transactionId>/payment-attempts',  // when transactionId present
]

merchantId extracted from transaction.metadata.merchant.source.id or attempt.metadata.merchant.source.id.

4. BullMQ (internal — within MQ-Pay)

QueueTypeConcurrencySource
schedulerscheduler jobs (3 partitions: P01–P03)10 jobs / partition@nx/mq-pay/src/services/queue/...
confirmationconfirmation jobs (3 partitions)10 jobs / partitionsame

Queues are internal to @nx/mq-pay. Workers consume from these. Mode-conditional: FULL and WORKER modes spawn workers; API mode produces jobs but does not consume.

5. Idempotency & Ordering

SurfaceDeliveryRecovery
Webhook (HTTP)at-most-once per dispatch; retried on transport failureWebhookDispatcherService retry policy (default: timeoutMs=30000, maxRetries=3 from WebhookConfig.metadata)
WebSocketbest-effortclients refetch via REST on reconnect
BullMQat-least-onceper-job idempotency key in @nx/mq-pay

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.