mojaloop / mojaloop-specification

This repo contains the specification document set of the Open API for FSP Interoperability

Home Page:https://docs.mojaloop.io/api

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Change Request: Refactoring 3PPI Transfer Interface

mjbrichards opened this issue · comments

Refactoring the 3PPI Transfer Interface

Table of Contents

1. Preface

___

This section contains basic information regarding the change request.

1.1 Change Request Information

| Requested By | Michael Richards, ModusBox |
| Change Request Status | In review ☒ / Approved ☐ / Rejected ☐ |
| Approved/Rejected Date | |

1.2 Document Version Information

Version Date Author Change Description
1.0 2022-01-28 Michael Richards Initial version of issue. Sent out for review.

2. Problem Description

___

2.1 Background

The current structure of a 3PPI transfer request is as follows:

  1. 3PPI requests identification of the recipient directly from the switch.
  2. Creditor DFSP responds to 3PPI with identification.
  3. 3PPI requests initiation of transfer by debtor DFSP.
  4. Debtor DFSP responds to 3PPI with acceptance of transfer initiation request.
  5. Debtor DFSP requests authorisation of transfer from 3PPI. This includes information about the terms of the transfer.
  6. 3PPI obtains confirmation from customer and responds to debtor DFSP.
  7. If terms rejected, debtor DFSP responds with a PATCH on request 3 (i.e. supersedes 4) and STOP
  8. If terms accepted, debtor DFSP checks authorisation.
  9. If authorisation check fails, debtor DFSP responds with a PUT (S/B PATCH?) on request 3 (i.e. supersedes 4) and STOP.
  10. Otherwise, transfer is executed and debtor DFSP responds with a PATCH on request 3 (i.e. supersedes 4) and STOP.

So, essentially, steps 1-2 correspond with the discovery phase, steps 3-5 with the agreement phase, and steps 7-10 with the execution phase. Failures in the execution phase, however, are recorded in a step that might be seen as properly belonging to the agreement phase.

Like many external APIs, the proposed Google Imali interface divides a transfer into the following phases:

  1. The 3PPI requests a transfer of a given amount to a specified account. (the Request phase)
  2. The debtor DFSP responds with the details of the transfer and a challenge to sign.
  3. The 3PPI requests execution of the transfer, giving the signed challenge to establish its bona fides. (the execution phase).
  4. The debtor DFSP responds with the outcome of the transfer execution.

So:

  1. The response at step 2 is equivalent to the request at step 5 in the Mojaloop sequence.
  2. The request at step 3 is equivalent to the response at step 6 in the Mojaloop sequence.

In mapping two-step transfer sequences like the Imali one onto our three-step sequence, we have tended to map the initial phase onto our discovery phase, and the second phase onto our agreement and execution phases. This thinking has given rise to the potential problem just described, where requests on either side can perfectly properly be ignored by the other side:

  • If the debtor DFSP makes a request for authorisation based on a 3PPI's prior request to initiate a transfer, that completes a request/response pair on the 3PPI's side, and the 3PPI would not intuitively expect to need to send a further request ("Please don't!") if the customer decides not to proceed.
  • If the 3PPI requests a transfer (including the signed challenge) and the debtor DFSP rejects the signature, or if the transfer fails for some other reason, then the response comes back as a modification to the two-phase model's transfer request, not the execution request.

This mismatch contains the possibility that errors and misunderstandings will arise over the course of implementation, increasing the likelihood of failure and hence the cost of operation of a system which supports transfers initiated by 3PPIs. If possible, we should consider aligning our interface with Google's Imali interface as a representative of a common pattern in transfer initiation which is consistent with Level 1 principles, as described below.

2.2 Current Behaviour

See above

2.3 Requested Behaviour

At present, we map our Discovery phase onto the two-phase Request phase, and our Agreement and Execution phases onto the two-phase Execution phase. But in the 3PPI context, the point of agreement, and the conclusion of the Request phase, is the user's authorisation of the agreed terms of the transfer. Hence the Execution phase cannot begin until the 3PPI has responded with the signed challenge, which is a request to execute the agreed transfer. This meets the Level One requirement that a customer should always be able to consent to the exact terms of the transfer before confirming that the transfer should be executed.

