Aller au contenu

Intégration des marchands

Le cycle de vie d'une connexion et ce qui est figé dès son acceptation par un marchand.

Une connexion possède deux statuts qui évoluent indépendamment. status indique si le marchand a accepté la connexion. payment_status indique si elle peut déplacer de l'argent. Une connexion live peut être active tout en refusant tous les paiements. C'est la surprise la plus fréquente de cette section.

Le cycle de vie

statusSignificationOrigine
pendingCréée, personne ne l'a acceptéePOST /accounts
activeUn marchand s'est connecté et l'a autoriséeIl a ouvert le lien d'autorisation
cancelledArrêtée par vousDELETE /accounts/{id}
expiredL'invitation a expiré avant son acceptationLe temps

Il n'existe ni statut restricted ni état intermédiaire. Une connexion annulée renvoie cancelled, une connexion associée à un marchand renvoie active, une invitation que personne n'a acceptée avant sa date renvoie expired, et toutes les autres renvoient pending. expires_at est la date de l'invitation : elle est effacée quand un marchand accepte, et une connexion acceptée n'expire jamais.

La création d'une connexion génère un jeton d'autorisation à usage unique et le renvoie dans une URL.

Réponse pour une connexion non revendiquée
{
"id": "acc_7Yh2MpL4tRb3nP8sZcXv",
"reference": "shop-amina",
"status": "pending",
"payment_status": "inactive",
"authorization_url": "https://sync.wajub.com/oauth/v2/authorize?access_token=…"
}

Le marchand ouvre le lien, se connecte à son propre compte Wajub, consulte les capacités demandées, puis accepte. Votre plateforme ne manipule jamais ses identifiants et ne crée rien de son côté.

Une invitation ne reste pas ouverte indéfiniment : elle expire 30 jours après la dernière émission de son lien, à la création de la connexion, par POST /accounts/{id}/token ou par Renvoyer l'invitation dans le Dashboard, chacun relançant les 30 jours. Les deux derniers révoquent les liens déjà envoyés. Trois jours avant, un rappel part à l'adresse du marchand si vous l'avez indiquée, avec email sur POST /accounts ou PUT /accounts/{id} ; elle est conservée dans profile.email. Le rappel porte son propre lien, et celui que vous avez déjà envoyé continue de fonctionner. Une fois l'invitation expirée, tous les liens qui dorment dans la boîte du marchand cessent de fonctionner. Une connexion Relay n'a ni invitation, ni lien d'autorisation (authorization_url vaut null), et n'expire jamais.

Vous pouvez avoir au plus 50 invitations ouvertes par environnement : des connexions créées et ni acceptées, ni annulées, ni expirées. Les connexions Relay ne comptent pas, puisqu'aucun marchand ne les accepte. Au-delà, POST /accounts répond 429 avec You have reached the maximum of 50 pending invitations. Annulez les invitations dont vous n'avez plus besoin, ou laissez-les expirer.

Vous ne voyez authorization_url qu'une fois, dans la réponse à l'appel qui l'a généré : la création du sous-compte, ou la régénération de son jeton. Lire le sous-compte ensuite ne le renvoie jamais, revendiqué ou non. Conservez-le dès que vous le recevez ; si vous le perdez, générez-en un nouveau. Une fois la connexion revendiquée, connected_merchant apparaît dans la réponse avec le nom et l'adresse e-mail de l'entreprise du marchand.

Une connexion ne se revendique qu'une fois, et les contrôles ont lieu au moment de la revendication, pas à l'ouverture du lien par le marchand :

  • une connexion que vous avez annulée reste annulée, et le lien ne peut pas la réactiver. POST /accounts/{id}/token, lui, le peut : sur une invitation annulée que personne n'a revendiquée, il génère un nouveau lien et remet la connexion à pending, pour 30 jours de nouveau ;
  • une connexion déjà revendiquée par un autre marchand ne peut pas être reprise ;
  • une invitation expirée est refusée ;
  • en mode live, le marchand ne peut pas la revendiquer avant d'avoir déposé son dossier KYC. Le dépôt suffit pour se connecter ; c'est la vérification qui débloque les paiements (voir plus bas) ;
  • le lien peut porter un redirect_uri, mais seulement s'il partage son origine avec le callback enregistré sur la connexion. Tout autre hôte est refusé, et une connexion sans callback n'en accepte aucun.

