Entrar no painel
Índice

(12) Referência

Referência da API

Todas as rotas, com os campos, as respostas e os exemplos, geradas da especificação OpenAPI.

Sobre esta referência

Gerada de contratos/openapi.yaml, a especificação OpenAPI 3.1.0 da API, versão 2026-10-16. É a mesma especificação que os testes da API confrontam com as rotas registadas, por isso o que está aqui é o que a API faz. Os exemplos usam https://payments-api.spolly.ao; os servidores são:

  • https://payments-api.spolly.ao: Produção. Um nome de um nível, coberto pelo certificado universal da Cloudflare (ADR 0005)
  • http://localhost:8084: Desenvolvimento

O Spolly Payments é só checkout alojado: um produto cria uma sessão, manda o comprador para checkout.payments.spolly.ao, e recebe um webhook assinado quando o pagamento entra. Os eventos estão descritos em contratos/eventos/.

Este documento cresce com os cartões: cada rota entra aqui no mesmo commit em que entra na API, e a verificação do cartão 4.1 confronta-o com as rotas registadas (TestAsRotasV1BatemComAOpenAPI, na api). Hoje existem a sonda de saúde (cartão 0.2), a conta da chave (cartão 2.4), a sessão de checkout (cartão 4.1), os estornos (cartão 7.1) e as subscrições (cartões 8.1 e 8.4).

Todos os POST de /v1 levam o cabeçalho Idempotency-Key: repetir o pedido com a mesma chave devolve a mesma resposta (com Idempotent-Replayed: true) e não cria nada outra vez. O modo (teste ou produção) é o da chave.

Dinheiro viaja sempre em unidades menores inteiras, e erros e mensagens em português europeu.

Os eventos que a API envia aos teus endpoints estão na referência dos eventos.

Operação

Sondas para quem opera o serviço

GET/saudesem chave

O serviço está vivo, e a base de dados responde?

Responde 200 só quando a base de dados respondeu dentro do tempo limite. Com a base em baixo responde 503, e diz que falhou: uma leitura falhada nunca se apresenta como uma resposta legítima.

Exemplo

Terminal
curl -s "https://payments-api.spolly.ao/saude"

Respostas

  • 200

    Vivo, e a base responde.

    estadotexto · obrigatório
    um de: ok, degradado
    versaotexto · obrigatório
    A versão da construção, ou dev em desenvolvimento.
    committexto · obrigatório
    O commit da construção, ou dev em desenvolvimento.
    basetexto · obrigatório
    um de: ok, falhou
    JSON
    {
      "estado": "ok",
      "versao": "v0.1.0",
      "commit": "3f2a1b9",
      "base": "ok"
    }
  • 503

    Vivo, mas a base não responde.

    estadotexto · obrigatório
    um de: ok, degradado
    versaotexto · obrigatório
    A versão da construção, ou dev em desenvolvimento.
    committexto · obrigatório
    O commit da construção, ou dev em desenvolvimento.
    basetexto · obrigatório
    um de: ok, falhou
    JSON
    {
      "estado": "degradado",
      "versao": "v0.1.0",
      "commit": "3f2a1b9",
      "base": "falhou"
    }

Conta

A conta a que a chave de API pertence

GET/v1/conta

De que comerciante e de que modo é esta chave?

A primeira coisa a chamar depois de configurar uma chave: confirma que uma sk_test_ não foi parar a um servidor de produção, e o contrário. Não devolve dados do KYC nem a conta bancária.

Exemplo

Terminal
curl -s "https://payments-api.spolly.ao/v1/conta" \
  -H "Authorization: Bearer $SPOLLY_SECRET_KEY"

Respostas

  • 200

    A chave é válida.

    merchant_idtexto · obrigatório
    padrão ^mer_
    nome_comercialtexto · obrigatório
    modotexto · obrigatório
    um de: test, live
    JSON
    {
      "merchant_id": "mer_01JA9SALUTO000000",
      "nome_comercial": "Saluto",
      "modo": "test"
    }
  • 401

    Sem chave, ou com uma chave revogada, expirada ou desconhecida.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "chave_invalida",
      "mensagem": "A chave de API não é válida."
    }
  • 503

    Não foi possível validar a chave agora (a base não respondeu). Repetir mais tarde.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "indisponivel",
      "mensagem": "Não foi possível validar a chave. Tenta de novo."
    }

Sessões de checkout

A sessão de checkout: o produto cria-a e manda o comprador para o url. O regresso ao success_url não prova pagamento: confirma-se pelo webhook payment.succeeded, ou pelo GET da sessão.

POST/v1/checkout/sessions

Criar uma sessão de checkout

Devolve a sessão com o url do checkout alojado, para onde se manda o comprador. Sem payment_methods, aceita todos os que têm gateway na moeda e no modo da chave (AOA: mcx, reference, baipaga, card; USD e EUR: só card). Uma referência emitida no checkout deixa de se poder pagar quando a sessão expira (expires_at), e nunca depois.

Confirmar o pagamento: pelo webhook, e não pelo regresso

Quando o comprador paga, o checkout manda-o para o success_url com session_id acrescentado. Esse regresso não prova nada: qualquer pessoa escreve aquele endereço no browser, o comprador pode fechar a página antes de lá chegar, e uma referência paga no multibanco horas depois nem sequer passa pelo browser. A encomenda dá-se por paga em dois sítios, e só nestes:

  1. No webhook payment.succeeded, assinado (Spolly-Signature), que chega ao endpoint configurado no painel. É o caminho principal, e o único que apanha os pagamentos que acabam longe do browser.
  2. Num GET /v1/checkout/sessions/{id} com a chave de API, do lado do servidor: status igual a complete e payment.status igual a succeeded. Serve para a página de agradecimento mostrar o estado certo enquanto o webhook não chega.

Exemplo, num servidor Node:

JavaScript
import { constructEvent } from "@spolly-ao/spolly-payments/webhook"; // o SDK em JavaScript

// 1. O webhook: a fonte da verdade.
app.post("/webhooks/spolly", express.raw({ type: "application/json" }), (req, res) => {
  const evento = constructEvent(req.body, req.get("Spolly-Signature"), segredo); // lança se não bater
  if (evento.type === "payment.succeeded") {
    const sessao = evento.data.object.checkout_session_id;
    marcarEncomendaPaga(sessao); // idempotente: o mesmo evento pode chegar duas vezes
  }
  res.sendStatus(200);
});

// 2. O regresso: só mostra; não marca nada como pago.
app.get("/obrigado", async (req, res) => {
  const r = await fetch(`https://payments-api.spolly.ao/v1/checkout/sessions/${req.query.session_id}`, {
    headers: { Authorization: `Bearer ${process.env.SPOLLY_SECRET_KEY}` },
  });
  const sessao = await r.json();
  res.render(sessao.status === "complete" ? "pago" : "a-confirmar");
});

