Usas IA para programar (Claude Code, Cursor, ChatGPT, Lovable, Bolt.new, v0, Replit…)? Copia o prompt abaixo e cola-o no teu assistente. Ele conduz a integração da FaciPay em quatro fases — descoberta, credenciais, implementação (frontend + backend + webhook) e verificação.
Âmbito: este prompt cobre aplicações web (frontend num browser + backend HTTP). Não cobre apps mobile nativas nem plugins de CMS (WordPress/WooCommerce, Shopify). Para mobile, vê as SDKs Android e iOS; para WooCommerce, vê o plugin.

Prompt de integração (web)

Copia tudo:
Vais integrar o sistema de pagamentos FaciPay (Angola, moeda AOA) no meu projecto —
frontend + backend. Executa em quatro fases, pela ordem indicada. Não escrevas código antes
da Fase 1 estar respondida.

## FASE 1 — Descoberta
Faz todas as perguntas abaixo numa só mensagem e espera as minhas respostas.
1. Stack — frontend (Next.js, React+Vite, Vue/Nuxt, SvelteKit, HTML puro…), backend
   (Next.js API routes, Express, NestJS, Fastify, Laravel…) e BD/ORM.
2. Autenticação — como identificas o utilizador no servidor (cookie, JWT, sessão)?
3. Produto — onde fica o botão de pagamento (produto, carrinho, checkout dedicado)?
4. Métodos — Multicaixa Express (FPMCXEXPRSS), Referência EMIS (FPSOLPGEXT), FaciPay directo
   (FPSOLPG), ou vários? (O FaciPay directo é sempre o padrão e não se desativa.)
5. Páginas de retorno — paths para sucesso, pendente, cancelado.
6. Webhook — tens domínio público? Se não, qual o túnel de dev (ngrok, Cloudflare Tunnel)?
7. Ambiente — sandbox (pk_test_…) ou produção (pk_live_…)?

## FASE 2 — Credenciais e ambiente
Vou colar:
  FACIPAY_PUBLISHABLE_KEY = pk_test_XXXX
  FACIPAY_CLIENT_ID       = XXXX
  FACIPAY_CLIENT_SECRET   = XXXX
  FACIPAY_WEBHOOK_SECRET  = XXXX
Regras:
- CLIENT_SECRET e WEBHOOK_SECRET ficam só no backend. PUBLISHABLE_KEY pode ir ao frontend.
- applicationUUID = PUBLISHABLE_KEY sem o prefixo pk_test_/pk_live_.
- Base URL: sandbox → https://sandbox.api.faciconnect.com · produção → https://api.faciconnect.com
- Acrescenta as variáveis ao .env e versões vazias ao .env.example.

## FASE 3 — Implementação
### Backend
Endpoint A — POST /api/facipay/create-order (chamado pelo frontend antes do popup):
1. Validar itens e RECALCULAR o total no servidor (nunca confiar no cliente).
2. Criar registo em `orders` com status 'PEN' e external_transaction_id = "order_" + UUID.
3. Obter access token (cache em memória até ~60s antes de expirar):
   POST {baseUrl}/token
   Authorization: Basic base64(CLIENT_ID:CLIENT_SECRET)
   Content-Type: application/x-www-form-urlencoded
   grant_type=client_credentials&validity_period=3600
4. Criar a ordem:
   POST {baseUrl}/facipaypartner/createPaymentOrder
   Authorization: Bearer <token>
   Accept-Language: pt
   {
     "externalTransactionId": "order_<UUID>",
     "clientId": "<id estável do utilizador>",
     "applicationUUID": "<PUBLISHABLE_KEY sem prefixo>",
     "name": "Compra",
     "amount": <inteiro AOA, sem decimais>,
     "quantity": 1,
     "additionalInfo": "{\"returnUrl\":\"...\",\"cancelUrl\":\"...\",\"webhookUrl\":\"...\"}"
   }
5. Persistir response.data.referenceNumber e devolvê-lo ao frontend (string não-vazia).

Endpoint B — POST /api/facipay/webhook (fonte da verdade):
1. Ler o body CRU antes de qualquer parser JSON.
2. Calcular HMAC SHA-256 do body cru com WEBHOOK_SECRET; comparar com o header
   x-facipay-content-token usando timingSafeEqual. Inválido → 401 e parar.
3. Só agora JSON.parse(raw).
4. Idempotência por externalTransactionId (se já CON/CAN, responder 200 sem reprocessar).
5. Atualizar orders.status segundo data.paymentStatus: CON=pago, CAN=cancelado, PEN=pendente.
6. Responder 200 em < 5s (tarefas pesadas vão para fila/background).

Endpoint C — GET /api/facipay/order/:externalTransactionId/status (fallback, só rede de
segurança): consulta GET {baseUrl}/facipaypartner/paymentByExternalTransaction.

### Frontend
Carregar a SDK uma vez:
  <script src="https://cdn.faciconnect.com/sdks/v1/facipay.min.js"></script>
Container no DOM antes do .render(). Inicialização:
  const facipay = FaciPay(PUBLISHABLE_KEY);
  facipay.generateButton({
    async createOrder() { /* POST /api/facipay/create-order → referenceNumber */ },
    async onApprove(data, actions) {
      actions.onPopupWindowClosed(() => location.href = `/sucesso?orderId=${data.payment.orderId}`);
    },
    async onPending(data, actions) {
      actions.onPopupWindowClosed(() => {
        const ref = data.payment.data.paymentReference;
        const entity = data.payment.data.entity.number;
        location.href = `/pendente?ref=${ref}&entity=${entity}`;
      });
    },
    async onCancel() { location.href = '/cancelado'; },
    async onError(e) { console.error('FaciPay error:', e); },
    options: {
      style: { width: '100%', shape: 'pill' },
      config: { lang: 'pt', showAmount: true },
      paymentConfig: {
        theme: 'light',
        allowedPaymentMethods: ['FPMCXEXPRSS', 'FPSOLPGEXT', 'FPSOLPG'],
        showUIOfProcessingInfo: true,
        referencePaymentLifeSpan: 1440
      }
    }
  }).render('#facipay-button-container');

## FASE 4 — Verificação
1. SDK carrega sem erros e o botão renderiza.
2. Multicaixa Express → onApprove → /sucesso.
3. Referência EMIS → onPending → /pendente com ref e entity.
4. Cancelar → onCancel → /cancelado.
5. Webhook com assinatura válida → estado muda na BD.
6. Webhook com assinatura inválida → 401, BD não muda.
7. Webhook duplicado → 200, sem reprocessar.
8. Endpoint C devolve o mesmo estado da BD após o webhook.

## Regras críticas
- Moeda AOA, amount inteiro sem decimais. Total recalculado no servidor.
- HMAC valida-se sobre o body cru, antes de JSON.parse. Webhook é a fonte da verdade e é
  idempotente por externalTransactionId.
- createOrder() devolve string não-vazia. Container existe no DOM antes de .render().
- Testar tudo com pk_test_ antes de pk_live_.

Começa pela Fase 1. Faz as perguntas. Espera.
Passa primeiro pelo Quickstart se quiseres perceber o fluxo antes de deixar a IA escrever o código.

Em breve

Estamos a preparar uma tela interativa para personalizar este prompt — escolher os métodos de pagamento padrão, o comportamento do botão e a tua stack, e gerar um prompt à medida do teu projeto.