Acapadev

AcapaPay · Laravel SDK · v2

API de Pagamento Direta (sem iFrame)

ℹ️
Esta é a documentação v2, focada nos dois métodos recomendados para integrações novas: WIP (WiPay, hospedado, AOA) e RDP (RedotPay, USD/cripto). REF, GPO e EKZ continuam a funcionar para apps registadas antes de 30/09/2026, mas já não fazem parte desta documentação — consulta a v1 se a tua app ainda os usa.

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.

ℹ️
Quando usar isto em vez do checkout hospedado? Usa a API direta quando precisas de controlo total sobre o design do ecrã de pagamento dentro da tua própria app — por exemplo, num fluxo de checkout nativo em mobile. Se te bastar redirecionar ou embutir a página já pronta do SSO/WiPay/RedotPay, o checkout hospedado é mais simples de integrar.

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.

⚠️
O SDK Laravel expõe esta API em 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

  1. POST /v1/billing/invoices — cria a fatura (cliente, itens, referência, metadata).
  2. POST /v1/billing/invoices/{id}/charges — gera a cobrança (WIP ou RDP) para essa fatura.
  3. 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.
  4. GET /v1/billing/invoices/{id}/status — usado em polling (a cada poucos segundos) até a fatura ficar paid. 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.
  5. Assim que a fatura é confirmada como paga (via polling ou via webhook do gateway), o SSO dispara o webhook invoice.paid para o webhook_url configurado na tua App, assinado em X-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.

⚠️
Ponto mais importante deste fluxo: os campos 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.

CampoTipoObrigatórioNotas
customer_namestringSimMáx. 255 caracteres
customer_emailstringNão—
customer_nifstringNãoMáx. 50 caracteres
currencystringNão3 letras, padrão AOA (usa USD para RDP)
itemsarraySimPelo menos 1 item
items[].descriptionstringSim—
items[].quantityintegerSimMínimo 1
items[].unit_pricenumberSimMínimo 0
app_referencestringNãoA tua referência interna do pedido
due_datedateNão—

POST /v1/billing/invoices/{id}/charges

Cria uma cobrança para a fatura {id}.

CampoTipoObrigatórioNotas
payment_methodstringSimWIP ou RDP
phone_numberstringOpcional em WIPNú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étodoValidadeFonte
WIP (WiPay)Sem expiração documentada pelo gatewayCheckout hospedado WiPay
RDP (RedotPay)1 hora a partir da criaçãoDevolvida pela RedotPay
✅
Se a tua App estiver em ambiente 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ódigoCategoria
403Autorização / restrição de sandbox
404Fatura não encontrada / não pertence à App
422Estado inválido da fatura (ex: já paga) ou método incompatível com a moeda da fatura
500Falha de comunicação com o gateway externo
Enter para selecionar ESC para sair