Cart Checkout
Crie um carrinho controlado pelo backend com Checkout Sessions e checkout hospedado.
Use Checkout Sessions quando seu backend controla um carrinho ou tentativa de compra específica. A sessão pode referenciar preços do catálogo com priceId ou salvar preços inline com priceData sem criar produtos, planos ou preços.
Mantenha
x-api-keyno backend. Não chame/api/v1pelo navegador nem aceite preços, recorrência ou conta como dados autorizados enviados pelo cliente.
Convenções da API
Crie chaves de API separadas com as permissões mínimas necessárias. Consultas exigem READ; criar, atualizar, excluir, expirar, cancelar e reembolsar exigem WRITE. Uma chave válida sem a permissão da operação retorna 403; uma chave ausente, inválida ou expirada retorna 401.
Liste Produtos, Links de pagamento e Checkout Sessions com limit (20 por padrão, 100 no máximo), startingAfter ou endingBefore. Use somente um cursor e obtenha o próximo em meta.lastId ou o anterior em meta.firstId. Todas as listas retornam { data, meta }.
Todos os erros retornam { error: { code, message, details? }, correlationId }. Registre o identificador de correlação com o identificador da sua solicitação. Tente novamente somente falhas seguras para a operação e reutilize o mesmo Idempotency-Key; consulte o recurso atualizado antes de repetir um conflito de versão. Consulte a Referência da API para ver status e esquemas de cada operação.
IDs de recursos
Os IDs do Pharos são strings opacas com prefixo de recurso, como cs_…, price_…, cus_… ou pi_…. Armazene e envie-os sem alterações; não interprete o sufixo. Durante a janela de compatibilidade, a API também aceita um CUID histórico retornado antes deste formato.
Arquitetura
Navegador do comprador
→ seu backend
→ Pharos /api/v1/checkout-sessions
→ checkout hospedado /checkout/session/:id
→ cálculo, fatura, assinatura e pagamento
→ webhook assinado ao backendSeu backend monta o carrinho e modifica a sessão. O Pharos gerencia o checkout e os registros comerciais. O navegador precisa apenas da url retornada.
Fluxo de compra
- O navegador solicita checkout ao backend usando a identidade do carrinho, não preços autorizados pelo navegador.
- Seu backend cria uma sessão com
POST /api/v1/checkout-sessionse umIdempotency-Keyestável para a operação. - Salve
id,version,ETageurlassociados ao carrinho. - Se precisar modificar, faça enquanto a sessão estiver
opene não preparada. Cada operação usa sua própria chave e o últimoETagemIf-Match. - Retorne a URL ao navegador para abrir o checkout hospedado.
- O comprador preenche dados e forma de pagamento. Alterações de quantidade respeitam os limites salvos pelo servidor.
- O Pharos calcula o resultado e cria cliente, fatura, assinatura e pagamento conforme necessário.
- O pagamento pode ser imediato, exigir ação ou continuar pendente.
- URLs de sucesso e cancelamento controlam navegação; não comprovam pagamento nem expiram a sessão.
- Reconcilie por
checkoutSession.ideclientReferenceIde confirme a compra somente cominvoice.paidverificado e deduplicado.
Criar uma sessão no backend
const pharosApi = 'https://api.example.com/api/v1'
type Cart = {
id: string
items: Array<{ priceId: string; quantity: number }>
}
export async function createCheckout(cart: Cart) {
const response = await fetch(`${pharosApi}/checkout-sessions`, {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-api-key': process.env.PHAROS_API_KEY!,
'idempotency-key': `cart:${cart.id}:checkout-session`,
},
body: JSON.stringify({
mode: 'PAYMENT',
theme: 'light',
clientReferenceId: cart.id,
lineItems: cart.items.map((item) => ({
...item,
adjustableQuantity: {
enabled: true,
minimum: 1,
maximum: 10,
},
})),
successUrl: 'https://shop.example/success?session={CHECKOUT_SESSION_ID}',
cancelUrl: 'https://shop.example/cart',
}),
})
if (!response.ok) {
throw new Error(await response.text())
}
return {
session: await response.json(),
etag: response.headers.get('etag'),
}
}Derive a chave de criação do carrinho ou tentativa. Repetir a mesma operação com a mesma chave retorna a mesma sessão lógica. Reutilizar com corpo diferente causa conflito de idempotência.
Associar um cliente conhecido
Envie customer com o ID de um Customer existente quando seu aplicativo já conhece o comprador. O Pharos valida que ele pertença à mesma conta e ambiente e preenche o checkout hospedado com o perfil salvo. Quando enviar customer, omita customerEmail; use customerEmail para um convidado cujo perfil deva ser resolvido ou criado durante o checkout.
customerUpdate define quais campos salvos o comprador pode substituir durante o checkout. Os campos configurados como auto são salvos automaticamente no Customer quando o checkout é concluído com sucesso. Cartões elegíveis são salvos automaticamente depois que o comprador aceita os termos obrigatórios do checkout. savedPaymentMethodOptions.redisplay: "enabled" controla se as formas salvas aparecem em sessões posteriores; a opção legada save é aceita por compatibilidade, mas não altera mais o comportamento do checkout.
Escolher catálogo ou preços inline
Use priceId quando a oferta já existe no catálogo. Use priceData para salvar um preço contextual definido pelo backend:
const customLineItem = {
priceData: {
currency: 'USD',
unitAmount: '25.00',
productData: {
name: 'Custom support package',
description: 'Configured for this order',
},
recurring: {
interval: 'MONTH',
intervalCount: 1,
trialDays: 7,
},
},
quantity: 1,
}Os dados inline ficam salvos na sessão. O navegador do checkout não pode substituir valores unitários ou recorrência.
Modificar um carrinho aberto
export async function addItem(sessionId: string, etag: string, priceId: string) {
const response = await fetch(`${pharosApi}/checkout-sessions/${sessionId}/line-items`, {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-api-key': process.env.PHAROS_API_KEY!,
'idempotency-key': crypto.randomUUID(),
'if-match': etag,
},
body: JSON.stringify({
lineItems: [{ priceId, quantity: 1 }],
}),
})
if (response.status === 409) {
throw new Error('Reload and reconcile the session before retrying')
}
if (!response.ok) {
throw new Error(await response.text())
}
return {
session: await response.json(),
etag: response.headers.get('etag'),
}
}Após uma alteração bem-sucedida, atualize sessão, version e ETag salvos. Em 409 SESSION_VERSION_CONFLICT, consulte a sessão, reconcilie o carrinho e crie uma nova alteração. Não repita cegamente dados de uma versão antiga.
Dentro de /api/v1, também pode usar:
PATCH /checkout-sessions/{id}para configurações não monetárias admitidas, como tema, redirecionamentos, email, referência ou metadata.PATCH /checkout-sessions/{id}/line-items/{itemId}para quantidade ou limites ajustáveis.DELETE /checkout-sessions/{id}/line-items/{itemId}para remover um item permitido.POST /checkout-sessions/{id}/expirepara impedir outro uso do carrinho.
Alterações por API exigem sessão aberta cujo snapshot não esteja bloqueado pela preparação do pagamento.
Redirecionar pelo navegador
const response = await fetch('/api/cart/checkout', { method: 'POST' })
if (!response.ok) throw new Error('Checkout could not be created')
const { url } = await response.json()
window.location.assign(url)Seu handler /api/cart/checkout realiza a chamada autenticada ao Pharos. Não exponha a chave de API na resposta ou código do cliente.
Estados e confirmação
Para uma cobrança direta pelo backend, crie um PaymentIntent com POST /api/v1/payment_intents. Ele não cobra por padrão: envie confirm: true ou chame /api/v1/payment_intents/{id}/confirm para criar um Charge. Consulte um Charge pelo ID e crie um Refund separado com POST /api/v1/refunds.
O status pode ser open, complete ou expired; descreve o fluxo do comprador. paymentStatus pode ser unpaid, processing, paid, failed ou no_payment_required; descreve a cobrança.
O redirecionamento de sucesso e checkout_session.completed não são sinais para entrega da compra. Consulte a sessão para reconciliar os registros e confirme somente com invoice.paid assinado. Um fluxo gratuito ou de teste exige sua política de acesso e contrato de eventos correspondente.
Resolver falhas
- Em falhas de rede, repita com a mesma chave apenas a operação exata.
- Em
400, corrija a requisição ou composição do carrinho. - Em
401ou403, confira chave, ambiente e permissões. - Em
404, confirme que chave e sessão correspondem à mesma conta. - Em
409, diferencie conflitos de idempotência e versão e reconcilie antes de repetir. - Se o snapshot estiver bloqueado, crie outra tentativa em vez de modificar.
- Se o pagamento estiver em processamento, aguarde o webhook assinado.
Configuração comercial
Consulte Produtos e preços, Cupons, Promoções e Descontos. Condições por produto exigem identidades correspondentes do catálogo; um nome inline igual não garante elegibilidade. Use apenas opções admitidas pela API, sem presumir que cada ajuste do painel tem equivalente público.
Consulte Assinaturas e Faturas para interpretar os registros criados.