Skip to content

ADR-0003. MQ-Pay payment events arrive via HTTP webhook, not polling

FieldValue
StatusAccepted
Date2026-02-15
Deciderssale-team, payment-team
Supersedes

Context

  • After a customer completes payment via MQ-Pay (which abstracts VNPay et al.), sale needs to update order status, allocation usage, customer points, and emit Kafka for downstream services.
  • Two ways for sale to learn the payment outcome:
    1. Poll MQ-Pay periodically.
    2. Webhook — MQ-Pay POSTs to sale.
  • Polling is wasteful and high-latency. Real-time POS UX needs instant status update.

Decision

MQ-Pay POSTs to POST /v1/api/sale/webhooks/payment with event types mq-pay:attempt.{success,failed,expired,cancelled} and mq-pay:transaction.{settled,cancelled}. Sale processes the event, transitions status, and emits KafkaTopics.PAYMENT_SUCCESS post-commit.

Sale does not poll MQ-Pay. The webhook is the only inbound payment signal.

The webhook endpoint has no authentication. Trust is enforced by Cilium network policy: only MQ-Pay can reach /webhooks/payment over the internal network.

Consequences

ProsCons
Real-time order status (sub-second after payment)Single point of failure — if webhook fails to reach sale, status is stuck
No wasteful pollingWebhook security depends on network policy, not auth header
MQ-Pay retries 5xx automaticallySale must be idempotent under retry (handler short-circuits if already at target status)
Decouples sale from payment provider quirksTrust boundary is implicit; new operators must understand it

Recovery

  • If webhook fails repeatedly, MQ-Pay queues the event and retries.
  • Operators can manually re-trigger redelivery via MQ-Pay admin.
  • Sale's idempotency means re-deliveries are safe.

Alternatives Considered

OptionProsConsWhy rejected
Poll MQ-Pay every N secondsPull model; sale controls timingWasteful; high latency; doesn't scale to thousands of ordersUX too slow for POS
Hybrid: webhook + safety-net poll for stuck ordersResilientAdds complexity; double-event riskCan be added later if webhook proves unreliable
Webhook with shared-secret authStronger securityOperational burden of secret rotation; MQ-Pay already in trusted networkNetwork policy is sufficient given current threat model

References

  • sale/src/common/webhook-types.ts:9 (PaymentWebhookEventTypes)
  • sale/src/controllers/payment-webhook/payment-webhook.controller.ts
  • sale/src/services/payment-webhook.service.ts (dispatcher)
  • sale/src/services/sale-order-payment-webhook.service.ts (event handlers)

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