Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: SDK support for request-to-pay documented #413

Merged
merged 8 commits into from
Jun 26, 2023
4 changes: 4 additions & 0 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,10 @@ module.exports = {
title: "Integration Flow Patterns",
path: "sdk-scheme-adapter/IntegrationFlowPatterns"
},
{
title: "Request To Pay - support",
path: "sdk-scheme-adapter/RequestToPay"
},
{
title: "Bulk Integration Flow Patterns",
path: "sdk-scheme-adapter/IntegrationBulkFlowPatterns"
Expand Down
22 changes: 22 additions & 0 deletions docs/technical/sdk-scheme-adapter/RequestToPay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Request To Pay (R2P) - use-case support

This documentation describes how the SDK Scheme Adaptor supports the request to pay use case. The request to pay use case is the bases for all Payee initiated transfers. Support for this use-case requires every DFSP in a Mojaloop switch to automatically process a transfer once a request to perform a transfer is received and validated. Having support for this use-case in the SDK-scheme-adapter is important as it minimised the development effort that each DFSP needs to make if a Scheme mandates participant support for this use case. This use case is particularly interesting from a testing perspective, as it enables remote testing as both a Payer DFSP and a Payee DFSP.
PaulGregoryBaker marked this conversation as resolved.
Show resolved Hide resolved

## Sequence Diagram

1. The Payee DFSP initiates the R2P use case with **POST** /RequestToPay api call.
PaulGregoryBaker marked this conversation as resolved.
Show resolved Hide resolved
2. The Payee DFSP optionally can validate the Payer.
3. The Payer DFSP executes the R2P request with a **POST** /requestToPayTransfer API call. If the Authentication type is not provided in this call, then the flow assumes that the Payer will confirm the transfer and terms through a **PUT** /requestToPayTransfer, otherwise the appropriate authentication flow is executed.
mdebarros marked this conversation as resolved.
Show resolved Hide resolved

The diagram summarises this flow.


![R2P Sequence Diagram](./assets/sequence/requestToPaySDK-R2P-SequenceDiagram.svg)

## Detailed sequence diagram

Below is a more detailed sequence diagram for the request to pay use case and the SDK Scheme Adapter API calls.

![R2P Detailed Sequence Diagram](./assets/sequence/SDKrequestToPay.svg)


Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
@startuml

actor "Payer" as Payer
box Payer DFSP
participant "Core Banking System" as PayerDFSP
participant "SDK" as PayerSDK
end box
participant "Mojaloop" as Mojaloop #d4f2f9

box Payee DFSP
participant "SDK" as PayeeSDK
participant "Core Banking System" as PayeeDFSP
end box
actor "Payee" as Payee
autonumber 1 "<b>[0]"

alt if (OTP)
PaulGregoryBaker marked this conversation as resolved.
Show resolved Hide resolved
Payer->PayerDFSP: Generate an OTP for me
PayerDFSP->PayerDFSP:Generate
PayerDFSP-->Payer: Here is your OTP
end
=== Payee initiated request to pay (R2P) ==
Payee->PayeeDFSP: I would like \nto receive 1000 TZS\n from +1234567890
PayeeDFSP->PayeeDFSP: Payer not within Payee System

PayeeDFSP->PayeeSDK: **POST** /requestToPayTransfer
note right
{
"requestToPayTransactionId": "string",
"from": {
"type": "CONSUMER",
"idType": "MSISDN",
"idValue": "+1234567890",
"idSubValue": "string"
},
"to": {...},
"amountType": "RECEIVE",
"currency": "TZS",
"amount": "1000.0",
"scenario": {...},
"initiator": "PAYEE",
"initiatorType": "CONSUMER",
"note": "Note sent to Payee."
}
end note
activate PayeeSDK

PayeeSDK->>Mojaloop: **GET** /parties
Mojaloop->>PayerSDK: **GET** /parties
PayerSDK->PayerDFSP: **GET** /parties
PayerDFSP->PayerDFSP: Lookup Validate Payer Account
PayerDFSP-->PayerSDK: return Payer information
note left
Payer information
end note
PayerSDK->>Mojaloop: **PUT** /parties
Mojaloop->>PayeeSDK: **PUT** /parties

alt If AutoAcceptParty = false
PayeeSDK-->PayeeDFSP: **POST**\n /requestToPayTransfer \n(synchronous return)
note left
{
"requestToPayTransactionId": "string",
"from": {
"type": "CONSUMER",
"idType": "MSISDN",
"idValue": "1234567890",
"idSubValue": "string",
"displayName": "ryZ037pWP'lHu,Tu9,Tjl MRMbdMSpRGAHt4m6 2jk5L4'ePRWT",
"firstName": "Henrik",
"middleName": "Johannes",
"lastName": "Karlsson",
"dateOfBirth": "1966-06-16",
"fspId": "string",
"extensionList": []
},
"to": {...},
"amountType": "RECEIVE",
"currency": "TZS",
"amount": "1000.0",
"transactionType": "TRANSFER",
"note": "Note sent to Payee.",
"currentState": "WAITING_FOR_PARTY_ACCEPTANCE",
}
end note
PayeeDFSP->PayeeSDK: **PUT** /requestToPay/\n{requestToPayId}
note right
{
acceptParty: true
}
end note
else
PayeeSDK->PayeeSDK: Automatically \nAcceptParty by updating\n status
end

PayeeSDK->>Mojaloop: **POST** /transactionRequests
Mojaloop->>PayerSDK: **POST** /transactionRequests
PayerSDK->PayerDFSP: **POST** /transactionRequests
PayerDFSP->PayerDFSP: Validate request\n to pay request
PayerDFSP-->PayerSDK: return
PayerSDK->>Mojaloop: **PUT** /transactionRequests/{ID}
note left
{
"transactionId": "b51ec534-ee48-4575-b6a9-ead2955b8069",
"transactionRequestState": "RECEIVED",
"AuthenticationType": {}
"extensionList": {extension:[]}
}
end note
Mojaloop->>PayeeSDK: **PUT** /transactionRequests
PayeeSDK-->PayeeDFSP: return
deactivate PayeeSDK

=== Payer DFSP executes R2P request ==

PayerDFSP->PayerSDK: **POST** /RequestToPayTransfer
note left
Initiate R2P with AuthType
end note
activate PayerSDK
PayerSDK->>Mojaloop: **POST** /quotes
Mojaloop->>PayeeSDK: **POST** /quotes
PayeeSDK->PayeeDFSP: **POST** /quoterequest
PayeeDFSP->PayeeSDK: return quote
PayeeSDK->>Mojaloop: **PUT** /quotes
Mojaloop->>PayerSDK: **PUT** /quotes

PayerSDK-->PayerDFSP: return \n(**POST** /RequestToPayTransfer)
deactivate PayerSDK

alt if AuthenticateType is null
PayerDFSP->Payer: Present payment terms\n to Payer for acceptance
Payer->PayerDFSP: I accept the payment terms
else if AuthenticateType is OTP
PayerDFSP->PayerDFSP: Generate OTP (optional)
PaulGregoryBaker marked this conversation as resolved.
Show resolved Hide resolved
PayerDFSP->Payer: Present OTP to Payer (optional)
PaulGregoryBaker marked this conversation as resolved.
Show resolved Hide resolved

loop x retries
PayerDFSP->PayerSDK: **PUT** /RequestToPayTransfer
note left
accept quote = true
retries left = x
end note

PayerSDK->>Mojaloop: **GET** \n/authorizations/\n{transactionRequestID}
Mojaloop->>PayeeSDK: **GET** \n/authorizations/\n{transactionRequestID}
PayeeSDK->PayeeDFSP: **GET** \n/auth/{authtype}/{requestToPayId}
PayeeDFSP->Payee: Get Payee to get\n Payer to enter OTP\n on POS
Payer->PayeeDFSP: Enter OTP
PayeeDFSP-->PayeeSDK: return OTP
note right
{
"otpValue": "string"
}
end note
PayeeSDK->>Mojaloop: **PUT** /authorizations/{ID}
Mojaloop->>PayerSDK: **PUT** /authorizations/{ID}
PayerSDK-->PayerDFSP: synchronous return \n **POST** /requestToPayTransfer/\n{requestToPayTransactionId}
PayerDFSP->PayerDFSP: Validate OTP

end loop

end



alt if can proceed with transfer
PayerDFSP->PayerDFSP: Reserve funds against \nPayer's account
PayerDFSP-->PayerSDK: **PUT** \n/requestToPayTransfer/\n{requestToPayTransactionId}

PayerSDK->>Mojaloop: **POST** /transfers
activate PayerSDK
Mojaloop->>PayeeSDK: **POST** /transfers
PayeeSDK-->PayeeDFSP: **PUT** / **POST**\n /requestToPayTransfer/\n{requestToPayTransactionId}\n return
PayeeDFSP->Payee: Notify user
PayeeSDK->>Mojaloop: **PUT** /transfers \nreturn fulfilment
Mojaloop->>PayerSDK: **PUT** /transfers
deactivate PayerSDK
PayerSDK->PayerDFSP: **POST** \n/newAPI \nNotify payer of transfer
PayerDFSP->PayerDFSP: Commit transfer \nto Payer's account
PayerDFSP->Payer: Notify Payer
mdebarros marked this conversation as resolved.
Show resolved Hide resolved

else if rejected

PayerDFSP-->PayerSDK: return
PayerSDK->>Mojaloop: **PUT** \n/requestToPayTransfer/\n{requestToPayTransactionId}\n rejected
Mojaloop->>PayeeSDK: **PUT**\n /requestToPayTransfer/\n{requestToPayTransactionId}\n rejected
PayeeSDK-->X PayeeDFSP: return rejected
end


@enduml
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
mdebarros marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading