# Modèles de transaction - Transfert

API Tiers de Mojaloop

# Table des matières

  1. Préface
    1.1 Conventions utilisées dans ce document
    1.2 Informations sur la version du document
    1.3 Références
  2. Introduction
    2.1 Spécification de l'API Tiers
  3. Transferts
    3.1 Découverte
    3.2 Accord
    3.3 Transfert
  4. Demande de statut de TransactionRequest
  5. Conditions d’erreur
    5.1 Recherche de Bénéficiaire Incorrecte
    5.2 Mauvaise demande de transaction tierce
    5.3 Échec API FSPIOP aval
    5.4 Challenge signé invalide
    5.5 Expiration de la demande de transaction tierce
  6. Annexe
    6.1 Dérivation du challenge

# 1. Préface

Cette section contient des informations sur l'utilisation de ce document.

# 1.1. Conventions utilisées dans ce document

Les conventions suivantes sont utilisées dans ce document pour identifier les types d'informations spécifiés.

Type d’information Convention Exemple
Éléments de l’API, comme les ressources Gras /authorization
Variables Italique entre chevrons {ID}
Termes du glossaire Italique à la première occurrence ; défini dans le Glossaire Le but de l'API est de permettre les transactions financières interopérables entre un Payeur (un payeur de fonds électroniques dans une transaction de paiement) situé dans un FSP (une entité qui fournit un service financier numérique à un utilisateur final) et un Bénéficiaire (un destinataire de fonds électroniques dans une transaction de paiement) situé dans un autre FSP.
Documents de référence Italique Les informations utilisateur ne devraient, en général, pas être utilisées par les déploiements d'API ; les mesures de sécurité détaillées dans Signature API et Chiffrement API devraient être utilisées à la place.

# 1.2. Informations sur la version du document

Version Date Description des modifications
1.0 2021-10-03 Version initiale

# 1.3. Références

Les références suivantes sont utilisées dans cette spécification :

Référence Description Version Lien
Réf. 1 Open API pour l'interopérabilité FSP 1.1 Définition d’API v1.1 (opens new window)

# 2. Introduction

Ce document présente les modèles de transaction pris en charge par l'API Tiers en lien avec l'initiation d'une demande de transaction (Transaction Request) provenant d’un PISP.

La conception de l’API et le style architectural de cette API sont basés sur la Section 3 (opens new window) de la Réf. 1 ci-dessus.

# 2.1 Spécification de l'API Tiers

La spécification de l’API Tiers Mojaloop inclut les documents suivants:

# 3. Transferts

Les transferts sont divisés en sections séparées :

  1. Découverte : Le PISP recherche le bénéficiaire auquel envoyer des fonds

  2. Accord : Le PISP confirme le bénéficiaire, et recherche les conditions de la transaction. Si l'Utilisateur accepte les conditions de la transaction, il signe la transaction avec le justificatif établi lors du flux d’API de liaison

  3. Transfert : Le DFSP du payeur initie la transaction et informe le PISP du résultat de celle-ci.

# 3.1 Découverte

Dans cette phase, un utilisateur saisit l'identifiant de l'utilisateur à qui il souhaite envoyer des fonds. Le PISP exécute un appel GET /parties/{Type}/{ID}** (ou GET /parties/{Type}/{ID}/{SubId}) et attend un callback du switch Mojaloop. Section 6.3 (opens new window) de la Réf. 1 décrit la ressource /parties en détail.

Si la demande GET /parties/{Type}/{ID} réussit, le PISP recevra un callback PUT /parties du switch Mojaloop. Le PISP confirme alors le bénéficiaire avec son utilisateur.

Si le PISP reçoit un callback PUT /parties/{Type}/{ID}/error (ou PUT /parties/{Type}/{ID}/{SubId}/error), il doit afficher l’erreur pertinente à l'utilisateur.

Discovery

# 3.2 Accord

