AcapaPay · Laravel SDK · v2
API de Pagamento Direta (sem iFrame)
Além do checkout hospedado (redirecionamento ou <x-acapapay::iframe>), o AcapaPay expõe uma API de pagamento direta: um conjunto de endpoints M2M em bruto que permitem à tua aplicação construir a própria interface de checkout, sem nunca redirecionar o utilizador para o SSO nem embutir nenhum iFrame.
1. Autenticação
Todos os endpoints abaixo são M2M (machine-to-machine), protegidos pelo mesmo middleware client do Passport usado pelo resto do AcapaPay. Obtém um token OAuth2 client_credentials com as mesmas credenciais (ACAPAPAY_CLIENT_ID / ACAPAPAY_CLIENT_SECRET) já usadas para checkoutSession():
POST https://api.{dominio}/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=...&client_secret=...&scope=*
Usa o access_token devolvido como Authorization: Bearer <token> em todos os pedidos seguintes.
AcapaPay::direct() — createInvoice(), charge(), status(), simulate() — que trata do token, dos erros e da forma dos payloads por ti. Os endpoints em bruto abaixo continuam a documentar o contrato.
2. Fluxo completo
POST /v1/billing/invoices— cria a fatura (cliente, itens, referência, metadata).POST /v1/billing/invoices/{id}/charges— gera a cobrança (WIPouRDP) para essa fatura.- A tua app mostra os dados devolvidos (checkout hospedado da WiPay, ou o link/carteiras de pagamento em cripto da RedotPay) na tua própria UI.
GET /v1/billing/invoices/{id}/status— usado em polling (a cada poucos segundos) até a fatura ficarpaid. Internamente há um rate-limit de 15s por fatura antes de voltar a consultar o gateway externo — pedidos mais frequentes devolvem o último estado conhecido sem novo pedido externo.- Assim que a fatura é confirmada como paga (via polling ou via webhook do gateway), o SSO dispara o webhook
invoice.paidpara owebhook_urlconfigurado na tua App, assinado emX-AcapaDev-Signature(HMAC-SHA256) — ver Webhooks.
Todas as cobranças e reconciliações geradas por esta API ficam visíveis no admin do SSO (Faturação → detalhe da fatura), exatamente como as geradas pela página de checkout hospedada.
Webhook URL dedicado para faturação (opcional)
Por omissão, o invoice.paid é enviado para o mesmo webhook_url usado pelos eventos de identidade (user.logout, user.updated, user.role_updated). Se o teu sistema de faturação for um serviço separado do que trata a sessão/autenticação, podes configurar um "Webhook URL de Faturação" dedicado no painel Developer → Webhooks & Eventos da tua app.
3. Como Apresentar o Pagamento — WIP e RDP
WIP (WiPay) — checkout hospedado
O método WIP devolve um data.pay_url (ou um iframe embutível) hospedado pela WiPay. A tua app redireciona o utilizador (ou embute o iframe) e a WiPay trata de todo o ecrã de pagamento — não há montagem de UI própria a fazer para este método.
$response = Http::withToken($token)->post("{$apiHost}/v1/billing/invoices/{$invoiceId}/charges", [
'payment_method' => 'WIP',
'phone_number' => $telefone, // opcional em WIP
]);
$payUrl = $response->json('data.pay_url');
return redirect($payUrl);
RDP (RedotPay) — 2 fluxos
Guia completo do dólar, com exemplos do SDK: WiPay + RedotPay — Pagamentos (v2).
Quando geras uma cobrança RDP, tens de escolher uma de duas formas de levar o utilizador a pagar. Nenhuma é objetivamente "melhor" — a escolha depende de quanto controlo visual precisas e de quanto trabalho de implementação estás disposto a fazer.
Fluxo 1 — Redirecionar (recomendado por omissão)
É o mais simples, e é o que a nossa própria página de checkout hospedado usa internamente. Chamas o endpoint de cobrança, recebes um data.pay_url, e envias o utilizador para lá — ponto final.
$response = Http::withToken($token)->post("{$apiHost}/v1/billing/invoices/{$invoiceId}/charges", [
'payment_method' => 'RDP',
]);
$payUrl = $response->json('data.pay_url');
return redirect($payUrl);
O que o utilizador vê ao chegar a esse pay_url: uma página hospedada pela RedotPay (não é tua, nem nossa) com a lista de carteiras suportadas (MetaMask, Phantom, Trust Wallet, Coinbase, TronLink, etc.), cada uma já com o seu próprio QR code desenhado e pronto a digitalizar.
Fluxo 2 — Construir a tua própria interface com o QR code embutido
Se quiseres que o utilizador nunca saia da tua página, usa o array data.paymentMethods devolvido pela API de cobrança.
webQrCode e h5QrCode que a RedotPay devolve não são imagens. São links. Quem tem de gerar a imagem de QR code, a partir desse link de texto, és tu.
Detalhes de implementação completos (estrutura do payload, geração de QR no servidor/browser, deteção de mobile): WiPay + RedotPay — Pagamentos (v2).
4. Exemplo Completo (Laravel)
<?php
use Illuminate\Support\Facades\Http;
class PagamentoDiretoController extends Controller
{
protected function tokenM2M(): string
{
$response = Http::asForm()->post(config('acapapay.host') . '/oauth/token', [
'grant_type' => 'client_credentials',
'client_id' => config('acapapay.client_id'),
'client_secret' => config('acapapay.client_secret'),
'scope' => '*',
]);
return $response->json('access_token');
}
public function criarFaturaECobranca(Request $request)
{
$token = $this->tokenM2M();
$apiHost = config('acapapay.api_host');
// 1. Criar a fatura
$invoice = Http::withToken($token)->post("{$apiHost}/v1/billing/invoices", [
'customer_name' => $request->user()->name,
'customer_email' => $request->user()->email,
'items' => [
['description' => 'Plano PRO', 'quantity' => 1, 'unit_price' => 15000],
],
'app_reference' => 'pedido-' . $request->user()->id,
])->json();
// 2. Gerar a cobrança (WiPay, hospedado)
$charge = Http::withToken($token)->post(
"{$apiHost}/v1/billing/invoices/{$invoice['invoice_id']}/charges",
['payment_method' => 'WIP']
)->json();
// $charge['data']['pay_url'] é o URL a mostrar/redirecionar na tua UI
return response()->json($charge);
}
}
5. Endpoints
POST /v1/billing/invoices
Cria a fatura. Um pagamento nunca existe sem estar associado a uma fatura.
| Campo | Tipo | Obrigatório | Notas |
|---|---|---|---|
customer_name | string | Sim | Máx. 255 caracteres |
customer_email | string | Não | — |
customer_nif | string | Não | Máx. 50 caracteres |
currency | string | Não | 3 letras, padrão AOA (usa USD para RDP) |
items | array | Sim | Pelo menos 1 item |
items[].description | string | Sim | — |
items[].quantity | integer | Sim | Mínimo 1 |
items[].unit_price | number | Sim | Mínimo 0 |
app_reference | string | Não | A tua referência interna do pedido |
due_date | date | Não | — |
POST /v1/billing/invoices/{id}/charges
Cria uma cobrança para a fatura {id}.
| Campo | Tipo | Obrigatório | Notas |
|---|---|---|---|
payment_method | string | Sim | WIP ou RDP |
phone_number | string | Opcional em WIP | Número de telemóvel (WiPay) |
Resposta 201:
{
"status": "success",
"payment_method": "WIP",
"is_mock": false,
"expires_at": null,
"data": { "pay_url": "https://checkout.wipayafrica.com/..." }
}
| Método | Validade | Fonte |
|---|---|---|
WIP (WiPay) | Sem expiração documentada pelo gateway | Checkout hospedado WiPay |
RDP (RedotPay) | 1 hora a partir da criação | Devolvida pela RedotPay |
sandbox (ou a fatura tiver metadata.sandbox_mode: true), a resposta é sempre um mock (is_mock: true), sem chamar o gateway real — útil para testar a tua interface sem gerar cobranças reais.GET /v1/billing/invoices/{id}/status
Consulta (e tenta atualizar, respeitando o rate-limit de 15s) o estado atual da fatura.
POST /v1/billing/invoices/{id}/simulate
Apenas em ambiente sandbox: marca a fatura como paga imediatamente (cria uma transação sandbox_test e dispara o webhook invoice.paid), sem depender do gateway real. Devolve 403 fora de sandbox.
6. Erros
| Código | Categoria |
|---|---|
403 | Autorização / restrição de sandbox |
404 | Fatura não encontrada / não pertence à App |
422 | Estado inválido da fatura (ex: já paga) ou método incompatível com a moeda da fatura |
500 | Falha de comunicação com o gateway externo |