Communication via postMessage and Error Handling

The communication between the store page and the iframe occurs through postMessage events sent by the checkout and the front-end's own states. These mechanisms allow adjusting the display, reacting to successes, capturing failures, and keeping the payment flow functional even when errors occur.

Front-end Errors

Failure to create the payment page

The most critical error occurs when the checkoutUrl is not present in the back-end response. The front-end stops loading and displays a friendly message.

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 does not appear

The iframe does not load when the checkoutUrl is empty or invalid. Validating the URL helps identify the issue:

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

CORS Error

Occurs when the Checkout API is called directly from the front-end. The call must always occur through the back-end.

Null or non-standard URL

Changes in the API structure can cause the URL to appear in different fields:

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

Back-end Errors

Error 401 (Unauthorized)

Indicates a problem with the MerchantId or authentication configuration. It is necessary to review the header sent to the API.

Failure to create checkout in the API

The back-end must handle API exceptions and return a consistent response to the 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
  });
}

This handling allows:

  • Differentiating API errors from internal failures;
  • Returning an appropriate HTTP status code;
  • Exposing a controlled error message to the front-end.

Best Practices for Error Handling

Some practices help make the checkout flow more resilient:

  • Implement timeouts in API calls;
  • Consider retries with exponential backoff in instability scenarios;
  • Apply idempotency in order or checkout creation;
  • Validate input data before sending to the back-end;
  • Monitor errors and API responses to detect failure patterns;
  • Use TLS and Content Security Policies (CSP) on the store's domain.

Timeout and iframe fallback

Loading the iframe uses a maximum time to detect when the checkout has not responded in time. When this limit is reached, the flow triggers the fallback, preventing the shopper from staying indefinitely on the loading screen and providing a safe alternative to continue the payment.

Timeout

const FAIL_TIMEOUT_MS = 7000; // ajuste conforme sua UX

Markup Structure

<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>

How It Works

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);
}

This flow keeps the loading process under control and provides an alternative when the iframe does not respond in time.