Sessions et sécurité
Où utiliser chaque clé, comment créer un jeton de session, ce qu'il permet de faire et où le checkout peut être intégré.
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. Wajub.js la refuse avec une
erreur secret_key_in_browser, et l'API la refuse avec une réponse 403 et prévient le
propriétaire de la clé par e-mail. 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": "buyer@example.com" }
}'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() crée la session depuis la page, avec une clé publique, de
sandbox ou live. 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: 'buyer@example.com' },
});
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.
- 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 tant 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 après un paiement échoué.
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.
L'intégration du checkout hébergé ne quitte jamais votre page, callback n'y a donc aucun effet.
Utilisez onSuccess pour l'interface et le webhook comme source fiable. Les champs de paiement
font exception : un paiement qui se poursuit sur la page du prestataire, un paiement en crypto par
exemple, y emmène votre page, et le payeur revient sur callback, avec les paramètres ci-dessus ou,
selon le moyen de paiement, sans eux.
Quand le paiement se poursuit sur la page du prestataire
explique ce que cette page doit faire.
Où le checkout peut être intégré
Paramètres → Intégration de la page de paiement contient les sites autorisés à intégrer votre checkout : jusqu'à 20
origines, chacune écrite https://domaine, avec un port facultatif et sans chemin. La page existe
sur tous les plans, Pay as you go compris, et la liste s'applique en live et en sandbox. Une fois la
liste remplie, le navigateur n'affiche votre checkout et vos champs de paiement que dans une page
servie depuis l'une de ces origines.
Sur toute autre origine, le cadre reste vide, et ni onReady ni onLoadError ne se déclenchent. Ajoutez chaque origine qui monte le
checkout ou un champ, préproduction comprise, avant de remplir la liste.
Déclarez vos origines avant de passer en live
Déclarez les sites autorisés à intégrer votre checkout dans Paramètres → Intégration de la page de paiement avant d'encaisser en live.
Boutiques sur plusieurs domaines
Une boutique sur plusieurs domaines n'a aucune option à passer dans le code : le checkout et les champs fonctionnent sur la page qui les monte. Ajoutez chaque domaine dans Paramètres → Intégration de la page de paiement.
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é |