Skip to main content

Documentation Index

Fetch the complete documentation index at: https://developer.pagou.ai/llms.txt

Use this file to discover all available pages before exploring further.

Use this page when you are moving from a demo integration to a production-safe checkout.

Production-safe flow

  1. Disable duplicate submits in the browser.
  2. Call elements.submit(...) once per checkout attempt.
  3. Let your backend create the transaction.
  4. Handle next_action when 3D Secure is required.
  5. Fulfill only from webhook or reconciliation state.

Frontend submit pattern

let isSubmitting = false;
let lastTokenData = null;

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  if (isSubmitting) return;

  isSubmitting = true;

  const result = await elements.submit({
    createTransaction: async (tokenData) => {
      lastTokenData = tokenData;

      const response = await fetch("/api/pay", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          token: tokenData.token,
          amount: 2490,
          orderId: "order_2001",
        }),
      });

      const payload = await response.json();
      return payload.data ?? payload;
    },
  });

  isSubmitting = false;

  if (result.status === "error") {
    messageEl.textContent = result.error ?? "Payment failed.";
    return;
  }

  if (lastTokenData) {
    cardSummaryEl.textContent = `${lastTokenData.brand} ending in ${lastTokenData.last4}`;
  }
});
elements.submit(...) is the tokenization trigger. It creates an Element session if needed, asks the hosted card field to tokenize, then calls your createTransaction callback with the token payload.

Token payload

Your createTransaction callback receives non-sensitive card metadata with the token:
{
  "token": "pgct_token_from_browser",
  "brand": "visa",
  "last4": "4242",
  "exp_month": "12",
  "exp_year": "2030"
}
Send tokenData.token to your backend to create the transaction. Use brand, last4, exp_month, and exp_year only for provisional checkout UI or your own backend bookkeeping; final payment state still comes from the transaction response, webhook, or reconciliation.

Backend request example

{
  "external_ref": "order_2001",
  "amount": 2490,
  "currency": "BRL",
  "method": "credit_card",
  "token": "pgct_token_from_browser",
  "installments": 1
}

Backend response example

{
  "success": true,
  "requestId": "req_4003",
  "data": {
    "id": "tr_2001",
    "status": "three_ds_required",
    "next_action": {
      "type": "three_ds_challenge",
      "challenge_session_id": "3ds_1001",
      "client_secret": "sec_1001",
      "expires_at": "2026-03-16T14:20:00.000Z"
    }
  }
}

Common error

{
  "type": "https://api.pagou.ai/problems/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "The request contains invalid data.",
  "errors": [
    {
      "field": "token",
      "message": "Token is required for credit card payments",
      "code": "invalid_type"
    }
  ]
}
Fix: do not create the transaction until the browser has a token from Payment Element. If a challenge flow was interrupted, reconcile the transaction before letting the customer retry.

Invalid state handling

Keep the submit button disabled until the card field reports a valid state:
let cardIsValid = false;

card.on("change", ({ valid, brand, errors }) => {
  cardIsValid = valid;
  submitButton.disabled = !cardIsValid || isSubmitting;
  brandEl.textContent = brand ?? "";
  errorEl.textContent = Object.values(errors ?? {})[0] ?? "";
});

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  if (!cardIsValid || isSubmitting) return;

  // Call elements.submit(...) here.
});
If elements.submit(...) returns { "status": "error" }, do not call your backend again with a missing or stale token. Show the returned error, let the buyer correct the card details, then run a new checkout attempt.

Final-state rule

A browser success message is not enough to fulfill an order. Final fulfillment belongs to webhook-confirmed or reconciled payment state.