# 3.2.1 Demande de transaction tierce

Après avoir confirmé les détails du bénéficiaire avec son utilisateur, le PISP demande à l'utilisateur de saisir le montant à envoyer au bénéficiaire, et s’il souhaite que le bénéficiaire reçoive ce montant, ou qu'il souhaite envoyer ce montant (champ amountType).

Si l'utilisateur a associé plus d'un compte avec l'application PISP, le PISP peut lui demander de choisir un compte source pour le virement. Une fois la source de fonds confirmée, le PISP peut déterminer :

  1. le FSPIOP-Destination comme le DFSP avec lequel le compte de l'utilisateur est associé
  2. Le champ payer du corps de la requête POST /thirdpartyRequests/transactions. partyIdType est THIRD_PARTY_LINK, fspId est le fspId du DFSP qui a émis la liaison, et partyIdentifier est l’accountId spécifié dans le corps POST /consents#scopes.

Voir Octroi du consentement pour plus d’informations.

Le PISP génère ensuite un transactionRequestId aléatoire de type UUID (voir RFC 4122 UUID (opens new window)).

1-2-1-agreement

Lors de la réception de l'appel POST /thirdpartyRequests/transactions du PISP, le DFSP effectue certaines validations telles que :

  1. Déterminer que l'identifiant payer existe, et a bien été émis par ce DFSP au PISP spécifié dans FSPIOP-Source.
  2. Confirmer que le Consentement identifié par payer existe et est valide.
  3. Confirmer que le compte de l'utilisateur est actif et a suffisamment de fonds pour effectuer la transaction.
  4. Toute autre validation que le DFSP souhaite effectuer.

Si cette validation réussit, le DFSP génère un transactionId unique pour la demande, et appelle PUT /thirdpartyRequests/transactions/{ID} avec ce transactionId et l’état transactionRequestState à RECEIVED.

Cet appel informe le PISP que la demande de transaction tierce a été acceptée, et l'informe du transactionId final à suivre ultérieurement.

Si la validation échoue, le DFSP doit envoyer un appel PUT /thirdpartyRequests/transactions/{ID}/error au PISP, contenant un message d’erreur expliquant l’échec. Voir Codes erreurs pour plus d’informations.

# 3.2.2 Demande d’autorisation tierce

Le DFSP payeur (c’est-à-dire, l’institution envoyant des fonds à la demande du PISP) peut alors émettre une demande de devis (POST /quotes) au DFSP bénéficiaire (l’institution recevant les fonds). Après réception du callback PUT /quotes/{ID} du DFSP bénéficiaire, le DFSP payeur doit confirmer les détails de la transaction auprès du PISP.

Il utilise l’appel d’API POST /thirdpartyRequests/authorizations. Le corps de la requête contient les champs suivants :

  • transactionRequestId – l'identifiant original de POST /thirdpartyRequests/transactions. Utilisé par le PISP pour corréler une demande d’autorisation à une demande de transaction tierce.
  • authorizationRequestId – un UUID aléatoire généré par le DFSP pour identifier cette demande d’autorisation tierce
  • challenge – le challenge est une BinaryString qui sera signé par la clé privée sur l'appareil de l'utilisateur. Bien qu'il puisse s'agir d'une chaîne aléatoire, il est recommandé qu’elle soit dérivée à partir de quelque chose de significatif pour les acteurs de la transaction, qui ne puisse être prédit à l’avance par le PISP. Voir Section 4.1 pour un exemple de dérivation du challenge.
  • transactionType – le champ transactionType de la demande initiale POST /thirdpartyRequests/transactions

1-2-2-authorization

# 3.2.3 Autorisation signée

Une fois la requête POST /thirdpartyRequests/authorizations reçue du DFSP Payeur, le PISP présente les conditions de la transaction à l'utilisateur, et lui demande s'il souhaite la poursuivre.

