Pular para o conteúdo principal

Checkout transparente com Stripe Elements

Num checkout transparente, o formulário do cartão fica na sua página: sem redirecionar para uma página hospedada, sem painel. O seu cliente digita o cartão em campos renderizados pelo Stripe; a Pilot Status credita a carteira ou salva o cartão. Use-o para abastecer um workspace — por exemplo, o workspace de cada cliente final quando você revende — de dentro do seu próprio produto. Os endpoints estão descritos em Carteira e cartão pela API. Este guia mostra como as peças se encaixam.

Como funciona

  1. O seu servidor pede um intent à Pilot Status, com a API key.
  2. A sua página carrega o Stripe.js com a publishableKey da resposta, monta o Payment Element com o clientSecret e confirma com o Stripe.
  3. O seu servidor chama o sync, que lê o resultado no Stripe e credita a carteira (ou salva o cartão) na hora.
Você não precisa de uma conta Stripe: a publishableKey e os intents são nossos.
PCI: os dados do cartão nunca chegam ao seu servidor nem ao nosso. O número, a validade e o CVC são digitados nos campos do próprio Stripe, dentro de um iframe servido pelo Stripe, e vão direto do navegador para o Stripe. O seu servidor só vê o clientSecret e os ids. Mantenha assim:
  • carregue o Stripe.js de https://js.stripe.com, nunca de uma cópia hospedada por você;
  • nunca crie campos de cartão próprios, e nunca envie dados de cartão para nenhuma API, a nossa incluída;
  • mantenha a API key só no seu servidor. O navegador recebe o clientSecret, nunca a chave;
  • não registre o clientSecret em log: quem o tiver pode confirmar o intent.

1. Seu servidor: crie o intent

Exponha uma rota no seu backend que chame a Pilot Status. Com uma chave de conta, acrescente x-workspace-id para abastecer um workspace específico.
server.js (Node 18+)
  • Idempotency-Key: gere um id por tentativa de checkout no navegador e reaproveite-o nas novas tentativas. Uma nova tentativa em até 24 horas recebe o mesmo intent de volta, em vez de um segundo pedido.
  • 503 PAYMENTS_NOT_CONFIGURED: pagamentos com cartão estão indisponíveis do nosso lado. Mostre outra opção — em workspaces em BRL, o Pix.

2. Sua página: monte o Payment Element e confirme

checkout.html

3. Seu servidor: sync e reação

O sync responde com o resultado na hora:
  • orderStatus: "COMPLETED" — a carteira já tem o dinheiro. credited é true na chamada que creditou, e balance é o novo saldo.
  • orderStatus: "PENDING" — o Stripe ainda não confirmou. Não faça nada: quando o Stripe confirmar, nós creditamos a carteira por conta própria.
  • orderStatus: "FAILED" — o pagamento foi cancelado. Comece uma nova tentativa com uma nova Idempotency-Key.
A carteira é creditada uma vez só, seja pelo seu sync, seja pelo nosso próprio aviso do Stripe. Para saber disso do seu lado sem consultar em loop, inscreva um webhook do workspace em wallet.topup_paid: ele dispara uma vez por recarga creditada, seja Pix ou cartão.

Erro no navegador, mas cobrou

O stripe.confirmPayment pode voltar com um erro de rede mesmo quando a Stripe já cobrou o cartão — a conexão caiu depois da cobrança e antes de a resposta chegar ao navegador. Isso aconteceu em produção em 29/10/2026. Nunca mostre “pagamento recusado” só por esse erro. Para um error do tipo api_connection_error (ou qualquer erro que não seja recusa do cartão), confira com o servidor antes de dizer qualquer coisa ao cliente:
  1. Consulte o POST /v1/billing/payment-intents/{paymentIntentId}/sync — ou o GET /v1/billing/topups/{orderId}, com o orderId do passo 1 — a cada 2–3 segundos, por cerca de 30 segundos.
  2. Pare assim que o sync responder orderStatus: "COMPLETED" (ou a recarga responder status: "paid"): o pagamento foi aprovado.
  3. Se ainda estiver pendente depois de ~30 s, diga ao cliente que o pagamento está sendo confirmado, não que falhou. Só mostre falha quando o sync responder orderStatus: "FAILED" (ou a recarga, status: "failed").
A fonte da verdade é o webhook de workspace wallet.topup_paid: ele dispara uma vez por recarga creditada, mesmo quando o navegador nunca soube que o pagamento deu certo. Concilie seus registros por ele, não pelo que o navegador viu.

Salvar um cartão sem cobrar

O fluxo é o mesmo com um setup intent:
  1. Servidor: POST /v1/billing/card/setup-intent sem corpo → clientSecret, setupIntentId, publishableKey.
  2. Página: stripe.elements({ clientSecret }), monte "payment", depois stripe.confirmSetup({ elements, redirect: "if_required" }).
  3. Servidor: POST /v1/billing/card/setup-intent/{setupIntentId}/sync → saved: true e o card (bandeira, 4 últimos dígitos, validade).
O cartão passa a ser o cartão salvo do workspace, substituindo o anterior. A partir daí, ele cobre a carteira em toda cobrança: números, renovações do plano e linhas telefônicas.

Testes

Use os cartões de teste do Stripe só num ambiente cuja publishableKey comece com pk_test_. Em produção (pk_live_), todo pagamento confirmado é uma cobrança real.