A more correct mapping of the Mojaloop phases onto the two-phase model might rather be:

  • The Request phase starts with step 1 of the Mojaloop sequence and completes at step 5.
  • The Execution phase starts with step 6 of the Mojaloop sequence and continues to the end.
  • The content of a Request phase, er, request is equivalent to our current step 3 (the body of a POST /thirdpartyRequests/transactions request, perhaps with the payee.fspId left blank to indicate the requirement for discovery).
  • The content of a Request phase response is our step 5 (the body of a POST thirdpartyRequests/authorizations request).
  • The content of an Execution phase request is our step 6 (the body of a PUT thirdpartyRequests/authorizations request).
  • The PATCH responses in our steps 7, 9 and 10 now appear as simple PUT responses to the request issued in our step 6.

This approach would require a revision of our endpoint definitions, but, I think, no revision to the message content. It would also remove the need for a 3PPI to support the FSPIOP GET /parties endpoint.

A sequence diagram to visualize the suggested mapping would help a lot for my understanding..

I attach a proposed revision of the API definition according to the principles outlined above, and a sequence diagram showing how the revised API would work with the proposed Google Imali interface
Draft PISP Specification Changes for Imali.docx
New interface PISP transfer interfacing with Imali

Thanks a lot @mjbrichards! I had a quick look at the sequence diagram and I just have some minor comments/questions for now:

  • Step 30: Why are you not using the name element in the Party complex type for the Payee instead of having an Extension with the display name? There is a note in the note regarding this, but I don't understand it as the Payee should be of type Party, which has a name element, unless this is some restriction in the third party API?
  • Between step 46 and 47: I would assume that there is a POST /transfers from Payer FSP to Switch to the Payee FSP to actually perform the transfer? Maybe this was left out on purpose, but it is a bit confusing when you have the FSPIOP quotes part, but not the FSPIOP transfers part.
  • Minor cosmetic improvement, usually your notes containing the message is between the HTTP request is sent and the HTTP response is received (for example between step 43 and 44), but for the note above step 14 this should be moved to between 14 and 15, and for the note above 30 this should be moved to between 30 and 31 instead.

(the body of a POST /thirdpartyRequests/transactions request, perhaps with the payee.fspId left blank to indicate the requirement for discovery)

Meaning that discovery would happen after 3PPI requests initiation of transfer by debtor DFSP?

In reference to my story mojaloop/project#2668, added additional notes related to implementation there

Hi @ehenrka , thanks for your comments.

  • On your point 1, you're completely correct. I had been concentrating on the PartyIdInfo, and had overlooked the Party. I've made that change.
  • On your point 2, I omitted the transfer part of the sequence because I think it is well-known to us and not of interest to the Imali side. I wanted to concentrate on the refactoring, which is complete by the POST /transfers point.
  • On your point 3, I have moved the notes as you suggest.

@kleyow, the question you raise relates to a thought that didn't make it through to the sequence diagram or the API definition. When we map our three-part transfer sequence onto the two-part sequence that Imali (and, I think, many others) use, we originally thought that the first part corresponded with discovery and the second part with agreement (quote) and execution. I now think it's a better fit to map discovery and agreement onto the first part, and execution onto the second. My impression is that that makes it easier for two-parters to understand. It then occurred to me that we could make it easier by folding the discovery part into the thirdPartyRequests/transactionRequests message, and that we could do that by leaving the payee fspId blank, which the payer DFSP would interpret as "please identify the DFSP to which this identifier belongs." But, as you see, that thinking didn't make it as far as the sequence diagram or the API definition.

New interface PISP transfer interfacing with Imali

It then occurred to me that we could make it easier by folding the discovery part into the thirdPartyRequests/transactionRequests message, and that we could do that by leaving the payee fspId blank, which the payer DFSP would interpret as "please identify the DFSP to which this identifier belongs." But, as you see, that thinking didn't make it as far as the sequence diagram or the API definition.

Yep I interpreted that statement exactly as you described.

Thanks for this Michael,

In general this looks like a great simplification to what we already have, and I'm yet to find any drawbacks. I'm looking forward to discussing it later on.

My notes below:

