(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
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
devem desenvolvimento. committexto · obrigatório- O commit da construção, ou
devem 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
devem desenvolvimento. committexto · obrigatório- O commit da construção, ou
devem 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
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óriomodotexto · 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>textoerrotexto · 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>textoerrotexto · 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:
- 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. - Num
GET /v1/checkout/sessions/{id}com a chave de API, do lado do servidor:statusigual acompleteepayment.statusigual asucceeded. Serve para a página de agradecimento mostrar o estado certo enquanto o webhook não chega.
Exemplo, num servidor Node:
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_idacrescentado (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 ahttps://kiala.ao/obrigado?pedido=1042&session_id=cs_live_...pode ter escrito o endereço à mão. A encomenda dá-se por paga no webhookpayment.succeeded, ou numGET /v1/checkout/sessions/{id}feito pelo servidor comstatusigual acomplete; a página de regresso só mostra esse estado. Exemplo completo na descrição dePOST /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
customerClientecustomer.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
kindtextosubscriptioncobra o primeiro ciclo de uma subscrição, que nasce quando ele é pago. O preço de cada ciclo é oamount. Chama-sekind, e nãomode, porque omodeda resposta é o da chave (testoulive).um de:payment,subscription· por omissão"payment"subscriptionPlanoDaSubscricao- Obrigatório com
kindsubscription, 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
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-Replayedtruequando 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órioline_itemslista de Linha · obrigatórioline_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óriocancel_urltexto ou nulo · obrigatóriometadataobjeto · obrigatóriometadata.<chave>textocustomerobjeto · obrigatóriocustomer.emailtexto · obrigatóriocustomer.nametexto · obrigatóriocustomer.phonetexto · obrigatórioexpires_attexto (data e hora) · obrigatóriocreated_attexto (data e hora) · obrigatóriocompleted_attexto (data e hora) ou nulo · obrigatóriopaymentnulo 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óriorenewalé a sessão de um ciclo seguinte, que o Spolly Payments abre e manda ao cliente.um de:payment,subscription,renewalsubscriptionnulo ou objeto · obrigatório- Numa sessão de subscrição ou de renovação, o plano e a subscrição;
nullnuma de pagamento. subscription.idtexto ou nulo · obrigatórionullenquanto 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óriosubscription.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>textoerrotexto · 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>textoerrotexto · 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, comcampos), moeda ou método sem gateway configurado neste modo (moeda_sem_gateway,metodo_sem_gateway), ou a mesmaIdempotency-Keycom outro corpo (idempotencia_reutilizada).camposobjeto- Só no 422
dados_invalidos, com uma mensagem por campo. campos.<chave>textoerrotexto · 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>textoerrotexto · 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_...oucs_live_...).padrão^cs_(test|live)_[0-9A-Za-z]{32}$
Exemplo
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órioline_itemslista de Linha · obrigatórioline_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óriocancel_urltexto ou nulo · obrigatóriometadataobjeto · obrigatóriometadata.<chave>textocustomerobjeto · obrigatóriocustomer.emailtexto · obrigatóriocustomer.nametexto · obrigatóriocustomer.phonetexto · obrigatórioexpires_attexto (data e hora) · obrigatóriocreated_attexto (data e hora) · obrigatóriocompleted_attexto (data e hora) ou nulo · obrigatóriopaymentnulo 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óriorenewalé a sessão de um ciclo seguinte, que o Spolly Payments abre e manda ao cliente.um de:payment,subscription,renewalsubscriptionnulo ou objeto · obrigatório- Numa sessão de subscrição ou de renovação, o plano e a subscrição;
nullnuma de pagamento. subscription.idtexto ou nulo · obrigatórionullenquanto 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óriosubscription.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>textoerrotexto · 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>textoerrotexto · 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>textoerrotexto · 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
statussucceeded(oupending, se o gateway ainda não o deu por feito). Se o gateway recusar,statuséfailed, comfailure_reason, e o saldo volta ao que era. Sem resposta do gateway,processing: a API volta a pedir sozinha, e oGETdiz o desfecho. - Multicaixa Express, referência, BAI Paga: a equipa do Spolly Payments devolve por transferência, e o estorno fica
pendingaté 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.succeededou nopaymentda 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
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órioprocessing: 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,failedreasontexto · obrigatóriofailure_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>textoerrotexto · 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>textoerrotexto · 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>textoerrotexto · 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>textoerrotexto · 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 mesmaIdempotency-Keycom outro corpo (idempotencia_reutilizada).camposobjeto- Só no 422
dados_invalidos, com uma mensagem por campo. campos.<chave>textoerrotexto · 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>textoerrotexto · 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
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órioprocessing: 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,failedreasontexto · obrigatóriofailure_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>textoerrotexto · 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>textoerrotexto · 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>textoerrotexto · 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
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óriopast_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_reasonunpaidoucompleted).um de:active,past_due,cancelled,expiredamountDinheiro · 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óriocycleinteiro · obrigatório- mínimo 1
current_period_starttexto (data e hora) · obrigatóriocurrent_period_endtexto (data e hora) · obrigatóriomethodtexto · obrigatório- Multicaixa Express, referência, BAI Paga, cartão (Stripe Checkout).um de:
mcx,reference,baipaga,card cancel_at_period_endbooleano · obrigatóriocancelled_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óriocustomer.emailtexto · obrigatóriocustomer.nametexto · obrigatóriocustomer.phonetexto · obrigatóriometadataobjeto · obrigatóriometadata.<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>textoerrotexto · 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>textoerrotexto · 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>textoerrotexto · 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
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óriopast_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_reasonunpaidoucompleted).um de:active,past_due,cancelled,expiredamountDinheiro · 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óriocycleinteiro · obrigatório- mínimo 1
current_period_starttexto (data e hora) · obrigatóriocurrent_period_endtexto (data e hora) · obrigatóriomethodtexto · obrigatório- Multicaixa Express, referência, BAI Paga, cartão (Stripe Checkout).um de:
mcx,reference,baipaga,card cancel_at_period_endbooleano · obrigatóriocancelled_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óriocustomer.emailtexto · obrigatóriocustomer.nametexto · obrigatóriocustomer.phonetexto · obrigatóriometadataobjeto · obrigatóriometadata.<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>textoerrotexto · 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>textoerrotexto · 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>textoerrotexto · 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>textoerrotexto · 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
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órioexpires_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>textoerrotexto · 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>textoerrotexto · 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>textoerrotexto · 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>textoerrotexto · 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).
| Gateway | Corpo | Assinatura | Segredo na API |
|---|---|---|---|
momenu | JSON | x-webhook-signature (HMAC-SHA256 hexadecimal de carimbo.corpo) e x-webhook-timestamp | PAYMENTS_MOMENU_WEBHOOK_SECRET |
baipaga | XML paymentCallback | dentro do corpo, em <signature> | PAYMENTS_BAIPAGA_HMAC_KEY |
proxypay | JSON do pagamento | X-Signature (HMAC-SHA256 hexadecimal do corpo) | PAYMENTS_PROXYPAY_API_KEY |
stripe | JSON do evento | Stripe-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
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>textoerrotexto · 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>textoerrotexto · 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>textoerrotexto · 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>textoerrotexto · 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óriomodotexto · 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_idacrescentado (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 ahttps://kiala.ao/obrigado?pedido=1042&session_id=cs_live_...pode ter escrito o endereço à mão. A encomenda dá-se por paga no webhookpayment.succeeded, ou numGET /v1/checkout/sessions/{id}feito pelo servidor comstatusigual acomplete; a página de regresso só mostra esse estado. Exemplo completo na descrição dePOST /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
customerClientecustomer.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
kindtextosubscriptioncobra o primeiro ciclo de uma subscrição, que nasce quando ele é pago. O preço de cada ciclo é oamount. Chama-sekind, e nãomode, porque omodeda resposta é o da chave (testoulive).um de:payment,subscription· por omissão"payment"subscriptionPlanoDaSubscricao- Obrigatório com
kindsubscription, 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órioline_itemslista de Linha · obrigatórioline_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óriocancel_urltexto ou nulo · obrigatóriometadataobjeto · obrigatóriometadata.<chave>textocustomerobjeto · obrigatóriocustomer.emailtexto · obrigatóriocustomer.nametexto · obrigatóriocustomer.phonetexto · obrigatórioexpires_attexto (data e hora) · obrigatóriocreated_attexto (data e hora) · obrigatóriocompleted_attexto (data e hora) ou nulo · obrigatóriopaymentnulo 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óriorenewalé a sessão de um ciclo seguinte, que o Spolly Payments abre e manda ao cliente.um de:payment,subscription,renewalsubscriptionnulo ou objeto · obrigatório- Numa sessão de subscrição ou de renovação, o plano e a subscrição;
nullnuma de pagamento. subscription.idtexto ou nulo · obrigatórionullenquanto 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óriosubscription.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óriopast_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_reasonunpaidoucompleted).um de:active,past_due,cancelled,expiredamountDinheiro · 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óriocycleinteiro · obrigatório- mínimo 1
current_period_starttexto (data e hora) · obrigatóriocurrent_period_endtexto (data e hora) · obrigatóriomethodtexto · obrigatório- Multicaixa Express, referência, BAI Paga, cartão (Stripe Checkout).um de:
mcx,reference,baipaga,card cancel_at_period_endbooleano · obrigatóriocancelled_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óriocustomer.emailtexto · obrigatóriocustomer.nametexto · obrigatóriocustomer.phonetexto · obrigatóriometadataobjeto · obrigatóriometadata.<chave>texto
PedidoEstorno
payment_idtexto · obrigatório- O pagamento a estornar, como veio no
payment.succeededou nopaymentda 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órioprocessing: 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,failedreasontexto · obrigatóriofailure_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>textoerrotexto · 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
devem desenvolvimento. committexto · obrigatório- O commit da construção, ou
devem desenvolvimento. basetexto · obrigatório- um de:
ok,falhou