Skip to content

Implementando o botão Apple Pay com o Appmax JS

Este guia cobre a parte do fluxo Apple Pay que roda no navegador, via appmax.min.js: como o botão é renderizado, o contrato de DOM que o script exige e como conectar os callbacks até o pagamento. Para o restante do fluxo, veja:

  • Instalação do aplicativo — o domain_name precisa estar configurado antes de qualquer coisa aqui funcionar.
  • Configuração de domínios para Apple Pay — publicação do arquivo .well-known, obrigatória em todos os modelos.
  • Appmax JS — assinatura completa do init, coleta de IP e tokenização de cartão (este guia assume que você já leu a seção "Como usar" de lá).
  • Pagamento com Apple Pay — o contrato do endpoint (payload, mapeamento do Apple Token, modelo integrado × fluxo direto, FAQ).

Pré-requisitos rápidos

  1. Instalação autorizada com domain_name (ou domain_names).
  2. Arquivo .well-known publicado no domínio.
  3. Script (scripts.appmax.com.br/appmax.min.js em produção) carregado na página.

Sem os três, o botão pode até aparecer, mas a validação do domínio falha na hora de abrir a PaymentSheet.


Renderizando o botão

O appmax.min.js reconhece dois seletores no DOM. Você só precisa de um deles — não dos dois.

SeletorPapel
.appmax-apple-pay-btnContainer que você renderiza vazio. O SDK substitui o innerHTML dele pelo botão oficial da Apple (SVG + estilos inclusos). Caminho recomendado — você não precisa desenhar o botão.
[data-appmax-apple-pay]O botão em si. É neste elemento que o SDK registra o click. Use esse atributo direto no seu próprio botão se preferir controlar o markup.

Caminho mais simples — deixe o SDK desenhar o botão:

html
<div class="appmax-apple-pay-btn"></div>

O SDK injeta algo como:

html
<button data-appmax-apple-pay class="applepay-button">
  <span class="applepay-button__label">Pagar com</span>
  <svg class="applepay-button__logo">...</svg>
</button>

O botão só aparece se o dispositivo suportar Apple Pay

Fora do Safari (ou em dispositivo sem Apple Pay configurado), ApplePaySession.canMakePayments() retorna false e o SDK não ativa o botão — ele pode ficar vazio ou oculto, dependendo de como você estilizou o container. Isso é esperado, não é bug.


Ordem de carregamento: o botão precisa existir antes do init

O AppmaxScripts.init(...) procura o botão uma única vez, no momento em que roda, e não observa mudanças no DOM depois disso — o mesmo contrato descrito em "Contrato de DOM" na página do Appmax JS. Para o botão do Apple Pay, a consequência prática é:

  • Se o container/botão só é montado depois do init — atrás de uma rota, um passo do checkout, um v-if/condicional — o clique não vai disparar nada. Sem erro, sem log.
  • Se o componente que chama init pode re-renderizar (StrictMode, Fast Refresh, um efeito mal dependenciado), lembre que init() não é idempotente: cada chamada registra um novo listener por cima do anterior.

Regra prática para SPA: garanta que o container do botão já está no DOM no exato momento em que AppmaxScripts.init(...) é chamado — nunca o contrário.


Inicializando com os callbacks do Apple Pay

Para Apple Pay, o init exige externalId, onUpdate e onAuthorize (além de onSuccess/onError, sempre obrigatórios). A referência completa de cada parâmetro está em Appmax JS → Inicializar o AppmaxScripts; aqui o foco é como esses três se conectam ao fluxo de Apple Pay especificamente.

html
<div class="appmax-apple-pay-btn"></div>

<script>
  // onUpdate: chamado quando a PaymentSheet abre e sempre que o usuário
  // muda algo nela. Deve retornar o carrinho atual, com valores numéricos
  // em reais — veja "O que o onUpdate deve retornar", abaixo.
  const onUpdate = () => ({
    total: 129.90,
    freight: 15.00,
    discount: 10.00,
    installments: 1,
    products: [{ name: 'Camiseta Preta', price: 62.45, quantity: 2 }],
  });

  // onAuthorize: chamado quando o pagamento é autorizado pelo usuário.
  // Recebe o Apple Token — o cartão tokenizado, pronto para
  // POST /v1/payments/apple-pay (veja o link acima para o payload completo).
  // Rejeite a Promise para sinalizar falha — veja "Como sinalizar falha",
  // abaixo.
  const onAuthorize = async (appleToken) => {
    await processarPagamento(appleToken);
  };

  const onSuccess = (data) => { /* ip coletado, se aplicável */ };
  const onError = (err) => console.error('Erro no Apple Pay:', err);

  window.AppmaxScripts.init(onSuccess, onError, externalId, onUpdate, onAuthorize);
</script>

Ordem e nomes dos parâmetros

init(onSuccess, onError, externalId, onUpdate, onAuthorize) — nessa ordem. Trocar onUpdate e onAuthorize de posição, ou escrever onAutorize, faz o SDK tratar o parâmetro errado como função de callback.

O externalId é o mesmo external_id retornado com HTTP 200 na etapa de instalação do aplicativo — sem ele, init lança exceção síncrona (veja o aviso em Appmax JS).


O que o onUpdate deve retornar

O onUpdate é chamado quando a PaymentSheet abre e sempre que o usuário altera algo nela. O objeto devolvido descreve o carrinho, com valores numéricos em reais — o SDK converte esse objeto no formato que a PaymentSheet do Safari consome. Você não monta os lineItems nem formata o total.