It would also remove the need for a 3PPI to support the FSPIOP GET /parties endpoint.
Can you please clarify this? I take it to mean that the 3PPI won't need to call GET /parties, but this could be construed otherwise.

  • Maintaining two similarly named paths: thirdpartyRequests/transactions and thirdpartyRequests/transfers seems dangerous to me. Are there better names we can use to better distinguish between the 2 phases?

In section 3.1.6.1.2 POST /thirdPartyRequests/transfers:

Used by: DFSP
The HTTP request POST /thirdPartyRequests/transfers is used to request the validation by a customer for the transfer described in the request.

This doesn't line up with the sequence diagram, where this call is made by the PISP, not the DFSP.

This proposal looks non-backwards compatible to me, as we are repurposing exsting endpoints for other purposes. So do you imagine we are going to be updating to the Third Party API to v2.0 here?

This proposal looks non-backwards compatible to me, as we are repurposing existing endpoints for other purposes. So do you imagine we are going to be updating to the Third Party API to v2.0 here?

Agreed in SIG Meeting

@MichaelJBRichards I've got the following suggestions for the 2 endpoint names (in no particular preference order):

  • thirdpartyRequests/initiate and thirdpartyRequests/transfer?
  • thirdpartyRequests/terms and thirdpartyRequests/accept
  • thirdpartyRequests/propose and thirdpartyRequests/execute

@lewisdaly and @mjbrichards, isn't it in essence a quote and a transfer that is done in the sequence flow, but requested from a thirdparty?

  • The first request from the third-party seems to be very similar to a quote, the information that is returned is the fee to perform the transaction and the Payee name, along with the challenge to be able to execute the transfer based on the quote.
  • The second request is then the request to execute the transfer.

Hence, my suggestion would simply be /3pQuotes and /3pTransfers, which names would align well with the new proposed/fxQuotes and /fxTransfers resources in #89.

Yeah I like that suggestion @ehenrka. And do I understand your statement well that we remove the thirdpartyRequests prefix?

Thanks @lewisdaly, yes I think it is fine to remove the longer prefix thirdpartyRequests/ as it should be easy to distinguish based on the 3p prefix. I'm also fine with using something longer like ThirdpartyQuotes and ThirdpartyTransfers if just prefix 3p is a bit cryptic.

Does that mean, @ehenrka and @lewisdaly, that we should add the prefix to all the currently non-prefixed endpoints (/accounts, /consentRequests, etc.)? This would make it clear that they all belong to the same family.

I've taken an executive decision and renamed all the PISP endpoints to use the prefix "tpp" (Third Party Provider). On the question of quotes, I think that what we actually have is an analogue to the FSPIOP /transactionRequests endpoint, so I have renamed it accordingly. Let me know what you think.
Draft PISP Specification Changes for Imali.docx

@mjbrichards, I would say that there is a major difference in the /transactionRequests endpoint compared to the flow in #107 (comment).

In the FSPIOP /transactionRequests, an FSP (the Payee FSP) is requesting another FSP (the Payer FSP) to ask if their account holder (the Payer) accepts a transaction. In this case it will be the Payee FSP that sends the POST /transactionRequests, but then it is the Payer FSP that sends the POST /quotes and the POST /transfers. So there will be a change in who is driving the flow.

In the sequence diagram in #107 (comment), the flow is controlled by the Third Party. The Payment Manager acting as the third party first sends a GET /parties to the Switch, then a POST /thirdpartyrequests/transactions to the Switch to get the quote for the transaction, then the POST /thirdpartyrequests/transfers to the Switch to perform the transaction as detailed in the quote. This is very similar to the normal Payer initiated flow in FSPIOP, where the Payer FSP first sends a GET /parties, then POST /quotes, then POST /transfers.

As ususal, @ehenrka, you are completely correct; but I think that this is the correct structure for the third party interaction. In the FSPIOP interaction, the requester is the creditor party and authorisation for the transfer is given out-of-band by the debtor party; so the debtor DFSP is driving the flow. In the TPP interaction, however, the requester is the debtor party, and the TPP is managing the authorisation process: so it seems to me correct that the TPP should be managing the flow.

