curl --request POST \
--url https://api.usevext.com/v1/embed/sessions/{code}/confirm \
--header 'Content-Type: application/json' \
--header 'X-Vext-Client-Secret: <x-vext-client-secret>' \
--header 'X-Vext-Publishable-Key: <api-key>' \
--data '{}'<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.usevext.com/v1/embed/sessions/{code}/confirm",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Vext-Client-Secret: <x-vext-client-secret>",
"X-Vext-Publishable-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}const options = {
method: 'POST',
headers: {
'X-Vext-Client-Secret': '<x-vext-client-secret>',
'X-Vext-Publishable-Key': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({})
};
fetch('https://api.usevext.com/v1/embed/sessions/{code}/confirm', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.usevext.com/v1/embed/sessions/{code}/confirm"
payload = {}
headers = {
"X-Vext-Client-Secret": "<x-vext-client-secret>",
"X-Vext-Publishable-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"object": "charge",
"id": "ch_01k1y6r6m6q2x0p3d9v4t7c8n2",
"status": "pending",
"payment_method": "pix",
"amount": 123,
"currency": "BRL",
"platform_fee": 123,
"net_amount": 123,
"refunded_amount": 123,
"description": "<string>",
"reference": "<string>",
"metadata": {
"carrinho": "cart_88f21",
"campanha": "black-friday"
},
"customer": {
"name": "<string>",
"email": "<string>",
"document": "111.***.***-35"
},
"pix": {
"qr_code": "<string>",
"expires_at": "2023-11-07T05:31:56Z",
"end_to_end_id": "<string>"
},
"card": {
"brand": "visa",
"last_four": "<string>",
"holder_name": "<string>",
"installments": 123,
"interest": 123,
"three_d_secure": {
"status": "Y",
"transaction_id": "<string>"
},
"authorized_at": "2023-11-07T05:31:56Z"
},
"created_at": "2023-11-07T05:31:56Z",
"paid_at": "2023-11-07T05:31:56Z",
"expired_at": "2023-11-07T05:31:56Z",
"refunded_at": "2023-11-07T05:31:56Z"
}Confirmar a sessão
Cobra pela sessão a partir do navegador e devolve a cobrança criada.
curl --request POST \
--url https://api.usevext.com/v1/embed/sessions/{code}/confirm \
--header 'Content-Type: application/json' \
--header 'X-Vext-Client-Secret: <x-vext-client-secret>' \
--header 'X-Vext-Publishable-Key: <api-key>' \
--data '{}'<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.usevext.com/v1/embed/sessions/{code}/confirm",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Vext-Client-Secret: <x-vext-client-secret>",
"X-Vext-Publishable-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}const options = {
method: 'POST',
headers: {
'X-Vext-Client-Secret': '<x-vext-client-secret>',
'X-Vext-Publishable-Key': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({})
};
fetch('https://api.usevext.com/v1/embed/sessions/{code}/confirm', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.usevext.com/v1/embed/sessions/{code}/confirm"
payload = {}
headers = {
"X-Vext-Client-Secret": "<x-vext-client-secret>",
"X-Vext-Publishable-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"object": "charge",
"id": "ch_01k1y6r6m6q2x0p3d9v4t7c8n2",
"status": "pending",
"payment_method": "pix",
"amount": 123,
"currency": "BRL",
"platform_fee": 123,
"net_amount": 123,
"refunded_amount": 123,
"description": "<string>",
"reference": "<string>",
"metadata": {
"carrinho": "cart_88f21",
"campanha": "black-friday"
},
"customer": {
"name": "<string>",
"email": "<string>",
"document": "111.***.***-35"
},
"pix": {
"qr_code": "<string>",
"expires_at": "2023-11-07T05:31:56Z",
"end_to_end_id": "<string>"
},
"card": {
"brand": "visa",
"last_four": "<string>",
"holder_name": "<string>",
"installments": 123,
"interest": 123,
"three_d_secure": {
"status": "Y",
"transaction_id": "<string>"
},
"authorized_at": "2023-11-07T05:31:56Z"
},
"created_at": "2023-11-07T05:31:56Z",
"paid_at": "2023-11-07T05:31:56Z",
"expired_at": "2023-11-07T05:31:56Z",
"refunded_at": "2023-11-07T05:31:56Z"
}vext.js, no clique do botão de pagar. O valor, os itens, o reference e o
metadata vêm da sessão e são ignorados no corpo — é essa a razão de a sessão existir: a chave
publicável está no código-fonte da sua página, e sem o congelamento ela seria uma porta de cobrança
de valor arbitrário. Ver Drop-in.pending, e esta resposta é o QR — não o pagamento. Liberar o produto ao
receber 201 entrega de graça para quem fechou o aplicativo do banco antes de pagar. Quem decide o
desfecho é o webhook charge.paid; o quadro acompanha por
GET /v1/embed/sessions/{code}/status.Authorizations
Chave publicável do vendedor, criada no painel em
Desenvolvedores → Embed, no formato pk_live_… / pk_test_….
Ao contrário da secreta, esta é para o NAVEGADOR: ela fica no código-fonte da página e qualquer pessoa a lê. Isso é esperado, e não um descuido - guardamos o valor em claro justamente para você poder copiá-lo de novo a cada site.
O que a protege é a lista de domínios autorizados da própria
chave. Uma requisição vinda de origem que não está na lista é
recusada com 403 origin_not_allowed, e a chave recém-criada, sem
nenhum domínio declarado, recusa tudo.
Ela não carrega escopo. Uma pk_ não lista cobrança, não
estorna e não consulta saldo: o que ela alcança são os endpoints
sob /v1/embed, e essa lista é fechada. Por isso os endpoints
deste grupo declaram publishableKey: [] - a lista vazia é
literal, e não uma omissão.
Na navegação que carrega o iframe não há como enviar cabeçalho; ali
a chave vai em ?pk=. Quando os dois vêm, o cabeçalho vence.
Headers
O client_secret devolvido na abertura da sessão. A chave
publicável diz de qual LOJA é o checkout; este diz qual VENDA.
Cabeçalho, e nunca query: na URL ele entraria no log de acesso e
vazaria pelo Referer de qualquer recurso que a página carregasse
depois.
Path Parameters
Código público da sessão, no formato cs_….
^cs_[0-9a-z]{26}$Body
O corpo da confirmação, montado pelo nosso embed: o comprador (nome, e-mail, documento e telefone) e, no cartão, o token, as parcelas, o nome impresso e o endereço de cobrança.
O que NÃO está aqui é o ponto: valor, itens e loja vêm da sessão. O
único campo deste corpo com uma decisão de verdade é o
payment_method, e é por isso que ele está documentado à parte -
os demais são o preenchimento do formulário.
Como o comprador escolheu pagar. Obrigatório, e restrito ao
que a SESSÃO oferece: o valor tem de estar em
payment_methods, lido em GET /v1/embed/sessions/{code}.
Fora dela, 422.
A lista permitida é a da sessão e não o enum inteiro pela mesma razão que o valor não vem do corpo - ela foi congelada pelo servidor do lojista, e aceitar aqui um método fora dela deixaria o navegador escolher o que o lojista recusou.
Ele decide que MAIS o corpo carrega:
- com
credit_card, vêminstallments,card_token,card_holder_nameebilling_address, além dos campos de 3DS quando a loja exige autenticação do portador; - com
pix, nenhum deles é enviado. Não há cartão para parcelar, endereço para o antifraude comparar nem portador para autenticar, e o que chega é só o comprador.
Numa sessão de um clique (card_id) o campo não é lido: o
método é sempre cartão, porque não existe PIX guardado no
cofre.
pix, credit_card Response
A cobrança.
Uma cobrança, como a API a devolve. Todo campo desta lista está
sempre presente - os anuláveis vêm com null, nunca ausentes.
Marcar como opcional o que nunca falta obrigaria você a testar a
existência de uma chave que sempre existe.
Nos webhooks há uma exceção documentada: veja ChargeMinimal.
"charge"Código público da cobrança. Nunca é a chave primária: expor o autoincremento diria a qualquer cliente quantas vendas a plataforma inteira processou.
"ch_01k1y6r6m6q2x0p3d9v4t7c8n2"
Situação da cobrança. Só os estados de trânsito - pending,
processing e in_analysis - ainda mudam sozinhos; os demais são
terminais, exceto os que ainda admitem estorno.
processing e in_analysis são do cartão: a autorização é
decidida na mesma requisição, mas pode parar na análise de fraude
antes de virar paid ou failed. No PIX a cobrança vai direto de
pending para o desfecho.
partially_refunded é estado próprio, e não um refunded com
asterisco: tratar uma venda devolvida pela metade como estornada
faria seu relatório descontar o valor inteiro.
chargedback é definitivo: o dinheiro voltou pelo caminho da
bandeira, e nem cancelamento nem estorno são mais possíveis.
pending, processing, in_analysis, paid, expired, canceled, partially_refunded, refunded, failed, chargedback O meio pelo qual esta cobrança foi criada. Omitir o campo na
criação continua significando pix - é o que toda integração
que existe hoje espera, e mudar isso as quebraria todas de uma
vez.
pix, credit_card Valor cobrado, em centavos inteiros.
"BRL"Nossa taxa, em centavos inteiros. Congelada na criação: reflete o contrato que valia quando a venda aconteceu, não o de hoje.
O que fica com o vendedor, em centavos inteiros: amount menos
a nossa taxa e menos a taxa do meio de pagamento, que é
descontada direto do recebimento dele. Por isso
amount - platform_fee dá um número MAIOR que este.
Total já devolvido ao comprador, em centavos inteiros. Acumulado entre estornos parciais.
O que você enviou na criação. Sempre um objeto, {} quando
não houve nada - nunca null, para o seu código não ter dois
casos a tratar.
Show child attributes
Show child attributes
{
"carrinho": "cart_88f21",
"campanha": "black-friday"
}
null na cobrança que não tem comprador vinculado. A chave em
si nunca falta.
Show child attributes
Show child attributes
Presente em toda cobrança, inclusive nas de cartão - onde os
três campos vêm null. Um objeto sempre presente evita o
if (charge.pix) antes de cada leitura.
Show child attributes
Show child attributes
Presente em toda cobrança, inclusive nas de PIX - onde os campos
vêm null. Mesma regra do pix acima, e pelo mesmo motivo.
O que não está aqui, e não vai estar: os seis primeiros
dígitos, o código de retorno da adquirente e a nota do
antifraude. Os dois primeiros são diagnóstico nosso; a nota numa
resposta pública seria um oráculo de risco para quem testa
cartão em lote. O código de recusa sai numa recusa, em
error.details.decline_code, onde quem lê é o seu servidor.
Show child attributes
Show child attributes