javascript
const onUpdate = () => ({
  total: 129.90,
  freight: 15.00,
  discount: 10.00,
  installments: 1,
  products: [
    { name: 'Camiseta Preta', price: 62.45, quantity: 2 },
  ],
});
CampoTipoObrigatórioO que aparece na PaymentSheet
totalnumberSimLinha de total. O rótulo é fixo em "Total" e não é configurável.
productsarrayRecomendadoUma linha por item. Sem ele, a sheet mostra só o total.
products[].namestringSim, se houver productsRótulo da linha.
products[].pricenumberSim, se houver productsPreço unitário, em reais.
products[].quantitynumberSim, se houver productsQuantidade. O valor exibido é price × quantity.
freightnumberNãoLinha "Frete". Omitida quando 0 ou ausente.
discountnumberNãoLinha "Desconto". Omitida quando 0 ou ausente.
installmentsnumberNãoNão altera a PaymentSheet. Aceito por compatibilidade.

Valores são números em reais — não centavos, nem strings

Esta é a causa mais comum de falha na abertura da PaymentSheet.

  • total: 12990 cobra R$ 12.990,00, não R$ 129,90.
  • total: '129.90' (string) faz o SDK lançar Error: Error processing payment... antes de a sheet abrir, porque ele aplica .toFixed(2) sobre o valor.

Pelo mesmo motivo, não devolva o objeto já formatado:

javascript
// ✗ Errado — este é o formato INTERNO, que o SDK gera a partir do seu retorno
{ total: { label: 'Total', amount: { currency: 'BRL', value: '129.90' } },
  displayItems: [ /* ... */ ] }

// ✓ Correto — o carrinho, com números
{ total: 129.90, products: [{ name: 'Camiseta Preta', price: 62.45, quantity: 2 }] }

Como sinalizar falha no onAuthorize

O SDK decide o resultado da PaymentSheet pelo estado da Promise que o seu onAuthorize devolve:

Sua PromiseO que o SDK faz
ResolvecompletePayment(STATUS_SUCCESS) — a sheet fecha com a confirmação da Apple.
RejeitacompletePayment(STATUS_FAILURE) — a sheet informa a falha ao comprador.

return false não sinaliza falha

Apenas a rejeição da Promise é lida como falha. Um try/catch em volta da chamada ao seu backend — o padrão natural para exibir a mensagem de erro na tela — faz a Promise resolver, e o comprador vê a confirmação da Apple em um pagamento que foi recusado.

Se você precisa tratar o erro, relance:

javascript
const onAuthorize = async (appleToken) => {
  try {
    const res = await fetch('/checkout/apple-pay', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ order_id: orderId, customer_id: customerId, appleToken }),
    });
    if (!res.ok) throw new Error('Pagamento recusado');
  } catch (err) {
    exibirErro(err);
    throw err; // obrigatório: sem isto, a sheet fecha como sucesso
  }
};

Do clique ao pagamento

  1. O usuário clica no botão → o SDK abre a ApplePaySession (PaymentSheet do Safari) e valida o merchant automaticamente — você não chama nenhum endpoint para isso.
  2. Se o usuário muda frete, parcelas ou item no meio do caminho, a Apple chama seu onUpdate para atualizar o total exibido.
  3. Ao confirmar (Face ID/Touch ID), o SDK chama o seu onAuthorize(appleToken).
  4. No onAuthorize, com o appleToken em mãos: crie o customer, crie a order e efetive o pagamento em POST /v1/payments/apple-pay — o mapeamento completo de appleToken → payload está na seção "Processar o pagamento" de Pagamento com Apple Pay.

Troubleshooting rápido

SintomaCausa provável
Botão não apareceFora do Safari, ou dispositivo sem cartão configurado no Apple Wallet. Confirme com ApplePaySession.canMakePayments().
Error processing payment... no onError e a PaymentSheet nunca abreO retorno do onUpdate está fora do contrato — normalmente valores em centavos, como string, ou o objeto já formatado. Veja "O que o onUpdate deve retornar".
Pagamento recusado aparece como aprovado para o compradorO onAuthorize capturou o erro sem relançar. Veja "Como sinalizar falha no onAuthorize".
Clique não faz nada, sem erroO botão foi montado depois do init (SPA) — veja "Ordem de carregamento" acima.
init() lança Error: External ID is required...externalId ausente ou inválido — é uma exceção síncrona, não passa por onError.
Erro genérico do Safari ao confirmar (ex.: DOMException)Sessão do merchant inválida — confira se o domínio tem o .well-known publicado (guia) e se o external_id é o mais recente da instalação.
Funciona em uma loja e não em outra, mesmo códigoDomínio da segunda loja não foi informado na instalação (domain_name) ou não tem o .well-known publicado.

A sheet nunca abriu, ou abriu e falhou?

Essa é a primeira pergunta a fazer, e ela separa dois mundos que não se misturam:

  • Nunca apareceu → o problema está no navegador, antes de qualquer rede: contrato do onUpdate, ordem de DOM/init, ou externalId ausente.
  • Apareceu e falhou no meio → o problema é de validação de merchant: domain_name da instalação, arquivo .well-known ou external_id.

Não é possível testar o fluxo completo em localhost — veja Testes e Sandbox para os detalhes de ambiente.


Projeto de referência para implementação

O repositório appmaxbrasil/appstore-demo-php contém uma implementação de referência da integração completa de Apple Pay — instalação via App Store, renderização do botão e callbacks do appmax.js no checkout, e a chamada ao endpoint de pagamento — em PHP puro e JavaScript vanilla, sem framework. Use-o como base de estudo e ponto de partida para a própria implementação, não como código de produção.

O passo a passo do Apple Pay do repositório traz um link direto para a linha de código correspondente a cada etapa descrita neste guia.