Parâmetros

Idempotency-Key (cabeçalho)texto · obrigatório
Um valor único por operação (um UUID serve), até 255 carateres. A mesma chave com o mesmo corpo devolve a mesma resposta durante 24 horas; com outro corpo dá 422 idempotencia_reutilizada.1 a 255 carateres

Corpo (application/json), obrigatório

amountinteiro · obrigatório
Em unidades menores inteiras. Em AOA, múltiplo de 100 (os gateways angolanos cobram kwanzas inteiros).mínimo 1
currencytexto · obrigatório
USD e EUR só pelo cartão (ADR 0003).um de: AOA, USD, EUR
descriptiontexto
O que o comprador vê que está a pagar. Obrigatória sem line_items.até 500 carateres
line_itemslista de Linha
As linhas têm de somar amount.até 100 elementos
line_items[].descriptiontexto · obrigatório
1 a 200 carateres
line_items[].unit_amountinteiro · obrigatório
Preço unitário em unidades menores (em AOA, kwanzas inteiros).mínimo 1
line_items[].quantityinteiro · obrigatório
de 1 a 10000
payment_methodslista de texto
Vazio aceita todos os que têm gateway na moeda e no modo.
success_urltexto (URL)
Para onde voltar depois de pagar, com session_id acrescentado (ou no lugar de {CHECKOUT_SESSION_ID}). Sem ele, o checkout mostra a página de confirmação da Spolly. https em produção. O regresso não é prova de pagamento. Quem chega a https://kiala.ao/obrigado?pedido=1042&session_id=cs_live_... pode ter escrito o endereço à mão. A encomenda dá-se por paga no webhook payment.succeeded, ou num GET /v1/checkout/sessions/{id} feito pelo servidor com status igual a complete; a página de regresso só mostra esse estado. Exemplo completo na descrição de POST /v1/checkout/sessions.até 2000 carateres
cancel_urltexto (URL)
Para onde voltar se o comprador desistir, com session_id. https em produção.até 2000 carateres
metadataobjeto
Devolvidos tal como vieram, na sessão e nos eventos.até 50 chaves
metadata.<chave>texto
até 500 carateres
customerCliente
customer.emailtexto
Para o email com a referência, e para o recibo.
customer.nametexto
até 200 carateres
customer.phonetexto
Telemóvel angolano; guarda-se normalizado (244XXXXXXXXX).
expires_ininteiro
A vida da sessão em segundos. 86400 (24 horas) por omissão.de 900 a 86400
kindtexto
subscription cobra o primeiro ciclo de uma subscrição, que nasce quando ele é pago. O preço de cada ciclo é o amount. Chama-se kind, e não mode, porque o mode da resposta é o da chave (test ou live).um de: payment, subscription · por omissão "payment"
subscriptionPlanoDaSubscricao
Obrigatório com kind subscription, e recusado sem ele.
subscription.intervaltexto · obrigatório
De quanto em quanto tempo renova. Os ciclos de um mês ou mais renovam no dia do mês do primeiro pagamento (hora de Luanda): quem paga a 31 renova a 28 de fevereiro e volta ao 31 em março.um de: week, month, quarter, half_year, year
subscription.cyclesinteiro ou nulo
Quantos ciclos se contratam, contando o primeiro. Sem ele, renova até ser cancelada.de 1 a 1000

Exemplo

Terminal
curl -s -X POST "https://payments-api.spolly.ao/v1/checkout/sessions" \
  -H "Authorization: Bearer $SPOLLY_SECRET_KEY" \
  -H "Idempotency-Key: <chave-unica>" \
  -H "Content-Type: application/json" \
  -d '{"amount":1500000,"currency":"AOA","description":"Vestido de capulana","success_url":"https://kiala.ao/obrigado?pedido=1042","cancel_url":"https://kiala.ao/carrinho","metadata":{"order_id":"1042"},"customer":{"email":"comprador@exemplo.ao"}}'

Respostas

  • 201

    A sessão criada (ou a mesma, numa repetição com a mesma chave).

    • Idempotent-Replayed

      true quando a resposta vem de um pedido anterior com a mesma chave.

    idtexto · obrigatório
    É a capacidade do comprador para abrir o checkout. Não se publica.padrão ^cs_(test|live)_[0-9A-Za-z]{32}$
    objecttexto · obrigatório
    um de: checkout_session
    modetexto · obrigatório
    um de: test, live
    statustexto · obrigatório
    um de: open, complete, expired
    urltexto (URL) · obrigatório
    O checkout alojado desta sessão.
    amountDinheiro · obrigatório
    Dinheiro em unidades menores inteiras, nunca com vírgula flutuante. 15.000,00 Kz é {"amount": 1500000, "currency": "AOA"}.
    amount.amountinteiro · obrigatório
    mínimo 0
    amount.currencytexto · obrigatório
    um de: AOA, USD, EUR
    descriptiontexto · obrigatório
    line_itemslista de Linha · obrigatório
    line_items[].descriptiontexto · obrigatório
    1 a 200 carateres
    line_items[].unit_amountinteiro · obrigatório
    Preço unitário em unidades menores (em AOA, kwanzas inteiros).mínimo 1
    line_items[].quantityinteiro · obrigatório
    de 1 a 10000
    payment_methodslista de texto · obrigatório
    Os métodos que o checkout oferece.
    success_urltexto ou nulo · obrigatório
    cancel_urltexto ou nulo · obrigatório
    metadataobjeto · obrigatório
    metadata.<chave>texto
    customerobjeto · obrigatório
    customer.emailtexto · obrigatório
    customer.nametexto · obrigatório
    customer.phonetexto · obrigatório
    expires_attexto (data e hora) · obrigatório
    created_attexto (data e hora) · obrigatório
    completed_attexto (data e hora) ou nulo · obrigatório
    paymentnulo ou objeto · obrigatório
    O pagamento que completou a sessão, ou null.
    payment.idtexto · obrigatório
    padrão ^pay_
    payment.statustexto · obrigatório
    um de: succeeded
    payment.methodtexto · obrigatório
    Multicaixa Express, referência, BAI Paga, cartão (Stripe Checkout).um de: mcx, reference, baipaga, card
    kindtexto · obrigatório
    renewal é a sessão de um ciclo seguinte, que o Spolly Payments abre e manda ao cliente.um de: payment, subscription, renewal
    subscriptionnulo ou objeto · obrigatório
    Numa sessão de subscrição ou de renovação, o plano e a subscrição; null numa de pagamento.
    subscription.idtexto ou nulo · obrigatório
    null enquanto o primeiro pagamento não foi confirmado.padrão ^sub_
    subscription.intervaltexto · obrigatório
    De quanto em quanto tempo renova. Os ciclos de um mês ou mais renovam no dia do mês do primeiro pagamento (hora de Luanda): quem paga a 31 renova a 28 de fevereiro e volta ao 31 em março.um de: week, month, quarter, half_year, year
    subscription.cyclesinteiro ou nulo · obrigatório
    subscription.cycleinteiro ou nulo · obrigatório
    Numa de renovação, o ciclo que ela paga.
    JSON
    {
      "id": "cs_test_4f8Gk2LmQ9rT1vXz7bN3cH6sW0pY5aDe",
      "object": "checkout_session",
      "mode": "test",
      "status": "open",
      "url": "https://checkout.payments.spolly.ao/cs_test_4f8Gk2LmQ9rT1vXz7bN3cH6sW0pY5aDe",
      "amount": {
        "amount": 1500000,
        "currency": "AOA"
      },
      "description": "Vestido de capulana",
      "line_items": [],
      "payment_methods": [
        "mcx",
        "reference",
        "card"
      ],
      "success_url": "https://kiala.ao/obrigado?pedido=1042",
      "cancel_url": "https://kiala.ao/carrinho",
      "metadata": {
        "order_id": "1042"
      },
      "customer": {
        "email": "comprador@exemplo.ao",
        "name": "",
        "phone": ""
      },
      "expires_at": "2026-11-30T10:15:00Z",
      "created_at": "2026-11-29T10:15:00Z",
      "completed_at": null,
      "payment": null,
      "kind": "payment",
      "subscription": null
    }
  • 400

    Falta o Idempotency-Key, ou o corpo não é JSON válido.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "idempotencia_em_falta",
      "mensagem": "Os pedidos POST precisam do cabeçalho Idempotency-Key."
    }
  • 401

    Sem chave, ou com uma chave revogada, expirada ou desconhecida.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "chave_invalida",
      "mensagem": "A chave de API não é válida."
    }
  • 422

    Campos por corrigir (dados_invalidos, com campos), moeda ou método sem gateway configurado neste modo (moeda_sem_gateway, metodo_sem_gateway), ou a mesma Idempotency-Key com outro corpo (idempotencia_reutilizada).

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "moeda_sem_gateway",
      "mensagem": "Não há nenhum gateway configurado para cobrar em USD pelos métodos card neste modo (produção)."
    }
  • 503

    Não foi possível validar a chave agora (a base não respondeu). Repetir mais tarde.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "indisponivel",
      "mensagem": "Não foi possível validar a chave. Tenta de novo."
    }