However, you raise an important point which I think we should consider. As I've said, the pattern that I propose seems to me appropriate for the interaction we're considering; but I think that this is a consequence of the fact that the TPP is acting on behalf of the authorising party in the process of authorisation. The API therefore needs to provide a break in the transactional flow to allow the debtor customer to review the terms of the transfer and give their consent to it. We should, however, also consider the case where the TPP wants to request the initiation of a payment, but where it wants authorisation to be carried out by the debtor's DFSP: for instance, where a fintech wants to request a subscription payment from a customer. Here the TPP is (acting for) the creditor, not the debtor; but it cannot rely on obtaining information about the transfer from its DFSP, since it is not making the request through the creditor DFSP, as would be the case if it were using MRTP via the FSPIOP API. It seems to me that, in this case, the correct response to the POST /tppTransactionRequests call would be a message which contains the content of the PUT /tppTransactionRequests call, but without the challenge field (since authorisation by the TPP is not required,) and with an additional transferState field informing the TPP of the outcome of the transfer.

So we might say that the OpenAPI definition for this message should include a oneOf containing the challenge field if authorisation is to be obtained by the TPP, or the transferState field if authorisation has been obtained by the debtor DFSP and the transfer has completed.

I'm not quite sure how we represent alternatives in our specification; but does that make sense?

Thanks @mjbrichards, but I still don't really understand why it should be /tppTransactionRequests in this case. There is not a Payee that is requesting a transaction through the PISP as in the FSPIOP version /transactionRequests, where the Payee requests the Payer to perform the transaction, and the Payee can then expect an incoming transaction later (if the request is accepted the Payer).

In the flow in #107 (comment), the Payer is requesting a quote for a transaction to the Payee via the PISP. The quote can later be executed by the Payer sending the signed challenge again via the PISP. The Payee is not involved in this flow at all, the Payee just receives funds. This is similar to the normal Payer initiated flow in FSPIOP, which just uses /quotes and /transfers, but the signed challenge is handled internally in the Payer FSP. Hence, I still think it should be /tppQuotes.

Based on your comment above, it seems like you want to use the same /tppTransactionRequests request for both a merchant initiated payment use case (or similar Payee-initiated transactions) and a person to person transfer (or other similar Payer-initiated transactions) but with alternative fields.

To me, it seems appropriate to use a similar pattern as in FSPIOP (Payer- vs Payee-initiated flow), where you use a separate request if you as a Payee is requesting a transaction from a Payer, instead of where the Payer is performing the transaction directly without involving the Payee. This would avoid any alternative fields that are based on the use case, and instead make it clear both which use case it is and which fields that should be used for that specific use case.

I think I (somehow) agree with both of you @ehenrka and @mjbrichards!

If I can try and restate your point @MichaelJBRichards, right now we are mainly imagining a P2P use case, but initiated by a 3rd party, so this should be considered a transaction request. But we could also imagine a future where this isn't necessarily the case (e.g. a PISP that helps users automate bill payments by sending transaction requests when bills are due)

Is there a 3rd term (not /transfers or /transactionRequests) that we could generalize over both (or all) use cases? Or at least allow us to disambiguate between this API and the FSPIOP API?

Sorry, @ehenrka and @lewisdaly, that I didn't express myself very clearly. The point I was trying to make is that there are really three separate decisions that a TPP needs to make, and that these decisions are orthogonal to each other. The first decision is whether the TPP is acting on behalf of the debtor (which is the GooglePay example) or of the creditor. The second is whether or not the TPP wants the debtor institution to delegate authorisation for the transfer to the TPP; and the third is whether the TPP wants the chance to assent to the final terms of the transfer.

Let's imagine a situation in which a fintech has developed an application for merchants to use. It will include a module which allows the merchant to request payment from a customer, and another module which allows the merchant to order supplies from one or more wholesalers based on monitoring stock levels. In the first module, the merchant is the creditor; in the second, it is the debtor. In both cases, the fintech is defining the terms of a payment which will then be agreed and executed by the appropriate DFSP parties.

In order to establish the argument for orthogonality between origination and authorisation, we need to answer the following questions in the negative: first, are there any circumstances in which we might want to support the merchant application authorising a payment even though it was acting for the creditor party; and, second, are there any circumstances in which we might want to support the merchant application asking the debtor DFSP to manage authorisation when the TPP was acting for the debtor party?

