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 xps_live_...

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

Domínios autorizados (obrigatório)

A autenticação é por chave (Bearer) — não é preciso SDK nem assinatura de pedido. Em contrapartida, a API só aceita pagamentos de domínios que registes em Definições → Domínios.

Envia o storeUrl (o URL da tua loja) em cada /payments/init. O domínio tem de constar na lista, senão a API responde 403 DOMAIN_NOT_ALLOWED; sem storeUrl nenhum, responde 400 STORE_DOMAIN_REQUIRED. Remover um domínio da lista corta o acesso de imediato.

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 activa, ou domínio não autorizado (DOMAIN_NOT_ALLOWED)
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 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)
storeUrlstringSimURL da loja. O domínio tem de estar registado em Definições → Domínios, senão 403 DOMAIN_NOT_ALLOWED. (Se vier do browser, o Origin/Referer serve.)
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 }
  ],
  "storeUrl": "https://aminhaloja.pt"
}

Sem SDK, por REST directo — só a chave e o storeUrl:

curl -X POST https://app.xpend.pt/api/v1/payments/init \
  -H "Authorization: Bearer xps_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "ORDER-123",
    "amount": 49.90,
    "storeUrl": "https://aminhaloja.pt",
    "customer": { "name": "João Silva", "email": "joao@email.com", "phone": "912345678" },
    "items": [{ "id": "sku-1", "name": "Ténis", "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 Xpend. 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 Xpend 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" }
  ]
}