GET/v1/checkout/sessions/{id}

Ler uma sessão de checkout

É o que se chama no regresso do comprador: antes de responder, a API pergunta ao gateway pelos pagamentos pendentes. status é complete quando a sessão está paga, e payment diz por que pagamento.

Parâmetros

id (caminho)texto · obrigatório
O identificador da sessão (cs_test_... ou cs_live_...).padrão ^cs_(test|live)_[0-9A-Za-z]{32}$

Exemplo

Terminal
curl -s "https://payments-api.spolly.ao/v1/checkout/sessions/<id>" \
  -H "Authorization: Bearer $SPOLLY_SECRET_KEY"

Respostas

  • 200

    A sessão.

    idtexto · obrigatório
    É a capacidade do comprador para abrir o checkout. Não se publica.padrão ^cs_(test|live)_[0-9A-Za-z]{32}$
    objecttexto · obrigatório
    um de: checkout_session
    modetexto · obrigatório
    um de: test, live
    statustexto · obrigatório
    um de: open, complete, expired
    urltexto (URL) · obrigatório
    O checkout alojado desta sessão.
    amountDinheiro · obrigatório
    Dinheiro em unidades menores inteiras, nunca com vírgula flutuante. 15.000,00 Kz é {"amount": 1500000, "currency": "AOA"}.
    amount.amountinteiro · obrigatório
    mínimo 0
    amount.currencytexto · obrigatório
    um de: AOA, USD, EUR
    descriptiontexto · obrigatório
    line_itemslista de Linha · obrigatório
    line_items[].descriptiontexto · obrigatório
    1 a 200 carateres
    line_items[].unit_amountinteiro · obrigatório
    Preço unitário em unidades menores (em AOA, kwanzas inteiros).mínimo 1
    line_items[].quantityinteiro · obrigatório
    de 1 a 10000
    payment_methodslista de texto · obrigatório
    Os métodos que o checkout oferece.
    success_urltexto ou nulo · obrigatório
    cancel_urltexto ou nulo · obrigatório
    metadataobjeto · obrigatório
    metadata.<chave>texto
    customerobjeto · obrigatório
    customer.emailtexto · obrigatório
    customer.nametexto · obrigatório
    customer.phonetexto · obrigatório
    expires_attexto (data e hora) · obrigatório
    created_attexto (data e hora) · obrigatório
    completed_attexto (data e hora) ou nulo · obrigatório
    paymentnulo ou objeto · obrigatório
    O pagamento que completou a sessão, ou null.
    payment.idtexto · obrigatório
    padrão ^pay_
    payment.statustexto · obrigatório
    um de: succeeded
    payment.methodtexto · obrigatório
    Multicaixa Express, referência, BAI Paga, cartão (Stripe Checkout).um de: mcx, reference, baipaga, card
    kindtexto · obrigatório
    renewal é a sessão de um ciclo seguinte, que o Spolly Payments abre e manda ao cliente.um de: payment, subscription, renewal
    subscriptionnulo ou objeto · obrigatório
    Numa sessão de subscrição ou de renovação, o plano e a subscrição; null numa de pagamento.
    subscription.idtexto ou nulo · obrigatório
    null enquanto o primeiro pagamento não foi confirmado.padrão ^sub_
    subscription.intervaltexto · obrigatório
    De quanto em quanto tempo renova. Os ciclos de um mês ou mais renovam no dia do mês do primeiro pagamento (hora de Luanda): quem paga a 31 renova a 28 de fevereiro e volta ao 31 em março.um de: week, month, quarter, half_year, year
    subscription.cyclesinteiro ou nulo · obrigatório
    subscription.cycleinteiro ou nulo · obrigatório
    Numa de renovação, o ciclo que ela paga.
    JSON
    {
      "id": "cs_test_4f8Gk2LmQ9rT1vXz7bN3cH6sW0pY5aDe",
      "object": "checkout_session",
      "mode": "test",
      "status": "complete",
      "url": "https://checkout.payments.spolly.ao/cs_test_4f8Gk2LmQ9rT1vXz7bN3cH6sW0pY5aDe",
      "amount": {
        "amount": 1500000,
        "currency": "AOA"
      },
      "description": "Vestido de capulana",
      "line_items": [],
      "payment_methods": [
        "mcx",
        "reference",
        "card"
      ],
      "success_url": "https://kiala.ao/obrigado?pedido=1042",
      "cancel_url": null,
      "metadata": {
        "order_id": "1042"
      },
      "customer": {
        "email": "",
        "name": "",
        "phone": ""
      },
      "expires_at": "2026-11-30T10:15:00Z",
      "created_at": "2026-11-29T10:15:00Z",
      "completed_at": "2026-11-29T10:17:12Z",
      "payment": {
        "id": "pay_9Hq2Lm4Rt8Vx1Zb6Nc3Kd0",
        "status": "succeeded",
        "method": "mcx"
      },
      "kind": "payment",
      "subscription": null
    }
  • 401

    Sem chave, ou com uma chave revogada, expirada ou desconhecida.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "chave_invalida",
      "mensagem": "A chave de API não é válida."
    }
  • 404

    Não há sessão com este identificador para esta chave (o modo conta).

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "sessao_nao_existe",
      "mensagem": "Não há nenhuma sessão com este identificador para esta chave."
    }
  • 503

    Não foi possível validar a chave agora (a base não respondeu). Repetir mais tarde.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "indisponivel",
      "mensagem": "Não foi possível validar a chave. Tenta de novo."
    }