I think that the answer to the second question is definitely yes. Our merchant application might want to manage the re-ordering process without also wanting to take the responsibility of assuring the DFSP which held the merchant's bank account that it had properly obtained the merchant's authorisation for the transfer. In this it would be like the existing MRTP flow, except that the flow is initiated by a representative of the debtor party, not the creditor party. So the app tells the merchant that she needs to order more cans of Coke from her supplier: the merchant initiates the order, and her DFSP contacts her to approve the funds transfer.

The more counter-intuitive question is the first; but I think that here too we can propose a use case in which the merchant app might want to authorise on behalf of the creditor party to a transfer. Let's imagine a merchant whose store is located in a semi-permanent refugee camp. The refugees in the camp don't have access to mobile money systems, or possibly even phones; but an NGO is disbursing funds to support their day-to-day existence. The NGO wants to ensure that the funds are really going to the intended beneficiaries. The NGO has an enrolment program which records an ID for each refugee, together with their fingerprint. Now a refugee can go to the merchant's store and buy goods. When they want to pay, the merchant app records their ID number and their fingerprint; and this is taken by the NGO's DFSP as authorisation to credit the merchant's account, and debit the refugee's share of the overall disbursement, in respect of that particular refugee. So the NGO can make disbursements without needing to hand out cash; the disbursements are spent with local merchants, not international suppliers; and the refugees are brought into the financial system. All of this depends on the merchant app being able to authorise the credit party to the satisfaction of the debit party.

Now we need to think about the question of approval by the TPP application. Here I think that the question of orthogonality turns on the following test: can we imagine a circumstance in which the TPP's customer would want to approve a transfer even though the TPP is not responsible for authorising the transfer? Here I think that the answer is definitely yes. Let's suppose that our merchant app is generating a purchase request to replenish the merchant's stock of cans of Coke. The merchant's normal supplier has moved their account to a DFSP which charges a significant sum for processing the transfer. The merchant's TPP app should be able to pass this information back to the merchant and allow her to decide whether to go ahead with the purchase and bear the charge, or to look for an alternative supplier. This needs to be done by the TPP app rather than the authorising DFSP app, because it's part of the service that the TPP app provides to know about alternative suppliers, and the DFSP app doesn't provide that service.

So I think that this means that we have three separate things that our API will need to cover: the first is a request to perform a transfer between two named parties. This will be the same whether the TPP is acting on behalf of the credit party or the debit party, and it will always be addressed to the debit party's DFSP. The second is the question of whether the transfer is to be authorised by the TPP or by a DFSP. The third is the question of whether the TPP wants to be able to approve the transfer before it is executed or not. Now, I don't have a strong preference about the names we give to these things; but I do think that we (will) need to accommodate them in the way I've described. Would you agree?

Thanks for the additional context @mjbrichards!

First a question regarding the following:

So I think that this means that we have three separate things that our API will need to cover: the first is a request to perform a transfer between two named parties. This will be the same whether the TPP is acting on behalf of the credit party or the debit party, and it will always be addressed to the debit party's DFSP.

Maybe I missed something here, but why will it be the same, and why will it always be addressed to the Payer FSP? If the TPP is acting on behalf of the Payee (the credit party), what is stopping you from sending a request to the Payee FSP, and the Payee FSP then sends a FSPIOP POST /transactionRequests to the Payer FSP to ask the Payer for confirmation? If this was the case, then only the Payee FSP would need to support the TPP interface (meaning a possible competitive advantage in selling fintech services for PISPs for the Payee FSP in this case).

Your refugee example seems to be similar to Payee Initiated Transaction using OTP, but instead of using OTP you are using ID number and fingerprint as the authorisation to pay. I think it makes good sense here to extend the possible authentication types in FSPIOP so that you can use additional methods. But yes, there must be a way for the TPP to say which type of authentication it should be.

Regarding your last example with generating a purchase request to replenish the merchant's stock, does the question to the merchant really have to be in the API? In my view, the TPP app just sends a quote for the transaction to the supplier, the TPP app sees that this transfer now costs a lot of money and therefore asks the merchant to confirm. I just don't see any need to have this part in the API, but I might have missed something.