Les résultats de la demande d'autorisation sont retournés au DFSP via PUT /thirdpartyRequests/authorizations/{ID}, où {ID} est le authorizationRequestId.

Si l’utilisateur rejette la transaction, la charge utile envoyée dans PUT /thirdpartyRequests/authorizations/{ID} est :

{
    "responseType": "REJECTED"
}

1-2-3-rejected-authorization

Si l’utilisateur accepte la transaction, la charge utile dépend du credentialType du Consent.credential :

  1. Si FIDO, le PISP demande à l’utilisateur de compléter le flux FIDO Assertion (opens new window) pour signer le challenge. Le signedPayload.fidoSignedPayload est le FIDOPublicKeyCredentialAssertion renvoyé suite au processus FIDO. Voir 3.2.3.1 Signature du challenge FIDO

  2. Si GENERIC, la clé privée créée lors du processus d’enregistrement du credential est utilisée pour signer le challenge. Voir 3.2.3.2 Signature du challenge avec un credential GENERIC

# 3.2.3.1 Signature du challenge FIDO

Pour un credentialType FIDO, le PISP demande à l’utilisateur de compléter le flux FIDO Assertion (opens new window) pour signer le challenge. Le champ signedPayload.value est le PublicKeyCredential (opens new window) renvoyé du processus FIDO Assertion, où les ArrayBuffer sont encodés en chaînes base64 utf-8. Le PublicKeyCredential est la réponse aussi bien pour l’attestation que pour l’assertion FIDO, nous définissons l’interface suivante : FIDOPublicKeyCredentialAssertion :

FIDOPublicKeyCredentialAssertion {
    "id": "string",
    "rawId": "string - base64 encodé utf-8",
    "response": {
        "authenticatorData": "string - base64 encodé utf-8",
        "clientDataJSON": "string - base64 encodé utf-8",
        "signature": "string - base64 encodé utf-8",
        "userHandle": "string - base64 encodé utf-8"
    },
    "type": "public-key"
}

Le payload final du PUT /thirdpartyRequests/authorizations/{ID} sera ainsi :

{
    "responseType": "ACCEPTED",
    "signedPayload": {
        "signedPayloadType": "FIDO",
        "fidoSignedPayload": FIDOPublicKeyCredentialAssertion
    }
}

1-2-3-signed-authorization-fido

# 3.2.3.2 Signature du challenge avec un credential GENERIC

Pour un credential GENERIC, le PISP effectue les étapes suivantes :

  1. Étant donné les entrées :
    • challenge (authorizationRequest.challenge) sous forme de chaîne base64 encodée utf-8
    • privatekey (stockée par le PISP lors de la création du credential), chaîne base64 encodée utf-8
    • SHA256() est une fonction de hachage à sens unique, voir RFC6234 (opens new window)
    • sign(data, key) est une fonction de signature prenant des données et une clé privée pour produire une signature
  2. Soit challengeHash le résultat de la fonction SHA256() appliquée au challenge
  3. Soit signature le résultat de la fonction sign() appliquée à challengeHash et privateKey

La réponse du PISP au DFSP utilise alors cette signature comme champ signedPayload.genericSignedPayload :

Le payload final du PUT /thirdpartyRequests/authorizations/{ID} est alors :

{
    "responseType": "ACCEPTED",
    "signedPayload": {
        "signedPayloadType": "GENERIC",
        "genericSignedPayload": "signature encodée utf-8 base64"
    }
}

1-2-3-signed-authorization-generic

# 3.2.4 Validation de l’autorisation

Note : Si le DFSP utilise un service d’autorisation auto-hébergé, cette étape peut être sautée.

Le DFSP doit maintenant vérifier que le challenge a bien été signé, et par la clé privée correspondant à la clé publique attachée à l'objet Consent.

