Sessions et sécurité
Où utiliser chaque clé, comment créer un jeton de session et ce qu'il permet de faire.
Toutes les fonctionnalités Wajub côté navigateur reposent sur une chaîne à courte durée de vie : le jeton de session. Cette page explique son origine, ce qu'il remplace et pourquoi la clé qui le crée ne doit jamais quitter votre serveur.
Trois identifiants, trois emplacements
Le préfixe d'une clé Wajub indique à lui seul où la clé peut être utilisée. Les clés live commencent
par sk. ou pk., les clés de sandbox par sk_test. ou pk_test..
| Identifiant | Format | Emplacement | Possibilités |
|---|---|---|---|
| Clé publique | pk. ou pk_test. | Navigateur, votre HTML | Créer une session, rien d'autre |
| Clé secrète | sk. ou sk_test. | Serveur, variable d'environnement | Tout faire sur l'API |
| Jeton de session | authorization_token | Navigateur, comme sessionId | Régler un seul paiement, une seule fois |
Grâce au jeton de session, les deux autres clés sont inutiles dans le navigateur. Il est limité à un seul paiement, ne donne aucun accès au compte et expire avec ce paiement.
Une clé secrète dans un navigateur est refusée et signalée
Une clé secrète ne doit jamais être utilisée depuis un navigateur. Quand Wajub en détecte une,
l'API la refuse avec une réponse 403 et alerte le propriétaire de la clé par e-mail. Le SDK la
refuse avant même l'appel avec une erreur secret_key_in_browser. Vous ne devriez jamais compter
sur ces protections.
Le flux correct
Respectez toujours ces deux étapes dans cet ordre.
- Votre serveur appelle
POST /paymentsavec la clé secrète et reçoit unauthorization_token. - Votre navigateur reçoit uniquement ce jeton et le transmet comme
sessionId.
curl https://api.wajub.com/payments \
-H "Authorization: sk_test.xxxxxxxxxxxxxxxx…" \
-H "Content-Type: application/json" \
-d '{
"amount": 35000,
"currency": "XOF",
"reference": "ORDER-123",
"customer": { "email": "[email protected]" }
}'La réponse contient trois éléments à conserver.
| Champ | Utilité |
|---|---|
authorization_token | Le sessionId du navigateur. Envoyez-lui uniquement cette valeur |
authorization_url | La page hébergée pour un checkout avec redirection |
transaction.id | Votre clé de rapprochement à la réception du webhook |
const { sessionId } = await fetch('/api/checkout/session', { method: 'POST' })
.then((r) => r.json());
await mount('#checkout', { sessionId });Avant que la commande existe
Les champs de paiement affichés pendant que l'acheteur est encore sur le checkout précèdent la
commande. Créez leur session avec deferred: true : le paiement est seulement préparé, reste hors
de vos paiements et n'envoie aucun webhook tant que votre serveur ne l'a pas activé avec le total
final et le client. Avant que la commande existe
détaille les trois appels.
Créer une session depuis le navigateur
Wajub(publishableKey).createPayment() existe et fonctionne avec une clé de sandbox : en 1.5.0, une
clé live, pk.…, est lue comme un id de session, et en live la session se crée donc sur votre
serveur. Cet outil convient à un prototype ou
à une page de démonstration dont le prix est fixe. Il ne convient pas à une boutique, car le montant
serait alors défini par du code que le client contrôle.
const client = Wajub('pk_test.xxxxxxxxxxxxxxxx…');
const { sessionId } = await client.createPayment({
amount: 35000,
currency: 'XOF',
customer: { email: '[email protected]' },
});
await client.mount('#checkout');Le prix fait partie du périmètre de confiance
Une clé publique ne peut pas lire votre compte, mais elle peut ouvrir un paiement pour tout montant demandé par la page. Toute valeur qu'un client pourrait modifier à son avantage doit être définie sur votre serveur.
Ce que le jeton permet de faire
Le jeton de session autorise exactement un paiement. L'environnement d'exécution applique cette limite.
- Il monte le checkout, ouvre la superposition ou pilote les composants de champs.
- Il lit l'aperçu de la session avec
fetchSession(sessionId). - Sur un paiement préparé, il affiche les moyens de paiement et ne débite rien : tout débit répond
409tant que votre serveur n'a pas activé le paiement. L'activation conserve le jeton. - Il reste valide tant que le paiement n'a pas atteint un état final, puis expire peu après
succeeded,failed,expiredoucancelled. - Il reste valide après un nouveau montage. Un composant démonté puis remonté réutilise donc le même jeton.
- Il vit aussi longtemps que le paiement : 24 heures par défaut, ou les
expires.inminutes fixées surPOST /payments(de 5 à 43 200). Au-delà, le paiement expire,onExpiredse déclenche sur le checkout et un champ de paiement rejette avecsession_expired. - Les tentatives de paiement sur une session sont limitées. Au-delà de la limite, la tentative est
refusée avec
rate_limitedjusqu'à ce qu'un court délai soit écoulé.
Monter un jeton qui n'est plus valide appelle onLoadError avec session_terminal_expired, et un
jeton que Wajub ne connaît pas l'appelle avec session_not_found. L'intégration elle-même reste
vide. Configurez donc ce callback, même sur une page où vous pensez que ce cas ne peut pas se
produire. Tant que le jeton est valide, un checkout monté sur un paiement terminé signale plutôt
son résultat, par onSuccess, onError, onExpired ou onCancel.
L'URL de callback
callback est une destination de redirection. Elle ne concerne donc que le flux avec redirection.
Transmettez une URL HTTPS de base sans vos propres paramètres d'URL. Wajub ajoute les siens.
| Paramètre d'URL | Valeurs |
|---|---|
status | succeeded, cancelled, failed, expired |
reference | L'identifiant Wajub du paiement, celui que prend GET /payments/{id}, si disponible |
trxref | Votre propre référence, si disponible |
Chaque retour porte reference et trxref, y compris celui d'un paiement échoué, qui ne portait que
status.
https://shop.example.com/order/complete?status=succeeded&reference=trx_CSUGajfv9xh0XQ5wu2lx&trxref=ORDER-123status=succeeded est une indication, pas une preuve de paiement
Il s'agit d'un paramètre dans une URL que le payeur peut modifier. Livrez la commande lorsque
payment.succeeded arrive sur votre endpoint, ou lorsqu'un
appel serveur GET /payments/{id} le confirme.
Une intégration ne quitte jamais votre page, callback n'y a donc aucun effet. Utilisez onSuccess
pour l'interface et le webhook comme source fiable.
Boutiques sur plusieurs domaines
Un domaine unique ne demande aucune configuration. Si votre checkout s'exécute sur un second domaine, indiquez explicitement le domaine canonique.
await mount('#checkout', { sessionId, embedOrigin: 'https://shop.example.com' });
const factory = await components(sessionId, {
componentOrigin: 'https://shop.example.com',
});Avant de passer en live
| Règle | Raison |
|---|---|
HTTPS sur votre site et pour callback | Les iframes de paiement et les redirections refusent le HTTP simple |
sk. uniquement dans les variables d'environnement du serveur | Jamais dans un bundle ni dans une variable PUBLIC_ |
Seul authorization_token atteint le navigateur | C'est le seul identifiant qui ne donne aucun accès sensible |
| Montage par le SDK | Le checkout ne fonctionne que lorsque le SDK le monte |
| Confirmation par webhook | onSuccess est un événement du navigateur, qui peut être fermé |