Estornos

Devolver todo ou parte de um pagamento confirmado ao comprador. Nunca mais do que se cobrou. O webhook refund.created sai quando o estorno fica aceite.

POST/v1/refunds

Estornar um pagamento, total ou parcialmente

Devolve ao comprador todo o pagamento (sem amount, estorna o que resta) ou parte dele. Pode haver vários estornos parciais do mesmo pagamento, e a soma nunca passa o que se cobrou: um estorno acima do que resta é recusado com 422 estorno_acima_do_cobrado, mesmo com dois pedidos ao mesmo tempo.

O valor sai do saldo disponível do comerciante e, quando não chega, da reserva; se nem assim chegar, o saldo fica negativo e os saques param até os pagamentos seguintes o cobrirem. A taxa da Spolly do pagamento não se devolve.

O caminho depende de como se pagou:

  • cartão: o gateway devolve ao cartão do comprador, e a resposta traz status succeeded (ou pending, se o gateway ainda não o deu por feito). Se o gateway recusar, status é failed, com failure_reason, e o saldo volta ao que era. Sem resposta do gateway, processing: a API volta a pedir sozinha, e o GET diz o desfecho.
  • Multicaixa Express, referência, BAI Paga: a equipa do Spolly Payments devolve por transferência, e o estorno fica pending até estar feita.

O webhook refund.created chega quando o estorno fica aceite (pending ou succeeded). Em modo teste, os pagamentos por cartão estornam-se como pelo gateway, sem dinheiro a mexer-se, e os outros ficam pending.

Parâmetros

Idempotency-Key (cabeçalho)texto · obrigatório
Um valor único por operação (um UUID serve), até 255 carateres. A mesma chave com o mesmo corpo devolve a mesma resposta durante 24 horas; com outro corpo dá 422 idempotencia_reutilizada.1 a 255 carateres

Corpo (application/json), obrigatório

payment_idtexto · obrigatório
O pagamento a estornar, como veio no payment.succeeded ou no payment da sessão.padrão ^pay_
amountinteiro
Quanto devolver, em unidades menores, na moeda do pagamento (em AOA, kwanzas inteiros). Sem ele, estorna-se o que resta.mínimo 1
reasontexto
O motivo, que volta no estorno e no webhook.até 500 carateres

Exemplo

Terminal
curl -s -X POST "https://payments-api.spolly.ao/v1/refunds" \
  -H "Authorization: Bearer $SPOLLY_SECRET_KEY" \
  -H "Idempotency-Key: <chave-unica>" \
  -H "Content-Type: application/json" \
  -d '{"payment_id":"pay_9Hq2Lm4Rt8Vx1Zb6Nc3Kd0","amount":500000,"reason":"Devolução parcial a pedido do cliente."}'

Respostas

  • 201

    O estorno, com o estado em que ficou.

    idtexto · obrigatório
    padrão ^re_
    objecttexto · obrigatório
    um de: refund
    payment_idtexto · obrigatório
    padrão ^pay_
    amountDinheiro · obrigatório
    Dinheiro em unidades menores inteiras, nunca com vírgula flutuante. 15.000,00 Kz é {"amount": 1500000, "currency": "AOA"}.
    amount.amountinteiro · obrigatório
    mínimo 0
    amount.currencytexto · obrigatório
    um de: AOA, USD, EUR
    statustexto · obrigatório
    processing: pedido ao gateway, sem resposta ainda. pending: à espera da transferência ao comprador (ou o gateway ainda não o deu por feito). succeeded: devolvido. failed: o gateway recusou, e o saldo voltou ao que era.um de: processing, pending, succeeded, failed
    reasontexto · obrigatório
    failure_reasontexto
    Só nos failed, a razão do gateway.
    created_attexto (data e hora) · obrigatório
    JSON
    {
      "id": "re_7Tq3Mn5Wx9Yz2Ab4Cd6Ef8",
      "object": "refund",
      "payment_id": "pay_9Hq2Lm4Rt8Vx1Zb6Nc3Kd0",
      "amount": {
        "amount": 500000,
        "currency": "AOA"
      },
      "status": "pending",
      "reason": "Devolução parcial a pedido do cliente.",
      "created_at": "2026-12-02T09:00:00Z"
    }
  • 400

    Falta o Idempotency-Key, ou o corpo não é JSON válido.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "idempotencia_em_falta",
      "mensagem": "Os pedidos POST precisam do cabeçalho Idempotency-Key."
    }
  • 401

    Sem chave, ou com uma chave revogada, expirada ou desconhecida.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "chave_invalida",
      "mensagem": "A chave de API não é válida."
    }
  • 404

    Não há pagamento com este identificador para esta chave (o modo conta).

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "pagamento_nao_existe",
      "mensagem": "Não há nenhum pagamento com este identificador para esta chave."
    }
  • 409

    O pagamento não está confirmado (pagamento_nao_pago), ou já foi todo estornado (pagamento_ja_estornado).

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "pagamento_ja_estornado",
      "mensagem": "Este pagamento já foi todo estornado."
    }
  • 422

    O estorno passa o que resta do pagamento (estorno_acima_do_cobrado, com o que resta na mensagem), campos por corrigir (dados_invalidos; em AOA o valor é em kwanzas inteiros), ou a mesma Idempotency-Key com outro corpo (idempotencia_reutilizada).

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "estorno_acima_do_cobrado",
      "mensagem": "O estorno passa o que resta do pagamento: restam 1000000 AOA (em unidades menores). Nunca se estorna mais do que se cobrou."
    }
  • 503

    Não foi possível validar a chave agora (a base não respondeu). Repetir mais tarde.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "indisponivel",
      "mensagem": "Não foi possível validar a chave. Tenta de novo."
    }

GET/v1/refunds/{id}

Ler um estorno

É o que diz o desfecho de um estorno que ficou processing ou pending.

Parâmetros

