Fluxos de consentimento
Autorize, rastreie e reutilize o consentimento da carteira para NuPay e PayPal Wallet.
Nesta página
Consentimento é a autorização do cliente que uma carteira suportada exige antes que a DEUNA possa recuperar opções de pagamento, armazenar uma conta ou concluir uma compra. A DEUNA expõe um contrato de consentimento público enquanto adapta o fluxo específico do fornecedor por trás dele.
Provedores e comportamento suportados#
| Provedor | Quem usa o consentimento | Experiência de aprovação | Fonte de status | Reutilizar |
|---|---|---|---|---|
| NuPay | Convidado ou cliente autenticado, quando habilitado para a conexão | O cliente aprova na experiência do Nubank | Webhook do provedor; GET /merchants/orders/{order_token}/consent retorna o último estado DEUNA | Vinculado ao pedido e à identidade do cliente; autorização expirada pode ser atualizada quando suportada |
| Carteira PayPal | Cliente autenticado usando PayPal Vault | Redirecionar o cliente para o retornado redirect_url | DEUNA pesquisa o PayPal quando o consentimento se torna elegível para verificação | A conta PayPal aprovada é exposta como método de pagamento armazenado para compras posteriores |
Outros métodos de pagamento podem usar redirecionamentos, OTP ou 3DS, mas não usam esta API de consentimento. Não chame os pontos de extremidade de consentimento, a menos que a conexão da carteira selecionada esteja configurada para consentimento.
Ciclo de vida#
A progressão normal do estado é pending para success. Tratar failed e expired como terminal. Trate qualquer estado não reconhecido ou negado como malsucedido e não envie a compra junto com ele.
Antes de começar#
- Configure a conexão NuPay ou PayPal Wallet para a loja e ambiente corretos.
- Criar um pedido e manter o seu
order_token. - Para contas reutilizáveis do PayPal, autentique o cliente e envie o token ao portador do usuário nas solicitações de consentimento.
- Leia a resposta do método de pagamento. Para o cofre do PayPal,
authorization.required: trueeauthorization.flow: "consent"indicar que o cliente precisa de consentimento. Use os campos de pesquisa retornados em vez de codificar uma cadência. - Configurar
order.webhook_urls.notify_orderpara que seu back-end receba alterações de estado de consentimento.
Criar consentimento#
POST /merchants/orders/{order_token}/consent
A rota pública do API Gateway intencionalmente não inclui a rota interna /api/v1 prefixo de serviço.
curl --request POST \
--url 'https://api.sandbox.deuna.io/merchants/orders/ORDER_TOKEN/consent' \
--header 'X-API-KEY: YOUR_PRIVATE_API_KEY' \
--header 'X-Store-Code: all' \
--header 'Authorization: Bearer USER_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"payment_method": "wallet",
"payment_method_id": "MERCHANT_PAYMENT_METHOD_ID",
"identity_document": "58188896454",
"identity_document_type": "CPF"
}'const response = await fetch("https://api.sandbox.deuna.io/merchants/orders/ORDER_TOKEN/consent", {
method: "POST",
headers: {
"X-API-KEY": "YOUR_PRIVATE_API_KEY",
"X-Store-Code": "all",
"Authorization": "Bearer USER_TOKEN",
"Content-Type": "application/json"
},
body: JSON.stringify({
"payment_method": "wallet",
"payment_method_id": "MERCHANT_PAYMENT_METHOD_ID",
"identity_document": "58188896454",
"identity_document_type": "CPF"
})
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
const data = await response.json();import requests
response = requests.request(
"POST",
"https://api.sandbox.deuna.io/merchants/orders/ORDER_TOKEN/consent",
headers={
"X-API-KEY": "YOUR_PRIVATE_API_KEY",
"X-Store-Code": "all",
"Authorization": "Bearer USER_TOKEN",
"Content-Type": "application/json"
},
json={
"payment_method": "wallet",
"payment_method_id": "MERCHANT_PAYMENT_METHOD_ID",
"identity_document": "58188896454",
"identity_document_type": "CPF"
},
)
response.raise_for_status()
data = response.json()<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.sandbox.deuna.io/merchants/orders/ORDER_TOKEN/consent",
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-API-KEY: YOUR_PRIVATE_API_KEY",
"X-Store-Code: all",
"Authorization: Bearer USER_TOKEN",
"Content-Type: application/json"
],
CURLOPT_POSTFIELDS => json_encode([
"payment_method" => "wallet",
"payment_method_id" => "MERCHANT_PAYMENT_METHOD_ID",
"identity_document" => "58188896454",
"identity_document_type" => "CPF"
])
]);
$response = curl_exec($curl);
curl_close($curl);import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.sandbox.deuna.io/merchants/orders/ORDER_TOKEN/consent"))
.header("X-API-KEY", "YOUR_PRIVATE_API_KEY")
.header("X-Store-Code", "all")
.header("Authorization", "Bearer USER_TOKEN")
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString("{\n \"payment_method\": \"wallet\",\n \"payment_method_id\": \"MERCHANT_PAYMENT_METHOD_ID\",\n \"identity_document\": \"58188896454\",\n \"identity_document_type\": \"CPF\"\n }"))
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(
request, HttpResponse.BodyHandlers.ofString()
);
System.out.println(response.body());
}
}package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
request, err := http.NewRequest("POST", "https://api.sandbox.deuna.io/merchants/orders/ORDER_TOKEN/consent", strings.NewReader("{\n \"payment_method\": \"wallet\",\n \"payment_method_id\": \"MERCHANT_PAYMENT_METHOD_ID\",\n \"identity_document\": \"58188896454\",\n \"identity_document_type\": \"CPF\"\n }"))
if err != nil { panic(err) }
request.Header.Set("X-API-KEY", "YOUR_PRIVATE_API_KEY")
request.Header.Set("X-Store-Code", "all")
request.Header.Set("Authorization", "Bearer USER_TOKEN")
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
body, _ := io.ReadAll(response.Body)
fmt.Println(string(body))
}| Campo | Obrigatório | Descrição |
|---|---|---|
payment_method | Recomendado | Uso wallet. Se omitido, a DEUNA utiliza o método de pagamento indicado na encomenda. |
payment_method_id | Recomendado | Identificador de conexão retornado pela resposta do método de pagamento do pedido. Evita ambiguidade quando mais de uma conexão de carteira está habilitada. |
identity_document | NuPay | Documento de identidade do cliente. Se omitido, a DEUNA pode derivá-lo do endereço do pedido, quando presente. |
identity_document_type | NuPay | Tipo de documento, como CPF. Se omitido, a DEUNA pode derivá-lo do endereço do pedido, quando presente. |
Para um usuário autenticado, o token ao portador permite que a DEUNA associe o consentimento bem-sucedido a esse usuário e conexão. O consentimento do hóspede permanece vinculado ao pedido.
Envelope de resposta
{
"id": "1625a32a-df4c-4d9b-aec1-4510b3625865",
"type": "transaction.authentication.pending",
"created": "1740608931",
"data": {
"request_id": "req_01JQ7X",
"order": {
"order_token": "7e975d44-a061-4d70-af0f-673f6ee56445",
"transaction_id": "merchant-order-1042",
"external_transaction_id": ""
},
"consent": {
"id": "6fe9a045-9a46-4b25-8b60-d5a9586494c5",
"status": "pending",
"expires_at": "2026-10-02T18:30:00Z",
"authorization_id": "provider-authorization-id",
"redirect_url": "https://provider.example/approve"
}
}
}Loja data.consent.id, mas conduza a UI de data.consent.status. Abrir redirect_url somente quando estiver presente. NuPay pode exigir aprovação no aplicativo do provedor sem retornar um redirecionamento do navegador.
Aprovação completa do fornecedor#
Para a Carteira PayPal, envie o cliente para data.consent.redirect_url. O provedor retorna através da rota de redirecionamento de consentimento da DEUNA e a DEUNA verifica o estado final do provedor. Se o cliente cancelar, o consentimento será marcado como falhado e não deverá ser usado para compra.
Para o NuPay, instrua o cliente a aprovar a solicitação na experiência do Nubank. O webhook do provedor atualiza o consentimento armazenado pela DEUNA.
Nunca crie você mesmo URLs de aprovação de provedor ou URLs de redirecionamento DEUNA. Use os URLs na resposta.
Leia o status mais recente#
GET /merchants/orders/{order_token}/consent
curl --request GET \
--url 'https://api.sandbox.deuna.io/merchants/orders/ORDER_TOKEN/consent?payment_method=wallet&payment_method_id=MERCHANT_PAYMENT_METHOD_ID' \
--header 'X-API-KEY: YOUR_PRIVATE_API_KEY' \
--header 'X-Store-Code: all' \
--header 'Authorization: Bearer USER_TOKEN'const response = await fetch("https://api.sandbox.deuna.io/merchants/orders/ORDER_TOKEN/consent?payment_method=wallet&payment_method_id=MERCHANT_PAYMENT_METHOD_ID", {
method: "GET",
headers: {
"X-API-KEY": "YOUR_PRIVATE_API_KEY",
"X-Store-Code": "all",
"Authorization": "Bearer USER_TOKEN"
}
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
const data = await response.json();import requests
response = requests.request(
"GET",
"https://api.sandbox.deuna.io/merchants/orders/ORDER_TOKEN/consent?payment_method=wallet&payment_method_id=MERCHANT_PAYMENT_METHOD_ID",
headers={
"X-API-KEY": "YOUR_PRIVATE_API_KEY",
"X-Store-Code": "all",
"Authorization": "Bearer USER_TOKEN"
},
)
response.raise_for_status()
data = response.json()<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.sandbox.deuna.io/merchants/orders/ORDER_TOKEN/consent?payment_method=wallet&payment_method_id=MERCHANT_PAYMENT_METHOD_ID",
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-API-KEY: YOUR_PRIVATE_API_KEY",
"X-Store-Code: all",
"Authorization: Bearer USER_TOKEN"
]
]);
$response = curl_exec($curl);
curl_close($curl);import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.sandbox.deuna.io/merchants/orders/ORDER_TOKEN/consent?payment_method=wallet&payment_method_id=MERCHANT_PAYMENT_METHOD_ID"))
.header("X-API-KEY", "YOUR_PRIVATE_API_KEY")
.header("X-Store-Code", "all")
.header("Authorization", "Bearer USER_TOKEN")
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(
request, HttpResponse.BodyHandlers.ofString()
);
System.out.println(response.body());
}
}package main
import (
"fmt"
"io"
"net/http"
)
func main() {
request, err := http.NewRequest("GET", "https://api.sandbox.deuna.io/merchants/orders/ORDER_TOKEN/consent?payment_method=wallet&payment_method_id=MERCHANT_PAYMENT_METHOD_ID", nil)
if err != nil { panic(err) }
request.Header.Set("X-API-KEY", "YOUR_PRIVATE_API_KEY")
request.Header.Set("X-Store-Code", "all")
request.Header.Set("Authorization", "Bearer USER_TOKEN")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
body, _ := io.ReadAll(response.Body)
fmt.Println(string(body))
}Uso authorization.start_polling_after_in_seconds e authorization.polling_interval_in_seconds da resposta do método de pagamento quando fornecido. Pare de pesquisar assim que o status não for mais pending, quando expires_at é alcançado ou quando o cliente sai do fluxo.
A resposta GET usa o mesmo envelope que criar consentimento. Sempre inspecione ambos data.consent.status e data.error; um resultado de terminal de nível de negócios pode ser representado no envelope mesmo quando a própria solicitação HTTP foi bem-sucedida.
Lidar com webhooks de consentimento#
DEUNA envia atualizações de consentimento para o pedido webhook_urls.notify_order URL. Lide com estes tipos de eventos:
| Tipo de evento | Significado |
|---|---|
transaction.authentication.pending | A autorização foi criada e ainda precisa de ação do cliente ou do fornecedor. |
transaction.authentication.updated | Autorização bem-sucedida; o consentimento pode ser usado. |
transaction.authentication.failed | A autorização falhou ou o cliente cancelou. |
transaction.authentication.expired | O consentimento expirou antes que pudesse ser usado. |
Responder com 2xx rapidamente, desduplicar por evento ide, em seguida, busque o consentimento mais recente se o seu processamento depender do estado atual. A entrega de webhook e a pesquisa GET se complementam; seu checkout deve tolerar chegar primeiro. Veja Webhooks.
Use o consentimento aprovado#
NuPay
Depois success, solicite as formas de pagamento do pedido e as opções de parcelamento do NuPay. A DEUNA utiliza a autorização de consentimento válida ao recuperar essas opções e ao processar a compra da carteira. Se a autorização expirou e o provedor oferece suporte à atualização, a DEUNA tenta atualizá-la.
Carteira PayPal
Para um usuário autenticado, a conta PayPal aprovada aparece em stored_payment_methods. Envie esse identificador de método armazenado como a compra payment_method; A DEUNA verifica se o consentimento pertence ao mesmo usuário, comerciante e conexão PayPal antes de utilizá-lo.
A remoção do método de pagamento da carteira armazenada também remove o consentimento reutilizável associado.
Remover uma conta reutilizável do PayPal
DELETE /users/payment-methods/{payment_method_id}/tokens/{payment_method}
Uso payment_method_id para o identificador de conexão do PayPal e payment_method para o identificador do método armazenado retornado em stored_payment_methods. Este endpoint autenticado pelo usuário remove a conta da carteira no provedor e exclui o consentimento reutilizável correspondente na DEUNA.
curl --request DELETE \
--url 'https://api.sandbox.deuna.io/users/payment-methods/MERCHANT_PAYMENT_METHOD_ID/tokens/STORED_PAYMENT_METHOD_ID' \
--header 'Authorization: Bearer USER_TOKEN' \
--header 'X-Merchant-ID: MERCHANT_ID'const response = await fetch("https://api.sandbox.deuna.io/users/payment-methods/MERCHANT_PAYMENT_METHOD_ID/tokens/STORED_PAYMENT_METHOD_ID", {
method: "DELETE",
headers: {
"Authorization": "Bearer USER_TOKEN",
"X-Merchant-ID": "MERCHANT_ID"
}
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
const data = await response.json();import requests
response = requests.request(
"DELETE",
"https://api.sandbox.deuna.io/users/payment-methods/MERCHANT_PAYMENT_METHOD_ID/tokens/STORED_PAYMENT_METHOD_ID",
headers={
"Authorization": "Bearer USER_TOKEN",
"X-Merchant-ID": "MERCHANT_ID"
},
)
response.raise_for_status()
data = response.json()<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.sandbox.deuna.io/users/payment-methods/MERCHANT_PAYMENT_METHOD_ID/tokens/STORED_PAYMENT_METHOD_ID",
CURLOPT_CUSTOMREQUEST => "DELETE",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer USER_TOKEN",
"X-Merchant-ID: MERCHANT_ID"
]
]);
$response = curl_exec($curl);
curl_close($curl);import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.sandbox.deuna.io/users/payment-methods/MERCHANT_PAYMENT_METHOD_ID/tokens/STORED_PAYMENT_METHOD_ID"))
.header("Authorization", "Bearer USER_TOKEN")
.header("X-Merchant-ID", "MERCHANT_ID")
.method("DELETE", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(
request, HttpResponse.BodyHandlers.ofString()
);
System.out.println(response.body());
}
}package main
import (
"fmt"
"io"
"net/http"
)
func main() {
request, err := http.NewRequest("DELETE", "https://api.sandbox.deuna.io/users/payment-methods/MERCHANT_PAYMENT_METHOD_ID/tokens/STORED_PAYMENT_METHOD_ID", nil)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer USER_TOKEN")
request.Header.Set("X-Merchant-ID", "MERCHANT_ID")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
body, _ := io.ReadAll(response.Body)
fmt.Println(string(body))
}Uma exclusão bem-sucedida retorna 204 No Content. Se o fornecedor reportar que a conta armazenada é inválida durante uma compra, a DEUNA também remove o consentimento reutilizável inválido para que não seja oferecido novamente.
Comportamento seguro de nova tentativa#
- Antes de criar um novo consentimento, leia o estado atual caso a resposta anterior tenha sido perdida.
- Um ainda válido
pendingousuccesso consentimento é reutilizado em vez de criar outra autorização de provedor. - Não tente novamente
failedouexpiredindefinidamente. Inicie uma nova tentativa orientada ao cliente após resolver a causa. - Um
404significa que a DEUNA não conseguiu encontrar consentimento para o pedido ou contexto do usuário. - Um
400pode indicar campos inválidos, uma conexão não suportada, um fluxo de convidados desabilitado ou um resultado de provedor de terminal. - Mantenha chaves de API privadas e tokens de usuário em superfícies confiáveis. Não registre cabeçalhos de solicitação ou dados de autorização do provedor.
Lista de verificação de integração#
- O pedido, a conexão, a moeda, a loja e o ambiente correspondem.
- O documento de identidade e o tipo estão disponíveis para NuPay.
- O cliente é autenticado antes de criar o consentimento reutilizável do PayPal.
- O aplicativo oferece suporte a experiências de redirecionamento de provedor e aprovação de aplicativo.
- A votação usa a cadência retornada pela DEUNA e para nos estados terminais.
notify_orderaceita e desduplica eventos de autenticação.- A compra só começa depois
data.consent.statusésuccess. - Os caminhos de falha, expiração, cancelamento e nova tentativa são testados no sandbox.