Comunicação via postMessage e tratamento de erros

A comunicação entre a página da loja e o iframe ocorre por eventos do postMessage enviados pelo checkout e pelos estados do próprio front-end. Esses mecanismos permitem ajustar a exibição, reagir a sucessos, capturar falhas e manter a jornada de pagamento funcional mesmo quando há erros.

Erros no front-end

Falha ao criar a página de pagamento

O erro mais crítico ocorre quando a checkoutUrl não está presente na resposta do back-end. O front-end interrompe o carregamento e exibe uma mensagem amigável.

try {
  const data = await CheckoutFetcher.callCreateCheckout(apiUrl, payload);
        
        // Extrai a checkoutUrl da resposta
        // A URL pode estar em diferentes lugares dependendo da versão da API
        let checkoutUrl = '';
        if (data?.data?.settings?.checkoutUrl) {
          checkoutUrl = data.data.settings.checkoutUrl;
        } else if (data?.data?.checkoutUrl) {
          checkoutUrl = data.data.checkoutUrl;
        } else if (data?.data?.url) {
          checkoutUrl = data.data.url;
        }
        
        this.checkoutUrl = checkoutUrl;
        
        if (!this.checkoutUrl) {
          this.error = 'URL de checkout não encontrada na resposta.';
        }
      } catch (e) {
        this.error = 'Erro ao criar checkout.';
        console.error(e);
      } finally {
        this.loading = false;
      }
    }
  }
};

Iframe não aparece

O iframe não carrega quando a checkoutUrl está vazia ou inválida. Validar a URL ajuda a identificar o problema:

console.log('checkoutUrl recebida:', checkoutUrl);

Erro CORS

Ocorre quando a API Checkout é chamada diretamente do front-end. A chamada deve sempre ocorrer pelo back-end.

URL nula ou fora do padrão

Alterações na estrutura da API podem fazer com que a URL apareça em campos diferentes:

const checkoutUrl =
  data?.data?.settings?.checkoutUrl ||
  data?.data?.checkoutUrl ||
  data?.data?.url;

Erros no backend

Erro 401 (Unauthorized)

Indica problema com o MerchantId ou configuração de autenticação. É necessário revisar o envio do header para a API.

Falha ao criar o checkout na API

O back-end deve tratar exceções da API e retornar uma resposta consistente para o front-end.

try {
  const { data } = await axios.post(
    'https://cieloecommerce.cielo.com.br/api/public/v1/orders/',
    payload,
    {
      headers: {
        'Content-Type': 'application/json',
        Accept: 'application/json',
        MerchantId: CIELO_MERCHANT_ID
      },
      timeout: 10000
    }
  );

  const checkoutUrl = data?.CheckoutUrl || data?.checkoutUrl || data?.url;
  return res.json({ checkoutUrl });

} catch (err) {
  return res.status(err.response?.status || 500).json({
    message: 'Falha ao criar checkout',
    detail: err.response?.data || err.message
  });
}

Esse tratamento permite:

  • Diferenciar erros da API de falhas internas;
  • Retornar código HTTP adequado;
  • Expor uma mensagem de erro controlada para o front-end.

Boas práticas de tratamento

Algumas práticas ajudam a tornar o fluxo de checkout mais resiliente:

  • Implementar timeouts nas chamadas para a API;
  • Considerar retries com intervalo exponencial em cenários de instabilidade;
  • Aplicar idempotência na criação de pedidos ou de checkouts;
  • Validar dados de entrada antes de enviar ao backend;
  • Monitorar erros e respostas da API para detectar padrões de falha;
  • Utilizar TLS e políticas de conteúdo (CSP) no domínio da loja.

Timeout e fallback do iframe

O carregamento do iframe usa um tempo máximo para identificar quando o checkout não respondeu a tempo. Quando esse limite é atingido, o fluxo ativa o fallback, evitando que a pessoa compradora permaneça indefinidamente na tela de carregamento e oferecendo uma alternativa segura para continuar o pagamento.

Timeout

const FAIL_TIMEOUT_MS = 7000; // ajuste conforme sua UX

Estrutura de marcação

<div id="checkout-loading" role="status">
  Carregando o checkout…
</div>

<div id="checkout-fallback" hidden>
  Não foi possível carregar o checkout embutido.
  <a id="checkout-direct-link" rel="noopener">Abrir pagamento</a>.
</div>

Funcionamento

function loadCheckout(url) {
  directLink.href = url;
  iframe.src = url;

  let finished = false;
  const FAIL_TIMEOUT_MS = 7000;

  iframe.addEventListener(
    'load',
    () => {
      finished = true;
      loadingEl.hidden = true;
    },
    { once: true }
  );

  setTimeout(() => {
    if (!finished) {
      loadingEl.hidden = true;
      fallbackEl.hidden = false;
    }
  }, FAIL_TIMEOUT_MS);
}

Esse fluxo mantém o carregamento sob controle e oferece alternativa quando o iframe não responde a tempo.