Hi @ehenrka, and thank you as usual for your acute comments. My answers are as follows...

I think that there's a problem with sending the request to the payee DFSP. If the Payee DFSP then sends a FSPIOP POST /transactionRequests to the Payer DFSP to ask the Payer for confirmation, how is it to notify to the payer DFSP that the TPP wants to undertake authorisation? The FSPIOP POST /transactionRequests doesn't accommodate that. So that scenario would work if the TPP wanted the Payer DFSP to undertake authorisation, but not if it didn't. Which introduces what seems to me an unpleasingly asymmetric quality to the interface; but perhaps that doesn't matter.

I think that the difference between my example and the OTP /authorizations endpoint is that in the OTP example, the authorisation process is still being carried out by the payer DFSP, but using material provided by the addressee of the request (e.g. the operator of an ATM.) In the TPP example, the TPP really is carrying out the authorisation itself, and is then providing the payee DFSP with a testable assertion that it has in fact performed the authorisation. Now, perhaps there's no substantive difference between the two; but they seem to me to be different things...

In the third point, I wanted to be able to draw a distinction between authorisation and confirmation. If the TPP wants the payer DFSP to perform authorisation, then in the normal run of things it wouldn't need to do a confirmation (which we kind of expect to be part of the authorisation.) It would just send the request to the DFSP and wait to hear what the response was. So it might be possible to say that the TPP could infer from an error response that the merchant had declined the authorisation, and that the TPP should therefore find out more about why and perhaps suggest alternatives. Perhaps that would be the way forward, provided that it was clear that the payer DFSP needed to send the declined status to the TPP so that the TPP could take action if it wanted to.

Thanks @mjbrichards,

I think that there's a problem with sending the request to the payee DFSP. If the Payee DFSP then sends a FSPIOP POST /transactionRequests to the Payer DFSP to ask the Payer for confirmation, how is it to notify to the payer DFSP that the TPP wants to undertake authorisation? The FSPIOP POST /transactionRequests doesn't accommodate that.

With this I assume that you mean that the Payer will be authorised in the TPP, like if I'm buying something from a merchant and enter my credentials/fingerprint in the merchant's device? At least that high-level use case is supported in the FSPIOP API, but just using OTP or QR code as of now. I don't see a problem with adding additional authorisation possibilities, potentially through some kind of testable assertion as you mention your following paragraph. One example could be that the Payer can prove that he/she is accepting the transaction by signing with his/her private key, which the Payee FSP can easily verify by the Payer's public key.

At least in my world, this would simplify things as the TPP would talk to its user's FSP, irregardless if the user is currently acting as the Payer or Payee. No need for both Payer and Payee FSP to support the Third Party API. It would also improve both the Third Party API and the FSPIOP API.

Following our discussion at the SIG, a thought on fraud. @jgeewax raised the question of the danger of impersonation frauds, in which bad actors convince account holders to re-route funds from the legitimate beneficiary to a fake account controlled by the fraudster. He was concerned, rightly of course, that in allowing PISPs to make payment requests to multiple subscribers (for instance, M-Kopa reminding its subscribers that their payments are due via an RTP) we should not create a use case which could be used fraudulently. I think that this worry should probably be allayed by the fact that any PISP joining a Mojaloop scheme would need to be explicitly approved by the scheme (and reviewed while a member.) So it should not be possible for an alert scheme to allow an actor called N-Kopa into the scheme as a PISP. These frauds are enabled by the fact that the bad actors can use out-of-band methods of persuasion like fake mails to convince their victims to make payments in which the fraudulent beneficiaries are ordinary customers of existing FIs. Being able to regulate PISPs as a specific class of actors should, in my view, help considerably in reducing the possibility of frauds of this type.

Following discussion in the newly formed PISP SIG, we hope to get agreement to the changes proposed so that a v2.0 API of the PISP can be published.
To help with this I have prepared and overview sequence diagram summarising what has been agreed to to date.
Please review to make sure that it aligns with everyone's understanding.
PISPTransfer_v2 0

Here is the update draft specification document for review.
Draft.PISP.Specification.Changes.for.v2_0

Which include the edits discussed above, and the new accounts request resource that enables authorisation to distribute the account information during discovery as described in issue #123.