id (caminho)texto · obrigatório
O identificador do estorno (re_...).padrão ^re_[0-9A-Za-z]{16,}$

Exemplo

Terminal
curl -s "https://payments-api.spolly.ao/v1/refunds/<id>" \
  -H "Authorization: Bearer $SPOLLY_SECRET_KEY"

Respostas

  • 200

    O estorno.

    idtexto · obrigatório
    padrão ^re_
    objecttexto · obrigatório
    um de: refund
    payment_idtexto · obrigatório
    padrão ^pay_
    amountDinheiro · obrigatório
    Dinheiro em unidades menores inteiras, nunca com vírgula flutuante. 15.000,00 Kz é {"amount": 1500000, "currency": "AOA"}.
    amount.amountinteiro · obrigatório
    mínimo 0
    amount.currencytexto · obrigatório
    um de: AOA, USD, EUR
    statustexto · obrigatório
    processing: pedido ao gateway, sem resposta ainda. pending: à espera da transferência ao comprador (ou o gateway ainda não o deu por feito). succeeded: devolvido. failed: o gateway recusou, e o saldo voltou ao que era.um de: processing, pending, succeeded, failed
    reasontexto · obrigatório
    failure_reasontexto
    Só nos failed, a razão do gateway.
    created_attexto (data e hora) · obrigatório
    JSON
    {
      "id": "re_7Tq3Mn5Wx9Yz2Ab4Cd6Ef8",
      "object": "refund",
      "payment_id": "pay_9Hq2Lm4Rt8Vx1Zb6Nc3Kd0",
      "amount": {
        "amount": 500000,
        "currency": "AOA"
      },
      "status": "succeeded",
      "reason": "Devolução parcial a pedido do cliente.",
      "created_at": "2026-12-02T09:00:00Z"
    }
  • 401

    Sem chave, ou com uma chave revogada, expirada ou desconhecida.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "chave_invalida",
      "mensagem": "A chave de API não é válida."
    }
  • 404

    Não há estorno com este identificador para esta chave (o modo conta).

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "estorno_nao_existe",
      "mensagem": "Não há nenhum estorno com este identificador para esta chave."
    }
  • 503

    Não foi possível validar a chave agora (a base não respondeu). Repetir mais tarde.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "indisponivel",
      "mensagem": "Não foi possível validar a chave. Tenta de novo."
    }

Subscrições

Uma sessão criada com kind: "subscription" cobra o primeiro ciclo e, paga, cria a subscrição (subscription.created). Cada ciclo seguinte é um pagamento normal, com o seu payment.succeeded e o seu recibo, e renova a subscrição (subscription.renewed). No Multicaixa Express, na referência e no BAI Paga, o Spolly Payments manda ao cliente, antes do fim do ciclo, um link de uso único que abre a sessão de renovação já preenchida; sem pagamento, a subscrição passa a past_due no fim do ciclo e expira cinco dias depois. No cartão, renova sozinha pelo Stripe.

GET/v1/subscriptions/{id}

Ler uma subscrição

A subscrição nasce quando o primeiro pagamento de uma sessão criada com kind: "subscription" é confirmado; o id vem no subscription.created e no campo subscription.id da sessão.

Parâmetros

id (caminho)texto · obrigatório
O identificador da subscrição (sub_...).padrão ^sub_[0-9A-Za-z]{22}$

Exemplo

Terminal
curl -s "https://payments-api.spolly.ao/v1/subscriptions/<id>" \
  -H "Authorization: Bearer $SPOLLY_SECRET_KEY"

Respostas

  • 200

    A subscrição.

    idtexto · obrigatório
    padrão ^sub_[0-9A-Za-z]{22}$
    objecttexto · obrigatório
    um de: subscription
    statustexto · obrigatório
    past_due: o ciclo acabou sem o seguinte pago, e ainda se paga até ao fim da tolerância (o acesso mantém-se). expired: terminou sem pedido (end_reason unpaid ou completed).um de: active, past_due, cancelled, expired
    amountDinheiro · obrigatório
    Dinheiro em unidades menores inteiras, nunca com vírgula flutuante. 15.000,00 Kz é {"amount": 1500000, "currency": "AOA"}.
    amount.amountinteiro · obrigatório
    mínimo 0
    amount.currencytexto · obrigatório
    um de: AOA, USD, EUR
    intervaltexto · obrigatório
    De quanto em quanto tempo renova. Os ciclos de um mês ou mais renovam no dia do mês do primeiro pagamento (hora de Luanda): quem paga a 31 renova a 28 de fevereiro e volta ao 31 em março.um de: week, month, quarter, half_year, year
    cyclesinteiro ou nulo · obrigatório
    cycleinteiro · obrigatório
    mínimo 1
    current_period_starttexto (data e hora) · obrigatório
    current_period_endtexto (data e hora) · obrigatório
    methodtexto · obrigatório
    Multicaixa Express, referência, BAI Paga, cartão (Stripe Checkout).um de: mcx, reference, baipaga, card
    cancel_at_period_endbooleano · obrigatório
    cancelled_attexto (data e hora)
    cancelled_bytexto
    um de: customer, merchant
    ended_attexto (data e hora)
    end_reasontexto
    um de: requested, unpaid, completed
    checkout_session_idtexto · obrigatório
    padrão ^cs_
    latest_payment_idtexto
    padrão ^pay_
    customerobjeto · obrigatório
    customer.emailtexto · obrigatório
    customer.nametexto · obrigatório
    customer.phonetexto · obrigatório
    metadataobjeto · obrigatório
    metadata.<chave>texto
  • 401

    Sem chave, ou com uma chave revogada, expirada ou desconhecida.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "chave_invalida",
      "mensagem": "A chave de API não é válida."
    }
  • 404

    Não há subscrição com este identificador para esta chave (o modo conta).

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "subscricao_nao_existe",
      "mensagem": "Não há nenhuma subscrição com este identificador para esta chave."
    }
  • 503

    Não foi possível validar a chave agora (a base não respondeu). Repetir mais tarde.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "indisponivel",
      "mensagem": "Não foi possível validar a chave. Tenta de novo."
    }

POST/v1/subscriptions/{id}/cancel

Cancelar uma subscrição

Por omissão cancela no fim do ciclo pago: a subscrição continua active até current_period_end, com cancel_at_period_end: true, e não renova. Com at_period_end: false termina já. O webhook subscription.cancelled sai no momento do pedido, nos dois casos; o fim do ciclo de uma cancelada no fim não manda outro.

Parâmetros

id (caminho)texto · obrigatório
O identificador da subscrição (sub_...).padrão ^sub_[0-9A-Za-z]{22}$
Idempotency-Key (cabeçalho)texto · obrigatório
Um valor único por operação (um UUID serve), até 255 carateres. A mesma chave com o mesmo corpo devolve a mesma resposta durante 24 horas; com outro corpo dá 422 idempotencia_reutilizada.1 a 255 carateres