Le DFSP utilise l'appel d’API POST /thirdpartyRequests/verifications, dont le corps est composé de :

  • verificationRequestId – Un UUID créé par le DFSP pour identifier cette vérification.
  • challenge – Le même challenge envoyé au PISP dans 3.2.2 Demande d’autorisation tierce
  • consentId – L’identifiant du Consent qui contient la clé publique credential à utiliser pour vérifier cette transaction.
  • signedPayloadType – Le type de SignedPayload, selon le type d’identifiant enregistré par le PISP
  • fidoValue ou genericValue – Le champ correspondant du corps de la requête PUT /thirdpartyRequests/authorizations du PISP. Le DFSP doit rechercher le consentId d’après les détails du payer de la ThirdpartyTransactionRequest.

1-2-4-verify-authorization

# 3.3 Transfert

Après validation du challenge signé, le DFSP peut lancer une transaction Mojaloop standard via l’API FSPIOP.

Après avoir reçu l’appel PUT /transfers/{ID} du switch, le DFSP recherche le ThirdpartyTransactionRequestId du transfert donné puis envoie un appel PATCH /thirdpartyRequests/transactions/{ID} au PISP.

Une fois ce callback reçu, le PISP sait que le transfert est réussi et peut en informer son utilisateur.

1-3-transfer

# 4. Demander le statut de la TransactionRequest

Un PISP peut effectuer un GET /thirdpartyRequests/transactions/{ID} pour obtenir le statut d’une demande de transaction.

PISPTransferSimpleAPI

  1. Le PISP effectue un GET /thirdpartyRequests/transactions/{ID}

  2. Le switch valide la demande et répond par 202 Accepted

  3. Le switch recherche l’endpoint pour dfspa pour la transférer à DFSP A

  4. DFSPA valide la demande et répond avec 202 Accepted

  5. Le DFSP recherche la demande de transaction via son transactionRequestId

    • Si elle est introuvable, il appelle PUT /thirdpartyRequests/transactions/{ID}/error vers le switch, avec un message d’erreur pertinent
  6. Le DFSP vérifie que l'entête FSPIOP-Source correspond à celui d’origine du POST /thirdpartyRequests/transactions

    • Sinon il appelle PUT /thirdpartyRequests/transactions/{ID}/error vers le switch, avec un message d'erreur pertinent
  7. Le DFSP appelle PUT /thirdpartyRequests/transactions/{ID} avec le corps suivant :

    {
      transactionRequestState: TransactionRequestState
    }
    

    transactionId est l’identifiant de transaction généré par le DFSP, et TransactionRequestState est RECEIVED, PENDING, ACCEPTED, REJECTED, comme défini dans 7.5.10 TransactionRequestState (opens new window) de la Définition d’API

  8. Le switch valide la demande et répond avec 200 OK

  9. Le switch recherche l’endpoint pour pispa et transmet au PISP

  10. Le PISP valide la demande et répond avec 200 OK

# 5. Conditions d’erreur

Après que le PISP a initié la demande de transaction tierce via POST /thirdpartyRequests/transactions, le DFSP doit envoyer soit un PUT /thirdpartyRequests/transactions/{ID}/error soit un callback PATCH /thirdpartyRequests/transactions/{ID} pour informer le PISP du statut final.

  • PATCH /thirdpartyRequests/transactions/{ID} est utilisé pour informer le PISP du statut final. Il peut s’agir soit d’un rejet par l’utilisateur, soit d’une approbation ayant abouti à un transfert réussi.
  • PUT /thirdpartyRequests/transactions/{ID}/error informe le PISP en cas d’échec de la demande.
  • Si le PISP ne reçoit aucun de ces callbacks avant l’expiration expiration spécifiée dans la requête POST /thirdpartyRequests/transactions, il peut considérer la demande comme échouée et en informer son utilisateur.

# 5.1 Recherche de bénéficiaire infructueuse

Quand le PISP effectue une recherche de bénéficiaire (GET /parties/{Type}/{ID}), il peut recevoir le callback PUT /parties/{Type}/{ID}/error.