Un marchand qui valide deux fois arrive sur sa propre connexion terminée, et non sur une erreur.

Retour sur votre page

Avant d'envoyer le lien, vous pouvez y ajouter &state=… (une valeur à vous, par exemple un jeton aléatoire lié à la session du vendeur) et &redirect_uri=…. Une fois la connexion acceptée, le marchand est envoyé vers ce redirect_uri, ou vers le callback de la connexion si le lien n'en porte pas, avec ces paramètres de requête :

ParamètreCe qu'il porte
accountL'id de la connexion (acc_…), celui de chaque appel et de chaque événement account.*
stateLe state que vous avez mis sur le lien d'autorisation, renvoyé tel quel. Comparez-le à celui que vous avez conservé, pour savoir que la visite répond à votre propre lien
statusconnected
Le retour, pour un lien ouvert avec state=8f3a
https://example.com/connected?account=acc_7Yh2MpL4tRb3nP8sZcXv&state=8f3a&status=connected

Le callback doit être une adresse HTTPS : POST et PUT /accounts répondent 422 sinon, sauf http://localhost et http://127.0.0.1 avec une clé sandbox.

Ce retour sert le navigateur du marchand, ce n'est pas une preuve : n'importe qui peut ouvrir cette adresse. Confirmez la connexion avec GET /accounts/{account} ou attendez account.connected sur votre endpoint de webhook avant d'agir.

Activer ensuite les paiements

L'acceptation ne donne pas toujours l'autorisation de débiter. payment_status est fixé au moment où le marchand accepte.

Le marchand acceptepayment_status
En sandboxactive immédiatement
En live, vérification déjà validéeactive immédiatement, à condition qu'une connexion lite ait aussi accès au rail Wajub
En live, vérification encore en coursinactive

Une connexion inactive passe seule à active lorsque la vérification du marchand est validée et, pour une connexion lite, une fois le rail Wajub accordé. Wajub envoie alors account.payment_activated.

payment_statusEffet sur les requêtes qui contiennent X-Sync
inactiveAppels qui déplacent de l'argent refusés avec 403 ; lectures exécutées
activeTout ce que permettent les capacités s'exécute
suspendedAppels qui déplacent de l'argent refusés avec 403 ; lectures exécutées

Ce qui est refusé tant que payment_status n'est pas active : créer, activer, modifier ou débiter un paiement, créer ou modifier un lien de paiement, un remboursement, un transfert ou un lot, ajouter, modifier ou vérifier un bénéficiaire ou enregistrer ce qu'un vendeur doit rendre, et accepter ou clore un litige. Ce qui fonctionne encore : tous les GET, pour suivre ce que fait la connexion pendant l'examen de la vérification du marchand, ainsi qu'annuler un paiement, supprimer un lien de paiement ou un bénéficiaire, et les recherches d'identité (/identity/resolve, /identity/validate).

Vous pouvez ensuite utiliser la suspension. Définir payment_status sur suspended arrête en même temps les paiements entrants et les transferts sortants. Ce comportement est volontaire : une connexion figée après l'échec d'un nouveau contrôle de conformité ne doit pas pouvoir vider son solde alors qu'elle ne peut plus recevoir d'argent.

L'interrupteur ne lève qu'une suspension que vous avez posée vous-même. Repasser payment_status à active répond 409 avec error_code: merchant_not_verified tant que la vérification du marchand est en cours ou a été refusée : ces connexions reviennent d'elles-mêmes une fois la vérification validée.

Le portail du vendeur Lite

Un vendeur lite n'a pas de dashboard marchand : c'est tout l'objet du mode. Son onboarding se termine sur la demande d'accès au rail Wajub, et il utilise ensuite un portail qui lui est propre, à vos couleurs.

