Flux de consentement
Autorisez, suivez et réutilisez le consentement du portefeuille pour NuPay et PayPal Wallet.
Sur cette page
Le consentement est l'autorisation du client dont un portefeuille pris en charge a besoin avant que DEUNA puisse récupérer des options de paiement, stocker un compte ou finaliser un achat. DEUNA expose un contrat de consentement public tout en adaptant le flux spécifique au fournisseur qui le sous-tend.
Fournisseurs et comportements pris en charge#
| Fournisseur | Qui utilise le consentement | Expérience d'approbation | Source d'état | Réutilisation |
|---|---|---|---|---|
| NuPay | Invité ou client authentifié, lorsqu'il est activé pour la connexion | Le client approuve dans l'expérience Nubank | Webhook du fournisseur ; GET /merchants/orders/{order_token}/consent renvoie le dernier état DEUNA | Lié à la commande et à l’identité du client ; l'autorisation expirée peut être actualisée lorsqu'elle est prise en charge |
| Portefeuille PayPal | Client authentifié utilisant PayPal Vault | Rediriger le client vers le retour redirect_url | DEUNA interroge PayPal lorsque le consentement devient éligible à la vérification | Le compte PayPal approuvé est exposé comme mode de paiement stocké pour les achats ultérieurs |
D'autres méthodes de paiement peuvent utiliser des redirections, OTP ou 3DS, mais elles n'utilisent pas cette API de consentement. N'appelez pas les points de terminaison de consentement à moins que la connexion au portefeuille sélectionnée ne soit configurée pour le consentement.
Cycle de vie#
La progression normale de l’état est pending à success. Traiter failed et expired comme terminal. Considérez tout état non reconnu ou refusé comme un échec et ne soumettez pas l'achat avec celui-ci.
Avant de commencer#
- Configurez la connexion NuPay ou PayPal Wallet pour le magasin et l'environnement appropriés.
- Créer une commande et conserver son
order_token. - Pour les comptes PayPal réutilisables, authentifiez le client et envoyez le jeton du porteur de l'utilisateur lors des demandes de consentement.
- Lisez la réponse relative au mode de paiement. Pour le coffre-fort PayPal,
authorization.required: trueetauthorization.flow: "consent"indiquer que le client a besoin de son consentement. Utilisez les champs d'interrogation renvoyés au lieu de coder en dur une cadence. - Configurer
order.webhook_urls.notify_orderafin que votre backend reçoive les changements d'état de consentement.
Créer un consentement#
POST /merchants/orders/{order_token}/consent
L'itinéraire public API Gateway n'inclut pas intentionnellement les informations internes /api/v1 préfixe de service.
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))
}| Champ | Obligatoire | Descriptif |
|---|---|---|
payment_method | Recommandé | Utilisation wallet. En cas d'omission, DEUNA utilise le mode de paiement indiqué sur la commande. |
payment_method_id | Recommandé | Identifiant de connexion renvoyé par la réponse du mode de paiement de la commande. Cela évite toute ambiguïté lorsque plusieurs connexions de portefeuille sont activées. |
identity_document | NuPay | Pièce d'identité du client. En cas d'omission, DEUNA peut la déduire de l'adresse de commande lorsqu'elle est présente. |
identity_document_type | NuPay | Type de document, tel que CPF. En cas d'omission, DEUNA peut la déduire de l'adresse de commande lorsqu'elle est présente. |
Pour un utilisateur authentifié, le jeton du porteur permet à DEUNA d'associer le consentement réussi à cet utilisateur et à cette connexion. Le consentement du client reste lié à la commande.
Enveloppe de réponse
{
"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"
}
}
}Magasin data.consent.id, mais conduisez l'interface utilisateur de data.consent.status. Ouvert redirect_url seulement quand il est présent. NuPay peut exiger une approbation dans l'application du fournisseur sans renvoyer de redirection du navigateur.
Approbation complète du fournisseur#
Pour PayPal Wallet, envoyez le client à data.consent.redirect_url. Le fournisseur revient via la route de redirection de consentement de DEUNA et DEUNA vérifie l'état final du fournisseur. Si le client annule, le consentement est marqué comme ayant échoué et ne doit pas être utilisé pour l'achat.
Pour NuPay, demandez au client d'approuver la demande dans l'expérience Nubank. Le webhook du fournisseur met à jour le consentement stocké par DEUNA.
Ne créez jamais vous-même des URL d’approbation de fournisseur ou des URL de redirection DEUNA. Utilisez les URL dans la réponse.
Lire le dernier statut#
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))
}Utilisation authorization.start_polling_after_in_seconds et authorization.polling_interval_in_seconds à partir de la réponse du mode de paiement une fois fournie. Arrêtez le sondage dès que le statut n'est plus pending, quand expires_at est atteint, ou lorsque le client quitte le flux.
La réponse GET utilise la même enveloppe que la création du consentement. Inspectez toujours les deux data.consent.status et data.error; un résultat de terminal au niveau métier peut être représenté dans l'enveloppe même lorsque la requête HTTP elle-même a réussi.
Gérer les webhooks de consentement#
DEUNA envoie des mises à jour de consentement à la commande webhook_urls.notify_order URL. Gérez ces types d'événements :
| Type d'événement | Signification |
|---|---|
transaction.authentication.pending | L'autorisation a été créée et nécessite toujours une action du client ou du fournisseur. |
transaction.authentication.updated | L'autorisation a réussi ; le consentement peut être utilisé. |
transaction.authentication.failed | L'autorisation a échoué ou le client a annulé. |
transaction.authentication.expired | Le consentement a expiré avant de pouvoir être utilisé. |
Répondez avec 2xx rapidement, dédupliqué par événement id, puis récupérez le dernier consentement si votre traitement dépend de l'état actuel. La livraison de webhooks et les sondages GET se complètent ; votre caisse doit tolérer l'arrivée en premier. Voir Webhooks.
Utiliser le consentement approuvé#
NuPay
Après success, demandez les modes de paiement de la commande et les options de versement NuPay. DEUNA utilise l'autorisation de consentement valide lors de la récupération de ces options et lors du traitement de l'achat du portefeuille. Si l'autorisation a expiré et que le fournisseur prend en charge l'actualisation, DEUNA tente de l'actualiser.
Portefeuille PayPal
Pour un utilisateur authentifié, le compte PayPal approuvé apparaît dans stored_payment_methods. Envoyer cet identifiant de méthode stocké comme achat payment_method; DEUNA vérifie que le consentement appartient au même utilisateur, commerçant et connexion PayPal avant de l'utiliser.
La suppression du mode de paiement du portefeuille stocké supprime également le consentement réutilisable associé.
Supprimer un compte PayPal réutilisable
DELETE /users/payment-methods/{payment_method_id}/tokens/{payment_method}
Utilisation payment_method_id pour l'identifiant de connexion PayPal et payment_method pour l'identifiant de méthode stocké renvoyé dans stored_payment_methods. Ce point de terminaison authentifié par l'utilisateur supprime le compte de portefeuille chez le fournisseur et supprime le consentement réutilisable correspondant dans 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))
}Une suppression réussie revient 204 No Content. Si le fournisseur signale que le compte enregistré est invalide lors d'un achat, DEUNA supprime également le consentement réutilisable invalide afin qu'il ne soit plus proposé.
Comportement de nouvelle tentative sécurisé#
- Avant de créer un nouveau consentement, lisez l'état actuel si la réponse précédente a été perdue.
- Un toujours valable
pendingousuccessle consentement est réutilisé au lieu de créer une autre autorisation de fournisseur. - Ne réessayez pas
failedouexpiredindéfiniment. Démarrez une nouvelle tentative axée sur le client après avoir résolu la cause. - Un
404signifie que DEUNA n'a pas pu trouver le consentement pour la commande ou le contexte utilisateur. - Un
400peut indiquer des champs invalides, une connexion non prise en charge, un flux d'invité désactivé ou un résultat du fournisseur de terminal. - Conservez les clés API privées et les jetons utilisateur sur des surfaces fiables. N'enregistrez pas les en-têtes de requête ou les données d'autorisation du fournisseur.
Liste de contrôle d'intégration#
- La commande, la connexion, la devise, le magasin et l'environnement correspondent.
- Le document d'identité et le type sont disponibles pour NuPay.
- Le client est authentifié avant de créer un consentement PayPal réutilisable.
- L'application prend en charge les expériences de redirection du fournisseur et d'approbation de l'application.
- L'interrogation utilise la cadence renvoyée par DEUNA et s'arrête sur les états terminaux.
notify_orderaccepte et déduplique les événements d'authentification.- L'achat ne commence qu'après
data.consent.statusestsuccess. - Les chemins d’échec, d’expiration, d’annulation et de nouvelle tentative sont testés dans le bac à sable.