transaction-patterns-transfer
Last updated
Was this helpful?
Last updated
Was this helpful?
Mojaloop Third Party API
1.1 1.2 1.3
2.1
3.1 3.2 3.3
5.1 5.2 5.3 5.4 5.5
6.1
This section contains information about how to use this document.
The following conventions are used in this document to identify the specified types of information.
Elements of the API, such as resources
Boldface
/authorization
Variables
Italics with in angle brackets
{ID}
Glossary terms
Italics on first occurrence; defined in Glossary
The purpose of the API is to enable interoperable financial transactions between a Payer (a payer of electronic funds in a payment transaction) located in one FSP (an entity that provides a digital financial service to an end user) and a Payee (a recipient of electronic funds in a payment transaction) located in another FSP.
Library documents
Italics
User information should, in general, not be used by API deployments; the security measures detailed in API Signature and API Encryption should be used instead.
1.0
2021-10-03
Initial Version
The following references are used in this specification:
Ref. 1
Open API for FSP Interoperability
1.1
This document introduces the transaction patterns supported by the Third Party API relating to the initiation of a Transaction Request from a PISP.
The Mojaloop Third Party API Specification includes the following documents:
Data Models
Transaction Patterns - Linking
Transfers is broken down into the separate sections:
Discovery: PISP looks up the Payee Party to send funds to
Agreement PISP confirms the Payee Party, and looks up the terms of the transaction. If the User accepts the terms of the transaction, they sign the transaction with the credential established in the Linking API flow
Transfer The Payer DFSP initiates the transaction, and informs the PISP of the transaction result.
If the GET /parties/{Type}/{ID} request is successful, the PISP will receive a PUT /parties callback from the Mojaloop switch. The PISP then confirms the Payee with their user.
Should the PISP receive a PUT /parties/{Type}/{ID}/error (or PUT /parties/{Type}/{ID}/{SubId}/error) callback, the PISP should display the relevant error to their user.
Upon confirming the details of the Payee with their user, the PISP asks the user to enter the amount
of funds they wish to send to the Payee, and whether or not they wish the Payee to receive that amount, or they wish to send that amount (amountType
field).
If the User has linked more than 1 account with the PISP application, the PISP application can ask the user to choose an account they wish to send funds from. Upon confirming the source of funds account, the PISP can determine:
the FSPIOP-Destination
as the DFSP who the User's account is linked with
The payer
field of the POST /thirdpartyRequests/transactions request body. The partyIdType
is THIRD_PARTY_LINK
, the fspId
is the fspId of the DFSP who issued the link, and the partyIdentifier
is the accountId
specified in the POST /consents#scopes body.
See Grant Consent for more information.
Upon receiving the POST /thirdpartyRequests/transactions call from the PISP, the DFSP performs some validation such as:
Determine that the payer
identifier exists, and is one that was issued by this DFSP to the PISP specified in the FSPIOP-Source
.
Confirms that the Consent
that is identified by the payer
identifier exists, and is valid.
Confirm that the User's account is active and holds enough funds to complete the transaction.
Any other validation that the DFSP wishes to do.
Should this validation succeed, the DFSP will generate a unique transactionId
for the request, and call PUT /thirdpartyRequests/transactions/{ID} with this transactionId
and a transactionRequestState
of RECEIVED
.
This call informs the PISP that the Thirdparty Transaction Request was accepted, and informs them of the final transactionId
to watch for at a later date.
If the above validation fail, the DFSP should send a PUT /thirdpartyRequests/transactions/{ID}/error call to the PISP, with an error message communicating the failure to the PISP. See Error Codes for more information.
The Payer DFSP (that is, the institution sending funds at the request of the PISP) may then issue a quotation request (POST /quotes) to the Payee DFSP (that is, the institution receiving the funds). Upon receiving the PUT /quotes/{ID} callback from the Payee DFSP, the Payer DFSP needs to confirm the details of the transaction with the PISP.
They use the API call POST /thirdpartyRequests/authorizations. The request body is populated with the following fields:
transactionRequestId
- the original id of the POST /thirdpartyRequests/transactions. Used by the PISP to correlate an Authorization Request to a Thirdparty Transaction Request
authorizationRequestId
- a random UUID generated by the DFSP to identify this Thirdparty Authorization Request
transactionType
the transactionType
field from the original POST /thirdpartyRequests/transactions request
Upon receiving the POST /thirdpartyRequests/authorizations request from the Payer DFSP, the PISP presents the terms of the proposed transaction to the user, and asks them if they want to proceed.
The results of the authorization request are returned to the DFSP via the PUT /thirdpartyRequests/authorizations/{ID}, where the {ID} is the authorizationRequestId
.
If the user rejects the transaction, the following is the payload sent in PUT /thirdpartyRequests/authorizations/{ID}:
Should the user accept the transaction, the payload will depend on the credentialType
of the Consent.credential
:
3.2.3.1 Signing the Challenge FIDO
The final payload of the PUT /thirdpartyRequests/authorizations/{ID} is then:
3.2.3.2 Signing the Challenge with a GENERIC Credential
For a GENERIC
credential, the PISP will perform the following steps:
Given the inputs:
challenge
(authorizationRequest.challenge
) as a base64 encoded utf-8 string
privatekey
(stored by the PISP when creating the credential), as a base64 encoded utf-8 string
sign(data, key) is a signature function that takes some data and a private key to produce a signature
Let challengeHash
be the result of applying the SHA256() function over the challenge
Let signature
be the result of applying the sign() function to the challengeHash
and privateKey
The response from the PISP to the DFSP then uses this signature as the signedPayload.genericSignedPayload
field:
The final payload of the PUT /thirdpartyRequests/authorizations/{ID} is then:
Note: If the DFSP uses a self-hosted authorization service, this step can be skipped.
The DFSP now needs to check that challenge has been signed correctly, and by the private key that corresponds to the public key that is attached to the Consent
object.
The DFSP uses the API call POST /thirdpartyRequests/verifications, the body of which is comprised of:
verificationRequestId
- A UUID created by the DFSP to identify this verification request.
consentId
- The consentId
of the Consent resource that contains the credential public key with which to verify this transaction.
signedPayloadType
- The type of the SignedPayload, depending on the type of credential registered by the PISP
fidoValue
or genericValue
- The corresponding field from the PUT /thirdpartyRequests/authorizations request body from the PISP. The DFSP must lookup the consentId
based on the payer
details of the ThirdpartyTransactionRequest
.
Upon validating the signed challenge, the DFSP can go ahead and initiate a standard Mojaloop Transaction using the FSPIOP API.
After receiving the PUT /transfers/{ID} call from the switch, the DFSP looks up the ThirdpartyTransactionRequestId for the given transfer, and sends a PATCH /thirdpartyRequests/transactions/{ID} call to the PISP.
Upon receiving this callback, the PISP knows that the transfer has completed successfully, and can inform their user.
A PISP can issue a GET /thirdpartyRequests/transactions/{ID} to find the status of a transaction request.
PISP issues a GET /thirdpartyRequests/transactions/{ID}
Switch validates request and responds with 202 Accepted
Switch looks up the endpoint for dfspa
for forwards to DFSP A
DFSPA validates the request and responds with 202 Accepted
DFSP looks up the transaction request based on its transactionRequestId
If it can't be found, it calls PUT /thirdpartyRequests/transactions/{ID}/error to the Switch, with a relevant error message
DFSP Ensures that the FSPIOP-Source
header matches that of the originator of the POST /thirdpartyRequests/transactions
If it does not match, it calls PUT /thirdpartyRequests/transactions/{ID}/error to the Switch, with a relevant error message
DFSP calls PUT /thirdpartyRequests/transactions/{ID} with the following request body:
Switch validates request and responds with 200 OK
Switch looks up the endpoint for pispa
for forwards to PISP
PISP validates the request and responds with 200 OK
After the PISP initiates the Thirdparty Transaction Request with POST /thirdpartyRequests/transactions, the DFSP must send either a PUT /thirdpartyRequests/transactions/{ID}/error or PATCH /thirdpartyRequests/transactions/{ID} callback to inform the PISP of a final status to the Thirdparty Transaction Request.
PATCH /thirdpartyRequests/transactions/{ID} is used to inform the PISP of the final status of the Thirdparty Transaction Request. This could be either a Thirdparty Transaction Request that was rejected by the user, or a Thirdparty Transaction Request that was approved and resulted in a successful transfer of funds.
PUT /thirdpartyRequests/transactions/{ID}/error is used to inform the PISP of a failed Thirdparty Transaction Request.
If a PISP doesn't receive either of the above callbacks within the expiration
DateTime specified in the POST /thirdpartyRequests/transactions, it can assume the Thirdparty Transaction Request failed, and inform their user accordingly
When the PISP performs a Payee lookup (GET /parties/{Type}/{ID}), they may receive the callback PUT /parties/{Type}/{ID}/error.
In this case, the PISP may wish to display an error message to their user informing them to try a different identifier, or try again at a later stage.
When the DFSP receives the POST /thirdpartyRequests/transactions request from the PISP, the following processing or validation errors may occur:
The payer.partyIdType
or payer.partyIdentifier
is not valid, or not linked with a valid Consent that the DFSP knows about
The user's account identified by payer.partyIdentifier
doesn't have enough funds to complete the transaction
The currency specified by amount.currency
is not a currency that the user's account transacts in
payee.partyIdInfo.fspId
is not set - it's an optional property, but payee fspId will be required to properly address quote request
Any other checks or verifications of the transaction request on the DFSP's side fail
In this case, the DFSP must inform the PISP of the failure by sending a PUT /thirdpartyRequests/transactions/{ID}/error callback to the PISP.
The PISP can then inform their user of the failure, and can ask them to restart the Thirdparty Transaction request if desired.
The DFSP may not want to (or may not be able to) expose details about downstream failures in the FSPIOP API to PISPs.
For example, before issuing a POST /thirdpartyRequests/authorizations to the PISP, if the POST /quotes call with the Payee FSP fails, the DFSP sends a PUT /thirdpartyRequests/transactions/{ID}/error callback to the PISP.
Another example is where the POST /transfers request fails:
After receiving a POST /thirdpartyRequests/authorizations call from the DFSP, the PISP asks the user to sign the challenge
using the credential that was registered during the account linking flow.
The signed challenge is returned to the DFSP with the call PUT /thirdpartyRequest/authorizations/{ID}.
The DFSP either:
Performs validation of the signed challenge itself
Queries the Auth-Service with the thirdpartyRequests/verifications resource to check the validity of the signed challenge against the publicKey registered for the Consent.
Should the signed challenge be invalid, the DFSP sends a PUT /thirdpartyRequests/transactions/{ID}/error callback to the PISP.
If a PISP doesn't receive either of the above callbacks within the expiration
DateTime specified in the POST /thirdpartyRequests/transactions, it can assume the Thirdparty Transaction Request failed, and inform their user accordingly.
Let quote
be the value of the response body from the PUT /quotes/{ID}_ call_
The DFSP must generate the value jsonString
from the output of CJSON(quote)
The challenge
is the value of SHA256(jsonString)
The API design and architectural style of this API are based on of Ref 1. above.
In this phase, a user enters the identifier of the user they wish to send funds to. The PISP executes a GET /parties/{Type}/{ID}** (or GET /parties/{Type}/{ID}/{SubId}) call and awaits a callback from the Mojaloop switch. of Ref 1. above describes the /parties resource in detail.
The PISP then generates a random transactionRequestId
of type UUID (see ).
challenge
- the challenge is a BinaryString
which will be signed by the private key on the User's device. While the challenge could be a random string, we recommend that it be derived from something meaningful to the actors involved in the transaction, that can't be predicted ahead of time by the PISP. See for an example of how the challenge could be derived.
If FIDO
, the PISP asks the user to complete the flow to sign the challenge. The signedPayload.fidoSignedPayload
is the FIDOPublicKeyCredentialAssertion
returned from the FIDO Assertion process. See
If GENERIC
, the private key created during the is used to sign the challenge. See
For a FIDO
credentialType
, the PISP asks the user to complete the flow to sign the challenge. The signedPayload.value
is the returned from the FIDO Assertion process, where the ArrayBuffer
s are parsed as base64 encoded utf-8 strings. As a PublicKeyCredential
is the response of both the FIDO Attestation and Assertion, we define the following interface: FIDOPublicKeyCredentialAssertion
:
SHA256() is a one way hash function, as defined in
challenge
- The same challenge that was sent to the PISP in
Where transactionId
is the DFSP-generated id of the transaction, and TransactionRequestState
is RECEIVED
, PENDING
, ACCEPTED
, REJECTED
, as defined in of the API Definition
See of the FSPIOP API Definition for details on how to interpret use this error callback.
Let the function CJSON()
be the implementation of a Canonical JSON to string, as specified in
Let the function SHA256()
be the implementation of a SHA-256 one way hash function, as specified in