Corpo (application/json)

at_period_endbooleano
por omissão true

Exemplo

Terminal
curl -s -X POST "https://payments-api.spolly.ao/v1/subscriptions/<id>/cancel" \
  -H "Authorization: Bearer $SPOLLY_SECRET_KEY" \
  -H "Idempotency-Key: <chave-unica>"

Respostas

  • 200

    A subscrição cancelada.

    idtexto · obrigatório
    padrão ^sub_[0-9A-Za-z]{22}$
    objecttexto · obrigatório
    um de: subscription
    statustexto · obrigatório
    past_due: o ciclo acabou sem o seguinte pago, e ainda se paga até ao fim da tolerância (o acesso mantém-se). expired: terminou sem pedido (end_reason unpaid ou completed).um de: active, past_due, cancelled, expired
    amountDinheiro · obrigatório
    Dinheiro em unidades menores inteiras, nunca com vírgula flutuante. 15.000,00 Kz é {"amount": 1500000, "currency": "AOA"}.
    amount.amountinteiro · obrigatório
    mínimo 0
    amount.currencytexto · obrigatório
    um de: AOA, USD, EUR
    intervaltexto · obrigatório
    De quanto em quanto tempo renova. Os ciclos de um mês ou mais renovam no dia do mês do primeiro pagamento (hora de Luanda): quem paga a 31 renova a 28 de fevereiro e volta ao 31 em março.um de: week, month, quarter, half_year, year
    cyclesinteiro ou nulo · obrigatório
    cycleinteiro · obrigatório
    mínimo 1
    current_period_starttexto (data e hora) · obrigatório
    current_period_endtexto (data e hora) · obrigatório
    methodtexto · obrigatório
    Multicaixa Express, referência, BAI Paga, cartão (Stripe Checkout).um de: mcx, reference, baipaga, card
    cancel_at_period_endbooleano · obrigatório
    cancelled_attexto (data e hora)
    cancelled_bytexto
    um de: customer, merchant
    ended_attexto (data e hora)
    end_reasontexto
    um de: requested, unpaid, completed
    checkout_session_idtexto · obrigatório
    padrão ^cs_
    latest_payment_idtexto
    padrão ^pay_
    customerobjeto · obrigatório
    customer.emailtexto · obrigatório
    customer.nametexto · obrigatório
    customer.phonetexto · obrigatório
    metadataobjeto · obrigatório
    metadata.<chave>texto
  • 401

    Sem chave, ou com uma chave revogada, expirada ou desconhecida.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "chave_invalida",
      "mensagem": "A chave de API não é válida."
    }
  • 404

    Não há subscrição com este identificador para esta chave (o modo conta).

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "subscricao_nao_existe",
      "mensagem": "Não há nenhuma subscrição com este identificador para esta chave."
    }
  • 409

    A subscrição já terminou.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "subscricao_terminada",
      "mensagem": "Esta subscrição já terminou: não há nada a cancelar."
    }
  • 503

    Não foi possível validar a chave agora (a base não respondeu). Repetir mais tarde.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "indisponivel",
      "mensagem": "Não foi possível validar a chave. Tenta de novo."
    }

POST/v1/subscriptions/{id}/portal

Um link de uso único da página do cliente

A página onde o cliente cancela, muda o método do próximo ciclo e vê os recibos, sem conta. O link serve uma vez e vale 24 horas: aberto, dá uma sessão de uma hora no checkout, e reaberto já não serve. O cliente pode também pedir os links por email, na própria página. Repetir o pedido dá outro link (a resposta não fica na memória da idempotência, porque o link é uma credencial), e os dois valem uma vez cada.

Parâmetros

id (caminho)texto · obrigatório
O identificador da subscrição (sub_...).padrão ^sub_[0-9A-Za-z]{22}$
Idempotency-Key (cabeçalho)texto · obrigatório
Um valor único por operação (um UUID serve), até 255 carateres. A mesma chave com o mesmo corpo devolve a mesma resposta durante 24 horas; com outro corpo dá 422 idempotencia_reutilizada.1 a 255 carateres

Exemplo

Terminal
curl -s -X POST "https://payments-api.spolly.ao/v1/subscriptions/<id>/portal" \
  -H "Authorization: Bearer $SPOLLY_SECRET_KEY" \
  -H "Idempotency-Key: <chave-unica>"

Respostas

  • 201

    O link.

    objecttexto · obrigatório
    um de: subscription_portal
    subscriptiontexto · obrigatório
    padrão ^sub_
    urltexto (URL) · obrigatório
    expires_attexto (data e hora) · obrigatório
  • 401

    Sem chave, ou com uma chave revogada, expirada ou desconhecida.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "chave_invalida",
      "mensagem": "A chave de API não é válida."
    }
  • 404

    Não há subscrição com este identificador para esta chave (o modo conta).

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "subscricao_nao_existe",
      "mensagem": "Não há nenhuma subscrição com este identificador para esta chave."
    }
  • 409

    A subscrição já terminou.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
  • 503

    Não foi possível validar a chave agora (a base não respondeu). Repetir mais tarde.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "indisponivel",
      "mensagem": "Não foi possível validar a chave. Tenta de novo."
    }

Avisos dos gateways (interna)

Interna: não é para quem integra. A entrada dos avisos que os gateways (MoMenu, BAI Paga, Proxypay, Stripe) mandam quando uma cobrança fica decidida. Está aqui para quem opera e para quem configura o endereço em cada gateway; nenhum SDK a chama.

POST/gateways/{gateway}sem chave

Recebe o aviso de um gateway (só os gateways chamam esta rota).

Rota pública, sem chave de API: a autorização é a assinatura de cada gateway, verificada com o segredo dele (cartão 5.1).

GatewayCorpoAssinaturaSegredo na API
momenuJSONx-webhook-signature (HMAC-SHA256 hexadecimal de carimbo.corpo) e x-webhook-timestampPAYMENTS_MOMENU_WEBHOOK_SECRET
baipagaXML paymentCallbackdentro do corpo, em <signature>PAYMENTS_BAIPAGA_HMAC_KEY
proxypayJSON do pagamentoX-Signature (HMAC-SHA256 hexadecimal do corpo)PAYMENTS_PROXYPAY_API_KEY
stripeJSON do eventoStripe-Signature (t=...,v1=...)PAYMENTS_STRIPE_WEBHOOK_SECRET (whsec_...)

O aviso nunca é a última palavra. Mesmo assinado, a API pergunta ao gateway pelo estado da cobrança (com a referência guardada no pagamento) e age pelo que o gateway responde: um aviso de pagamento que o gateway desmente não muda nada. O corpo de cada entrega fica guardado para auditoria, e a reentrega de um evento que já teve efeito não faz mais nada (um evento, um movimento no livro).