Ce qu'il y faitComment
Se connecterUn code à usage unique envoyé à l'adresse de la boutique, sans mot de passe, avec une session courte
Voir ses ventes et ses versementsUniquement ceux de cette connexion : jamais son autre activité, jamais celle d'une autre plateforme
Demander l'accès au rail WajubÉtape hébergée de l'onboarding ; la connexion ne peut pas encaisser tant qu'elle n'est pas approuvée
Déclarer où va son argentLa page des coordonnées de versement du portail

Ce que le marchand doit accepter à nouveau

Dès l'acceptation du marchand, deux des quatre champs modifiables cessent de dépendre de vous seul.

ChampAprès l'acceptation
capabilities202 Accepted : proposé au marchand, renvoyé dans pending_changes
pricing202 Accepted : proposé au marchand, renvoyé dans pending_changes
callbackReste librement modifiable
payment_statusReste librement modifiable, et ne l'est qu'à partir de ce moment

Cette asymétrie est volontaire. Ce que le marchand a accepté sur l'écran d'autorisation ne peut pas être modifié à son insu : une connexion élargie ou une commission relevée devient une proposition qu'il accepte ou refuse depuis son dashboard, la connexion continuant entre-temps aux conditions en vigueur. La suspension, elle, reste la vôtre et ne se propose pas.

De son côté, le marchand retrouve toutes les plateformes auxquelles il est connecté sous Settings, Connected apps : les capacités accordées, la commission acceptée, l'état des paiements et toute proposition en attente. Accepter ou refuser une proposition depuis cette page vous envoie account.change_accepted ou account.change_declined. La même page lui permet de se déconnecter de votre plateforme, ce qui met fin à la connexion exactement comme votre propre DELETE.

Mettre fin à une connexion

DELETE /accounts/{id} révoque les jetons, définit status sur cancelled et émet account.deauthorized. Si le marchand avait accepté, payment_status passe également à inactive et la réponse contient Account disconnected. Pour une invitation non revendiquée, elle contient Account cancelled.

Déconnecter un marchand
curl -X DELETE https://api.wajub.com/accounts/acc_7Yh2MpL4tRb3nP8sZcXv \
  -H "Authorization: sk.xxxxxxxxxxxxxxxx…"

La déconnexion ne modifie rien de ce qui s'est déjà produit. Le marchand conserve son compte, ses paiements et son solde. Les commissions déjà acquises restent acquises. Seule votre capacité à agir en son nom prend fin.

Les événements à écouter

Le cycle de vie d'une connexion est décrit par les événements ci-dessous, sur les endpoints de votre plateforme. Créer une connexion envoie account.created, avec le compte mais sans son lien : authorization_url ne figure que dans la réponse à POST /accounts. Le catalogue des événements les décrit tous.

ÉvénementMoment de l'envoi
account.createdVous la créez avec POST /accounts
account.onboarding_startedLe marchand ouvre le lien d'autorisation
account.onboarding_email_verifiedIl confirme son adresse e-mail
account.onboarding_account_selectedIl choisit le compte Wajub à connecter
account.onboarding_kyc_submittedIl dépose son KYC pendant l'onboarding
account.connectedIl accepte la connexion
account.payment_activatedLa connexion obtient la capacité de débiter
account.payment_suspendedElle est suspendue, par vous ou après l'échec d'une vérification
account.change_requestedVous proposez de nouvelles capacités ou une nouvelle tarification
account.change_acceptedLe marchand accepte une proposition
account.change_declinedLe marchand refuse une proposition
account.updatedElle change : par PUT /accounts/{id} (ni une proposition seule, ni un appel qui ne change rien), quand POST /accounts/{id}/token remet une invitation annulée en pending, ou quand Wajub envoie le versement d'un marchand Lite (last_payout_at)
account.commission_receivable_recordedUne commission est due sur une vente encaissée hors de Wajub
account.commission_receivable_collectedCette commission est recouvrée
account.deauthorizedLa connexion est déconnectée ou annulée

De plus, chaque paiement, remboursement et transfert effectué sur un compte connecté, et chaque litige ouvert sur l'un de ses paiements, déclenche son webhook normal pour le marchand et une copie pour votre plateforme. Vous n'avez pas besoin d'interroger régulièrement une connexion pour connaître son activité.

Que pensez-vous de ce contenu ?