Um fluxo de pagamento no servidor com proteção para novas tentativas e duplicatas.
1. Prepare os IDs e o acesso
Comece com uma loja habilitada e métodos de pagamento testados. Em Configurações → Acesso à API, crie uma credencial de leitura/escrita limitada ao projeto necessário. Mantenha o token no seu backend, nunca no código do navegador nem em um repositório público.
Copie o ID de API do projeto e ID de API da loja da caixa da loja Dados básicos → IDs de API . São UUIDs, não o identificador legível do projeto nem o número do seu pedido. Use seu próprio nome de host da API.
Em Loja → IPNcrie um segredo de assinatura antes de fornecer um ipn_url. Seu receptor HTTPS deve estar acessível pelo VPS do comerciante.
2. Crie uma fatura
Substitua os marcadores e envie esta solicitação pelo seu backend. Os valores são strings decimais, não cálculos de ponto flutuante.
curl --fail-with-body --request POST \
'https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices' \
--header 'Authorization: Bearer YOUR_MERCHANT_API_TOKEN' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: order-1042-attempt-1' \
--data '{
"amount": "10.00",
"currency": "EUR",
"order_id": "order-1042",
"description": "Example order",
"ipn_url": "https://shop.example.com/payments/wholly-ipn",
"metadata": {
"cart_id": "cart-1042"
}
}'Salve data.invoice_id com seu pedido e depois redirecione o cliente para links.checkout. Use uma chave de idempotência única para uma nova tentativa de pagamento. Se o tempo limite for atingido, tente novamente com a mesma credencial, chave e bytes exatos do corpo.
Omitir payment_methods usa os métodos aceitos pela loja. Você pode restringi-los por fatura com slugs de redes e símbolos; isso nunca habilita um ativo não aceito. Todos os campos de solicitação e exemplos de resposta →
3. Escolha IPN, webhooks ou ambos
IPN acompanha o ciclo de vida da fatura. Defina a URL IPN padrão da loja ou substitua por ipn_url para uma fatura. Os webhooks inscrevem um endpoint em eventos selecionados, por exemplo invoice.settled.
Usam o mesmo formato de assinatura, mas segredos diferentes: IPN usa o segredo IPN da loja; cada endpoint de webhook tem seu próprio segredo. Nenhum usa o token bearer da API para assinar.
Se ambos entregarem ao seu aplicativo, espere notificações sobrepostas. Não credite um pedido duas vezes.
4. Verifique e salve a notificação
- Leia o corpo bruto exato da solicitação antes de analisar o JSON. Verifique
Wholly-Signaturecom o segredo correspondente e verificações de horário/repetição. Os SDKs oficiais fornecem verificadores. - Valide a identidade de projeto, loja, fatura e evento do corpo assinado. Cabeçalhos de entrega não assinados não são uma fonte de autenticação.
- Armazene o evento de forma persistente com um
event_idúnico e depois retorne HTTP 2xx prontamente. Processe os pedidos em um processo em segundo plano. - Obtenha a fatura atual pelo host de API configurado, não por um host arbitrário fornecido em uma solicitação. Compare o projeto, a loja, o valor, a moeda e a referência de pedido que você salvou.
PHP · Python · JavaScript / TypeScript · Especificação de assinatura e exemplos de receptores
5. Atenda o pedido uma vez, na liquidação
Para um receptor baseado em eventos , trate event_type = invoice.settlede depois verifique o valor atual de status = settled e sua política de exceções. Atenda o pedido uma única vez dentro de uma transação do banco de dados/restrição de pedido único.
Uma rede com finalização rápida pode enviar tanto payment.received e invoice.settled com status = settled. Em outra rede, payment.received ainda pode indicar processing. Nenhum dos dois fluxos é um erro.
Remova eventos duplicados por event_id, não apenas pela sequência: tipos diferentes de eventos podem compartilhar uma sequência. Já os manipuladores de SDK baseados em estado agrupam as revisões da fatura e verificam o estado independentemente do tipo de evento. Não misture esse agrupamento com um filtro por tipo de evento. As duas abordagens ainda precisam de proteção contra duplicatas no nível do pedido.
Inspecione requires_review e as resoluções manuais antes de atender o pedido. amount_status = paid sozinho não comprova confirmação. Os campos de ativo pago de nível superior resumem a liquidação; payment_info contém os detalhes dos pagamentos recebidos e os dados de cotação. Todos os estados, eventos e regras de exceções →
6. Teste novas tentativas e recuperação
Teste um pagamento pequeno, uma entrega duplicada, uma fatura expirada e um receptor temporariamente indisponível. Repetir um evento não deve gerar um segundo crédito do pedido. Trate eventos fora de ordem sem sobrescrever um estado mais recente.
Inspecione Loja → IPN / Webhooks → Histórico → Detalhes, ou as seções de entrega em Detalhes da fatura. Reenviar reutiliza o evento registrado, não uma nova liquidação.
Crédito de processamento baixo pausa IPN/webhooks enquanto os pagamentos continuam. Concilie os pedidos pendentes pela API e trate as entregas retidas após a recuperação. Nunca atenda um pedido com base em um redirecionamento do navegador nem em uma captura de tela do cliente.