No stripe, as subscrições por cartão (cartão 8.2): invoice.paid e invoice.payment_succeeded de um ciclo confirmam-se contra a factura, e cada factura paga é um pagamento (com o subscription.renewed), e um só, mesmo com os dois avisos; invoice.payment_failed, customer.subscription.updated e customer.subscription.deleted confirmam-se contra a subscrição no Stripe, e dão subscription.past_due e subscription.cancelled.

Respostas: 200 quando o aviso foi tratado (incluindo ignorado, desmentido ou duplicado), 401 com a assinatura errada, 503 quando o gateway deve voltar a tentar (a consulta falhou, ou falta o segredo).

Parâmetros

gateway (caminho)texto · obrigatório
um de: momenu, baipaga, proxypay, stripe

Corpo (application/json, application/xml), obrigatório

O corpo tal como vem, sem campos fixos.

Exemplo

Terminal
curl -s -X POST "https://payments-api.spolly.ao/gateways/<gateway>"

Respostas

  • 200

    O aviso foi tratado.

    recebidotrue · obrigatório
    JSON
    {
      "recebido": true
    }
  • 401

    A assinatura não confere.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "assinatura_invalida",
      "mensagem": "A assinatura do aviso não confere."
    }
  • 404

    Não há entrada para este gateway (o de ensaio não tem).

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "gateway_desconhecido",
      "mensagem": "Não há entrada de avisos para este gateway."
    }
  • 413

    O corpo passa de 64 KiB.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "aviso_grande_demais",
      "mensagem": "O aviso passa do tamanho máximo."
    }
  • 503

    Não foi processado agora; o gateway deve voltar a tentar.

    camposobjeto
    Só no 422 dados_invalidos, com uma mensagem por campo.
    campos.<chave>texto
    errotexto · obrigatório
    Código estável, para o programa decidir.
    mensagemtexto · obrigatório
    Explicação em português, para mostrar a uma pessoa.
    JSON
    {
      "erro": "tente_mais_tarde",
      "mensagem": "O aviso não foi processado agora. Tente outra vez mais tarde."
    }

Os modelos

Os objetos que os pedidos e as respostas usam, tal como estão em components.schemas.

Conta

merchant_idtexto · obrigatório
padrão ^mer_
nome_comercialtexto · obrigatório
modotexto · obrigatório
um de: test, live

Dinheiro

Dinheiro em unidades menores inteiras, nunca com vírgula flutuante. 15.000,00 Kz é {"amount": 1500000, "currency": "AOA"}.

amountinteiro · obrigatório
mínimo 0
currencytexto · obrigatório
um de: AOA, USD, EUR

Metodo

Multicaixa Express, referência, BAI Paga, cartão (Stripe Checkout).

Um valor simples: string, um de mcx, reference, baipaga, card.

Linha

descriptiontexto · obrigatório
1 a 200 carateres
unit_amountinteiro · obrigatório
Preço unitário em unidades menores (em AOA, kwanzas inteiros).mínimo 1
quantityinteiro · obrigatório
de 1 a 10000

Cliente

emailtexto
Para o email com a referência, e para o recibo.
nametexto
até 200 carateres
phonetexto
Telemóvel angolano; guarda-se normalizado (244XXXXXXXXX).

PedidoSessao

amountinteiro · obrigatório
Em unidades menores inteiras. Em AOA, múltiplo de 100 (os gateways angolanos cobram kwanzas inteiros).mínimo 1
currencytexto · obrigatório
USD e EUR só pelo cartão (ADR 0003).um de: AOA, USD, EUR
descriptiontexto
O que o comprador vê que está a pagar. Obrigatória sem line_items.até 500 carateres
line_itemslista de Linha
As linhas têm de somar amount.até 100 elementos
line_items[].descriptiontexto · obrigatório
1 a 200 carateres
line_items[].unit_amountinteiro · obrigatório
Preço unitário em unidades menores (em AOA, kwanzas inteiros).mínimo 1
line_items[].quantityinteiro · obrigatório
de 1 a 10000
payment_methodslista de texto
Vazio aceita todos os que têm gateway na moeda e no modo.
success_urltexto (URL)
Para onde voltar depois de pagar, com session_id acrescentado (ou no lugar de {CHECKOUT_SESSION_ID}). Sem ele, o checkout mostra a página de confirmação da Spolly. https em produção. O regresso não é prova de pagamento. Quem chega a https://kiala.ao/obrigado?pedido=1042&session_id=cs_live_... pode ter escrito o endereço à mão. A encomenda dá-se por paga no webhook payment.succeeded, ou num GET /v1/checkout/sessions/{id} feito pelo servidor com status igual a complete; a página de regresso só mostra esse estado. Exemplo completo na descrição de POST /v1/checkout/sessions.até 2000 carateres
cancel_urltexto (URL)
Para onde voltar se o comprador desistir, com session_id. https em produção.até 2000 carateres
metadataobjeto
Devolvidos tal como vieram, na sessão e nos eventos.até 50 chaves
metadata.<chave>texto
até 500 carateres
customerCliente
customer.emailtexto
Para o email com a referência, e para o recibo.
customer.nametexto
até 200 carateres
customer.phonetexto
Telemóvel angolano; guarda-se normalizado (244XXXXXXXXX).
expires_ininteiro
A vida da sessão em segundos. 86400 (24 horas) por omissão.de 900 a 86400
kindtexto
subscription cobra o primeiro ciclo de uma subscrição, que nasce quando ele é pago. O preço de cada ciclo é o amount. Chama-se kind, e não mode, porque o mode da resposta é o da chave (test ou live).um de: payment, subscription · por omissão "payment"
subscriptionPlanoDaSubscricao
Obrigatório com kind subscription, e recusado sem ele.
subscription.intervaltexto · obrigatório
De quanto em quanto tempo renova. Os ciclos de um mês ou mais renovam no dia do mês do primeiro pagamento (hora de Luanda): quem paga a 31 renova a 28 de fevereiro e volta ao 31 em março.um de: week, month, quarter, half_year, year
subscription.cyclesinteiro ou nulo
Quantos ciclos se contratam, contando o primeiro. Sem ele, renova até ser cancelada.de 1 a 1000

PlanoDaSubscricao

Obrigatório com kind subscription, e recusado sem ele.

intervaltexto · obrigatório
De quanto em quanto tempo renova. Os ciclos de um mês ou mais renovam no dia do mês do primeiro pagamento (hora de Luanda): quem paga a 31 renova a 28 de fevereiro e volta ao 31 em março.um de: week, month, quarter, half_year, year
cyclesinteiro ou nulo
Quantos ciclos se contratam, contando o primeiro. Sem ele, renova até ser cancelada.de 1 a 1000

Intervalo

De quanto em quanto tempo renova. Os ciclos de um mês ou mais renovam no dia do mês do primeiro pagamento (hora de Luanda): quem paga a 31 renova a 28 de fevereiro e volta ao 31 em março.

