# 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
    }
    

    OĂč 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)