Xpend

Referência da API

Endpoints, parâmetros, respostas e códigos de erro.

Base URL

https://app.xpend.pt/api/v1

Autenticação

Todas as chamadas requerem o header Authorization com a tua chave secreta.

Authorization: Bearer vps_live_...

A chave secreta é visível em /dashboard/settings/api-keys. Nunca a uses em código frontend.

Request Signing (obrigatório)

Além do Bearer, todas as chamadas a /api/v1/payments/* e /api/v1/shipments requerem um header adicional X-VP-Trace — uma assinatura HMAC-SHA256 do payload do pedido, para prevenir replay attacks e garantir integridade. Pedidos sem esta assinatura são rejeitados com 403 INTEGRATION_SIGNATURE_REQUIRED.

A forma correcta (e única suportada) de gerar esta assinatura é usar o SDK oficial. O SDK trata automaticamente da geração da assinatura, timestamp anti-replay, e context envelope. Chamadas HTTP directas (curl, Postman, fetch sem passar pelo SDK) não funcionarão — não tentes replicar o algoritmo de assinatura manualmente, é intencionalmente opaco e sujeito a mudança sem aviso.

Instala o SDK →

Formato do header (referência):

X-VP-Trace: <hmac12>.<base64payload>

A janela anti-replay é de 5 minutos — o timestamp no context envelope deve estar dentro dessa janela ou o request é rejeitado com SIGNATURE_EXPIRED.

Códigos de erro

CódigoSignificado
400Body inválido ou parâmetros em falta
401Header Authorization em falta ou chave inválida
403Conta não está activa OU assinatura em falta/inválida (INTEGRATION_SIGNATURE_REQUIRED, INVALID_SIGNATURE, SIGNATURE_EXPIRED)
404Recurso não encontrado (ex: transactionId inexistente)
429Rate limit excedido
500Erro interno do Xpend
502Erro a comunicar com a rede bancária

Formato dos erros:

{
  "error": "Missing required checkout data: customer.email, items",
  "fields": ["customer.email", "items"]
}

Rate limits

Pedidos POST em /api/v1/* têm rate-limit de 30 pedidos por minuto por IP. Acima deste limite recebes 429 Too Many Requests com o headerRetry-After em segundos. Faz backoff exponencial e tenta de novo.


POST /payments/init

Cria uma transacção pending. Devolve um transactionId para usar nos passos seguintes.

Chamada server-side apenas. Não existe widget Xpend no checkout do lojista. Recolhe customer e items no teu checkout e envia-os neste endpoint a partir do teu backend (SDK Node.js, PHP, plugin WooCommerce, etc.).

Envia customer e items em todos os pagamentos — mapeia os campos que o cliente preenche no checkout (nome, email, morada, produtos). Ficam guardados na transacção, alimentam o VorkRastreio e o registo interno de leads. Lojistas com vendas pagas anteriores ficam isentos (grandfather). Contas novas (0 vendas paid) exigem customer + items por defeito.

Request body

ParâmetroTipoObrigatórioDescrição
orderIdstringSimO teu identificador interno do pedido
amountnumberSimValor em EUR (positivo)
currencystringNãoDefault EUR (única suportada)
customerobjectRecomendadoname, email, phone, address, city, postalCode, country
shippingAddressobjectNãoAlternativa: line1, city, postalCode, country
itemsarrayRecomendado*Produtos: id, name, quantity, priceInCents (ou price em EUR)
storeUrlstringNãoURL da loja (compliance / domínio)
trackingParametersobjectNãoUTMs e clids: utm_source, utm_medium, utm_campaign, fbclid, gclid, ttclid, src, sck

* customer + items obrigatórios para contas sem vendas pagas. Lojistas com histórico de vendas paid ficam isentos. Rollback: XPEND_REQUIRE_CHECKOUT_DATA=false. Aliases aceites: customerName no topo, shippingAddress.line1, address_line1, zippostalCode.

{
  "orderId": "ORDER-123",
  "amount": 49.90,
  "customer": {
    "name": "João Silva",
    "email": "joao@email.com",
    "phone": "912345678",
    "address": "Rua das Flores 12",
    "city": "Lisboa",
    "postalCode": "1000-001",
    "country": "Portugal"
  },
  "items": [
    { "id": "sku-1", "name": "Ténis Running", "quantity": 1, "priceInCents": 4990 }
  ]
}

Response

{
  "transactionId": "txn_42",
  "amount": 49.90,
  "currency": "EUR"
}

POST /payments/mbway

Dispara uma notificação push MB WAY para o cliente.

Request body

ParâmetroTipoObrigatórioDescrição
transactionIdstringSimID devolvido por /init
phoneNumberstringSim9 dígitos, formato 9XXXXXXXX

Response

{
  "success": true,
  "message": "Notificação enviada. O cliente tem 4 minutos para confirmar."
}

POST /payments/multibanco

Gera uma referência multibanco (entidade + referência).

Request body

ParâmetroTipoObrigatórioDescrição
transactionIdstringSimID devolvido por /init

Response

{
  "entity": "24000",
  "reference": "123456789",
  "amount": 49.90,
  "expiresAt": "2026-05-13T14:32:00Z"
}

GET /payments/status

Consulta o estado actual de uma transacção já persistido na base de dados. Este endpoint não consulta o banco em tempo real nem confirma pagamentos — a confirmação vem do webhook SIBS. Usa-o como fallback de UX no checkout (polling) ou para consultas pontuais; a confirmação oficial deve ser o webhook payment.success.

Polling vs webhook: o polling lê a BD depois do webhook SIBS actualizar o estado. Não substitui o webhook — não marca pagamentos por si só.

Query params

ParâmetroTipoObrigatório
transactionIdstringSim

Response — MB WAY pago

{
  "transactionId": "txn_42",
  "externalOrderId": "ORDER-123",
  "status": "paid",
  "paymentMethod": "MBWAY",
  "amount": 49.90,
  "currency": "EUR",
  "paidAt": "2026-05-10T14:32:00Z"
}

Response — Multibanco pendente (com referência)

{
  "transactionId": "txn_42",
  "externalOrderId": "ORDER-123",
  "status": "pending",
  "paymentMethod": "REFERENCE",
  "amount": 49.90,
  "currency": "EUR",
  "paidAt": null,
  "mb": {
    "entity": "24000",
    "reference": "123456789",
    "expiresAt": "2026-05-13T14:32:00Z"
  }
}

O objecto mb só está presente quando paymentMethod = "REFERENCE" e a referência já foi gerada.

Estados possíveis: pending · paid · failed · cancelled

Nota: algumas vendas internamente liquidadas podem continuar a aparecer como pending na API do lojista (política interna de settlement). O webhook payment.success reflecte o estado visível ao lojista.


GET /payments/methods

Lista os métodos de pagamento activos na plataforma.

Response

{
  "methods": [
    { "id": "MBWAY", "name": "MB WAY", "active": true },
    { "id": "REFERENCE", "name": "Referência Multibanco", "active": true },
    { "id": "CARD", "name": "Cartão", "active": false, "reason": "Em breve" }
  ]
}

POST /shipments · GET /shipments

API VorkRastreio — cria e consulta envios. Requer vorkrastreiosEnabled na conta (contacta suporte@xpend.pt para activar).

POST /shipments — Request body

ParâmetroTipoObrigatórioDescrição
orderIdstringSimID do pedido na tua loja
carrierIdnumberNãoTransportadora (default da conta)
shippingFeenumberNãoPortes em EUR
customerobjectNãoname, email, phone
shippingAddressobjectNãoMorada de entrega
itemsarrayNãoProdutos do envio

POST /shipments — Response

{
  "shipmentId": "shp_123",
  "trackingToken": "abc...",
  "trackingCode": "VR123456",
  "trackingUrl": "https://app.xpend.pt/track/abc...",
  "carrier": { "id": 1, "name": "CTT", "logoUrl": "..." },
  "shippingFee": 4.99,
  "estimatedDelivery": { "min": "2026-07-15T00:00:00Z", "max": "2026-07-20T00:00:00Z" }
}

GET /shipments

Query: ?orderId=ORDER-123 ou ?shipmentId=shp_123