Um valor simples: string, um de week, month, quarter, half_year, year.

Sessao

idtexto · obrigatório
É a capacidade do comprador para abrir o checkout. Não se publica.padrão ^cs_(test|live)_[0-9A-Za-z]{32}$
objecttexto · obrigatório
um de: checkout_session
modetexto · obrigatório
um de: test, live
statustexto · obrigatório
um de: open, complete, expired
urltexto (URL) · obrigatório
O checkout alojado desta sessão.
amountDinheiro · obrigatório
Dinheiro em unidades menores inteiras, nunca com vírgula flutuante. 15.000,00 Kz é {"amount": 1500000, "currency": "AOA"}.
amount.amountinteiro · obrigatório
mínimo 0
amount.currencytexto · obrigatório
um de: AOA, USD, EUR
descriptiontexto · obrigatório
line_itemslista de Linha · obrigatório
line_items[].descriptiontexto · obrigatório
1 a 200 carateres
line_items[].unit_amountinteiro · obrigatório
Preço unitário em unidades menores (em AOA, kwanzas inteiros).mínimo 1
line_items[].quantityinteiro · obrigatório
de 1 a 10000
payment_methodslista de texto · obrigatório
Os métodos que o checkout oferece.
success_urltexto ou nulo · obrigatório
cancel_urltexto ou nulo · obrigatório
metadataobjeto · obrigatório
metadata.<chave>texto
customerobjeto · obrigatório
customer.emailtexto · obrigatório
customer.nametexto · obrigatório
customer.phonetexto · obrigatório
expires_attexto (data e hora) · obrigatório
created_attexto (data e hora) · obrigatório
completed_attexto (data e hora) ou nulo · obrigatório
paymentnulo ou objeto · obrigatório
O pagamento que completou a sessão, ou null.
payment.idtexto · obrigatório
padrão ^pay_
payment.statustexto · obrigatório
um de: succeeded
payment.methodtexto · obrigatório
Multicaixa Express, referência, BAI Paga, cartão (Stripe Checkout).um de: mcx, reference, baipaga, card
kindtexto · obrigatório
renewal é a sessão de um ciclo seguinte, que o Spolly Payments abre e manda ao cliente.um de: payment, subscription, renewal
subscriptionnulo ou objeto · obrigatório
Numa sessão de subscrição ou de renovação, o plano e a subscrição; null numa de pagamento.
subscription.idtexto ou nulo · obrigatório
null enquanto o primeiro pagamento não foi confirmado.padrão ^sub_
subscription.intervaltexto · obrigatório
De quanto em quanto tempo renova. Os ciclos de um mês ou mais renovam no dia do mês do primeiro pagamento (hora de Luanda): quem paga a 31 renova a 28 de fevereiro e volta ao 31 em março.um de: week, month, quarter, half_year, year
subscription.cyclesinteiro ou nulo · obrigatório
subscription.cycleinteiro ou nulo · obrigatório
Numa de renovação, o ciclo que ela paga.

Subscricao

O mesmo objeto dos eventos (contratos/eventos/objetos.schema.json#/$defs/subscription).

idtexto · obrigatório
padrão ^sub_[0-9A-Za-z]{22}$
objecttexto · obrigatório
um de: subscription
statustexto · obrigatório
past_due: o ciclo acabou sem o seguinte pago, e ainda se paga até ao fim da tolerância (o acesso mantém-se). expired: terminou sem pedido (end_reason unpaid ou completed).um de: active, past_due, cancelled, expired
amountDinheiro · obrigatório
Dinheiro em unidades menores inteiras, nunca com vírgula flutuante. 15.000,00 Kz é {"amount": 1500000, "currency": "AOA"}.
amount.amountinteiro · obrigatório
mínimo 0
amount.currencytexto · obrigatório
um de: AOA, USD, EUR
intervaltexto · obrigatório
De quanto em quanto tempo renova. Os ciclos de um mês ou mais renovam no dia do mês do primeiro pagamento (hora de Luanda): quem paga a 31 renova a 28 de fevereiro e volta ao 31 em março.um de: week, month, quarter, half_year, year
cyclesinteiro ou nulo · obrigatório
cycleinteiro · obrigatório
mínimo 1
current_period_starttexto (data e hora) · obrigatório
current_period_endtexto (data e hora) · obrigatório
methodtexto · obrigatório
Multicaixa Express, referência, BAI Paga, cartão (Stripe Checkout).um de: mcx, reference, baipaga, card
cancel_at_period_endbooleano · obrigatório
cancelled_attexto (data e hora)
cancelled_bytexto
um de: customer, merchant
ended_attexto (data e hora)
end_reasontexto
um de: requested, unpaid, completed
checkout_session_idtexto · obrigatório
padrão ^cs_
latest_payment_idtexto
padrão ^pay_
customerobjeto · obrigatório
customer.emailtexto · obrigatório
customer.nametexto · obrigatório
customer.phonetexto · obrigatório
metadataobjeto · obrigatório
metadata.<chave>texto

PedidoEstorno

payment_idtexto · obrigatório
O pagamento a estornar, como veio no payment.succeeded ou no payment da sessão.padrão ^pay_
amountinteiro
Quanto devolver, em unidades menores, na moeda do pagamento (em AOA, kwanzas inteiros). Sem ele, estorna-se o que resta.mínimo 1
reasontexto
O motivo, que volta no estorno e no webhook.até 500 carateres

Estorno

idtexto · obrigatório
padrão ^re_
objecttexto · obrigatório
um de: refund
payment_idtexto · obrigatório
padrão ^pay_
amountDinheiro · obrigatório
Dinheiro em unidades menores inteiras, nunca com vírgula flutuante. 15.000,00 Kz é {"amount": 1500000, "currency": "AOA"}.
amount.amountinteiro · obrigatório
mínimo 0
amount.currencytexto · obrigatório
um de: AOA, USD, EUR
statustexto · obrigatório
processing: pedido ao gateway, sem resposta ainda. pending: à espera da transferência ao comprador (ou o gateway ainda não o deu por feito). succeeded: devolvido. failed: o gateway recusou, e o saldo voltou ao que era.um de: processing, pending, succeeded, failed
reasontexto · obrigatório
failure_reasontexto
Só nos failed, a razão do gateway.
created_attexto (data e hora) · obrigatório

Erro

camposobjeto
Só no 422 dados_invalidos, com uma mensagem por campo.
campos.<chave>texto
errotexto · obrigatório
Código estável, para o programa decidir.
mensagemtexto · obrigatório
Explicação em português, para mostrar a uma pessoa.

Saude

estadotexto · obrigatório
um de: ok, degradado
versaotexto · obrigatório
A versão da construção, ou dev em desenvolvimento.
committexto · obrigatório
O commit da construção, ou dev em desenvolvimento.
basetexto · obrigatório
um de: ok, falhou