# Mom Factura Payment API > API de pagamentos para Angola com Multicaixa Express, E-kwanza e Referencia Bancaria. Geracao automatica de faturas SAFT-AO. ## Base URL https://api.momenu.online ## Authentication All requests require the x-api-key header. Required headers: - Content-Type: application/json - x-api-key: Optional headers: - x-env-qa: true — QA test environment (any origin) - x-dev-mode: true — Bypass domain validation (localhost only) ## Endpoints ### POST /api/payment/mcx — Multicaixa Express (Immediate Payment) Creates order as PAID and generates invoice on success. Request body: - paymentInfo.amount (number, required) — Value in Kwanzas - paymentInfo.phoneNumber (string, required) — Format: 244XXXXXXXXX - products (array, optional) — Items for detailed invoice - products[].id (string), products[].productName (string), products[].productPrice (number), products[].productQuantity (number) - products[].iva (number, optional) — IVA rate 0-14, default 14 - customer (object, optional) — name (string), nif (string), phone (string) - simulateResult (string, optional) — QA only: success, insufficient_balance, timeout, rejected, invalid_number Success response (200): { "success": true, "transactionId": "abc123...", "invoiceUrl": "https://invoice-momenu.toquemedia.net/invoices/..." } ### POST /api/payment/ekwanza — E-kwanza (Deferred Payment) Creates order as OPEN, returns QR code. Confirmation via webhook (payment.confirmed + invoice.created events). Status polling available as fallback. Request body: - paymentInfo.amount (number, required) — Value in Kwanzas - paymentInfo.phoneNumber (string, required) — Client phone number - products (array, optional) — Same structure as MCX - customer (object, optional) — Same structure as MCX Success response (200): { "success": true, "merchantTransactionId": "17066400000000", "code": "EKZ123456", "qrCode": "data:image/png;base64,...", "expirationDate": "2024-01-15T12:00:00Z", "paymentTimeout": 180 } Use merchantTransactionId as query param when checking status: GET /api/payment/ekwanza/status/{code}?merchantTransactionId={merchantTransactionId} ### POST /api/payment/reference — Bank Reference (Deferred Payment) Generates bank reference for ATM/Internet Banking payment. Confirmation via webhook. Request body: - paymentInfo.amount (number, required) — Value in Kwanzas - products (array, optional) — Same structure as MCX - customer (object, optional) — Same structure as MCX Success response (200): { "success": true, "operationId": "op-123...", "transactionId": "17066400000000", "referenceNumber": "123456789", "entity": "12345", "dueDate": "2024-01-20" } Use transactionId as merchantTransactionId query param when checking status: GET /api/payment/reference/status/{operationId}?merchantTransactionId={transactionId} ### GET /api/payment/ekwanza/status/:code — E-kwanza Status (Fallback for webhook) Path params: code (string) — E-kwanza code from payment creation Query params: merchantTransactionId (string, recommended) — Transaction ID from payment creation, used for order lookup Response (pending): { "success": true, "status": "pending", "operationCode": "OP123..." } Response (paid): { "success": true, "status": "paid", "operationCode": "OP123...", "invoiceUrl": "https://invoice-momenu.toquemedia.net/invoices/..." } ### GET /api/payment/reference/status/:operationId — Reference Status (Fallback for webhook) Path params: operationId (string) — Operation ID from reference creation Query params: merchantTransactionId (string, recommended) — Transaction ID (transactionId) from reference creation, used for order lookup Response (pending): { "success": true, "payment": { "status": "pending", "message": "Aguardando pagamento" } } Response (paid): { "success": true, "payment": { "status": "paid", "message": "Pagamento confirmado" }, "invoiceUrl": "https://invoice-momenu.toquemedia.net/invoices/..." } ## Webhook (Payment Confirmation) Configure webhook URL at momenu.toquemedia.net > Desenvolvedores menu. Works for all deferred payment methods (Bank Reference and E-kwanza). When a payment is confirmed (operationStatus "1"), the API sends two sequential webhook events: ### Event 1: payment.confirmed Sent immediately after order status is updated to PAID (before invoice generation). Payload: { "event": "payment.confirmed", "merchantTransactionId": "abc123...", "ekwanzaTransactionId": "EKZ456...", "operationStatus": "1", "operationData": { ... } } ### Event 2: invoice.created Sent after invoice PDF is generated and uploaded. Includes the invoice download URL. Payload: { "event": "invoice.created", "merchantTransactionId": "abc123...", "ekwanzaTransactionId": "EKZ456...", "operationStatus": "1", "operationData": { ... }, "invoiceUrl": "https://invoice-momenu.toquemedia.net/invoices/..." } ### Non-paid events Events with operationStatus 3 (Cancelled), 4 (Failed), 5 (Error) are sent without the "event" field for backward compatibility. Payload: { "merchantTransactionId": "abc123...", "ekwanzaTransactionId": "EKZ456...", "operationStatus": "3", "operationData": { ... } } operationStatus values: "1" Paid, "3" Cancelled/Expired, "4" Failed/Refused, "5" Error Webhook is fire-and-forget (no retries). Use status endpoints as fallback. ## Amount Validation If both paymentInfo.amount and products are provided, they must match. Calculation: total = SUM(productPrice * productQuantity) Mismatch returns error code AMOUNT_MISMATCH. ## Error Codes All errors return: { "success": false, "error": "message", "code": "ERROR_CODE" } - MISSING_API_KEY — x-api-key header missing - INVALID_API_KEY — Key invalid or inactive - DOMAIN_NOT_ALLOWED — Origin not registered - INVALID_AMOUNT — Invalid payment amount - AMOUNT_MISMATCH — amount != SUM(products) - MISSING_PHONE — Phone required for MCX and E-kwanza - MISSING_RESTAURANT_ID — Merchant not identified - RATE_LIMIT_EXCEEDED — 100 req/min exceeded - PAYMENT_RATE_LIMIT_EXCEEDED — 20 payment req/min exceeded - INTERNAL_ERROR — Server error ## Rate Limiting - General: 100 requests/min per IP (all endpoints) - Payments: 20 requests/min per IP (POST only) - Minimum polling interval: 5 seconds (E-kwanza), 30 seconds (Reference) ## Fees 2% processing fee on all payments. feeAmount = totalAmount * 0.02 netAmount = totalAmount - feeAmount ## QA Test Environment Activate with header x-env-qa: true. Combine with x-dev-mode: true for local development. Use simulateResult field in MCX body: success, insufficient_balance, timeout, rejected, invalid_number. ## FATURAS STANDALONE (PRO) ### Criar Fatura POST /api/invoices Cria uma fatura independente (não vinculada a pagamento). Disponível apenas para contas Pro. Headers: - x-api-key: string (obrigatório) - Idempotency-Key: string (opcional) — chave gerada pelo cliente para evitar duplicação em retries; resposta cacheada é devolvida no segundo POST Body: - invoiceType: "FR" | "FT" | "PROFORMA" (obrigatório) - customer.name: string (obrigatório) - customer.nif: string (obrigatório) - customer.phone: string (opcional) - customer.address: string (opcional) - items: array (obrigatório, mínimo 1) - description: string (obrigatório) - quantity: number (obrigatório, > 0) - unitPrice: number (obrigatório) - iva: number (opcional, 0-14, default 14) - paymentMethod: string (obrigatório para FR/FT, opcional para PROFORMA) Valores: "Multicaixa Express", "E-Kwanza", "Referência Bancária", "Dinheiro", "Transferência Bancária" - notes: string (opcional) - discount: number (opcional) - format: "A4" | "C7" (opcional, default "A4"). PROFORMA é sempre A4. - validUntil: string ISO 8601 (opcional, apenas PROFORMA) - validityDays: number (opcional, apenas PROFORMA, default 30) Notas sobre PROFORMA: - Documento sem valor fiscal (sem hash SAFT, sem registo no SAFT-AO). - Numeração própria com prefixo "PRF-" e contador independente (proformaCounters). - Inclui watermark "PROFORMA" e disclaimer "Este documento não serve como factura". - Estado inicial "ACTIVE" e data de validade (30 dias por defeito). Resposta (201): { "success": true, "invoiceId": "uuid", "invoiceNumber": "FR-MOM2025-001", "invoiceType": "FR", "invoiceFile": "base64_pdf", "invoiceUrl": "https://...", "total": 120000, "format": "A4", "createdAt": "2025-01-15T10:30:00.000Z" } Resposta de Proforma (201): { "success": true, "invoiceId": "uuid", "invoiceNumber": "PRF-MOM2026-001", "invoiceType": "PROFORMA", "invoiceUrl": "https://...", "total": 250000, "format": "A4", "validUntil": "2026-05-21T...", "status": "ACTIVE", "createdAt": "2026-05-06T..." } Erro (403) - Conta não Pro: { "success": false, "error": "Esta funcionalidade requer uma conta Pro", "code": "PRO_ACCOUNT_REQUIRED", "currentPlan": "GRATIS" } Erro (403) - Plano Expirado: { "success": false, "error": "Plano Pro expirado. Renove para continuar a usar esta funcionalidade", "code": "PRO_PLAN_EXPIRED", "planExpiry": "2025-01-01T00:00:00.000Z" } ### Listar Faturas GET /api/invoices?limit=50&offset=0 Lista todas as faturas do comerciante (standalone FR/FT/PROFORMA e geradas por pagamentos). Resposta inclui validUntil/status quando o documento é proforma. Query Parameters: - limit: number (opcional, default 50, max 100) - offset: number (opcional, default 0) Resposta (200): { "success": true, "invoices": [ { "invoiceId": "uuid", "invoiceNumber": "FR-MOM2025-001", "invoiceType": "FR", "customerName": "João Silva", "customerNif": "123456789", "total": 120000, "createdAt": "2025-01-15T10:30:00.000Z", "invoiceUrl": "https://..." } ], "total": 1, "limit": 50, "offset": 0 } ### Buscar Fatura GET /api/invoices/:invoiceNumber Retorna os detalhes completos de uma fatura específica, incluindo o PDF codificado em Base64. Path Parameters: - invoiceNumber: string — número da fatura devolvido na criação (ex.: "FR-MOM2026-001") Resposta (200): { "success": true, "invoice": { "invoiceId": "uuid", "invoiceNumber": "FR-MOM2025-001", "invoiceType": "FR", "customer": { "name": "...", "nif": "...", "phone": "..." }, "items": [...], "paymentMethod": "Transferência Bancária", "total": 120000, "totalTaxes": 14736.84, "partialTotal": 105263.16, "createdAt": "2025-01-15T10:30:00.000Z", "invoiceFile": "base64_pdf", "invoiceUrl": "https://..." } } Erro (404): { "success": false, "error": "Fatura não encontrada", "code": "INVOICE_NOT_FOUND" } NOTAS IMPORTANTES: - Endpoint exclusivo para contas com plano Profissional ativo (não expirado) - Faturas standalone são independentes das faturas geradas automaticamente pelos pagamentos - O PDF pode ser emitido em formato A4 (padrão) ou C7 (talão compacto) - O total é calculado automaticamente: SUM(quantity * unitPrice) - discount - O IVA está incluído no preço unitário ## Agent Skills Install pre-built skills for LLM agents: npx skills add ithustle/momenu-skills --all Repository: https://github.com/ithustle/momenu-skills Available skills: - mom-factura-payments — Full payment integration - mom-factura-webhooks — Webhook and status polling - mom-factura-testing — QA environment and simulation ## Links - Documentation: https://api.momenu.online/docs - API JSON spec: https://api.momenu.online/api/docs - Skills repo: https://github.com/ithustle/momenu-skills