Voir 6.3.4 Parties Error Callbacks (opens new window) de la Définition d’API FSPIOP pour plus d’informations sur ce callback d’erreur.

Dans ce cas, le PISP peut vouloir afficher un message d’erreur à l’utilisateur, en l’invitant à essayer avec un autre identifiant ou plus tard.

# 5.2 Mauvaise demande de transaction tierce

Quand le DFSP reçoit le POST /thirdpartyRequests/transactions du PISP, les erreurs suivantes peuvent se produire :

  1. Le payer.partyIdType ou payer.partyIdentifier est invalide ou pas lié à un consentement valide connu du DFSP
  2. Le compte utilisateur identifié par payer.partyIdentifier n'a pas assez de fonds
  3. La devise précisée dans amount.currency n’est pas prise en charge par le compte de l’utilisateur
  4. payee.partyIdInfo.fspId n’est pas défini — il s’agit d’une propriété optionnelle, mais le fspId bénéficiaire sera requis pour adresser correctement la demande de devis
  5. Tout autre contrôle ou vérification côté DFSP échoue

Dans ce cas, le DFSP doit informer le PISP de l’échec en envoyant un callback PUT /thirdpartyRequests/transactions/{ID}/error.

3-2-1-bad-tx-request

Le PISP peut alors informer son utilisateur de l’échec, et proposer de relancer une demande s’il le souhaite.

# 5.3 Échec API FSPIOP aval

Le DFSP peut ne pas vouloir, ou ne pas être en mesure, d’exposer des détails sur les échecs API FSPIOP aval au PISP.

Par exemple, avant d’émettre un POST /thirdpartyRequests/authorizations au PISP, si le POST /quotes avec le FSP bénéficiaire échoue, le DFSP envoie un callback PUT /thirdpartyRequests/transactions/{ID}/error au PISP.

3-3-1-bad-quote-request

Un autre exemple : si la requête POST /transfers échoue :

3-3-2-bad-transfer-request

# 5.4 Challenge signé invalide

Après réception d’un POST /thirdpartyRequests/authorizations du DFSP, le PISP demande à l'utilisateur de signer le challenge via le justificatif enregistré lors du flux de liaison de comptes.

Le challenge signé est retourné au DFSP via PUT /thirdpartyRequest/authorizations/{ID}.

Le DFSP :

  1. Valide lui-même le challenge signé
  2. Ou interroge le Auth-Service via thirdpartyRequests/verifications pour vérifier la signature contre la clé publique enregistrée dans le Consent.

Si le challenge signé est invalide, le DFSP appelle alors PUT /thirdpartyRequests/transactions/{ID}/error vers le PISP.

# Cas 1 : DFSP se charge de vérifier le challenge

3-4-1-bad-signed-challenge-self-hosted

# Cas 2 : DFSP utilise le Auth-Service hébergé par le hub pour vérifier le challenge signé contre le credential enregistré.

3-4-2-bad-signed-challenge-auth-service

# 5.5 Expiration de la demande de transaction tierce

Si le PISP ne reçoit aucun des callbacks ci-dessus avant la date d’expiration expiration définie dans POST /thirdpartyRequests/transactions, il peut considérer la demande comme échouée et en informer immédiatement l'utilisateur.

3-6-tpr-timeout

# 6. Annexe

# 6.1 Dérivation du challenge

  1. Soit quote la valeur du corps de la réponse de l’appel PUT /quotes/{ID}_
  2. La fonction CJSON() est l’implémentation du JSON Canonical vers une chaîne, conforme à RFC-8785 - Canonical JSON format (opens new window)
  3. La fonction SHA256() est la fonction de hachage SHA-256, conforme à RFC-6234 (opens new window)
  4. Le DFSP doit générer la valeur jsonString en appliquant CJSON(quote)
  5. Le challenge est la valeur de SHA256(jsonString)