diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 681aa8034..54ed717ae 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -444,9 +444,338 @@ module.exports = { "sidebarDepth": 2 }, { - // TODO: Placeholder and temporary link for this section until it can be migrated from legacy docs. - title: 'Central Ledger Services', - path: 'https://docs.mojaloop.io/legacy/mojaloop-technical-overview/central-ledger/' + "title": "Central Ledger", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-ledger/" + }, + { + "title": "Admin Operations", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-ledger/admin-operations/", + }, + { + "title": "POST Participant Limit", + "path": "central-ledger/admin-operations/1.0.0-post-participant-position-limit" + }, + { + "title": "GET Participant Limit Details", + "path": "central-ledger/admin-operations/1.1.0-get-participant-limit-details" + }, + { + "title": "GET All Participant Limits", + "path": "central-ledger/admin-operations/1.0.0-get-limits-for-all-participants" + }, + { + "title": "POST Participant limits", + "path": "central-ledger/admin-operations/1.1.0-post-participant-limits" + }, + { + "title": "GET Transfer Status", + "path": "central-ledger/admin-operations/1.1.5-get-transfer-status" + }, + { + "title": "POST Participant Callback", + "path": "central-ledger/admin-operations/3.1.0-post-participant-callback-details" + }, + { + "title": "GET Participant Callback", + "path": "central-ledger/admin-operations/3.1.0-get-participant-callback-details" + }, + { + "title": "GET Participant Position", + "path": "central-ledger/admin-operations/4.1.0-get-participant-position-details" + }, + { + "title": "GET All Participants Positions", + "path": "central-ledger/admin-operations/4.2.0-get-positions-of-all-participants" + } + ] + }, + { + "title": "Transfers Operations", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-ledger/transfers/" + }, + { + "title": "Prepare Handler", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-ledger/transfers/1.1.0-prepare-transfer-request" + }, + { + "title": "Prepare Handler Consume", + "path": "central-ledger/transfers/1.1.1.a-prepare-handler-consume" + } + ] + }, + { + "title": "Prepare Position Handler", + "path": "central-ledger/transfers/1.3.0-position-handler-consume" + }, + { + "title": "Prepare Position Handler v1.1", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-ledger/transfers/1.3.0-position-handler-consume-v1.1" + }, + { + "title": "Prepare Position Handler", + "path": "central-ledger/transfers/1.3.1-prepare-position-handler-consume" + }, + { + "title": "Position Handler Consume", + "path": "central-ledger/transfers/1.1.2.a-position-handler-consume" + } + ] + }, + { + "title": "Fulfil Handler", + "path": "central-ledger/transfers/2.1.0-fulfil-transfer-request" + }, + { + "title": "Fulfil Handler v1.1", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-ledger/transfers/2.1.0-fulfil-transfer-request-v1.1" + }, + { + "title": "Fulfil Handler Consume", + "path": "central-ledger/transfers/2.1.1-fulfil-handler-consume" + }, + { + "title": "Fulfil Handler Consume v1.1", + "path": "central-ledger/transfers/2.1.1-fulfil-handler-consume-v1.1" + } + ] + }, + { + "title": "Fulfil Position Handler", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-ledger/transfers/1.3.0-position-handler-consume" + }, + { + "title": "Fulfil Position Handler", + "path": "central-ledger/transfers/1.3.2-fulfil-position-handler-consume" + }, + { + "title": "Fulfil Position Handler v1.1", + "path": "central-ledger/transfers/1.3.2-fulfil-position-handler-consume-v1.1" + } + ] + }, + { + "title": "Fulfil Reject Transfer", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-ledger/transfers/2.2.0-fulfil-reject-transfer" + }, + { + "title": "Fulfil Reject Transfer (a)", + "path": "central-ledger/transfers/2.2.0.a-fulfil-abort-transfer" + }, + { + "title": "Fulfil Handler (Reject-Abort)", + "path": "central-ledger/transfers/2.2.1-fulfil-reject-handler" + } + ] + }, + { + "title": "Fulfil Reject Transfer v1.1", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-ledger/transfers/2.2.0-fulfil-reject-transfer-v1.1" + }, + { + "title": "Fulfil Reject Transfer (a) v1.1", + "path": "central-ledger/transfers/2.2.0.a-fulfil-abort-transfer-v1.1" + }, + { + "title": "Fulfil Handler (Reject-Abort) v1.1", + "path": "central-ledger/transfers/2.2.1-fulfil-reject-handler-v1.1" + } + ] + }, + { + "title": "Notifications", + "collapsable": true, + "children": [ + { + "title": "Notification to Participant (a)", + "path": "central-ledger/transfers/1.1.4.a-send-notification-to-participant" + }, + { + "title": "Notification to Participant (a) - v1.1", + "path": "central-ledger/transfers/1.1.4.a-send-notification-to-participant-v1.1" + }, + { + "title": "Notification to Participant (b)", + "path": "central-ledger/transfers/1.1.4.b-send-notification-to-participant" + } + ] + }, + { + "title": "Reject/Abort", + "collapsable": true, + "children": [ + { + "title": "Abort Position Handler", + "path": "central-ledger/transfers/1.3.3-abort-position-handler-consume" + } + ] + }, + { + "title": "Timeout", + "collapsable": true, + "children": [ + { + "title": "Transfer Timeout", + "path": "central-ledger/transfers/2.3.0-transfer-timeout" + }, + { + "title": "Timeout Handler Consume", + "path": "central-ledger/transfers/2.3.1-timeout-handler-consume" + } + ] + }, + ] + }, + { + "title": "Bulk Transfers Operations", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-bulk-transfers/" + }, + { + "title": "Bulk Prepare", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-bulk-transfers/transfers/1.1.0-bulk-prepare-transfer-request-overview" + }, + { + "title": "Bulk Prepare Handler", + "path": "central-bulk-transfers/transfers/1.1.1-bulk-prepare-handler-consume" + } + ] + }, + { + "title": "Prepare Handler", + "collapsable": true, + "path": "central-bulk-transfers/transfers/1.2.1-prepare-handler-consume-for-bulk" + }, + { + "title": "Position Handler", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-bulk-transfers/transfers/1.3.0-position-handler-consume-overview" + }, + { + "title": "Prepare Position Handler Consume", + "path": "central-bulk-transfers/transfers/1.3.1-prepare-position-handler-consume" + }, + { + "title": "Fulfil Position Handler Consume", + "path": "central-bulk-transfers/transfers/2.3.1-fulfil-position-handler-consume" + }, + { + "title": "Fulfil Abort Position Handler Consume", + "path": "central-bulk-transfers/transfers/2.3.2-position-consume-abort" + } + ] + }, + { + "title": "Bulk Fulfil Handler", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-bulk-transfers/transfers/2.1.0-bulk-fulfil-transfer-request-overview" + }, + { + "title": "Bulk Fulfil Handler Consume", + "path": "central-bulk-transfers/transfers/2.1.1-bulk-fulfil-handler-consume" + }, + { + "title": "Fulfil Handler - Commit", + "path": "central-bulk-transfers/transfers/2.2.1-fulfil-commit-for-bulk" + }, + { + "title": "Fulfil Handler - Reject/Abort", + "path": "central-bulk-transfers/transfers/2.2.2-fulfil-abort-for-bulk" + } + ] + }, + { + "title": "Bulk Processing Handler", + "path": "central-bulk-transfers/transfers/1.4.1-bulk-processing-handler" + }, + { + "title": "Notifications", + "collapsable": true, + "children": [ + { + "title": "Notification to Participant (a)", + "path": "central-ledger/transfers/1.1.4.a-send-notification-to-participant" + }, + { + "title": "Notification to Participant (b)", + "path": "central-ledger/transfers/1.1.4.b-send-notification-to-participant" + } + ] + }, + { + "title": "Timeout", + "collapsable": true, + "children": [ + { + "title": "Overview", + "path": "central-bulk-transfers/transfers/3.1.0-transfer-timeout-overview-for-bulk" + }, + { + "title": "Timeout Handler Consume", + "path": "central-bulk-transfers/transfers/3.1.1-transfer-timeout-handler-consume" + } + ] + }, + { + "title": "Bulk Abort Overview", + "path": "central-bulk-transfers/transfers/4.1.0-transfer-abort-overview-for-bulk" + }, + { + "title": "Get Bulk Transfer Overview", + "path": "central-bulk-transfers/transfers/5.1.0-transfer-get-overview-for-bulk" + } + ] + } + ], + "sidebarDepth": 2 }, { // TODO: Placeholder and temporary link for this section until it can be migrated from legacy docs. diff --git a/docs/technical/central-bulk-transfers/README.md b/docs/technical/central-bulk-transfers/README.md new file mode 100644 index 000000000..c1d0b3d54 --- /dev/null +++ b/docs/technical/central-bulk-transfers/README.md @@ -0,0 +1,298 @@ +# Bulk Transfers Design + +The Bulk Transfers scenario is described in the API Definition document regarding the resource /bulkTransfers. For details _(refer to section `6.10`)_ as per the [Mojaloop Specification](https://github.com/mojaloop/mojaloop-specification/blob/master/API%20Definition%20v1.0.pdf) + +1. [Introduction](introduction) +2. [Design Considerations](design-considerations) +3. [Steps involved in the high-level Architecture](steps-involved-in-the-high-level-architecture) +4. [Notes](notes) + 1. [Discussion items](discussion-items) + 2. [Proposed new tables](proposed-new-tables) + 3. [Bulk Transfers States](bulk-transfers-states) + 4. [Additional Notes](additional-notes) +5. [Roadmap Topics](roadmap-topics) + +## 1. Introduction + +The Bulk Transfers process is discussed in section 6.10 of the API Definition 1.0 document, depicted in Figure 60, of which a snapshot is posted below. +![Figure 60](./assets/diagrams/architecture/Figure60-Example-Bulk-Transfer-Process-Spec1.0.png) + +The key items implied in the specification in its current version 1.0 are that + +- Reservation of funds is done for each individual transfer from the Payer FSP to the Payee FSP +- Even if a single individual transfer fails during the prepare process, the whole bulk is to be rejected. + +## 2. Design Considerations + +According to the Figure-60 of the specification, below are a few key implications from the Specification. + +1. The Payer DFSP performs user look-ups for the individual parties involved in the bulk payment separately +2. The Payer DFSP performs bulk quoting per Payee DFSP +3. The onus is on the Payer DFSP to prepare bulk transfers based on Payee FSPs and send out a bulk transfer request to a single Payee FSP +4. This seems to be an all-or-nothing process where even if a single individual transfer fails to be reserved, then the whole bulk needs to be rejected because it cannot be sent to the Payee as it is if it has an individual transfer for which funds couldn't be reserved. +5. In light of the above, the proposal being made right now is to empower the Switch (needs updating the Specification) to send out the POST /bulkTransfers request with the list of individual transfers for which funds were able to be reserved on the Switch. +6. The implication is that the Switch aggregates commits/failures from the Payee FSP for the bulk and sends out a single PUT /bulkTransfers/{ID} call to the Payer FSP that includes the entire list of transfers that includes individual transfers that failed both at the Switch and the Payee FSP +7. For example: If there are 1000 individual transfers in a Bulk Transfer and if the Switch is able to reserve funds for 900 of the individual transfers, then a prepare bulk transfer request to the Payee DFSP is sent with the list of those 900 individual transfers. Once the Payee FSP sends the Bulk Fulfil request for those 900 transfers of which lets say, 800 can be committed and 100 are aborted, then the Switch processes those individual transfers accordingly and sends out the PUT callback (PUT /bulkTransfers/{ID}) notification to the Payer FSP with all the 1000 individual transfers, 800 of which are committed and 200 of which are aborted. +8. There will be implications to aspects such as Signature, Encryption, PKI and other security aspects that will need to be addressed. +9. The ordering of the individual transfers need to be considered as well by the Scheme. A Goal for implementation in emerging markets is to maximize the number of transactions involved and so a well designed Scheme may re-order individual transfers in the ascending order of the magnitude of amounts and then process them. But this can be a Scheme consideration. +10. However, a recommended Scheme Rule is that the Payee FSPs shouldn't be allowed to re-order the individual transfers in a bulk to avoid bias towards Payee parties. +11. For Settlements with bulk transfers where Government payments are involved with large sums of money needs to be discussed to allow for moving through transfers without strict liquidity rules needs to be discussed. + +## 3. Steps involved in the high-level Architecture + +Below are the steps involved at a high level for bulk transfers. + +![architecture diagram](./assets/diagrams/architecture/bulk-transfer-arch-flows.svg) + +1. [1.0, 1.1, 1.2] An Incoming bulk Transfer request (POST /bulkTransfers) on the bulk-api-adapter is placed in an object store and a notification with a reference to the actual message is sent via a kafka topic “bulk-prepare” and a 202 is sent to the Payer FSP +2. [1.3] Bulk Prepare handler consumes the request, records the status as RECEIVED + + a. Bulk Prepare handler then validates the Bulk and changes state to PENDING if the validation is successful + + b. One validation rule proposed in addition, here is to reject a bulk if there are duplicate transfer IDs used in the bulk itself. + + c. [1.4] If validation fails, Bulk Prepare handler changes the bulkTransferState to PENDING_INVALID (an internal state) and produces a message onto the bulk processing topic + i. Bulk processing Handler then updates the bulkTransferState to REJECTED and sends a notification to the Payer + +3. [1.4] [Continuing 2.a] Bulk Prepare handler breaks down the bulk into individual transfers and puts each of them on the prepare topic + + a. As part of this, each transfer is individually assigned the 'expiration time' of the bulk Transfer itself (and other fields necessary for individual transfers) + +4. [1.5, 1.6, 1.7] Prepare handler, Position handler are refactored to handle individual transfers in a bulk, using flags such as type, action, status, etc. + + a. Reservation of funds --> This is left to the individual handlers and the whole bulk is then aggregated in the Bulk Processing Handler. + +5. [1.8] Position Handler produces messages corresponding to individual transfers that are part of a bulk to bulk processing topic +6. [1.9] For every message consumed from the bulk processing topic a check is made on the Bulk processing Handler to see if that’s the last individual transfer in a bulk for that processing phase. +7. [1.10, 1.11, 1.12] If it is the last transfer, aggregate the state of all the individual transfers and + + a. If all of them are in reserved state --> Send POST /bulkTransfers to the Payee (by producing a message to the notifications topic which is then consumed by the notification handler) + + b. Once the bulkTransfer prepare request is sent to the Payee, then change status to ACCEPTED + +8. In a successful case of Prepare - when the PUT from the Payee FSP for bulkFulfil is received, a notification is put on the bulk fulfil topic with a reference to the actual Fulfil message that's stored in an Object store. +9. This is to be consumed by the bulkFulfilHandler, which then changes state to PROCESSING. +10. The bulk-fulfil-handler breaks down the bulk into individual transfers and sends each of them through the refactored Fulfil, Position Handlers to commit/abort each of them based on the PUT /bulkTransfers/{ID} message by the Payee and commit/release funds on the Switch +11. The bulk-processing-handler is to then aggregate all the individual transfer results and change the state of bulkTransfer to COMPLETED/REJECTED based on success/failure + + a. If the Payee sends COMMITTED for even one of the individual transfers the proposal is to change bulk state to COMPLETED. + + b. However, for step-8 or if the Payee sends REJECTED as bulkTransferState then final state on Switch should be REJECTED. + +12. Send notifications to both Payer and Payee (similar to Single transfers, though diverging from the Spec 1.0). The Payer-FSP receives the notification that includes an exhaustive list of individual transfers, same as the list present in the prepare request sent by the Payer. The Payee-FSP receives a notification only for sub-set of transfers, which were sent to it from the Switch as the Bulk prepare request (that were able to be reserved at the Switch). + +## 4. Implementation Details + +### 4.1 Bulk Transfer States + +Below are the states of a Bulk transfer as per the Mojaloop API Specification + +1. RECEIVED +2. PENDING +3. ACCEPTED +4. PROCESSING +5. COMPLETED +6. REJECTED +7. Internal state - PENDING_PREPARE (mapped to PENDING) +8. Internal state - PENDING_INVALID (mapped to PENDING) +9. Internal state - PENDING_FULFIL (mapped to PROCESSING) +10. Internal state - EXPIRING (mapped to PROCESSING) +11. Internal state - EXPIRED (mapped to COMPLETED) +12. Internal state - INVALID (mapped to REJECTED) +13. Additional micro-states may be added for internal use on the Switch + +### 4.2 Proposed New tables + +Below are the proposed tables as part of designing the Bulk transfers + +- bulkTransfer +- bulkTransferStateChange +- bulkTransferError +- bulkTransferDuplicateCheck +- bulkTransferFulfilment +- bulkTransferFulfilmentDuplicateCheck +- bulkTransferAssociation +- bulkTransferExtension +- bulkTransferState +- bulkProcessingState + +### 4.3 Internal Type-Action-Status combinations + +#### 1. Bulk transfer that passes schema validation [ml-api-adapter -> bulk-prepare-handler] + + 1. type: bulk-prepare + 2. action: bulk-prepare + 3. Status: success + 4. Result: bulkTransferState=RECEIVED, bulkProcessingState=RECEIVED + +#### 2. Duplicate [bulk-prepare-handler -> notification handler] + + 1. type: notification + 2. action: bulk-prepare-duplicate + 3. Status: success + 4. Result: bulkTransferState=N/A, bulkProcessingState=N/A + +#### 3. Validate Bulk Prepare transfer failure [bulk-prepare-handler -> notification-handler] + + 1. type: notification + 2. action: bulk-abort + 3. Status: error + +#### 4. For a Valid Bulk Prepare transfer (broken down and sent as individual transfers) [bulk-prepare-handler -> prepare-handler] + + 1. type: prepare + 2. action: bulk-prepare + 3. Status: success + +#### 5. Duplicate of individual transfer that is part of a bulk-transfer [prepare-handler -> bulk-processing-handler] + + 1. type: bulk-processing + 2. action: prepare-duplicate + 3. Status: success + 4. Expected action: Add error message indicating it’s a duplicate + 5. Result: bulkTransferState=PENDING_PREPARE/ACCEPTED (depending on whether it’s the last one), bulkProcessingState=RECEIVED_DUPLICATE + +#### 6. For individual Prepare transfer that’s a valid duplicate in prepare handler [prepare-handler -> bulk-processing-handler] + + 1. type: bulk-processing + 2. action: prepare-duplicate + 3. Status: error + 4. Result: bulkTransferState=PENDING_PREPARE/ACCEPTED (depending on whether it’s the last one), bulkProcessingState=RECEIVED_DUPLICATE + +#### 7. For a Valid individual Prepare transfer that’s part of a bulk [prepare-handler -> position-handler] + + 1. type: position + 2. action: bulk-prepare + 3. Status: success + +#### 8. For individual Prepare transfer that’s part of a bulk that failed validation in prepare handler [prepare-handler -> bulk-processing-handler] + + 1. type: bulk-processing + 2. action: bulk-prepare + 3. Status: error + 4. Result: bulkTransferState=PENDING_PREPARE/ACCEPTED (depending on whether it’s the last one), bulkProcessingState=RECEIVED_INVALID + +#### 9. For a Valid individual Prepare transfer that’s part of a bulk [position-handler -> bulk-processing-handler] + + 1. type: bulk-processing + 2. action: bulk-prepare + 3. Status: success + 4. Result: bulkTransferState=PENDING_PREPARE/ACCEPTED (depending on whether it’s the last one), bulkProcessingState=ACCEPTED + +#### 10. For individual Prepare transfer that’s part of a bulk that failed validation in position handler [position-handler -> bulk-processing-handler] + + 1. type: bulk-processing + 2. action: bulk-prepare + 3. Status: error + 4. Result: bulkTransferState=PENDING_PREPARE/ACCEPTED (depending on whether it’s the last one), bulkProcessingState=RECEIVED_INVALID + +#### 11. For a Valid individual Fulfil transfer (for commit) that’s part of a bulk [position-handler -> bulk-processing-handler] + + 1. type: bulk-processing + 2. action: bulk-commit + 3. Status: success + 4. Result: bulkTransferState=PENDING_FULFIL/COMPLETED (depending on whether it’s the last one), bulkProcessingState=COMPLETED + +#### 12. For Bulk transfer Fulfil message that passes validation [ml-api-adapter -> bulk-fulfil-handler] + + 1. type: bulk-fulfil + 2. action: bulk-commit + 3. Status: success + +#### 13. For a valid individual transfer part of a bulk that timed-out in position handler [position-handler -> bulk-processing-handler] + + 1. type: bulk-processing + 2. action: bulk-timeout-reserved + 3. Status: error + 4. Result: bulkTransferState=PENDING_FULFIL/COMPLETED (depending on whether it’s the last one), bulkProcessingState=FULFIL_INVALID + +#### 14. For a Valid individual Fulfil transfer (for reject) that’s part of a bulk [position-handler -> bulk-processing-handler] + + 1. type: bulk-processing + 2. action: reject + 3. Status: success + 4. Result: bulkTransferState=PENDING_FULFIL/COMPLETED (depending on whether it’s the last one), bulkProcessingState=REJECTED + +#### 15. Invalid Fulfil duplicate of an individual transfer in a bulk [fulfil-handler -> bulk-processing-handler] + + 1. type: bulk-processing + 2. action: fulfil-duplicate + 3. Status: error + 4. Result: bulkTransferState=PENDING_FULFIL/COMPLETED (depending on whether it’s the last one), bulkProcessingState=FULFIL_DUPLICATE + +#### 16. Valid Fulfil duplicate of an individual transfer in a bulk [fulfil-handler -> bulk-processing-handler] + + 1. type: bulk-processing + 2. action: fulfil-duplicate + 3. Status: success + 4. Result: bulkTransferState=PENDING_FULFIL/COMPLETED (depending on whether it’s the last one), bulkProcessingState=FULFIL_DUPLICATE + +#### 17. Valid Fulfil message of an individual transfer in a bulk [fulfil-handler -> position-handler] + + 1. type: position + 2. action: bulk-commit + 3. Status: success + +#### 18. For individual Fulfil transfer that’s part of a bulk that failed validation in fulfil handler [fulfil-handler -> bulk-processing-handler] + + 1. type: bulk-processing + 2. action: bulk-commit + 3. Status: error + 4. Result: bulkTransferState=PENDING_FULFIL/COMPLETED (depending on whether it’s the last one), bulkProcessingState=FULFIL_INVALID + +#### 19. Fulfil transfer request that’s part of a bulk that passes validation [bulk-fulfil-handler -> fulfil-handler] + + 1. type: bulk-fulfil + 2. action: bulk-commit + 3. Status: success + +#### 20. For Bulk transfers failing validation at bulk-fulfil-handler level [bulk-fulfil-handler -> notification-handler] + + 1. type: notification + 2. action: bulk-abort + 3. Status: error + +#### 21. For Bulk transfer notifications to FSPs [bulk-processing-handler -> notification-handler] + + 1. type: notification + 2. action: bulk-prepare / bulk-commit + 3. Status: success + +#### 22. For timeout notification [timeout-handler -> bulk-processing-handler] + + 1. type: bulk-processing + 2. action: bulk-timeout-received + 3. Status: error + 4. Result: bulkTransferState=COMPLETED (for the last one), bulkProcessingState=EXPIRED + +#### 23. For timeout notification [timeout-handler -> position-handler] + + 1. type: position + 2. action: bulk-timeout-reserved + 3. Status: error + +#### 24. For timeout notification after position adjust [position-handler -> bulk-processing-handler] + + 1. type: bulk-processing + 2. action: bulk-timeout-reserved + 3. Status: error + 4. Result: bulkTransferState=COMPLETED (for the last one), bulkProcessingState=EXPIRED + +### 4.4 Additional Notes + +1. Document GET /bulkTransfers to indicate the difference in responses the Payer-FSP & Payee-FSP receive for Bulk Transfers +2. Used a separate service: bulk-api-adapter to support bulk transfers end-points (that includes persistence as discussed above) + +## 5. Roadmap Topics + +1. Re-assess the need to support multiple Payee FSPs as part of a Bulk and the changes to the Specification needed. +2. Issues, learnings from the PoC that are documented are addressed in order of priority +3. Find out a need to support something like a Bulk make resource (/bulkMake ?) in which the Switch accepts an entire Bulk as it is and then takes care of all three phases - lookup, quote and transfers. +4. Throttling of individual transfers in a bulk? +5. The aspect of ordering in a Bulk - the order of processing at the Switch and at the FSPs. Recommendation to Scheme to incorporate Rule to mandate all FSPs to process transactions a bulk in the existing order and not have preferential processing. On the Switch currently being neutral with ordering but best practice is to sort in ascending order of amounts and process. +6. For Settlements with bulk transfers where Government payments are involved with large sums of money needs to be discussed to allow for moving through transfers without strict liquidity rules needs to be discussed. +7. Implement GET /bulkTransfers +8. Implement notifications/logging for all bulk negative scenarios +9. Full unit tests code coverage +10. Integration testing of successful bulk transfer (golden path) +11. Regression testing, including negative scenarios diff --git a/docs/technical/central-bulk-transfers/assets/database/central-ledger-ddl-MySQLWorkbench.sql b/docs/technical/central-bulk-transfers/assets/database/central-ledger-ddl-MySQLWorkbench.sql new file mode 100644 index 000000000..f6995bc85 --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/database/central-ledger-ddl-MySQLWorkbench.sql @@ -0,0 +1,1600 @@ +-- MySQL dump 10.13 Distrib 8.0.16, for macos10.14 (x86_64) +-- +-- Host: 127.0.0.1 Database: central_ledger +-- ------------------------------------------------------ +-- Server version 8.0.13 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; + SET NAMES utf8 ; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `amountType` +-- + +DROP TABLE IF EXISTS `amountType`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `amountType` ( + `amountTypeId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(256) NOT NULL, + `description` varchar(1024) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System dateTime stamp pertaining to the inserted record', + PRIMARY KEY (`amountTypeId`), + UNIQUE KEY `amounttype_name_unique` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `balanceOfPayments` +-- + +DROP TABLE IF EXISTS `balanceOfPayments`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `balanceOfPayments` ( + `balanceOfPaymentsId` int(10) unsigned NOT NULL, + `name` varchar(256) NOT NULL, + `description` varchar(1024) DEFAULT NULL COMMENT 'Possible values and meaning are defined in https://www.imf.org/external/np/sta/bopcode/', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System dateTime stamp pertaining to the inserted record', + PRIMARY KEY (`balanceOfPaymentsId`), + UNIQUE KEY `balanceofpayments_name_unique` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='See https://www.imf.org/external/np/sta/bopcode/guide.htm'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bulkProcessingState` +-- + +DROP TABLE IF EXISTS `bulkProcessingState`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `bulkProcessingState` ( + `bulkProcessingStateId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `description` varchar(512) DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`bulkProcessingStateId`), + UNIQUE KEY `bulkprocessingstate_name_unique` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bulkTransfer` +-- + +DROP TABLE IF EXISTS `bulkTransfer`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `bulkTransfer` ( + `bulkTransferId` varchar(36) NOT NULL, + `bulkQuoteId` varchar(36) DEFAULT NULL, + `payerParticipantId` int(10) unsigned DEFAULT NULL, + `payeeParticipantId` int(10) unsigned DEFAULT NULL, + `expirationDate` datetime NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`bulkTransferId`), + KEY `bulktransfer_payerparticipantid_index` (`payerParticipantId`), + KEY `bulktransfer_payeeparticipantid_index` (`payeeParticipantId`), + CONSTRAINT `bulktransfer_bulktransferid_foreign` FOREIGN KEY (`bulkTransferId`) REFERENCES `bulkTransferDuplicateCheck` (`bulktransferid`), + CONSTRAINT `bulktransfer_payeeparticipantid_foreign` FOREIGN KEY (`payeeParticipantId`) REFERENCES `participant` (`participantid`), + CONSTRAINT `bulktransfer_payerparticipantid_foreign` FOREIGN KEY (`payerParticipantId`) REFERENCES `participant` (`participantid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bulkTransferAssociation` +-- + +DROP TABLE IF EXISTS `bulkTransferAssociation`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `bulkTransferAssociation` ( + `bulkTransferAssociationId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `transferId` varchar(36) NOT NULL, + `bulkTransferId` varchar(36) NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `bulkProcessingStateId` int(10) unsigned NOT NULL, + `lastProcessedDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `errorCode` int(10) unsigned DEFAULT NULL, + `errorDescription` varchar(128) DEFAULT NULL, + PRIMARY KEY (`bulkTransferAssociationId`), + UNIQUE KEY `bulktransferassociation_transferid_bulktransferid_unique` (`transferId`,`bulkTransferId`), + KEY `bulktransferassociation_bulktransferid_foreign` (`bulkTransferId`), + KEY `bulktransferassociation_bulkprocessingstateid_foreign` (`bulkProcessingStateId`), + CONSTRAINT `bulktransferassociation_bulkprocessingstateid_foreign` FOREIGN KEY (`bulkProcessingStateId`) REFERENCES `bulkProcessingState` (`bulkprocessingstateid`), + CONSTRAINT `bulktransferassociation_bulktransferid_foreign` FOREIGN KEY (`bulkTransferId`) REFERENCES `bulkTransfer` (`bulktransferid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bulkTransferDuplicateCheck` +-- + +DROP TABLE IF EXISTS `bulkTransferDuplicateCheck`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `bulkTransferDuplicateCheck` ( + `bulkTransferId` varchar(36) NOT NULL, + `hash` varchar(256) NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`bulkTransferId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bulkTransferError` +-- + +DROP TABLE IF EXISTS `bulkTransferError`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `bulkTransferError` ( + `bulkTransferErrorId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `bulkTransferStateChangeId` bigint(20) unsigned NOT NULL, + `errorCode` int(10) unsigned NOT NULL, + `errorDescription` varchar(128) NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`bulkTransferErrorId`), + KEY `bulktransfererror_bulktransferstatechangeid_index` (`bulkTransferStateChangeId`), + CONSTRAINT `bulktransfererror_bulktransferstatechangeid_foreign` FOREIGN KEY (`bulkTransferStateChangeId`) REFERENCES `bulkTransferStateChange` (`bulktransferstatechangeid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bulkTransferExtension` +-- + +DROP TABLE IF EXISTS `bulkTransferExtension`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `bulkTransferExtension` ( + `bulkTransferExtensionId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `bulkTransferId` varchar(36) NOT NULL, + `isFulfilment` tinyint(1) NOT NULL DEFAULT '0', + `key` varchar(128) NOT NULL, + `value` text NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`bulkTransferExtensionId`), + KEY `bulktransferextension_bulktransferid_index` (`bulkTransferId`), + CONSTRAINT `bulktransferextension_bulktransferid_foreign` FOREIGN KEY (`bulkTransferId`) REFERENCES `bulkTransfer` (`bulktransferid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bulkTransferFulfilment` +-- + +DROP TABLE IF EXISTS `bulkTransferFulfilment`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `bulkTransferFulfilment` ( + `bulkTransferId` varchar(36) NOT NULL, + `completedDate` datetime NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`bulkTransferId`), + CONSTRAINT `bulktransferfulfilment_bulktransferid_foreign` FOREIGN KEY (`bulkTransferId`) REFERENCES `bulkTransferFulfilmentDuplicateCheck` (`bulktransferid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bulkTransferFulfilmentDuplicateCheck` +-- + +DROP TABLE IF EXISTS `bulkTransferFulfilmentDuplicateCheck`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `bulkTransferFulfilmentDuplicateCheck` ( + `bulkTransferId` varchar(36) NOT NULL, + `hash` varchar(256) NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`bulkTransferId`), + CONSTRAINT `bulktransferfulfilmentduplicatecheck_bulktransferid_foreign` FOREIGN KEY (`bulkTransferId`) REFERENCES `bulkTransfer` (`bulktransferid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bulkTransferState` +-- + +DROP TABLE IF EXISTS `bulkTransferState`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `bulkTransferState` ( + `bulkTransferStateId` varchar(50) NOT NULL, + `enumeration` varchar(50) NOT NULL COMMENT 'bulkTransferState associated to the Mojaloop API', + `description` varchar(512) DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`bulkTransferStateId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bulkTransferStateChange` +-- + +DROP TABLE IF EXISTS `bulkTransferStateChange`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `bulkTransferStateChange` ( + `bulkTransferStateChangeId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `bulkTransferId` varchar(36) NOT NULL, + `bulkTransferStateId` varchar(50) NOT NULL, + `reason` varchar(512) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`bulkTransferStateChangeId`), + KEY `bulktransferstatechange_bulktransferid_index` (`bulkTransferId`), + KEY `bulktransferstatechange_bulktransferstateid_index` (`bulkTransferStateId`), + CONSTRAINT `bulktransferstatechange_bulktransferid_foreign` FOREIGN KEY (`bulkTransferId`) REFERENCES `bulkTransfer` (`bulktransferid`), + CONSTRAINT `bulktransferstatechange_bulktransferstateid_foreign` FOREIGN KEY (`bulkTransferStateId`) REFERENCES `bulkTransferState` (`bulktransferstateid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `contactType` +-- + +DROP TABLE IF EXISTS `contactType`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `contactType` ( + `contactTypeId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `description` varchar(512) DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`contactTypeId`), + UNIQUE KEY `contacttype_name_unique` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `currency` +-- + +DROP TABLE IF EXISTS `currency`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `currency` ( + `currencyId` varchar(3) NOT NULL, + `name` varchar(128) DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `scale` int(10) unsigned NOT NULL DEFAULT '4', + PRIMARY KEY (`currencyId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `endpointType` +-- + +DROP TABLE IF EXISTS `endpointType`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `endpointType` ( + `endpointTypeId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `description` varchar(512) DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`endpointTypeId`), + UNIQUE KEY `endpointtype_name_unique` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `event` +-- + +DROP TABLE IF EXISTS `event`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `event` ( + `eventId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(128) NOT NULL, + `description` varchar(512) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`eventId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `geoCode` +-- + +DROP TABLE IF EXISTS `geoCode`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `geoCode` ( + `geoCodeId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `quotePartyId` bigint(20) unsigned NOT NULL COMMENT 'Optionally the GeoCode for the Payer/Payee may have been provided. If the Quote Response has the GeoCode for the Payee, an additional row is added', + `latitude` varchar(50) NOT NULL COMMENT 'Latitude of the initiating Party', + `longitude` varchar(50) NOT NULL COMMENT 'Longitude of the initiating Party', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System dateTime stamp pertaining to the inserted record', + PRIMARY KEY (`geoCodeId`), + KEY `geocode_quotepartyid_foreign` (`quotePartyId`), + CONSTRAINT `geocode_quotepartyid_foreign` FOREIGN KEY (`quotePartyId`) REFERENCES `quoteParty` (`quotepartyid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ilpPacket` +-- + +DROP TABLE IF EXISTS `ilpPacket`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `ilpPacket` ( + `transferId` varchar(36) NOT NULL, + `value` text NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferId`), + CONSTRAINT `ilppacket_transferid_foreign` FOREIGN KEY (`transferId`) REFERENCES `transfer` (`transferid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ledgerAccountType` +-- + +DROP TABLE IF EXISTS `ledgerAccountType`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `ledgerAccountType` ( + `ledgerAccountTypeId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `description` varchar(512) DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`ledgerAccountTypeId`), + UNIQUE KEY `ledgeraccounttype_name_unique` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ledgerEntryType` +-- + +DROP TABLE IF EXISTS `ledgerEntryType`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `ledgerEntryType` ( + `ledgerEntryTypeId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `description` varchar(512) DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`ledgerEntryTypeId`), + UNIQUE KEY `ledgerentrytype_name_unique` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `migration` +-- + +DROP TABLE IF EXISTS `migration`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `migration` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `batch` int(11) DEFAULT NULL, + `migration_time` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=138 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `migration_lock` +-- + +DROP TABLE IF EXISTS `migration_lock`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `migration_lock` ( + `index` int(10) unsigned NOT NULL AUTO_INCREMENT, + `is_locked` int(11) DEFAULT NULL, + PRIMARY KEY (`index`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `participant` +-- + +DROP TABLE IF EXISTS `participant`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `participant` ( + `participantId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(256) NOT NULL, + `description` varchar(512) DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` varchar(128) NOT NULL, + PRIMARY KEY (`participantId`), + UNIQUE KEY `participant_name_unique` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `participantContact` +-- + +DROP TABLE IF EXISTS `participantContact`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `participantContact` ( + `participantContactId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `participantId` int(10) unsigned NOT NULL, + `contactTypeId` int(10) unsigned NOT NULL, + `value` varchar(256) NOT NULL, + `priorityPreference` int(11) NOT NULL DEFAULT '9', + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` varchar(128) NOT NULL, + PRIMARY KEY (`participantContactId`), + KEY `participantcontact_participantid_index` (`participantId`), + KEY `participantcontact_contacttypeid_index` (`contactTypeId`), + CONSTRAINT `participantcontact_contacttypeid_foreign` FOREIGN KEY (`contactTypeId`) REFERENCES `contactType` (`contacttypeid`), + CONSTRAINT `participantcontact_participantid_foreign` FOREIGN KEY (`participantId`) REFERENCES `participant` (`participantid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `participantCurrency` +-- + +DROP TABLE IF EXISTS `participantCurrency`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `participantCurrency` ( + `participantCurrencyId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `participantId` int(10) unsigned NOT NULL, + `currencyId` varchar(3) NOT NULL, + `ledgerAccountTypeId` int(10) unsigned NOT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` varchar(128) NOT NULL, + PRIMARY KEY (`participantCurrencyId`), + UNIQUE KEY `participantcurrency_pcl_unique` (`participantId`,`currencyId`,`ledgerAccountTypeId`), + KEY `participantcurrency_ledgeraccounttypeid_foreign` (`ledgerAccountTypeId`), + KEY `participantcurrency_participantid_index` (`participantId`), + KEY `participantcurrency_currencyid_index` (`currencyId`), + CONSTRAINT `participantcurrency_currencyid_foreign` FOREIGN KEY (`currencyId`) REFERENCES `currency` (`currencyid`), + CONSTRAINT `participantcurrency_ledgeraccounttypeid_foreign` FOREIGN KEY (`ledgerAccountTypeId`) REFERENCES `ledgerAccountType` (`ledgeraccounttypeid`), + CONSTRAINT `participantcurrency_participantid_foreign` FOREIGN KEY (`participantId`) REFERENCES `participant` (`participantid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `participantEndpoint` +-- + +DROP TABLE IF EXISTS `participantEndpoint`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `participantEndpoint` ( + `participantEndpointId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `participantId` int(10) unsigned NOT NULL, + `endpointTypeId` int(10) unsigned NOT NULL, + `value` varchar(512) NOT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` varchar(128) NOT NULL, + PRIMARY KEY (`participantEndpointId`), + KEY `participantendpoint_participantid_index` (`participantId`), + KEY `participantendpoint_endpointtypeid_index` (`endpointTypeId`), + CONSTRAINT `participantendpoint_endpointtypeid_foreign` FOREIGN KEY (`endpointTypeId`) REFERENCES `endpointType` (`endpointtypeid`), + CONSTRAINT `participantendpoint_participantid_foreign` FOREIGN KEY (`participantId`) REFERENCES `participant` (`participantid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `participantLimit` +-- + +DROP TABLE IF EXISTS `participantLimit`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `participantLimit` ( + `participantLimitId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `participantCurrencyId` int(10) unsigned NOT NULL, + `participantLimitTypeId` int(10) unsigned NOT NULL, + `value` decimal(18,4) NOT NULL DEFAULT '0.0000', + `thresholdAlarmPercentage` decimal(5,2) NOT NULL DEFAULT '10.00', + `startAfterParticipantPositionChangeId` bigint(20) unsigned DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` varchar(128) NOT NULL, + PRIMARY KEY (`participantLimitId`), + KEY `participantlimit_participantcurrencyid_index` (`participantCurrencyId`), + KEY `participantlimit_participantlimittypeid_index` (`participantLimitTypeId`), + KEY `participantlimit_startafterparticipantpositionchangeid_index` (`startAfterParticipantPositionChangeId`), + CONSTRAINT `participantlimit_participantcurrencyid_foreign` FOREIGN KEY (`participantCurrencyId`) REFERENCES `participantCurrency` (`participantcurrencyid`), + CONSTRAINT `participantlimit_participantlimittypeid_foreign` FOREIGN KEY (`participantLimitTypeId`) REFERENCES `participantLimitType` (`participantlimittypeid`), + CONSTRAINT `participantlimit_startafterparticipantpositionchangeid_foreign` FOREIGN KEY (`startAfterParticipantPositionChangeId`) REFERENCES `participantPositionChange` (`participantpositionchangeid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `participantLimitType` +-- + +DROP TABLE IF EXISTS `participantLimitType`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `participantLimitType` ( + `participantLimitTypeId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `description` varchar(512) DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`participantLimitTypeId`), + UNIQUE KEY `participantlimittype_name_unique` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `participantParty` +-- + +DROP TABLE IF EXISTS `participantParty`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `participantParty` ( + `participantPartyId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `participantId` int(10) unsigned NOT NULL, + `partyId` bigint(20) unsigned NOT NULL, + PRIMARY KEY (`participantPartyId`), + UNIQUE KEY `participantparty_participantid_partyid_unique` (`participantId`,`partyId`), + KEY `participantparty_participantid_index` (`participantId`), + CONSTRAINT `participantparty_participantid_foreign` FOREIGN KEY (`participantId`) REFERENCES `participant` (`participantid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `participantPosition` +-- + +DROP TABLE IF EXISTS `participantPosition`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `participantPosition` ( + `participantPositionId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `participantCurrencyId` int(10) unsigned NOT NULL, + `value` decimal(18,4) NOT NULL, + `reservedValue` decimal(18,4) NOT NULL, + `changedDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`participantPositionId`), + KEY `participantposition_participantcurrencyid_index` (`participantCurrencyId`), + CONSTRAINT `participantposition_participantcurrencyid_foreign` FOREIGN KEY (`participantCurrencyId`) REFERENCES `participantCurrency` (`participantcurrencyid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `participantPositionChange` +-- + +DROP TABLE IF EXISTS `participantPositionChange`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `participantPositionChange` ( + `participantPositionChangeId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `participantPositionId` bigint(20) unsigned NOT NULL, + `transferStateChangeId` bigint(20) unsigned NOT NULL, + `value` decimal(18,4) NOT NULL, + `reservedValue` decimal(18,4) NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`participantPositionChangeId`), + KEY `participantpositionchange_participantpositionid_index` (`participantPositionId`), + KEY `participantpositionchange_transferstatechangeid_index` (`transferStateChangeId`), + CONSTRAINT `participantpositionchange_participantpositionid_foreign` FOREIGN KEY (`participantPositionId`) REFERENCES `participantPosition` (`participantpositionid`), + CONSTRAINT `participantpositionchange_transferstatechangeid_foreign` FOREIGN KEY (`transferStateChangeId`) REFERENCES `transferStateChange` (`transferstatechangeid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `party` +-- + +DROP TABLE IF EXISTS `party`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `party` ( + `partyId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `quotePartyId` bigint(20) unsigned NOT NULL, + `firstName` varchar(128) DEFAULT NULL, + `middleName` varchar(128) DEFAULT NULL, + `lastName` varchar(128) DEFAULT NULL, + `dateOfBirth` datetime DEFAULT NULL, + PRIMARY KEY (`partyId`), + KEY `party_quotepartyid_foreign` (`quotePartyId`), + CONSTRAINT `party_quotepartyid_foreign` FOREIGN KEY (`quotePartyId`) REFERENCES `quoteParty` (`quotepartyid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Optional pers. data provided during Quote Request & Response'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `partyIdentifierType` +-- + +DROP TABLE IF EXISTS `partyIdentifierType`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `partyIdentifierType` ( + `partyIdentifierTypeId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `description` varchar(512) NOT NULL, + PRIMARY KEY (`partyIdentifierTypeId`), + UNIQUE KEY `partyidentifiertype_name_unique` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `partyType` +-- + +DROP TABLE IF EXISTS `partyType`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `partyType` ( + `partyTypeId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(128) NOT NULL, + `description` varchar(256) NOT NULL, + PRIMARY KEY (`partyTypeId`), + UNIQUE KEY `partytype_name_unique` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `quote` +-- + +DROP TABLE IF EXISTS `quote`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `quote` ( + `quoteId` varchar(36) NOT NULL, + `transactionReferenceId` varchar(36) NOT NULL COMMENT 'Common ID (decided by the Payer FSP) between the FSPs for the future transaction object', + `transactionRequestId` varchar(36) DEFAULT NULL COMMENT 'Optional previously-sent transaction request', + `note` text COMMENT 'A memo that will be attached to the transaction', + `expirationDate` datetime DEFAULT NULL COMMENT 'Optional expiration for the requested transaction', + `transactionInitiatorId` int(10) unsigned NOT NULL COMMENT 'This is part of the transaction initiator', + `transactionInitiatorTypeId` int(10) unsigned NOT NULL COMMENT 'This is part of the transaction initiator type', + `transactionScenarioId` int(10) unsigned NOT NULL COMMENT 'This is part of the transaction scenario', + `balanceOfPaymentsId` int(10) unsigned DEFAULT NULL COMMENT 'This is part of the transaction type that contains the elements- balance of payment', + `transactionSubScenarioId` int(10) unsigned DEFAULT NULL COMMENT 'This is part of the transaction type sub scenario as defined by the local scheme', + `amountTypeId` int(10) unsigned NOT NULL COMMENT 'This is part of the transaction type that contains valid elements for - Amount Type', + `amount` decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT 'The amount that the quote is being requested for. Need to be interpert in accordance with the amount type', + `currencyId` varchar(255) DEFAULT NULL COMMENT 'Trading currency pertaining to the Amount', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System dateTime stamp pertaining to the inserted record', + PRIMARY KEY (`quoteId`), + KEY `quote_transactionreferenceid_foreign` (`transactionReferenceId`), + KEY `quote_transactionrequestid_foreign` (`transactionRequestId`), + KEY `quote_transactioninitiatorid_foreign` (`transactionInitiatorId`), + KEY `quote_transactioninitiatortypeid_foreign` (`transactionInitiatorTypeId`), + KEY `quote_transactionscenarioid_foreign` (`transactionScenarioId`), + KEY `quote_balanceofpaymentsid_foreign` (`balanceOfPaymentsId`), + KEY `quote_transactionsubscenarioid_foreign` (`transactionSubScenarioId`), + KEY `quote_amounttypeid_foreign` (`amountTypeId`), + KEY `quote_currencyid_foreign` (`currencyId`), + CONSTRAINT `quote_amounttypeid_foreign` FOREIGN KEY (`amountTypeId`) REFERENCES `amountType` (`amounttypeid`), + CONSTRAINT `quote_balanceofpaymentsid_foreign` FOREIGN KEY (`balanceOfPaymentsId`) REFERENCES `balanceOfPayments` (`balanceofpaymentsid`), + CONSTRAINT `quote_currencyid_foreign` FOREIGN KEY (`currencyId`) REFERENCES `currency` (`currencyid`), + CONSTRAINT `quote_transactioninitiatorid_foreign` FOREIGN KEY (`transactionInitiatorId`) REFERENCES `transactionInitiator` (`transactioninitiatorid`), + CONSTRAINT `quote_transactioninitiatortypeid_foreign` FOREIGN KEY (`transactionInitiatorTypeId`) REFERENCES `transactionInitiatorType` (`transactioninitiatortypeid`), + CONSTRAINT `quote_transactionreferenceid_foreign` FOREIGN KEY (`transactionReferenceId`) REFERENCES `transactionReference` (`transactionreferenceid`), + CONSTRAINT `quote_transactionrequestid_foreign` FOREIGN KEY (`transactionRequestId`) REFERENCES `transactionReference` (`transactionreferenceid`), + CONSTRAINT `quote_transactionscenarioid_foreign` FOREIGN KEY (`transactionScenarioId`) REFERENCES `transactionScenario` (`transactionscenarioid`), + CONSTRAINT `quote_transactionsubscenarioid_foreign` FOREIGN KEY (`transactionSubScenarioId`) REFERENCES `transactionSubScenario` (`transactionsubscenarioid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `quoteDuplicateCheck` +-- + +DROP TABLE IF EXISTS `quoteDuplicateCheck`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `quoteDuplicateCheck` ( + `quoteId` varchar(36) NOT NULL COMMENT 'Common ID between the FSPs for the quote object, decided by the Payer FSP', + `hash` varchar(1024) DEFAULT NULL COMMENT 'hash value received for the quote request', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System dateTime stamp pertaining to the inserted record', + PRIMARY KEY (`quoteId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `quoteError` +-- + +DROP TABLE IF EXISTS `quoteError`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `quoteError` ( + `quoteErrorId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `quoteId` varchar(36) NOT NULL COMMENT 'Common ID between the FSPs for the quote object, decided by the Payer FSP', + `quoteResponseId` bigint(20) unsigned DEFAULT NULL COMMENT 'The response to the intial quote', + `errorCode` int(10) unsigned NOT NULL, + `errorDescription` varchar(128) NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`quoteErrorId`), + KEY `quoteerror_quoteid_foreign` (`quoteId`), + KEY `quoteerror_quoteresponseid_foreign` (`quoteResponseId`), + CONSTRAINT `quoteerror_quoteid_foreign` FOREIGN KEY (`quoteId`) REFERENCES `quote` (`quoteid`), + CONSTRAINT `quoteerror_quoteresponseid_foreign` FOREIGN KEY (`quoteResponseId`) REFERENCES `quoteResponse` (`quoteresponseid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `quoteExtension` +-- + +DROP TABLE IF EXISTS `quoteExtension`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `quoteExtension` ( + `quoteExtensionId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `quoteId` varchar(36) NOT NULL COMMENT 'Common ID between the FSPs for the quote object, decided by the Payer FSP', + `quoteResponseId` bigint(20) unsigned NOT NULL COMMENT 'The response to the intial quote', + `transactionId` varchar(36) NOT NULL COMMENT 'The transaction reference that is part of the initial quote', + `key` varchar(128) NOT NULL, + `value` text NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System dateTime stamp pertaining to the inserted record', + PRIMARY KEY (`quoteExtensionId`), + KEY `quoteextension_quoteid_foreign` (`quoteId`), + KEY `quoteextension_quoteresponseid_foreign` (`quoteResponseId`), + KEY `quoteextension_transactionid_foreign` (`transactionId`), + CONSTRAINT `quoteextension_quoteid_foreign` FOREIGN KEY (`quoteId`) REFERENCES `quote` (`quoteid`), + CONSTRAINT `quoteextension_quoteresponseid_foreign` FOREIGN KEY (`quoteResponseId`) REFERENCES `quoteResponse` (`quoteresponseid`), + CONSTRAINT `quoteextension_transactionid_foreign` FOREIGN KEY (`transactionId`) REFERENCES `transactionReference` (`transactionreferenceid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `quoteParty` +-- + +DROP TABLE IF EXISTS `quoteParty`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `quoteParty` ( + `quotePartyId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `quoteId` varchar(36) NOT NULL COMMENT 'Common ID between the FSPs for the quote object, decided by the Payer FSP', + `partyTypeId` int(10) unsigned NOT NULL COMMENT 'Specifies the type of party this row relates to; typically PAYER or PAYEE', + `partyIdentifierTypeId` int(10) unsigned NOT NULL COMMENT 'Specifies the type of identifier used to identify this party e.g. MSISDN, IBAN etc...', + `partyIdentifierValue` varchar(128) NOT NULL COMMENT 'The value of the identifier used to identify this party', + `partySubIdOrTypeId` int(10) unsigned DEFAULT NULL COMMENT 'A sub-identifier or sub-type for the Party', + `fspId` varchar(255) DEFAULT NULL COMMENT 'This is the FSP ID as provided in the quote. For the switch between multi-parties it is required', + `participantId` int(10) unsigned DEFAULT NULL COMMENT 'Reference to the resolved FSP ID (if supplied/known). If not an error will be reported', + `merchantClassificationCode` varchar(4) DEFAULT NULL COMMENT 'Used in the context of Payee Information, where the Payee happens to be a merchant accepting merchant payments', + `partyName` varchar(128) DEFAULT NULL COMMENT 'Display name of the Party, could be a real name or a nick name', + `transferParticipantRoleTypeId` int(10) unsigned NOT NULL COMMENT 'The role this Party is playing in the transaction', + `ledgerEntryTypeId` int(10) unsigned NOT NULL COMMENT 'The type of financial entry this Party is presenting', + `amount` decimal(18,4) NOT NULL, + `currencyId` varchar(3) NOT NULL COMMENT 'Trading currency pertaining to the party amount', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System dateTime stamp pertaining to the inserted record', + PRIMARY KEY (`quotePartyId`), + KEY `quoteparty_quoteid_foreign` (`quoteId`), + KEY `quoteparty_partytypeid_foreign` (`partyTypeId`), + KEY `quoteparty_partyidentifiertypeid_foreign` (`partyIdentifierTypeId`), + KEY `quoteparty_partysubidortypeid_foreign` (`partySubIdOrTypeId`), + KEY `quoteparty_participantid_foreign` (`participantId`), + KEY `quoteparty_transferparticipantroletypeid_foreign` (`transferParticipantRoleTypeId`), + KEY `quoteparty_ledgerentrytypeid_foreign` (`ledgerEntryTypeId`), + KEY `quoteparty_currencyid_foreign` (`currencyId`), + CONSTRAINT `quoteparty_currencyid_foreign` FOREIGN KEY (`currencyId`) REFERENCES `currency` (`currencyid`), + CONSTRAINT `quoteparty_ledgerentrytypeid_foreign` FOREIGN KEY (`ledgerEntryTypeId`) REFERENCES `ledgerEntryType` (`ledgerentrytypeid`), + CONSTRAINT `quoteparty_participantid_foreign` FOREIGN KEY (`participantId`) REFERENCES `participant` (`participantid`), + CONSTRAINT `quoteparty_partyidentifiertypeid_foreign` FOREIGN KEY (`partyIdentifierTypeId`) REFERENCES `partyIdentifierType` (`partyidentifiertypeid`), + CONSTRAINT `quoteparty_partysubidortypeid_foreign` FOREIGN KEY (`partySubIdOrTypeId`) REFERENCES `partyIdentifierType` (`partyidentifiertypeid`), + CONSTRAINT `quoteparty_partytypeid_foreign` FOREIGN KEY (`partyTypeId`) REFERENCES `partyType` (`partytypeid`), + CONSTRAINT `quoteparty_quoteid_foreign` FOREIGN KEY (`quoteId`) REFERENCES `quote` (`quoteid`), + CONSTRAINT `quoteparty_transferparticipantroletypeid_foreign` FOREIGN KEY (`transferParticipantRoleTypeId`) REFERENCES `transferParticipantRoleType` (`transferparticipantroletypeid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Temporary view structure for view `quotePartyView` +-- + +DROP TABLE IF EXISTS `quotePartyView`; +/*!50001 DROP VIEW IF EXISTS `quotePartyView`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8mb4; +/*!50001 CREATE VIEW `quotePartyView` AS SELECT + 1 AS `quoteId`, + 1 AS `quotePartyId`, + 1 AS `partyType`, + 1 AS `identifierType`, + 1 AS `partyIdentifierValue`, + 1 AS `partySubIdOrType`, + 1 AS `fspId`, + 1 AS `merchantClassificationCode`, + 1 AS `partyName`, + 1 AS `firstName`, + 1 AS `lastName`, + 1 AS `middleName`, + 1 AS `dateOfBirth`, + 1 AS `longitude`, + 1 AS `latitude`*/; +SET character_set_client = @saved_cs_client; + +-- +-- Table structure for table `quoteResponse` +-- + +DROP TABLE IF EXISTS `quoteResponse`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `quoteResponse` ( + `quoteResponseId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `quoteId` varchar(36) NOT NULL COMMENT 'Common ID between the FSPs for the quote object, decided by the Payer FSP', + `transferAmountCurrencyId` varchar(3) NOT NULL COMMENT 'CurrencyId of the transfer amount', + `transferAmount` decimal(18,4) NOT NULL COMMENT 'The amount of money that the Payer FSP should transfer to the Payee FSP', + `payeeReceiveAmountCurrencyId` varchar(3) DEFAULT NULL COMMENT 'CurrencyId of the payee receive amount', + `payeeReceiveAmount` decimal(18,4) DEFAULT NULL COMMENT 'The amount of Money that the Payee should receive in the end-to-end transaction. Optional as the Payee FSP might not want to disclose any optional Payee fees', + `payeeFspFeeCurrencyId` varchar(3) DEFAULT NULL COMMENT 'CurrencyId of the payee fsp fee amount', + `payeeFspFeeAmount` decimal(18,4) DEFAULT NULL COMMENT 'Payee FSP’s part of the transaction fee', + `payeeFspCommissionCurrencyId` varchar(3) DEFAULT NULL COMMENT 'CurrencyId of the payee fsp commission amount', + `payeeFspCommissionAmount` decimal(18,4) DEFAULT NULL COMMENT 'Transaction commission from the Payee FSP', + `ilpCondition` varchar(256) NOT NULL, + `responseExpirationDate` datetime DEFAULT NULL COMMENT 'Optional expiration for the requested transaction', + `isValid` tinyint(1) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System dateTime stamp pertaining to the inserted record', + PRIMARY KEY (`quoteResponseId`), + KEY `quoteresponse_quoteid_foreign` (`quoteId`), + KEY `quoteresponse_transferamountcurrencyid_foreign` (`transferAmountCurrencyId`), + KEY `quoteresponse_payeereceiveamountcurrencyid_foreign` (`payeeReceiveAmountCurrencyId`), + KEY `quoteresponse_payeefspcommissioncurrencyid_foreign` (`payeeFspCommissionCurrencyId`), + CONSTRAINT `quoteresponse_payeefspcommissioncurrencyid_foreign` FOREIGN KEY (`payeeFspCommissionCurrencyId`) REFERENCES `currency` (`currencyid`), + CONSTRAINT `quoteresponse_payeereceiveamountcurrencyid_foreign` FOREIGN KEY (`payeeReceiveAmountCurrencyId`) REFERENCES `currency` (`currencyid`), + CONSTRAINT `quoteresponse_quoteid_foreign` FOREIGN KEY (`quoteId`) REFERENCES `quote` (`quoteid`), + CONSTRAINT `quoteresponse_transferamountcurrencyid_foreign` FOREIGN KEY (`transferAmountCurrencyId`) REFERENCES `currency` (`currencyid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='This table is the primary store for quote responses'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `quoteResponseDuplicateCheck` +-- + +DROP TABLE IF EXISTS `quoteResponseDuplicateCheck`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `quoteResponseDuplicateCheck` ( + `quoteResponseId` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'The response to the intial quote', + `quoteId` varchar(36) NOT NULL COMMENT 'Common ID between the FSPs for the quote object, decided by the Payer FSP', + `hash` varchar(255) DEFAULT NULL COMMENT 'hash value received for the quote response', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System dateTime stamp pertaining to the inserted record', + PRIMARY KEY (`quoteResponseId`), + KEY `quoteresponseduplicatecheck_quoteid_foreign` (`quoteId`), + CONSTRAINT `quoteresponseduplicatecheck_quoteid_foreign` FOREIGN KEY (`quoteId`) REFERENCES `quote` (`quoteid`), + CONSTRAINT `quoteresponseduplicatecheck_quoteresponseid_foreign` FOREIGN KEY (`quoteResponseId`) REFERENCES `quoteResponse` (`quoteresponseid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `quoteResponseIlpPacket` +-- + +DROP TABLE IF EXISTS `quoteResponseIlpPacket`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `quoteResponseIlpPacket` ( + `quoteResponseId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `value` text NOT NULL COMMENT 'ilpPacket returned from Payee in response to a quote request', + PRIMARY KEY (`quoteResponseId`), + CONSTRAINT `quoteresponseilppacket_quoteresponseid_foreign` FOREIGN KEY (`quoteResponseId`) REFERENCES `quoteResponse` (`quoteresponseid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Temporary view structure for view `quoteResponseView` +-- + +DROP TABLE IF EXISTS `quoteResponseView`; +/*!50001 DROP VIEW IF EXISTS `quoteResponseView`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8mb4; +/*!50001 CREATE VIEW `quoteResponseView` AS SELECT + 1 AS `quoteResponseId`, + 1 AS `quoteId`, + 1 AS `transferAmountCurrencyId`, + 1 AS `transferAmount`, + 1 AS `payeeReceiveAmountCurrencyId`, + 1 AS `payeeReceiveAmount`, + 1 AS `payeeFspFeeCurrencyId`, + 1 AS `payeeFspFeeAmount`, + 1 AS `payeeFspCommissionCurrencyId`, + 1 AS `payeeFspCommissionAmount`, + 1 AS `ilpCondition`, + 1 AS `responseExpirationDate`, + 1 AS `isValid`, + 1 AS `createdDate`, + 1 AS `ilpPacket`, + 1 AS `longitude`, + 1 AS `latitude`*/; +SET character_set_client = @saved_cs_client; + +-- +-- Temporary view structure for view `quoteView` +-- + +DROP TABLE IF EXISTS `quoteView`; +/*!50001 DROP VIEW IF EXISTS `quoteView`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8mb4; +/*!50001 CREATE VIEW `quoteView` AS SELECT + 1 AS `quoteId`, + 1 AS `transactionReferenceId`, + 1 AS `transactionRequestId`, + 1 AS `note`, + 1 AS `expirationDate`, + 1 AS `transactionInitiator`, + 1 AS `transactionInitiatorType`, + 1 AS `transactionScenario`, + 1 AS `balanceOfPaymentsId`, + 1 AS `transactionSubScenario`, + 1 AS `amountType`, + 1 AS `amount`, + 1 AS `currency`*/; +SET character_set_client = @saved_cs_client; + +-- +-- Table structure for table `segment` +-- + +DROP TABLE IF EXISTS `segment`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `segment` ( + `segmentId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `segmentType` varchar(50) NOT NULL, + `enumeration` int(11) NOT NULL DEFAULT '0', + `tableName` varchar(50) NOT NULL, + `value` bigint(20) NOT NULL, + `changedDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`segmentId`), + KEY `segment_keys_index` (`segmentType`,`enumeration`,`tableName`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `settlement` +-- + +DROP TABLE IF EXISTS `settlement`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `settlement` ( + `settlementId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `reason` varchar(512) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `currentStateChangeId` bigint(20) unsigned DEFAULT NULL, + PRIMARY KEY (`settlementId`), + KEY `settlement_currentstatechangeid_foreign` (`currentStateChangeId`), + CONSTRAINT `settlement_currentstatechangeid_foreign` FOREIGN KEY (`currentStateChangeId`) REFERENCES `settlementStateChange` (`settlementstatechangeid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `settlementParticipantCurrency` +-- + +DROP TABLE IF EXISTS `settlementParticipantCurrency`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `settlementParticipantCurrency` ( + `settlementParticipantCurrencyId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `settlementId` bigint(20) unsigned NOT NULL, + `participantCurrencyId` int(10) unsigned NOT NULL, + `netAmount` decimal(18,4) NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `currentStateChangeId` bigint(20) unsigned DEFAULT NULL, + `settlementTransferId` varchar(36) DEFAULT NULL, + PRIMARY KEY (`settlementParticipantCurrencyId`), + KEY `settlementparticipantcurrency_settlementid_index` (`settlementId`), + KEY `settlementparticipantcurrency_participantcurrencyid_index` (`participantCurrencyId`), + KEY `settlementparticipantcurrency_settlementtransferid_index` (`settlementTransferId`), + KEY `spc_currentstatechangeid_foreign` (`currentStateChangeId`), + CONSTRAINT `settlementparticipantcurrency_participantcurrencyid_foreign` FOREIGN KEY (`participantCurrencyId`) REFERENCES `participantCurrency` (`participantcurrencyid`), + CONSTRAINT `settlementparticipantcurrency_settlementid_foreign` FOREIGN KEY (`settlementId`) REFERENCES `settlement` (`settlementid`), + CONSTRAINT `spc_currentstatechangeid_foreign` FOREIGN KEY (`currentStateChangeId`) REFERENCES `settlementParticipantCurrencyStateChange` (`settlementparticipantcurrencystatechangeid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `settlementParticipantCurrencyStateChange` +-- + +DROP TABLE IF EXISTS `settlementParticipantCurrencyStateChange`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `settlementParticipantCurrencyStateChange` ( + `settlementParticipantCurrencyStateChangeId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `settlementParticipantCurrencyId` bigint(20) unsigned NOT NULL, + `settlementStateId` varchar(50) NOT NULL, + `reason` varchar(512) DEFAULT NULL, + `externalReference` varchar(50) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`settlementParticipantCurrencyStateChangeId`), + KEY `spcsc_settlementparticipantcurrencyid_index` (`settlementParticipantCurrencyId`), + KEY `spcsc_settlementstateid_index` (`settlementStateId`), + CONSTRAINT `spcsc_settlementparticipantcurrencyid_foreign` FOREIGN KEY (`settlementParticipantCurrencyId`) REFERENCES `settlementParticipantCurrency` (`settlementparticipantcurrencyid`), + CONSTRAINT `spcsc_settlementstateid_foreign` FOREIGN KEY (`settlementStateId`) REFERENCES `settlementState` (`settlementstateid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `settlementSettlementWindow` +-- + +DROP TABLE IF EXISTS `settlementSettlementWindow`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `settlementSettlementWindow` ( + `settlementSettlementWindowId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `settlementId` bigint(20) unsigned NOT NULL, + `settlementWindowId` bigint(20) unsigned NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`settlementSettlementWindowId`), + UNIQUE KEY `settlementsettlementwindow_unique` (`settlementId`,`settlementWindowId`), + KEY `settlementsettlementwindow_settlementid_index` (`settlementId`), + KEY `settlementsettlementwindow_settlementwindowid_index` (`settlementWindowId`), + CONSTRAINT `settlementsettlementwindow_settlementid_foreign` FOREIGN KEY (`settlementId`) REFERENCES `settlement` (`settlementid`), + CONSTRAINT `settlementsettlementwindow_settlementwindowid_foreign` FOREIGN KEY (`settlementWindowId`) REFERENCES `settlementWindow` (`settlementwindowid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `settlementState` +-- + +DROP TABLE IF EXISTS `settlementState`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `settlementState` ( + `settlementStateId` varchar(50) NOT NULL, + `enumeration` varchar(50) NOT NULL, + `description` varchar(512) DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`settlementStateId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `settlementStateChange` +-- + +DROP TABLE IF EXISTS `settlementStateChange`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `settlementStateChange` ( + `settlementStateChangeId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `settlementId` bigint(20) unsigned NOT NULL, + `settlementStateId` varchar(50) NOT NULL, + `reason` varchar(512) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`settlementStateChangeId`), + KEY `settlementstatechange_settlementid_index` (`settlementId`), + KEY `settlementstatechange_settlementstateid_index` (`settlementStateId`), + CONSTRAINT `settlementstatechange_settlementid_foreign` FOREIGN KEY (`settlementId`) REFERENCES `settlement` (`settlementid`), + CONSTRAINT `settlementstatechange_settlementstateid_foreign` FOREIGN KEY (`settlementStateId`) REFERENCES `settlementState` (`settlementstateid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `settlementTransferParticipant` +-- + +DROP TABLE IF EXISTS `settlementTransferParticipant`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `settlementTransferParticipant` ( + `settlementTransferParticipantId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `settlementId` bigint(20) unsigned NOT NULL, + `settlementWindowId` bigint(20) unsigned NOT NULL, + `participantCurrencyId` int(10) unsigned NOT NULL, + `transferParticipantRoleTypeId` int(10) unsigned NOT NULL, + `ledgerEntryTypeId` int(10) unsigned NOT NULL, + `amount` decimal(18,4) NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`settlementTransferParticipantId`), + KEY `settlementtransferparticipant_settlementid_index` (`settlementId`), + KEY `settlementtransferparticipant_settlementwindowid_index` (`settlementWindowId`), + KEY `settlementtransferparticipant_participantcurrencyid_index` (`participantCurrencyId`), + KEY `stp_transferparticipantroletypeid_index` (`transferParticipantRoleTypeId`), + KEY `settlementtransferparticipant_ledgerentrytypeid_index` (`ledgerEntryTypeId`), + CONSTRAINT `settlementtransferparticipant_ledgerentrytypeid_foreign` FOREIGN KEY (`ledgerEntryTypeId`) REFERENCES `ledgerEntryType` (`ledgerentrytypeid`), + CONSTRAINT `settlementtransferparticipant_participantcurrencyid_foreign` FOREIGN KEY (`participantCurrencyId`) REFERENCES `participantCurrency` (`participantcurrencyid`), + CONSTRAINT `settlementtransferparticipant_settlementid_foreign` FOREIGN KEY (`settlementId`) REFERENCES `settlement` (`settlementid`), + CONSTRAINT `settlementtransferparticipant_settlementwindowid_foreign` FOREIGN KEY (`settlementWindowId`) REFERENCES `settlementWindow` (`settlementwindowid`), + CONSTRAINT `stp_transferparticipantroletypeid_foreign` FOREIGN KEY (`transferParticipantRoleTypeId`) REFERENCES `transferParticipantRoleType` (`transferparticipantroletypeid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `settlementWindow` +-- + +DROP TABLE IF EXISTS `settlementWindow`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `settlementWindow` ( + `settlementWindowId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `reason` varchar(512) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `currentStateChangeId` bigint(20) unsigned DEFAULT NULL, + PRIMARY KEY (`settlementWindowId`), + KEY `settlementwindow_currentstatechangeid_foreign` (`currentStateChangeId`), + CONSTRAINT `settlementwindow_currentstatechangeid_foreign` FOREIGN KEY (`currentStateChangeId`) REFERENCES `settlementWindowStateChange` (`settlementwindowstatechangeid`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `settlementWindowState` +-- + +DROP TABLE IF EXISTS `settlementWindowState`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `settlementWindowState` ( + `settlementWindowStateId` varchar(50) NOT NULL, + `enumeration` varchar(50) NOT NULL, + `description` varchar(512) DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`settlementWindowStateId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `settlementWindowStateChange` +-- + +DROP TABLE IF EXISTS `settlementWindowStateChange`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `settlementWindowStateChange` ( + `settlementWindowStateChangeId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `settlementWindowId` bigint(20) unsigned NOT NULL, + `settlementWindowStateId` varchar(50) NOT NULL, + `reason` varchar(512) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`settlementWindowStateChangeId`), + KEY `settlementwindowstatechange_settlementwindowid_index` (`settlementWindowId`), + KEY `settlementwindowstatechange_settlementwindowstateid_index` (`settlementWindowStateId`), + CONSTRAINT `settlementwindowstatechange_settlementwindowid_foreign` FOREIGN KEY (`settlementWindowId`) REFERENCES `settlementWindow` (`settlementwindowid`), + CONSTRAINT `settlementwindowstatechange_settlementwindowstateid_foreign` FOREIGN KEY (`settlementWindowStateId`) REFERENCES `settlementWindowState` (`settlementwindowstateid`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `token` +-- + +DROP TABLE IF EXISTS `token`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `token` ( + `tokenId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `participantId` int(10) unsigned NOT NULL, + `value` varchar(256) NOT NULL, + `expiration` bigint(20) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`tokenId`), + UNIQUE KEY `token_value_unique` (`value`), + KEY `token_participantid_index` (`participantId`), + CONSTRAINT `token_participantid_foreign` FOREIGN KEY (`participantId`) REFERENCES `participant` (`participantid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transactionInitiator` +-- + +DROP TABLE IF EXISTS `transactionInitiator`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transactionInitiator` ( + `transactionInitiatorId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(256) NOT NULL, + `description` varchar(1024) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System dateTime stamp pertaining to the inserted record', + PRIMARY KEY (`transactionInitiatorId`), + UNIQUE KEY `transactioninitiator_name_unique` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transactionInitiatorType` +-- + +DROP TABLE IF EXISTS `transactionInitiatorType`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transactionInitiatorType` ( + `transactionInitiatorTypeId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(256) NOT NULL, + `description` varchar(1024) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System dateTime stamp pertaining to the inserted record', + PRIMARY KEY (`transactionInitiatorTypeId`), + UNIQUE KEY `transactioninitiatortype_name_unique` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transactionReference` +-- + +DROP TABLE IF EXISTS `transactionReference`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transactionReference` ( + `transactionReferenceId` varchar(36) NOT NULL COMMENT 'Common ID (decided by the Payer FSP) between the FSPs for the future transaction object', + `quoteId` varchar(36) DEFAULT NULL COMMENT 'Common ID between the FSPs for the quote object, decided by the Payer FSP', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System row creation timestamp', + PRIMARY KEY (`transactionReferenceId`), + KEY `transactionreference_quoteid_index` (`quoteId`), + CONSTRAINT `transactionreference_quoteid_foreign` FOREIGN KEY (`quoteId`) REFERENCES `quoteDuplicateCheck` (`quoteid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transactionScenario` +-- + +DROP TABLE IF EXISTS `transactionScenario`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transactionScenario` ( + `transactionScenarioId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(256) NOT NULL, + `description` varchar(1024) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System dateTime stamp pertaining to the inserted record', + PRIMARY KEY (`transactionScenarioId`), + UNIQUE KEY `transactionscenario_name_unique` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transactionSubScenario` +-- + +DROP TABLE IF EXISTS `transactionSubScenario`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transactionSubScenario` ( + `transactionSubScenarioId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(256) NOT NULL, + `description` varchar(1024) DEFAULT NULL COMMENT 'Possible sub-scenario, defined locally within the scheme', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'System dateTime stamp pertaining to the inserted record', + PRIMARY KEY (`transactionSubScenarioId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transfer` +-- + +DROP TABLE IF EXISTS `transfer`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transfer` ( + `transferId` varchar(36) NOT NULL, + `amount` decimal(18,4) NOT NULL, + `currencyId` varchar(3) NOT NULL, + `ilpCondition` varchar(256) NOT NULL, + `expirationDate` datetime NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferId`), + KEY `transfer_currencyid_index` (`currencyId`), + CONSTRAINT `transfer_currencyid_foreign` FOREIGN KEY (`currencyId`) REFERENCES `currency` (`currencyid`), + CONSTRAINT `transfer_transferid_foreign` FOREIGN KEY (`transferId`) REFERENCES `transferDuplicateCheck` (`transferid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transferDuplicateCheck` +-- + +DROP TABLE IF EXISTS `transferDuplicateCheck`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transferDuplicateCheck` ( + `transferId` varchar(36) NOT NULL, + `hash` varchar(256) NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transferError` +-- + +DROP TABLE IF EXISTS `transferError`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transferError` ( + `transferId` varchar(36) NOT NULL, + `transferStateChangeId` bigint(20) unsigned NOT NULL, + `errorCode` int(10) unsigned NOT NULL, + `errorDescription` varchar(128) NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferId`), + KEY `transfererror_transferstatechangeid_foreign` (`transferStateChangeId`), + CONSTRAINT `transfererror_transferstatechangeid_foreign` FOREIGN KEY (`transferStateChangeId`) REFERENCES `transferStateChange` (`transferstatechangeid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transferErrorDuplicateCheck` +-- + +DROP TABLE IF EXISTS `transferErrorDuplicateCheck`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transferErrorDuplicateCheck` ( + `transferId` varchar(36) NOT NULL, + `hash` varchar(256) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferId`), + CONSTRAINT `transfererrorduplicatecheck_transferid_foreign` FOREIGN KEY (`transferId`) REFERENCES `transfer` (`transferid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transferExtension` +-- + +DROP TABLE IF EXISTS `transferExtension`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transferExtension` ( + `transferExtensionId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `transferId` varchar(36) NOT NULL, + `key` varchar(128) NOT NULL, + `value` text NOT NULL, + `isFulfilment` tinyint(1) NOT NULL DEFAULT '0', + `isError` tinyint(1) NOT NULL DEFAULT '0', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferExtensionId`), + KEY `transferextension_transferid_foreign` (`transferId`), + CONSTRAINT `transferextension_transferid_foreign` FOREIGN KEY (`transferId`) REFERENCES `transfer` (`transferid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transferFulfilment` +-- + +DROP TABLE IF EXISTS `transferFulfilment`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transferFulfilment` ( + `transferId` varchar(36) NOT NULL, + `ilpFulfilment` varchar(256) DEFAULT NULL, + `completedDate` datetime NOT NULL, + `isValid` tinyint(1) DEFAULT NULL, + `settlementWindowId` bigint(20) unsigned DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferId`), + KEY `transferfulfilment_settlementwindowid_foreign` (`settlementWindowId`), + CONSTRAINT `transferfulfilment_settlementwindowid_foreign` FOREIGN KEY (`settlementWindowId`) REFERENCES `settlementWindow` (`settlementwindowid`), + CONSTRAINT `transferfulfilment_transferid_foreign` FOREIGN KEY (`transferId`) REFERENCES `transferFulfilmentDuplicateCheck` (`transferid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transferFulfilmentDuplicateCheck` +-- + +DROP TABLE IF EXISTS `transferFulfilmentDuplicateCheck`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transferFulfilmentDuplicateCheck` ( + `transferId` varchar(36) NOT NULL, + `hash` varchar(256) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferId`), + CONSTRAINT `transferfulfilmentduplicatecheck_transferid_foreign` FOREIGN KEY (`transferId`) REFERENCES `transfer` (`transferid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transferParticipant` +-- + +DROP TABLE IF EXISTS `transferParticipant`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transferParticipant` ( + `transferParticipantId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `transferId` varchar(36) NOT NULL, + `participantCurrencyId` int(10) unsigned NOT NULL, + `transferParticipantRoleTypeId` int(10) unsigned NOT NULL, + `ledgerEntryTypeId` int(10) unsigned NOT NULL, + `amount` decimal(18,4) NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferParticipantId`), + KEY `transferparticipant_transferid_index` (`transferId`), + KEY `transferparticipant_participantcurrencyid_index` (`participantCurrencyId`), + KEY `transferparticipant_transferparticipantroletypeid_index` (`transferParticipantRoleTypeId`), + KEY `transferparticipant_ledgerentrytypeid_index` (`ledgerEntryTypeId`), + CONSTRAINT `transferparticipant_ledgerentrytypeid_foreign` FOREIGN KEY (`ledgerEntryTypeId`) REFERENCES `ledgerEntryType` (`ledgerentrytypeid`), + CONSTRAINT `transferparticipant_participantcurrencyid_foreign` FOREIGN KEY (`participantCurrencyId`) REFERENCES `participantCurrency` (`participantcurrencyid`), + CONSTRAINT `transferparticipant_transferid_foreign` FOREIGN KEY (`transferId`) REFERENCES `transfer` (`transferid`), + CONSTRAINT `transferparticipant_transferparticipantroletypeid_foreign` FOREIGN KEY (`transferParticipantRoleTypeId`) REFERENCES `transferParticipantRoleType` (`transferparticipantroletypeid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transferParticipantRoleType` +-- + +DROP TABLE IF EXISTS `transferParticipantRoleType`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transferParticipantRoleType` ( + `transferParticipantRoleTypeId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `description` varchar(512) DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferParticipantRoleTypeId`), + UNIQUE KEY `transferparticipantroletype_name_unique` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transferRules` +-- + +DROP TABLE IF EXISTS `transferRules`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transferRules` ( + `transferRulesId` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(128) NOT NULL, + `description` varchar(512) DEFAULT NULL, + `rule` text NOT NULL, + `enabled` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferRulesId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transferState` +-- + +DROP TABLE IF EXISTS `transferState`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transferState` ( + `transferStateId` varchar(50) NOT NULL, + `enumeration` varchar(50) NOT NULL COMMENT 'transferState associated to the Mojaloop API', + `description` varchar(512) DEFAULT NULL, + `isActive` tinyint(1) NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferStateId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transferStateChange` +-- + +DROP TABLE IF EXISTS `transferStateChange`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transferStateChange` ( + `transferStateChangeId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `transferId` varchar(36) NOT NULL, + `transferStateId` varchar(50) NOT NULL, + `reason` varchar(512) DEFAULT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferStateChangeId`), + KEY `transferstatechange_transferid_index` (`transferId`), + KEY `transferstatechange_transferstateid_index` (`transferStateId`), + CONSTRAINT `transferstatechange_transferid_foreign` FOREIGN KEY (`transferId`) REFERENCES `transfer` (`transferid`), + CONSTRAINT `transferstatechange_transferstateid_foreign` FOREIGN KEY (`transferStateId`) REFERENCES `transferState` (`transferstateid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `transferTimeout` +-- + +DROP TABLE IF EXISTS `transferTimeout`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transferTimeout` ( + `transferTimeoutId` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `transferId` varchar(36) NOT NULL, + `expirationDate` datetime NOT NULL, + `createdDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`transferTimeoutId`), + UNIQUE KEY `transfertimeout_transferid_unique` (`transferId`), + CONSTRAINT `transfertimeout_transferid_foreign` FOREIGN KEY (`transferId`) REFERENCES `transfer` (`transferid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Final view structure for view `quotePartyView` +-- + +/*!50001 DROP VIEW IF EXISTS `quotePartyView`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8 */; +/*!50001 SET character_set_results = utf8 */; +/*!50001 SET collation_connection = utf8_general_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`central_ledger`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `quotePartyView` AS select `qp`.`quoteId` AS `quoteId`,`qp`.`quotePartyId` AS `quotePartyId`,`pt`.`name` AS `partyType`,`pit`.`name` AS `identifierType`,`qp`.`partyIdentifierValue` AS `partyIdentifierValue`,`spit`.`name` AS `partySubIdOrType`,`qp`.`fspId` AS `fspId`,`qp`.`merchantClassificationCode` AS `merchantClassificationCode`,`qp`.`partyName` AS `partyName`,`p`.`firstName` AS `firstName`,`p`.`lastName` AS `lastName`,`p`.`middleName` AS `middleName`,`p`.`dateOfBirth` AS `dateOfBirth`,`gc`.`longitude` AS `longitude`,`gc`.`latitude` AS `latitude` from (((((`quoteParty` `qp` join `partyType` `pt` on((`pt`.`partyTypeId` = `qp`.`partyTypeId`))) join `partyIdentifierType` `pit` on((`pit`.`partyIdentifierTypeId` = `qp`.`partyIdentifierTypeId`))) left join `party` `p` on((`p`.`quotePartyId` = `qp`.`quotePartyId`))) left join `partyIdentifierType` `spit` on((`spit`.`partyIdentifierTypeId` = `qp`.`partySubIdOrTypeId`))) left join `geoCode` `gc` on((`gc`.`quotePartyId` = `qp`.`quotePartyId`))) */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `quoteResponseView` +-- + +/*!50001 DROP VIEW IF EXISTS `quoteResponseView`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8 */; +/*!50001 SET character_set_results = utf8 */; +/*!50001 SET collation_connection = utf8_general_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`central_ledger`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `quoteResponseView` AS select `qr`.`quoteResponseId` AS `quoteResponseId`,`qr`.`quoteId` AS `quoteId`,`qr`.`transferAmountCurrencyId` AS `transferAmountCurrencyId`,`qr`.`transferAmount` AS `transferAmount`,`qr`.`payeeReceiveAmountCurrencyId` AS `payeeReceiveAmountCurrencyId`,`qr`.`payeeReceiveAmount` AS `payeeReceiveAmount`,`qr`.`payeeFspFeeCurrencyId` AS `payeeFspFeeCurrencyId`,`qr`.`payeeFspFeeAmount` AS `payeeFspFeeAmount`,`qr`.`payeeFspCommissionCurrencyId` AS `payeeFspCommissionCurrencyId`,`qr`.`payeeFspCommissionAmount` AS `payeeFspCommissionAmount`,`qr`.`ilpCondition` AS `ilpCondition`,`qr`.`responseExpirationDate` AS `responseExpirationDate`,`qr`.`isValid` AS `isValid`,`qr`.`createdDate` AS `createdDate`,`qrilp`.`value` AS `ilpPacket`,`gc`.`longitude` AS `longitude`,`gc`.`latitude` AS `latitude` from ((((`quoteResponse` `qr` join `quoteResponseIlpPacket` `qrilp` on((`qrilp`.`quoteResponseId` = `qr`.`quoteResponseId`))) join `quoteParty` `qp` on((`qp`.`quoteId` = `qr`.`quoteId`))) join `partyType` `pt` on((`pt`.`partyTypeId` = `qp`.`partyTypeId`))) left join `geoCode` `gc` on((`gc`.`quotePartyId` = `qp`.`quotePartyId`))) where (`pt`.`name` = 'PAYEE') */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `quoteView` +-- + +/*!50001 DROP VIEW IF EXISTS `quoteView`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8 */; +/*!50001 SET character_set_results = utf8 */; +/*!50001 SET collation_connection = utf8_general_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`central_ledger`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `quoteView` AS select `q`.`quoteId` AS `quoteId`,`q`.`transactionReferenceId` AS `transactionReferenceId`,`q`.`transactionRequestId` AS `transactionRequestId`,`q`.`note` AS `note`,`q`.`expirationDate` AS `expirationDate`,`ti`.`name` AS `transactionInitiator`,`tit`.`name` AS `transactionInitiatorType`,`ts`.`name` AS `transactionScenario`,`q`.`balanceOfPaymentsId` AS `balanceOfPaymentsId`,`tss`.`name` AS `transactionSubScenario`,`amt`.`name` AS `amountType`,`q`.`amount` AS `amount`,`q`.`currencyId` AS `currency` from (((((`quote` `q` join `transactionInitiator` `ti` on((`ti`.`transactionInitiatorId` = `q`.`transactionInitiatorId`))) join `transactionInitiatorType` `tit` on((`tit`.`transactionInitiatorTypeId` = `q`.`transactionInitiatorTypeId`))) join `transactionScenario` `ts` on((`ts`.`transactionScenarioId` = `q`.`transactionScenarioId`))) join `amountType` `amt` on((`amt`.`amountTypeId` = `q`.`amountTypeId`))) left join `transactionSubScenario` `tss` on((`tss`.`transactionSubScenarioId` = `q`.`transactionSubScenarioId`))) */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2019-10-14 21:12:45 diff --git a/docs/technical/central-bulk-transfers/assets/database/central-ledger-schema-DBeaver.erd b/docs/technical/central-bulk-transfers/assets/database/central-ledger-schema-DBeaver.erd new file mode 100644 index 000000000..12059bab5 --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/database/central-ledger-schema-DBeaver.erd @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + COLOR LEGEND: +Green - subject specific entity +Gray - transfer specific entity +Brown - bulk transfer entity +Red - settlement specific entity +Blue - lookup entity +Cyan - impl. specific +Yellow - tbd + + \ No newline at end of file diff --git a/docs/technical/central-bulk-transfers/assets/database/central-ledger-schema.png b/docs/technical/central-bulk-transfers/assets/database/central-ledger-schema.png new file mode 100644 index 000000000..6afdaf2ea Binary files /dev/null and b/docs/technical/central-bulk-transfers/assets/database/central-ledger-schema.png differ diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/architecture/Figure60-Example-Bulk-Transfer-Process-Spec1.0.png b/docs/technical/central-bulk-transfers/assets/diagrams/architecture/Figure60-Example-Bulk-Transfer-Process-Spec1.0.png new file mode 100644 index 000000000..aa361a64a Binary files /dev/null and b/docs/technical/central-bulk-transfers/assets/diagrams/architecture/Figure60-Example-Bulk-Transfer-Process-Spec1.0.png differ diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/architecture/bulk-transfer-arch-flows.svg b/docs/technical/central-bulk-transfers/assets/diagrams/architecture/bulk-transfer-arch-flows.svg new file mode 100644 index 000000000..b22f23afe --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/architecture/bulk-transfer-arch-flows.svg @@ -0,0 +1,3 @@ + + +1.7 Increment Position (fsp1)[Not supported by viewer]1.6 IncrementPosition (fsp1)[Not supported by viewer]FulfilSucess[Not supported by viewer]2.6 FulfilSuccess[Not supported by viewer]<alt> 2.6 Fulfil Reject[Not supported by viewer]FulfilReject[Not supported by viewer]<alt> 2.7 DecrementPosition (fsp1)[Not supported by viewer]Central - Services<font style="font-size: 18px">Central - Services</font>3.0 Reject[Not supported by viewer]1.0 Bulk Transfer Request[Not supported by viewer]3.1 DecrementPosition (fsp1)[Not supported by viewer]ML-Adapter[Not supported by viewer]Bulk TransferBulk Transfer<br>NotificationEvent Handler[Not supported by viewer]notificationsnotificationsFSP1(Payer)[Not supported by viewer]FSP2(Payee)[Not supported by viewer]1.1 Prepare Request[Not supported by viewer]1.2 Accpeted(202)[Not supported by viewer]Bulk TransferBulk Transfer<br>2.0 Bulk Fulfil Success / Reject[Not supported by viewer]bulk fulfilbulk fulfil2.7 DecrementPosition (fsp2)[Not supported by viewer]2.1 Bulk Fulfil Success / Reject[Not supported by viewer]2.2 OK(202)[Not supported by viewer]OK (200)[Not supported by viewer]OK (200)[Not supported by viewer]Transfer TimeoutHandler[Not supported by viewer]2.12 Bulk Fulfil Notify Callback /<alt> 2.12 Bulk Reject Response (Fulfil reject) /3.6 Bulk Reject Response (Timeout) /<alt> 1.8 Bulk Prepare Failure [Not supported by viewer]1.12 Bulk Prepare Notify /2.13 Bulk Fulfil Notify /<alt> 2.13 Bulk Reject Response (Fulfil reject)3.7 Bulk Reject Response (Timeout)[Not supported by viewer]positionspositions<br>bulk processingbulk processingBulkFulfilHandler[Not supported by viewer]1.3 Bulk Prepare Consume[Not supported by viewer]BulkPrepareHandlerBulkPrepare<br>Handler prepare preparefulfilfulfilBulkProcessing HandlerBulkProcessing Handler<br>1.4 Individual Prepare[Not supported by viewer]1.5 Individual Prepare[Not supported by viewer]1.5 / 1.9 / 2.9 / 3.3 Individual Notify[Not supported by viewer]1.8 / 2.8 / 3.2 IndividualNotify[Not supported by viewer]1.6 / 1.10 / 2.10 / 3.4 Bulk Notify[Not supported by viewer]1.7 / 1.11 / 2.11 / 3.5 Bulk Notify[Not supported by viewer]2.3 Bulk Fulfil Consume[Not supported by viewer]2.4 IndividualFulfil[Not supported by viewer]2.5 IndividualFulfil[Not supported by viewer]FulfilHandlerFulfilHandlerSuccess/Reject[Not supported by viewer]PositionHandlerPositionHandlerPrepareHandlerPrepareHandlerObject Store[Not supported by viewer]<alt> 1.4 Bulk Prepare Failure[Not supported by viewer]<alt> 1.6 Individual Prepare Failure[Not supported by viewer]<alt> 1.8 IndividualFailure Notify[Not supported by viewer]<alt> 2.4 Bulk Fulfil Failure[Not supported by viewer]<alt> 2.6 Individual Fulfil Failure[Not supported by viewer]bulk preparebulk prepareObject Store[Not supported by viewer]Object Store[Not supported by viewer] \ No newline at end of file diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.1.0-bulk-prepare-overview.plantuml b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.1.0-bulk-prepare-overview.plantuml new file mode 100644 index 000000000..5bdd8119f --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.1.0-bulk-prepare-overview.plantuml @@ -0,0 +1,217 @@ +/'***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + * Samuel Kummary + -------------- + ******'/ + +@startuml +' declare title +title 1.1.0. DFSP1 sends a Bulk Prepare Transfer request to DFSP2 + +autonumber + +' Actor Keys: +' boundary - APIs/Interfaces, etc +' collections - Kafka Topics +' control - Kafka Consumers +' entity - Database Access Objects +' database - Database Persistance Store + +' declare actors +actor "DFSP1\nPayer" as DFSP1 +actor "DFSP2\nPayee" as DFSP2 +boundary "Bulk API Adapter" as BULK_API +control "Bulk API Notification \nHandler" as NOTIFY_HANDLER +collections "mojaloop-\nobject-store\n(**MLOS**)" as OBJECT_STORE +boundary "Central Service API" as CSAPI +collections "topic-\nbulk-prepare" as TOPIC_BULK_PREPARE +control "Bulk Prepare\nHandler" as BULK_PREP_HANDLER +collections "topic-\ntransfer-prepare" as TOPIC_TRANSFER_PREPARE +control "Prepare Handler" as PREP_HANDLER +collections "topic-\ntransfer-position" as TOPIC_TRANSFER_POSITION +control "Position Handler" as POS_HANDLER +collections "topic-\nbulk-processing" as TOPIC_BULK_PROCESSING +control "Bulk Processing\nHandler" as BULK_PROC_HANDLER +collections "topic-\nnotifications" as TOPIC_NOTIFICATIONS + +box "Financial Service Providers" #lightGray + participant DFSP1 + participant DFSP2 +end box + +box "Bulk API Adapter Service" #LightBlue + participant BULK_API + participant NOTIFY_HANDLER +end box + +box "Central Service" #LightYellow + participant OBJECT_STORE + participant CSAPI + participant TOPIC_BULK_PREPARE + participant BULK_PREP_HANDLER + participant TOPIC_TRANSFER_PREPARE + participant PREP_HANDLER + participant TOPIC_TRANSFER_POSITION + participant POS_HANDLER + participant TOPIC_BULK_PROCESSING + participant BULK_PROC_HANDLER + participant TOPIC_NOTIFICATIONS +end box + +' start flow +activate NOTIFY_HANDLER +activate BULK_PREP_HANDLER +activate PREP_HANDLER +activate POS_HANDLER +activate BULK_PROC_HANDLER +group DFSP1 sends a Bulk Prepare Transfer request to DFSP2 + note right of DFSP1 #yellow + Headers - transferHeaders: { + Content-Length: , + Content-Type: , + Date: , + FSPIOP-Source: , + FSPIOP-Destination: , + FSPIOP-Encryption: , + FSPIOP-Signature: , + FSPIOP-URI: , + FSPIOP-HTTP-Method: + } + + Payload - bulkTransferMessage: + { + bulkTransferId: , + bulkQuoteId: , + payeeFsp: , + payerFsp: , + individualTransfers: [ + { + transferId: , + transferAmount: + { + currency: , + amount: + }, + ilpPacket: , + condition: , + extensionList: { extension: [ + { key: , value: } + ] } + } + ], + extensionList: { extension: [ + { key: , value: } + ] }, + expiration: + } + end note + DFSP1 ->> BULK_API: POST - /bulkTransfers + activate BULK_API + BULK_API -> BULK_API: Validate incoming message\nError codes: 3000-3002, 3100-3107 + loop + BULK_API -> OBJECT_STORE: Persist individual transfers in the bulk to\nobject store: **MLOS.individualTransfers** + activate OBJECT_STORE + OBJECT_STORE --> BULK_API: Return messageId reference to the stored object(s) + deactivate OBJECT_STORE + end + note right of BULK_API #yellow + Message: + { + id: + to: , + from: , + type: "application/json" + content: { + headers: , + payload: { + bulkTransferId: , + bulkQuoteId": , + payerFsp: , + payeeFsp: , + expiration: , + hash: + } + }, + metadata: { + event: { + id: , + type: "bulk-prepare", + action: "bulk-prepare", + createdAt: , + state: { + status: "success", + code: 0 + } + } + } + } + end note + BULK_API -> TOPIC_BULK_PREPARE: Route & Publish Bulk Prepare event \nfor Payer\nError code: 2003 + activate TOPIC_BULK_PREPARE + TOPIC_BULK_PREPARE <-> TOPIC_BULK_PREPARE: Ensure event is replicated \nas configured (ACKS=all)\nError code: 2003 + TOPIC_BULK_PREPARE --> BULK_API: Respond replication acknowledgements \nhave been received + deactivate TOPIC_BULK_PREPARE + BULK_API -->> DFSP1: Respond HTTP - 202 (Accepted) + deactivate BULK_API + ||| + TOPIC_BULK_PREPARE <- BULK_PREP_HANDLER: Consume message + ref over TOPIC_BULK_PREPARE, BULK_PREP_HANDLER, TOPIC_TRANSFER_PREPARE: Bulk Prepare Handler Consume \n + alt Success + BULK_PREP_HANDLER -> TOPIC_TRANSFER_PREPARE: Produce (stream) single transfer message\nfor each individual transfer [loop] + else Failure + BULK_PREP_HANDLER --> TOPIC_NOTIFICATIONS: Produce single message for the entire bulk + end + ||| + TOPIC_TRANSFER_PREPARE <- PREP_HANDLER: Consume message + ref over TOPIC_TRANSFER_PREPARE, PREP_HANDLER, TOPIC_TRANSFER_POSITION: Prepare Handler Consume\n + alt Success + PREP_HANDLER -> TOPIC_TRANSFER_POSITION: Produce message + else Failure + PREP_HANDLER --> TOPIC_BULK_PROCESSING: Produce message + end + ||| + TOPIC_TRANSFER_POSITION <- POS_HANDLER: Consume message + ref over TOPIC_TRANSFER_POSITION, POS_HANDLER, TOPIC_BULK_PROCESSING: Position Handler Consume\n + POS_HANDLER -> TOPIC_BULK_PROCESSING: Produce message + ||| + TOPIC_BULK_PROCESSING <- BULK_PROC_HANDLER: Consume message + ref over TOPIC_BULK_PROCESSING, BULK_PROC_HANDLER, TOPIC_NOTIFICATIONS: Bulk Processing Handler Consume\n + BULK_PROC_HANDLER -> OBJECT_STORE: Persist bulk message by destination to the\nobject store: **MLOS.bulkTransferResults** + activate OBJECT_STORE + OBJECT_STORE --> BULK_PROC_HANDLER: Return the reference to the stored \nnotification object(s): **messageId** + deactivate OBJECT_STORE + BULK_PROC_HANDLER -> TOPIC_NOTIFICATIONS: Send Bulk Prepare notification + ||| + TOPIC_NOTIFICATIONS <- NOTIFY_HANDLER: Consume message + NOTIFY_HANDLER -> OBJECT_STORE: Retrieve bulk notification(s) by reference & destination:\n**MLOS.bulkTransferResults.messageId + destination** + activate OBJECT_STORE + OBJECT_STORE --> NOTIFY_HANDLER: Return notification(s) payload + deactivate OBJECT_STORE + ref over DFSP2, TOPIC_NOTIFICATIONS: Send notification to Participant (Payee)\n + NOTIFY_HANDLER -> DFSP2: Send Bulk Prepare notification to Payee + ||| +end +deactivate POS_HANDLER +deactivate BULK_PREP_HANDLER +deactivate PREP_HANDLER +deactivate BULK_PROC_HANDLER +deactivate NOTIFY_HANDLER +@enduml diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.1.0-bulk-prepare-overview.svg b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.1.0-bulk-prepare-overview.svg new file mode 100644 index 000000000..b24804c17 --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.1.0-bulk-prepare-overview.svg @@ -0,0 +1,788 @@ + + + + + + + + + + + 1.1.0. DFSP1 sends a Bulk Prepare Transfer request to DFSP2 + + + + Financial Service Providers + + + + Bulk API Adapter Service + + + + Central Service + + + + + + + + + + DFSP1 + + + Payer + + + + + DFSP1 + + + Payer + + + + + DFSP2 + + + Payee + + + + + DFSP2 + + + Payee + + + + + Bulk API Adapter + + + + + Bulk API Adapter + + + + + Bulk API Notification + + + Handler + + + + + Bulk API Notification + + + Handler + + + + + + + mojaloop- + + + object-store + + + ( + + + MLOS + + + ) + + + + + mojaloop- + + + object-store + + + ( + + + MLOS + + + ) + + + Central Service API + + + + + Central Service API + + + + + + + topic- + + + bulk-prepare + + + + + topic- + + + bulk-prepare + + + Bulk Prepare + + + Handler + + + + + Bulk Prepare + + + Handler + + + + + + + topic- + + + transfer-prepare + + + + + topic- + + + transfer-prepare + + + Prepare Handler + + + + + Prepare Handler + + + + + + + topic- + + + transfer-position + + + + + topic- + + + transfer-position + + + Position Handler + + + + + Position Handler + + + + + + + topic- + + + bulk-processing + + + + + topic- + + + bulk-processing + + + Bulk Processing + + + Handler + + + + + Bulk Processing + + + Handler + + + + + + + topic- + + + notifications + + + + + topic- + + + notifications + + + + + + DFSP1 sends a Bulk Prepare Transfer request to DFSP2 + + + + + Headers - transferHeaders: { + + + Content-Length: <int>, + + + Content-Type: <string>, + + + Date: <date>, + + + FSPIOP-Source: <string>, + + + FSPIOP-Destination: <string>, + + + FSPIOP-Encryption: <string>, + + + FSPIOP-Signature: <string>, + + + FSPIOP-URI: <uri>, + + + FSPIOP-HTTP-Method: <string> + + + } + + + Payload - bulkTransferMessage: + + + { + + + bulkTransferId: <uuid>, + + + bulkQuoteId: <uuid>, + + + payeeFsp: <string>, + + + payerFsp: <string>, + + + individualTransfers: [ + + + { + + + transferId: <uuid>, + + + transferAmount: + + + { + + + currency: <string>, + + + amount: <string> + + + }, + + + ilpPacket: <string>, + + + condition: <string>, + + + extensionList: { extension: [ + + + { key: <string>, value: <string> } + + + ] } + + + } + + + ], + + + extensionList: { extension: [ + + + { key: <string>, value: <string> } + + + ] }, + + + expiration: <string> + + + } + + + + 1 + + + POST - /bulkTransfers + + + + + 2 + + + Validate incoming message + + + Error codes: + + + 3000-3002, 3100-3107 + + + + + loop + + + + + 3 + + + Persist individual transfers in the bulk to + + + object store: + + + MLOS.individualTransfers + + + + + 4 + + + Return messageId reference to the stored object(s) + + + + + Message: + + + { + + + id: <messageId> + + + to: <payeeFspName>, + + + from: <payerFspName>, + + + type: "application/json" + + + content: { + + + headers: <bulkTransferHeaders>, + + + payload: { + + + bulkTransferId: <uuid>, + + + bulkQuoteId": <uuid>, + + + payerFsp: <string>, + + + payeeFsp: <string>, + + + expiration: <timestamp>, + + + hash: <string> + + + } + + + }, + + + metadata: { + + + event: { + + + id: <uuid>, + + + type: "bulk-prepare", + + + action: "bulk-prepare", + + + createdAt: <timestamp>, + + + state: { + + + status: "success", + + + code: 0 + + + } + + + } + + + } + + + } + + + + + 5 + + + Route & Publish Bulk Prepare event + + + for Payer + + + Error code: + + + 2003 + + + + + 6 + + + Ensure event is replicated + + + as configured (ACKS=all) + + + Error code: + + + 2003 + + + + + 7 + + + Respond replication acknowledgements + + + have been received + + + + + 8 + + + Respond HTTP - 202 (Accepted) + + + + + 9 + + + Consume message + + + + + ref + + + Bulk Prepare Handler Consume + + + + + alt + + + [Success] + + + + + 10 + + + Produce (stream) single transfer message + + + for each individual transfer [loop] + + + + [Failure] + + + + + 11 + + + Produce single message for the entire bulk + + + + + 12 + + + Consume message + + + + + ref + + + Prepare Handler Consume + + + + + alt + + + [Success] + + + + + 13 + + + Produce message + + + + [Failure] + + + + + 14 + + + Produce message + + + + + 15 + + + Consume message + + + + + ref + + + Position Handler Consume + + + + + 16 + + + Produce message + + + + + 17 + + + Consume message + + + + + ref + + + Bulk Processing Handler Consume + + + + + 18 + + + Persist bulk message by destination to the + + + object store: + + + MLOS.bulkTransferResults + + + + + 19 + + + Return the reference to the stored + + + notification object(s): + + + messageId + + + + + 20 + + + Send Bulk Prepare notification + + + + + 21 + + + Consume message + + + + + 22 + + + Retrieve bulk notification(s) by reference & destination: + + + MLOS.bulkTransferResults.messageId + destination + + + + + 23 + + + Return notification(s) payload + + + + + ref + + + Send notification to Participant (Payee) + + + + + 24 + + + Send Bulk Prepare notification to Payee + + diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.1.1-bulk-prepare-handler.plantuml b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.1.1-bulk-prepare-handler.plantuml new file mode 100644 index 000000000..a9c53292f --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.1.1-bulk-prepare-handler.plantuml @@ -0,0 +1,320 @@ +/'***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + * Samuel Kummary + -------------- + ******'/ + +@startuml +' declare title +title 1.1.1. Bulk Prepare Handler Consume + +autonumber + +' Actor Keys: +' boundary - APIs/Interfaces, etc +' collections - Kafka Topics +' control - Kafka Consumers +' entity - Database Access Objects +' database - Database Persistance Store + +' declare actors +collections "topic-\nbulk-prepare" as TOPIC_BULK_PREPARE +collections "mojaloop-\nobject-store\n(**MLOS**)" as OBJECT_STORE +control "Bulk Prepare \nHandler" as BULK_PREP_HANDLER +collections "topic-\ntransfer-prepare" as TOPIC_TRANSFER_PREPARE +collections "topic-event" as TOPIC_EVENTS +collections "topic-\nnotification" as TOPIC_NOTIFICATIONS +collections "topic-bulk-\nprocessing" as TOPIC_BULK_PROCESSING +entity "Bulk DAO" as BULK_DAO +entity "Participant DAO" as PARTICIPANT_DAO +database "Central Store" as DB + +box "Central Service" #LightYellow + participant OBJECT_STORE + participant TOPIC_BULK_PREPARE + participant BULK_PREP_HANDLER + participant TOPIC_TRANSFER_PREPARE + participant TOPIC_EVENTS + participant TOPIC_NOTIFICATIONS + participant TOPIC_BULK_PROCESSING + participant BULK_DAO + participant PARTICIPANT_DAO + participant DB +end box + +' start flow +activate BULK_PREP_HANDLER +group Bulk Prepare Handler Consume + TOPIC_BULK_PREPARE <- BULK_PREP_HANDLER: Consume Bulk Prepare message + activate TOPIC_BULK_PREPARE + deactivate TOPIC_BULK_PREPARE + group Validate Bulk Prepare Transfer + group Duplicate Check + note right of BULK_PREP_HANDLER #cyan + The Specification doesn't touch on the duplicate handling + of bulk transfers, so the current design mostly follows the + strategy used for individual transfers, except in two places: + + 1. For duplicate requests where hash matches, the current design + includes only the status of the bulk & timestamp (if completed), + but not the individual transfers (for which a GET should be used). + + 2. For duplicate requests where hash matches, but are not in a + finalized state, only the state of the bulkTransfer is sent. + end note + ||| + BULK_PREP_HANDLER -> DB: Request Duplicate Check + ref over BULK_PREP_HANDLER, DB: Request Duplicate Check (using message.content.payload)\n + DB --> BULK_PREP_HANDLER: Return { hasDuplicateId: Boolean, hasDuplicateHash: Boolean } + end + + alt hasDuplicateId == TRUE && hasDuplicateHash == TRUE + break Return TRUE & Log ('Not implemented') + end + else hasDuplicateId == TRUE && hasDuplicateHash == FALSE + note right of BULK_PREP_HANDLER #yellow + { + id: , + from: , + to: , + type: "application/json", + content: { + headers: , + payload: { + errorInformation: { + errorCode: "3106", + errorDescription: "Modified request", + extensionList: { + extension: [ + { + key: "_cause", + value: + } + ] + } + }, + uriParams: { + id: + } + } + }, + metadata: { + correlationId: , + event: { + id: , + type: "notification", + action: "bulk-prepare", + createdAt: , + state: { + status: "error", + code: "3106", + description: "Modified request" + }, + responseTo: + } + } + } + end note + BULK_PREP_HANDLER -> TOPIC_NOTIFICATIONS: Publish Notification (failure) event for Payer\nError codes: 3106 + activate TOPIC_NOTIFICATIONS + deactivate TOPIC_NOTIFICATIONS + else hasDuplicateId == FALSE + group Validate Bulk Transfer Prepare Request + BULK_PREP_HANDLER <-> BULK_PREP_HANDLER: FSPIOP Source matches Payer + BULK_PREP_HANDLER <-> BULK_PREP_HANDLER: Check expiration + BULK_PREP_HANDLER <-> BULK_PREP_HANDLER: Payer and Payee FSP's are different + group Validate Payer + BULK_PREP_HANDLER -> PARTICIPANT_DAO: Request to retrieve Payer Participant details (if it exists) + activate PARTICIPANT_DAO + PARTICIPANT_DAO -> DB: Request Participant details + hnote over DB #lightyellow + participant + participantCurrency + end note + activate DB + PARTICIPANT_DAO <-- DB: Return Participant details if it exists + deactivate DB + PARTICIPANT_DAO --> BULK_PREP_HANDLER: Return Participant details if it exists + deactivate PARTICIPANT_DAO + BULK_PREP_HANDLER <-> BULK_PREP_HANDLER: Validate Payer\nError codes: 3202 + end + group Validate Payee + BULK_PREP_HANDLER -> PARTICIPANT_DAO: Request to retrieve Payee Participant details (if it exists) + activate PARTICIPANT_DAO + PARTICIPANT_DAO -> DB: Request Participant details + hnote over DB #lightyellow + participant + participantCurrency + end note + activate DB + PARTICIPANT_DAO <-- DB: Return Participant details if it exists + deactivate DB + PARTICIPANT_DAO --> BULK_PREP_HANDLER: Return Participant details if it exists + deactivate PARTICIPANT_DAO + BULK_PREP_HANDLER <-> BULK_PREP_HANDLER: Validate Payee\nError codes: 3203 + end + end + ||| + alt Validate Bulk Transfer Prepare Request (success) + group Persist Bulk Transfer State (with bulkTransferState='RECEIVED') + BULK_PREP_HANDLER -> BULK_DAO: Request to persist bulk transfer\nError codes: 2003 + activate BULK_DAO + BULK_DAO -> DB: Persist bulkTransfer + hnote over DB #lightyellow + bulkTransfer + bulkTransferExtension + bulkTransferStateChange + end note + activate DB + deactivate DB + BULK_DAO --> BULK_PREP_HANDLER: Return state + deactivate BULK_DAO + end + else Validate Bulk Transfer Prepare Request (failure) + group Persist Bulk Transfer State (with bulkTransferState='INVALID') (Introducing a new status INVALID to mark these entries) + BULK_PREP_HANDLER -> BULK_DAO: Request to persist bulk transfer\n(when Payee/Payer/crypto-condition validation fails)\nError codes: 2003 + activate BULK_DAO + BULK_DAO -> DB: Persist transfer + hnote over DB #lightyellow + bulkTransfer + bulkTransferExtension + bulkTransferStateChange + bulkTransferError + end note + activate DB + deactivate DB + BULK_DAO --> BULK_PREP_HANDLER: Return state + deactivate BULK_DAO + end + end + end + end + alt Validate Bulk Prepare Transfer (success) + loop for each individual transfer in the bulk + BULK_PREP_HANDLER -> OBJECT_STORE: Retrieve individual transfers from the bulk using\nreference: **MLOS.individualTransfers.messageId** + activate OBJECT_STORE + note right of OBJECT_STORE #lightgrey + Add elements such as Expiry time, Payer FSP, Payee FSP, etc. to each + transfer to make their format similar to a single transfer + end note + OBJECT_STORE --> BULK_PREP_HANDLER: Stream bulk's individual transfers + deactivate OBJECT_STORE + + group Insert Bulk Transfer Association (with bulkProcessingState='RECEIVED') + BULK_PREP_HANDLER -> BULK_DAO: Request to persist bulk transfer association\nError codes: 2003 + activate BULK_DAO + BULK_DAO -> DB: Insert bulkTransferAssociation + hnote over DB #lightyellow + bulkTransferAssociation + bulkTransferStateChange + end note + activate DB + deactivate DB + BULK_DAO --> BULK_PREP_HANDLER: Return state + deactivate BULK_DAO + end + + note right of BULK_PREP_HANDLER #yellow + Message: + { + id: + from: , + to: , + type: application/json + content: { + headers: , + payload: + }, + metadata: { + event: { + id: , + responseTo: , + type: "prepare", + action: "bulk-prepare", + createdAt: , + state: { + status: "success", + code: 0, + description:"action successful" + } + } + } + } + end note + BULK_PREP_HANDLER -> TOPIC_TRANSFER_PREPARE: Route & Publish Prepare event to the Payee for the Individual Transfer\nError codes: 2003 + activate TOPIC_TRANSFER_PREPARE + deactivate TOPIC_TRANSFER_PREPARE + end + else Validate Bulk Prepare Transfer (failure) + note right of BULK_PREP_HANDLER #yellow + Message: + { + id: + from: , + to: , + type: "application/json", + content: { + headers: , + payload: { + "errorInformation": { + "errorCode": + "errorDescription": "", + "extensionList": + } + }, + metadata: { + event: { + id: , + responseTo: , + type: "bulk-processing", + action: "bulk-abort", + createdAt: , + state: { + status: "error", + code: + description: + } + } + } + } + end note + BULK_PREP_HANDLER -> TOPIC_BULK_PROCESSING: Publish Processing (failure) event for Payer\nError codes: 2003 + activate TOPIC_BULK_PROCESSING + deactivate TOPIC_BULK_PROCESSING + group Insert Bulk Transfer Association (with bulkProcessingState='INVALID') + BULK_PREP_HANDLER -> BULK_DAO: Request to persist bulk transfer association\nError codes: 2003 + activate BULK_DAO + BULK_DAO -> DB: Insert bulkTransferAssociation + hnote over DB #lightyellow + bulkTransferAssociation + bulkTransferStateChange + end note + activate DB + deactivate DB + BULK_DAO --> BULK_PREP_HANDLER: Return state + deactivate BULK_DAO + end + + end +end +deactivate BULK_PREP_HANDLER +@enduml + diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.1.1-bulk-prepare-handler.svg b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.1.1-bulk-prepare-handler.svg new file mode 100644 index 000000000..eb4b20b44 --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.1.1-bulk-prepare-handler.svg @@ -0,0 +1,1009 @@ + + + + + + + + + + + 1.1.1. Bulk Prepare Handler Consume + + + + Central Service + + + + + + + + + + + + + + + + + + + + + + mojaloop- + + + object-store + + + ( + + + MLOS + + + ) + + + + + mojaloop- + + + object-store + + + ( + + + MLOS + + + ) + + + + + topic- + + + bulk-prepare + + + + + topic- + + + bulk-prepare + + + Bulk Prepare + + + Handler + + + + + Bulk Prepare + + + Handler + + + + + + + topic- + + + transfer-prepare + + + + + topic- + + + transfer-prepare + + + + + topic-event + + + + + topic-event + + + + + topic- + + + notification + + + + + topic- + + + notification + + + + + topic-bulk- + + + processing + + + + + topic-bulk- + + + processing + + + Bulk DAO + + + + + Bulk DAO + + + + + Participant DAO + + + + + Participant DAO + + + + + Central Store + + + + + Central Store + + + + + + + + Bulk Prepare Handler Consume + + + + + 1 + + + Consume Bulk Prepare message + + + + + Validate Bulk Prepare Transfer + + + + + Duplicate Check + + + + + The Specification doesn't touch on the duplicate handling + + + of bulk transfers, so the current design mostly follows the + + + strategy used for individual transfers, except in two places: + + + 1. For duplicate requests where hash matches, the current design + + + includes only the status of the bulk & timestamp (if completed), + + + but not the individual transfers (for which a GET should be used). + + + 2. For duplicate requests where hash matches, but are not in a + + + finalized state, only the state of the bulkTransfer is sent. + + + + + 2 + + + Request Duplicate Check + + + + + ref + + + Request Duplicate Check (using message.content.payload) + + + + + 3 + + + Return { hasDuplicateId: Boolean, hasDuplicateHash: Boolean } + + + + + alt + + + [hasDuplicateId == TRUE && hasDuplicateHash == TRUE] + + + + + break + + + [Return TRUE & Log ('Not implemented')] + + + + [hasDuplicateId == TRUE && hasDuplicateHash == FALSE] + + + + + { + + + id: <messageId>, + + + from: <ledgerName>, + + + to: <payerFspName>, + + + type: "application/json", + + + content: { + + + headers: <bulkTransferHeaders>, + + + payload: { + + + errorInformation: { + + + errorCode: "3106", + + + errorDescription: "Modified request", + + + extensionList: { + + + extension: [ + + + { + + + key: "_cause", + + + value: <FSPIOPError> + + + } + + + ] + + + } + + + }, + + + uriParams: { + + + id: <bulkTransferId> + + + } + + + } + + + }, + + + metadata: { + + + correlationId: <uuid>, + + + event: { + + + id: <uuid>, + + + type: "notification", + + + action: "bulk-prepare", + + + createdAt: <timestamp>, + + + state: { + + + status: "error", + + + code: "3106", + + + description: "Modified request" + + + }, + + + responseTo: <uuid> + + + } + + + } + + + } + + + + + 4 + + + Publish Notification (failure) event for Payer + + + Error codes: + + + 3106 + + + + [hasDuplicateId == FALSE] + + + + + Validate Bulk Transfer Prepare Request + + + + + 5 + + + FSPIOP Source matches Payer + + + + + 6 + + + Check expiration + + + + + 7 + + + Payer and Payee FSP's are different + + + + + Validate Payer + + + + + 8 + + + Request to retrieve Payer Participant details (if it exists) + + + + + 9 + + + Request Participant details + + + + participant + + + participantCurrency + + + + + 10 + + + Return Participant details if it exists + + + + + 11 + + + Return Participant details if it exists + + + + + 12 + + + Validate Payer + + + Error codes: + + + 3202 + + + + + Validate Payee + + + + + 13 + + + Request to retrieve Payee Participant details (if it exists) + + + + + 14 + + + Request Participant details + + + + participant + + + participantCurrency + + + + + 15 + + + Return Participant details if it exists + + + + + 16 + + + Return Participant details if it exists + + + + + 17 + + + Validate Payee + + + Error codes: + + + 3203 + + + + + alt + + + [Validate Bulk Transfer Prepare Request (success)] + + + + + Persist Bulk Transfer State (with bulkTransferState='RECEIVED') + + + + + 18 + + + Request to persist bulk transfer + + + Error codes: + + + 2003 + + + + + 19 + + + Persist bulkTransfer + + + + bulkTransfer + + + bulkTransferExtension + + + bulkTransferStateChange + + + + + 20 + + + Return state + + + + [Validate Bulk Transfer Prepare Request (failure)] + + + + + Persist Bulk Transfer State (with bulkTransferState='INVALID') (Introducing a new status INVALID to mark these entries) + + + + + 21 + + + Request to persist bulk transfer + + + (when Payee/Payer/crypto-condition validation fails) + + + Error codes: + + + 2003 + + + + + 22 + + + Persist transfer + + + + bulkTransfer + + + bulkTransferExtension + + + bulkTransferStateChange + + + bulkTransferError + + + + + 23 + + + Return state + + + + + alt + + + [Validate Bulk Prepare Transfer (success)] + + + + + loop + + + [for each individual transfer in the bulk] + + + + + 24 + + + Retrieve individual transfers from the bulk using + + + reference: + + + MLOS.individualTransfers.messageId + + + + + Add elements such as Expiry time, Payer FSP, Payee FSP, etc. to each + + + transfer to make their format similar to a single transfer + + + + + 25 + + + Stream bulk's individual transfers + + + + + Insert Bulk Transfer Association (with bulkProcessingState='RECEIVED') + + + + + 26 + + + Request to persist bulk transfer association + + + Error codes: + + + 2003 + + + + + 27 + + + Insert bulkTransferAssociation + + + + bulkTransferAssociation + + + bulkTransferStateChange + + + + + 28 + + + Return state + + + + + Message: + + + { + + + id: <messageId> + + + from: <payerFspName>, + + + to: <payeeFspName>, + + + type: application/json + + + content: { + + + headers: <transferHeaders>, + + + payload: <transferMessage> + + + }, + + + metadata: { + + + event: { + + + id: <uuid>, + + + responseTo: <previous.uuid>, + + + type: "prepare", + + + action: "bulk-prepare", + + + createdAt: <timestamp>, + + + state: { + + + status: "success", + + + code: 0, + + + description:"action successful" + + + } + + + } + + + } + + + } + + + + + 29 + + + Route & Publish Prepare event to the Payee for the Individual Transfer + + + Error codes: + + + 2003 + + + + [Validate Bulk Prepare Transfer (failure)] + + + + + Message: + + + { + + + id: <messageId> + + + from: <ledgerName>, + + + to: <bulkTransferMessage.payerFsp>, + + + type: "application/json", + + + content: { + + + headers: <bulkTransferHeaders>, + + + payload: { + + + "errorInformation": { + + + "errorCode": <possible codes: [2003, 3100, 3105, 3106, 3202, 3203, 3300, 3301]> + + + "errorDescription": "<refer to section 7.6 for description>", + + + "extensionList": <transferMessage.extensionList> + + + } + + + }, + + + metadata: { + + + event: { + + + id: <uuid>, + + + responseTo: <previous.uuid>, + + + type: "bulk-processing", + + + action: "bulk-abort", + + + createdAt: <timestamp>, + + + state: { + + + status: "error", + + + code: <errorInformation.errorCode> + + + description: <errorInformation.errorDescription> + + + } + + + } + + + } + + + } + + + + + 30 + + + Publish Processing (failure) event for Payer + + + Error codes: + + + 2003 + + + + + Insert Bulk Transfer Association (with bulkProcessingState='INVALID') + + + + + 31 + + + Request to persist bulk transfer association + + + Error codes: + + + 2003 + + + + + 32 + + + Insert bulkTransferAssociation + + + + bulkTransferAssociation + + + bulkTransferStateChange + + + + + 33 + + + Return state + + diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.2.1-prepare-handler.plantuml b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.2.1-prepare-handler.plantuml new file mode 100644 index 000000000..aee02560a --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.2.1-prepare-handler.plantuml @@ -0,0 +1,324 @@ +/'***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + * Georgi Georgiev + * Miguel de Barros + * Rajiv Mothilal + * Samuel Kummary + * Shashikant Hirugade + -------------- + ******'/ + +@startuml +' declate title +title 1.2.1. Prepare Handler Consume individual transfers from Bulk + +autonumber + +' Actor Keys: +' boundary - APIs/Interfaces, etc +' collections - Kafka Topics +' control - Kafka Consumers +' entity - Database Access Objects +' database - Database Persistance Store + +' declare actors +collections "topic-\ntransfer-prepare" as TOPIC_TRANSFER_PREPARE +control "Prepare Handler" as PREP_HANDLER +collections "topic-\ntransfer-position" as TOPIC_TRANSFER_POSITION +collections "topic-\nbulk-processing" as TOPIC_BULK_PROCESSING +collections "topic-\nevent" as TOPIC_EVENTS +collections "topic-\nnotification" as TOPIC_NOTIFICATIONS +entity "Position DAO" as POS_DAO +entity "Participant DAO" as PARTICIPANT_DAO +database "Central Store" as DB + +box "Central Service" #LightYellow + participant TOPIC_TRANSFER_PREPARE + participant PREP_HANDLER + participant TOPIC_TRANSFER_POSITION + participant TOPIC_EVENTS + participant TOPIC_NOTIFICATIONS + participant TOPIC_BULK_PROCESSING + participant POS_DAO + participant PARTICIPANT_DAO + participant DB +end box + +' start flow +activate PREP_HANDLER +group Prepare Handler Consume + TOPIC_TRANSFER_PREPARE <- PREP_HANDLER: Consume Prepare event message + activate TOPIC_TRANSFER_PREPARE + deactivate TOPIC_TRANSFER_PREPARE + + break + group Validate Event + PREP_HANDLER <-> PREP_HANDLER: Validate event - Rule: type == 'prepare' && action == 'bulk-prepare'\nError codes: 2001 + end + end + + group Persist Event Information + ||| + PREP_HANDLER -> TOPIC_EVENTS: Publish event information + ref over PREP_HANDLER, TOPIC_EVENTS : Event Handler Consume\n + ||| + end + + group Validate Prepare Transfer + PREP_HANDLER <-> PREP_HANDLER: Schema validation of the incoming message + PREP_HANDLER <-> PREP_HANDLER: Verify the message's signature (to be confirmed in future requirement) + + group Validate Duplicate Check + ||| + PREP_HANDLER -> DB: Request Duplicate Check + ref over PREP_HANDLER, DB: Request Duplicate Check\n + DB --> PREP_HANDLER: Return { hasDuplicateId: Boolean, hasDuplicateHash: Boolean } + end + + alt hasDuplicateId == TRUE && hasDuplicateHash == TRUE + note right of PREP_HANDLER #lightgrey + In the context of a bulk (when compared to regular transfers), duplicate + individual transfers are now considered and reported with Modified Request, + because they could have already been handled for another bulk. + end note + break + note right of PREP_HANDLER #yellow + { + id: + from: , + to: , + type: "application/json", + content: { + headers: , + payload: { + errorInformation: { + errorCode: "3106", + errorDescription: "Modified request - Individual transfer prepare duplicate", + extensionList: { extension: [ { key: "_cause", value: } ] } + } + }, + uriParams: { id: } + }, + metadata: { + correlationId: , + event: { + type: "bulk-processing", + action: "prepare-duplicate", + createdAt: , + state: { + code: "3106", + status: "error", + description: "Modified request - Individual transfer prepare duplicate" + }, + id: , + responseTo: + } + } + end note + PREP_HANDLER -> TOPIC_BULK_PROCESSING: Publish Processing (failure) event for Payer\nError codes: 2003 + activate TOPIC_BULK_PROCESSING + deactivate TOPIC_BULK_PROCESSING + end + else hasDuplicateId == TRUE && hasDuplicateHash == FALSE + break + note right of PREP_HANDLER #yellow + { + id: + from: , + to: , + type: "application/json", + content: { + headers: , + payload: { + errorInformation: { + errorCode: "3106", + errorDescription: "Modified request", + extensionList: { extension: [ { key: "_cause", value: } ] } + } + }, + uriParams: { id: } + }, + metadata: { + correlationId: , + event: { + type: "bulk-processing", + action: "prepare-duplicate", + createdAt: , + state: { + code: "3106", + status: "error", + description: "Modified request" + }, + id: , + responseTo: + } + } + end note + PREP_HANDLER -> TOPIC_BULK_PROCESSING: Publish Processing (failure) event for Payer\nError codes: 2003 + activate TOPIC_BULK_PROCESSING + deactivate TOPIC_BULK_PROCESSING + end + else hasDuplicateId == FALSE + note right of PREP_HANDLER #lightgrey + The validation of Payer, Payee can be skipped for individual transfers in Bulk + as they should've/would've been validated already in the bulk prepare part. + However, leaving it here for now, as in the future, this can be leveraged + when bulk transfers to multiple Payees are supported by the Specification. + end note + group Validate Payer + PREP_HANDLER -> PARTICIPANT_DAO: Request to retrieve Payer Participant details (if it exists) + activate PARTICIPANT_DAO + PARTICIPANT_DAO -> DB: Request Participant details + hnote over DB #lightyellow + participant + participantCurrency + end note + activate DB + PARTICIPANT_DAO <-- DB: Return Participant details if it exists + deactivate DB + PARTICIPANT_DAO --> PREP_HANDLER: Return Participant details if it exists + deactivate PARTICIPANT_DAO + PREP_HANDLER <-> PREP_HANDLER: Validate Payer\nError codes: 3202 + end + group Validate Payee + PREP_HANDLER -> PARTICIPANT_DAO: Request to retrieve Payee Participant details (if it exists) + activate PARTICIPANT_DAO + PARTICIPANT_DAO -> DB: Request Participant details + hnote over DB #lightyellow + participant + participantCurrency + end note + activate DB + PARTICIPANT_DAO <-- DB: Return Participant details if it exists + deactivate DB + PARTICIPANT_DAO --> PREP_HANDLER: Return Participant details if it exists + deactivate PARTICIPANT_DAO + PREP_HANDLER <-> PREP_HANDLER: Validate Payee\nError codes: 3203 + end + + alt Validate Prepare Transfer (success) + group Persist Transfer State (with transferState='RECEIVED-PREPARE') + PREP_HANDLER -> POS_DAO: Request to persist transfer\nError codes: 2003 + activate POS_DAO + POS_DAO -> DB: Persist transfer + hnote over DB #lightyellow + transfer + transferParticipant + transferStateChange + transferExtension + ilpPacket + end note + activate DB + deactivate DB + POS_DAO --> PREP_HANDLER: Return success + deactivate POS_DAO + end + else Validate Prepare Transfer (failure) + group Persist Transfer State (with transferState='INVALID') (Introducing a new status INVALID to mark these entries) + PREP_HANDLER -> POS_DAO: Request to persist transfer\n(when Payee/Payer/crypto-condition validation fails)\nError codes: 2003 + activate POS_DAO + POS_DAO -> DB: Persist transfer + hnote over DB #lightyellow + transfer + transferParticipant + transferStateChange + transferExtension + transferError + ilpPacket + end note + activate DB + deactivate DB + POS_DAO --> PREP_HANDLER: Return success + deactivate POS_DAO + end + end + end + end + + alt Validate Prepare Transfer (success) + note right of PREP_HANDLER #yellow + Message: + { + id: + from: , + to: , + type: "application/json", + content: { + headers: , + payload: + }, + metadata: { + event: { + id: , + responseTo: , + type: "position", + action: "bulk-prepare", + createdAt: , + state: { + status: "success", + code: 0, + description:"action successful" + } + } + } + } + end note + PREP_HANDLER -> TOPIC_TRANSFER_POSITION: Route & Publish Position event for Payer\nError codes: 2003 + else Validate Prepare Transfer (failure) + note right of PREP_HANDLER #yellow + Message: + { + id: + from: , + to: , + type: "application/json" + content: { + headers: , + payload: { + "errorInformation": { + "errorCode": + "errorDescription": "", + "extensionList": + } + }, + metadata: { + event: { + id: , + responseTo: , + type: "bulk-processing", + action: "prepare", + createdAt: , + state: { + status: 'error', + code: + description: + } + } + } + } + end note + PREP_HANDLER -> TOPIC_BULK_PROCESSING: Publish Prepare failure event to Bulk Processing Topic (for Payer) \nError codes: 2003 + end +end + +deactivate PREP_HANDLER +@enduml + diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.2.1-prepare-handler.svg b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.2.1-prepare-handler.svg new file mode 100644 index 000000000..af0e4479c --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.2.1-prepare-handler.svg @@ -0,0 +1,975 @@ + + + + + + + + + + + 1.2.1. Prepare Handler Consume individual transfers from Bulk + + + + Central Service + + + + + + + + + + + + + + + + + + + + topic- + + + transfer-prepare + + + + + topic- + + + transfer-prepare + + + Prepare Handler + + + + + Prepare Handler + + + + + + + topic- + + + transfer-position + + + + + topic- + + + transfer-position + + + + + topic- + + + event + + + + + topic- + + + event + + + + + topic- + + + notification + + + + + topic- + + + notification + + + + + topic- + + + bulk-processing + + + + + topic- + + + bulk-processing + + + Position DAO + + + + + Position DAO + + + + + Participant DAO + + + + + Participant DAO + + + + + Central Store + + + + + Central Store + + + + + + + + Prepare Handler Consume + + + + + 1 + + + Consume Prepare event message + + + + + break + + + + + Validate Event + + + + + 2 + + + Validate event - Rule: type == 'prepare' && action == 'bulk-prepare' + + + Error codes: + + + 2001 + + + + + Persist Event Information + + + + + 3 + + + Publish event information + + + + + ref + + + Event Handler Consume + + + + + Validate Prepare Transfer + + + + + 4 + + + Schema validation of the incoming message + + + + + 5 + + + Verify the message's signature (to be confirmed in future requirement) + + + + + Validate Duplicate Check + + + + + 6 + + + Request Duplicate Check + + + + + ref + + + Request Duplicate Check + + + + + 7 + + + Return { hasDuplicateId: Boolean, hasDuplicateHash: Boolean } + + + + + alt + + + [hasDuplicateId == TRUE && hasDuplicateHash == TRUE] + + + + + In the context of a bulk (when compared to regular transfers), duplicate + + + individual transfers are now considered and reported with Modified Request, + + + because they could have already been handled for another bulk. + + + + + break + + + + + { + + + id: <messageId> + + + from: <ledgerName>, + + + to: <payerFspName>, + + + type: "application/json", + + + content: { + + + headers: <transferHeaders>, + + + payload: { + + + errorInformation: { + + + errorCode: "3106", + + + errorDescription: "Modified request - Individual transfer prepare duplicate", + + + extensionList: { extension: [ { key: "_cause", value: <FSPIOPError> } ] } + + + } + + + }, + + + uriParams: { id: <transferId> } + + + }, + + + metadata: { + + + correlationId: <uuid>, + + + event: { + + + type: "bulk-processing", + + + action: "prepare-duplicate", + + + createdAt: <timestamp>, + + + state: { + + + code: "3106", + + + status: "error", + + + description: "Modified request - Individual transfer prepare duplicate" + + + }, + + + id: <uuid>, + + + responseTo: <uuid> + + + } + + + } + + + + + 8 + + + Publish Processing (failure) event for Payer + + + Error codes: + + + 2003 + + + + [hasDuplicateId == TRUE && hasDuplicateHash == FALSE] + + + + + break + + + + + { + + + id: <messageId> + + + from: <ledgerName>, + + + to: <payerFspName>, + + + type: "application/json", + + + content: { + + + headers: <transferHeaders>, + + + payload: { + + + errorInformation: { + + + errorCode: "3106", + + + errorDescription: "Modified request", + + + extensionList: { extension: [ { key: "_cause", value: <FSPIOPError> } ] } + + + } + + + }, + + + uriParams: { id: <transferId> } + + + }, + + + metadata: { + + + correlationId: <uuid>, + + + event: { + + + type: "bulk-processing", + + + action: "prepare-duplicate", + + + createdAt: <timestamp>, + + + state: { + + + code: "3106", + + + status: "error", + + + description: "Modified request" + + + }, + + + id: <uuid>, + + + responseTo: <uuid> + + + } + + + } + + + + + 9 + + + Publish Processing (failure) event for Payer + + + Error codes: + + + 2003 + + + + [hasDuplicateId == FALSE] + + + + + The validation of Payer, Payee can be skipped for individual transfers in Bulk + + + as they should've/would've been validated already in the bulk prepare part. + + + However, leaving it here for now, as in the future, this can be leveraged + + + when bulk transfers to multiple Payees are supported by the Specification. + + + + + Validate Payer + + + + + 10 + + + Request to retrieve Payer Participant details (if it exists) + + + + + 11 + + + Request Participant details + + + + participant + + + participantCurrency + + + + + 12 + + + Return Participant details if it exists + + + + + 13 + + + Return Participant details if it exists + + + + + 14 + + + Validate Payer + + + Error codes: + + + 3202 + + + + + Validate Payee + + + + + 15 + + + Request to retrieve Payee Participant details (if it exists) + + + + + 16 + + + Request Participant details + + + + participant + + + participantCurrency + + + + + 17 + + + Return Participant details if it exists + + + + + 18 + + + Return Participant details if it exists + + + + + 19 + + + Validate Payee + + + Error codes: + + + 3203 + + + + + alt + + + [Validate Prepare Transfer (success)] + + + + + Persist Transfer State (with transferState='RECEIVED-PREPARE') + + + + + 20 + + + Request to persist transfer + + + Error codes: + + + 2003 + + + + + 21 + + + Persist transfer + + + + transfer + + + transferParticipant + + + transferStateChange + + + transferExtension + + + ilpPacket + + + + + 22 + + + Return success + + + + [Validate Prepare Transfer (failure)] + + + + + Persist Transfer State (with transferState='INVALID') (Introducing a new status INVALID to mark these entries) + + + + + 23 + + + Request to persist transfer + + + (when Payee/Payer/crypto-condition validation fails) + + + Error codes: + + + 2003 + + + + + 24 + + + Persist transfer + + + + transfer + + + transferParticipant + + + transferStateChange + + + transferExtension + + + transferError + + + ilpPacket + + + + + 25 + + + Return success + + + + + alt + + + [Validate Prepare Transfer (success)] + + + + + Message: + + + { + + + id: <messageId> + + + from: <payerFspName>, + + + to: <payeeFspName>, + + + type: "application/json", + + + content: { + + + headers: <transferHeaders>, + + + payload: <transferMessage> + + + }, + + + metadata: { + + + event: { + + + id: <uuid>, + + + responseTo: <previous.uuid>, + + + type: "position", + + + action: "bulk-prepare", + + + createdAt: <timestamp>, + + + state: { + + + status: "success", + + + code: 0, + + + description:"action successful" + + + } + + + } + + + } + + + } + + + + + 26 + + + Route & Publish Position event for Payer + + + Error codes: + + + 2003 + + + + [Validate Prepare Transfer (failure)] + + + + + Message: + + + { + + + id: <messageId> + + + from: <ledgerName>, + + + to: <payerFspName>, + + + type: "application/json" + + + content: { + + + headers: <transferHeaders>, + + + payload: { + + + "errorInformation": { + + + "errorCode": <possible codes: [2003, 3100, 3105, 3106, 3202, 3203, 3300, 3301]> + + + "errorDescription": "<refer to section 35.1.3 for description>", + + + "extensionList": <transferMessage.extensionList> + + + } + + + }, + + + metadata: { + + + event: { + + + id: <uuid>, + + + responseTo: <previous.uuid>, + + + type: "bulk-processing", + + + action: "prepare", + + + createdAt: <timestamp>, + + + state: { + + + status: 'error', + + + code: <errorInformation.errorCode> + + + description: <errorInformation.errorDescription> + + + } + + + } + + + } + + + } + + + + + 27 + + + Publish Prepare failure event to Bulk Processing Topic (for Payer) + + + Error codes: + + + 2003 + + diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.3.0-position-overview.plantuml b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.3.0-position-overview.plantuml new file mode 100644 index 000000000..44d640284 --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.3.0-position-overview.plantuml @@ -0,0 +1,116 @@ +/'***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + * Georgi Georgiev + * Miguel de Barros + * Rajiv Mothilal + * Samuel Kummary + -------------- + ******'/ + +@startuml +' declate title +title 1.3.0. Position Handler Consume individual transfers from Bulk + +autonumber + +' Actor Keys: +' boundary - APIs/Interfaces, etc +' collections - Kafka Topics +' control - Kafka Consumers +' entity - Database Access Objects +' database - Database Persistance Store + +' declare actors +collections "topic-transfer-position" as TOPIC_TRANSFER_POSITION +control "Position Handler" as POS_HANDLER +collections "Event-Topic" as TOPIC_EVENTS +collections "Notification-Topic" as TOPIC_NOTIFICATIONS + + +box "Central Service" #LightYellow + participant TOPIC_TRANSFER_POSITION + participant POS_HANDLER + participant TOPIC_EVENTS + participant TOPIC_NOTIFICATIONS +end box + +' start flow +activate POS_HANDLER +group Position Handler Consume + alt Consume Prepare message for Payer + TOPIC_TRANSFER_POSITION <- POS_HANDLER: Consume Position event message for Payer + activate TOPIC_TRANSFER_POSITION + deactivate TOPIC_TRANSFER_POSITION + break + group Validate Event + POS_HANDLER <-> POS_HANDLER: Validate event - Rule: type == 'position' && action == 'bulk-prepare'\nError codes: 2001 + end + end + group Persist Event Information + ||| + POS_HANDLER -> TOPIC_EVENTS: Publish event information + ref over POS_HANDLER, TOPIC_EVENTS: Event Handler Consume\n + ||| + end + ||| + ref over POS_HANDLER: Prepare Position Handler Consume\n + POS_HANDLER -> TOPIC_NOTIFICATIONS: Produce message + else Consume Fulfil message for Payee + TOPIC_TRANSFER_POSITION <- POS_HANDLER: Consume Position event message for Payee + activate TOPIC_TRANSFER_POSITION + deactivate TOPIC_TRANSFER_POSITION + break + group Validate Event + POS_HANDLER <-> POS_HANDLER: Validate event - Rule: type == 'position' && action == 'bulk-commit'\nError codes: 2001 + end + end + group Persist Event Information + ||| + POS_HANDLER -> TOPIC_EVENTS: Publish event information + ref over POS_HANDLER, TOPIC_EVENTS : Event Handler Consume\n + ||| + end + ||| + ref over POS_HANDLER: Fulfil Position Handler Consume\n + POS_HANDLER -> TOPIC_NOTIFICATIONS: Produce message + else Consume Abort message + TOPIC_TRANSFER_POSITION <- POS_HANDLER: Consume Position event message + activate TOPIC_TRANSFER_POSITION + deactivate TOPIC_TRANSFER_POSITION + break + group Validate Event + POS_HANDLER <-> POS_HANDLER: Validate event - Rule: type == 'position' && action == 'timeout-reserved'\nError codes: 2001 + end + end + group Persist Event Information + ||| + POS_HANDLER -> TOPIC_EVENTS: Publish event information + ref over POS_HANDLER, TOPIC_EVENTS : Event Handler Consume\n + ||| + end + ||| + ref over POS_HANDLER: Abort Position Handler Consume\n + POS_HANDLER -> TOPIC_NOTIFICATIONS: Produce message + end + +end +deactivate POS_HANDLER +@enduml diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.3.0-position-overview.svg b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.3.0-position-overview.svg new file mode 100644 index 000000000..a6c1ce040 --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.3.0-position-overview.svg @@ -0,0 +1,298 @@ + + + + + + + + + + + 1.3.0. Position Handler Consume individual transfers from Bulk + + + + Central Service + + + + + + + + + + + + + + + + + topic-transfer-position + + + + + topic-transfer-position + + + Position Handler + + + + + Position Handler + + + + + + + Event-Topic + + + + + Event-Topic + + + + + Notification-Topic + + + + + Notification-Topic + + + + + + Position Handler Consume + + + + + alt + + + [Consume Prepare message for Payer] + + + + + 1 + + + Consume Position event message for Payer + + + + + break + + + + + Validate Event + + + + + 2 + + + Validate event - Rule: type == 'position' && action == 'bulk-prepare' + + + Error codes: + + + 2001 + + + + + Persist Event Information + + + + + 3 + + + Publish event information + + + + + ref + + + Event Handler Consume + + + + + ref + + + Prepare Position Handler Consume + + + + + 4 + + + Produce message + + + + [Consume Fulfil message for Payee] + + + + + 5 + + + Consume Position event message for Payee + + + + + break + + + + + Validate Event + + + + + 6 + + + Validate event - Rule: type == 'position' && action == 'bulk-commit' + + + Error codes: + + + 2001 + + + + + Persist Event Information + + + + + 7 + + + Publish event information + + + + + ref + + + Event Handler Consume + + + + + ref + + + Fulfil Position Handler Consume + + + + + 8 + + + Produce message + + + + [Consume Abort message] + + + + + 9 + + + Consume Position event message + + + + + break + + + + + Validate Event + + + + + 10 + + + Validate event - Rule: type == 'position' && action == 'timeout-reserved' + + + Error codes: + + + 2001 + + + + + Persist Event Information + + + + + 11 + + + Publish event information + + + + + ref + + + Event Handler Consume + + + + + ref + + + Abort Position Handler Consume + + + + + 12 + + + Produce message + + diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.3.1-position-prepare.plantuml b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.3.1-position-prepare.plantuml new file mode 100644 index 000000000..370eaf388 --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.3.1-position-prepare.plantuml @@ -0,0 +1,365 @@ +/'***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + * Georgi Georgiev + * Miguel de Barros + * Rajiv Mothilal + * Samuel Kummary + -------------- + ******'/ + +@startuml +' declate title +title 1.3.1. Prepare Position Handler Consume (single message, includes individual transfers from Bulk) + +autonumber + +' Actor Keys: +' boundary - APIs/Interfaces, etc +' collections - Kafka Topics +' control - Kafka Consumers +' entity - Database Access Objects +' database - Database Persistence Store + +' declare actors +control "Position Handler" as POS_HANDLER + +entity "Position\nManagement\nFacade" as POS_MGMT +collections "topic-\nnotification" as TOPIC_NOTIFICATIONS +collections "topic-\nbulk-processing" as TOPIC_BULK_PROCESSING +entity "Position DAO" as POS_DAO +database "Central Store" as DB + +box "Central Service" #LightYellow + participant POS_HANDLER + participant TOPIC_NOTIFICATIONS + participant TOPIC_BULK_PROCESSING + participant POS_MGMT + participant POS_DAO + participant DB +end box + +' start flow +activate POS_HANDLER +group Prepare Position Handler Consume + POS_HANDLER -> POS_MGMT: Request transfers to be processed + activate POS_MGMT + POS_MGMT -> POS_MGMT: Check 1st transfer to select the Participant and Currency + group DB TRANSACTION + ' DB Trans: This is where 1st DB Transaction would start in 2 DB transacation future model for horizontal scaling + POS_MGMT -> POS_MGMT: Loop through batch and build list of transferIds and calculate sumTransfersInBatch,\nchecking all in Batch are for the correct Paricipant and Currency\nError code: 2001, 3100 + POS_MGMT -> DB: Retrieve current state of all transfers in array from DB with select whereIn\n(FYI: The two DB transaction model needs to add a mini-state step here (RECEIVED_PREPARE => RECEIVDED_PREPARE_PROCESSING) so that the transfers are left alone if processing has started) + activate DB + hnote over DB #lightyellow + transferStateChange + transferParticipant + end note + DB --> POS_MGMT: Return current state of all selected transfers from DB + deactivate DB + POS_MGMT <-> POS_MGMT: Validate current state (transferStateChange.transferStateId == 'RECEIVED_PREPARE')\nError code: 2001 against failing transfers\nBatch is not rejected as a whole. + + note right of POS_MGMT #lightgray + List of transfers used during processing + **reservedTransfers** is list of transfers to be processed in the batch + **abortedTransfers** is the list of transfers in the incorrect state going into the process. Currently the transferStateChange is set to ABORTED - this should only be done if not already in a final state (idempotency) + **processedTransfers** is the list of transfers that have gone through the position management algorithm. Both successful and failed trasnfers appear here as the order and "running position" against each is necessary for reconciliation + + Scalar intermidate values used in the algorithm + **transferAmount** = payload.amount.amount + **sumTransfersInBatch** = SUM amount against each Transfer in batch + **currentPosition** = participantPosition.value + **reservedPosition** = participantPosition.{original}reservedValue + **effectivePosition** = currentPosition + reservedPosition + **heldPosition** = effectivePosition + sumTransfersInBatch + **availablePosition** = participantLimit(NetDebitCap) - effectivePosition + **sumReserved** = SUM of transfers that have met rule criteria and processed + end note + note over POS_MGMT,DB + Going to reserve the sum of the valid transfers in the batch against the Participants Positon in the Currency of this batch + and calculate the available position for the Participant to use + end note + POS_MGMT -> DB: Select effectivePosition FOR UPDATE from DB for Payer + activate DB + hnote over DB #lightyellow + participantPosition + end note + DB --> POS_MGMT: Return effectivePosition (currentPosition and reservedPosition) from DB for Payer + deactivate DB + POS_MGMT -> POS_MGMT: Increment reservedValue to heldPosition\n(reservedValue = reservedPosition + sumTransfersInBatch) + POS_MGMT -> DB: Persist reservedValue + activate DB + hnote over DB #lightyellow + UPDATE **participantPosition** + SET reservedValue += sumTransfersInBatch + end note + deactivate DB + ' DB Trans: This is where 1st DB Transaction would end in 2 DB transacation future model for horizontal scaling + + + POS_MGMT -> DB: Request position limits for Payer Participant + activate DB + hnote over DB #lightyellow + FROM **participantLimit** + WHERE participantLimit.limitTypeId = 'NET-DEBIT-CAP' + AND participantLimit.participantId = payload.payerFsp + AND participantLimit.currencyId = payload.amount.currency + end note + DB --> POS_MGMT: Return position limits + deactivate DB + POS_MGMT <-> POS_MGMT: **availablePosition** = participantLimit(netDebitCap) - effectivePosition (same as = netDebitCap - currentPosition - reservedPosition) + note over POS_MGMT,DB + For each transfer in the batch, validate the availablility of position to meet the transfer amount + this will be as per the position algorithm documented below + end note + POS_MGMT <-> POS_MGMT: Validate availablePosition for each tranfser (see algorithm below)\nError code: 4001 + note right of POS_MGMT #lightgray + 01: sumReserved = 0 // Record the sum of the transfers we allow to progress to RESERVED + 02: sumProcessed =0 // Record the sum of the transfers already processed in this batch + 03: processedTransfers = {} // The list of processed transfers - so that we can store the additional information around the decision. Most importantly the "running" position + 04: foreach transfer in reservedTransfers + 05: sumProcessed += transfer.amount // the total processed so far **(NEED TO UPDATE IN CODE)** + 06: if availablePosition >= transfer.amount + 07: transfer.state = "RESERVED" + 08: availablePosition -= preparedTransfer.amount + 09: sumRESERVED += preparedTransfer.amount + 10: else + 11: preparedTransfer.state = "ABORTED" + 12: preparedTransfer.reason = "Net Debit Cap exceeded by this request at this time, please try again later" + 13: end if + 14: runningPosition = currentPosition + sumReserved // the initial value of the Participants position plus the total value that has been accepted in the batch so far + 15: runningReservedValue = sumTransfersInBatch - sumProcessed + reservedPosition **(NEED TO UPDATE IN CODE)** // the running down of the total reserved value at the begining of the batch. + 16: Add transfer to the processedTransfer list recording the transfer state and running position and reserved values { transferState, transfer, rawMessage, transferAmount, runningPosition, runningReservedValue } + 16: end foreach + end note + note over POS_MGMT,DB + Once the outcome for all transfers is known,update the Participant's position and remove the reserved amount associated with the batch + (If there are any alarm limits, process those returning limits in which the threshold has been breached) + Do a bulk insert of the trasnferStateChanges associated with processing, using the result to complete the participantPositionChange and bulk insert of these to persist the running position + end note + POS_MGMT->POS_MGMT: Assess any limit thresholds on the final position\nadding to alarm list if triggered + + ' DB Trans: This is where 2nd DB Transaction would start in 2 DB transacation future model for horizontal scaling + POS_MGMT->DB: Persist latest position **value** and **reservedValue** to DB for Payer + hnote over DB #lightyellow + UPDATE **participantPosition** + SET value += sumRESERVED, + reservedValue -= sumTransfersInBatch + end note + activate DB + deactivate DB + + POS_MGMT -> DB: Bulk persist transferStateChange for all processedTransfers + hnote over DB #lightyellow + batch INSERT **transferStateChange** + select for update from transfer table where transferId in ([transferBatch.transferId,...]) + build list of transferStateChanges from transferBatch + + end note + activate DB + deactivate DB + + POS_MGMT->POS_MGMT: Populate batchParticipantPositionChange from the resultant transferStateChange and the earlier processedTransfer list + + note right of POS_MGMT #lightgray + Effectively: + SET transferStateChangeId = processedTransfer.transferStateChangeId, + participantPositionId = preparedTransfer.participantPositionId, + value = preparedTransfer.positionValue, + reservedValue = preparedTransfer.positionReservedValue + end note + POS_MGMT -> DB: Bulk persist the participant position change for all processedTransfers + hnote over DB #lightyellow + batch INSERT **participantPositionChange** + end note + activate DB + deactivate DB + ' DB Trans: This is where 2nd DB Transaction would end in 2 DB transacation future model for horizontal scaling + end + POS_MGMT --> POS_HANDLER: Return a map of transferIds and their transferStateChanges + deactivate POS_MGMT + alt Calculate & Validate Latest Position Prepare (success) + POS_HANDLER -> POS_HANDLER: Notifications for Position Validation Success \nReference: Position Validation Success case (Prepare) + else Calculate & Validate Latest Position Prepare (failure) + note right of POS_HANDLER #red: Validation failure! + + group Persist Transfer State (with transferState='ABORTED' on position check fail) + POS_HANDLER -> POS_DAO: Request to persist transfer\nError code: 2003 + activate POS_DAO + note right of POS_HANDLER #lightgray + transferStateChange.state = "ABORTED", + transferStateChange.reason = "Net Debit Cap exceeded by this request at this time, please try again later" + end note + POS_DAO -> DB: Persist transfer state + hnote over DB #lightyellow + transferStateChange + end note + activate DB + deactivate DB + POS_DAO --> POS_HANDLER: Return success + deactivate POS_DAO + end + POS_HANDLER -> POS_HANDLER: Notifications for failures\nReference: Failure in Position Validation (Prepare) + end +end + + +group Reference: Failure in Position Validation (Prepare) + alt If action == 'bulk-prepare' + note right of POS_HANDLER #yellow + Message: + { + id: "" + from: , + to: , + type: "application/json" + content: { + headers: , + payload: { + "errorInformation": { + "errorCode": 4001, + "errorDescription": "Payer FSP insufficient liquidity", + "extensionList": + } + }, + metadata: { + event: { + id: , + responseTo: , + type: "bulk-processing", + action: "bulk-prepare", + createdAt: , + state: { + status: "error", + code: + description: + } + } + } + } + end note + POS_HANDLER -> TOPIC_BULK_PROCESSING: Publish Position failure event (in Prepare) to Bulk Processing Topic (for Payer) \nError codes: 2003 + activate TOPIC_BULK_PROCESSING + deactivate TOPIC_BULK_PROCESSING + else If action == 'prepare' + note right of POS_HANDLER #yellow + Message: + { + id: "" + from: , + to: , + type: "application/json" + content: { + headers: , + payload: { + "errorInformation": { + "errorCode": 4001, + "errorDescription": "Payer FSP insufficient liquidity", + "extensionList": + } + }, + metadata: { + event: { + id: , + responseTo: , + type: "notification", + action: "position", + createdAt: , + state: { + status: "error", + code: + description: + } + } + } + } + end note + POS_HANDLER -> TOPIC_NOTIFICATIONS: Publish Notification (failure) event for Payer\nError code: 2003 + activate TOPIC_NOTIFICATIONS + deactivate TOPIC_NOTIFICATIONS + end + +end + +group Reference: Position Validation Success case (Prepare) + alt If action == 'bulk-prepare' + note right of POS_HANDLER #yellow + Message: + { + id: "" + from: , + to: , + type: "application/json" + content: { + headers: , + payload: + }, + metadata: { + event: { + id: , + responseTo: , + type: "bulk-processing", + action: "bulk-prepare", + createdAt: , + state: { + status: "success", + code: 0 + } + } + } + } + end note + POS_HANDLER -> TOPIC_BULK_PROCESSING: Publish Position Success event (in Prepare) to Bulk Processing Topic\nError codes: 2003 + activate TOPIC_BULK_PROCESSING + deactivate TOPIC_BULK_PROCESSING + else If action == 'prepare' + note right of POS_HANDLER #yellow + Message: + { + id: "" + from: , + to: , + type: "application/json" + content: { + headers: , + payload: + }, + metadata: { + event: { + id: , + responseTo: , + type: "notification", + action: "abort", + createdAt: , + state: { + status: "success", + code: 0 + } + } + } + } + end note + POS_HANDLER -> TOPIC_NOTIFICATIONS: Publish Notification event\nError code: 2003 + activate TOPIC_NOTIFICATIONS + deactivate TOPIC_NOTIFICATIONS + end +end + +deactivate POS_HANDLER +@enduml diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.3.1-position-prepare.svg b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.3.1-position-prepare.svg new file mode 100644 index 000000000..1403dc6ab --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.3.1-position-prepare.svg @@ -0,0 +1,1092 @@ + + + + + + + + + + + 1.3.1. Prepare Position Handler Consume (single message, includes individual transfers from Bulk) + + + + Central Service + + + + + + + + + + + + + + Position Handler + + + + + Position Handler + + + + + + + topic- + + + notification + + + + + topic- + + + notification + + + + + topic- + + + bulk-processing + + + + + topic- + + + bulk-processing + + + Position + + + Management + + + Facade + + + + + Position + + + Management + + + Facade + + + + + Position DAO + + + + + Position DAO + + + + + Central Store + + + + + Central Store + + + + + + + + Prepare Position Handler Consume + + + + + 1 + + + Request transfers to be processed + + + + + 2 + + + Check 1st transfer to select the Participant and Currency + + + + + DB TRANSACTION + + + + + 3 + + + Loop through batch and build list of transferIds and calculate sumTransfersInBatch, + + + checking all in Batch are for the correct Paricipant and Currency + + + Error code: + + + 2001, 3100 + + + + + 4 + + + Retrieve current state of all transfers in array from DB with select whereIn + + + (FYI: The two DB transaction model needs to add a mini-state step here (RECEIVED_PREPARE => RECEIVDED_PREPARE_PROCESSING) so that the transfers are left alone if processing has started) + + + + transferStateChange + + + transferParticipant + + + + + 5 + + + Return current state of all selected transfers from DB + + + + + 6 + + + Validate current state (transferStateChange.transferStateId == 'RECEIVED_PREPARE') + + + Error code: + + + 2001 + + + against failing transfers + + + Batch is not rejected as a whole. + + + + + List of transfers used during processing + + + reservedTransfers + + + is list of transfers to be processed in the batch + + + abortedTransfers + + + is the list of transfers in the incorrect state going into the process. Currently the transferStateChange is set to ABORTED - this should only be done if not already in a final state (idempotency) + + + processedTransfers + + + is the list of transfers that have gone through the position management algorithm. Both successful and failed trasnfers appear here as the order and "running position" against each is necessary for reconciliation + + + Scalar intermidate values used in the algorithm + + + transferAmount + + + = payload.amount.amount + + + sumTransfersInBatch + + + = SUM amount against each Transfer in batch + + + currentPosition + + + = participantPosition.value + + + reservedPosition + + + = participantPosition.{original}reservedValue + + + effectivePosition + + + = currentPosition + reservedPosition + + + heldPosition + + + = effectivePosition + sumTransfersInBatch + + + availablePosition + + + = participantLimit(NetDebitCap) - effectivePosition + + + sumReserved + + + = SUM of transfers that have met rule criteria and processed + + + + + Going to reserve the sum of the valid transfers in the batch against the Participants Positon in the Currency of this batch + + + and calculate the available position for the Participant to use + + + + + 7 + + + Select effectivePosition FOR UPDATE from DB for Payer + + + + participantPosition + + + + + 8 + + + Return effectivePosition (currentPosition and reservedPosition) from DB for Payer + + + + + 9 + + + Increment reservedValue to heldPosition + + + (reservedValue = reservedPosition + sumTransfersInBatch) + + + + + 10 + + + Persist reservedValue + + + + UPDATE + + + participantPosition + + + SET reservedValue += sumTransfersInBatch + + + + + 11 + + + Request position limits for Payer Participant + + + + FROM + + + participantLimit + + + WHERE participantLimit.limitTypeId = 'NET-DEBIT-CAP' + + + AND participantLimit.participantId = payload.payerFsp + + + AND participantLimit.currencyId = payload.amount.currency + + + + + 12 + + + Return position limits + + + + + 13 + + + availablePosition + + + = participantLimit(netDebitCap) - effectivePosition (same as = netDebitCap - currentPosition - reservedPosition) + + + + + For each transfer in the batch, validate the availablility of position to meet the transfer amount + + + this will be as per the position algorithm documented below + + + + + 14 + + + Validate availablePosition for each tranfser (see algorithm below) + + + Error code: + + + 4001 + + + + + 01: sumReserved = 0 // Record the sum of the transfers we allow to progress to RESERVED + + + 02: sumProcessed =0 // Record the sum of the transfers already processed in this batch + + + 03: processedTransfers = {} // The list of processed transfers - so that we can store the additional information around the decision. Most importantly the "running" position + + + 04: foreach transfer in reservedTransfers + + + 05: sumProcessed += transfer.amount // the total processed so far + + + (NEED TO UPDATE IN CODE) + + + 06: if availablePosition >= transfer.amount + + + 07: transfer.state = "RESERVED" + + + 08: availablePosition -= preparedTransfer.amount + + + 09: sumRESERVED += preparedTransfer.amount + + + 10: else + + + 11: preparedTransfer.state = "ABORTED" + + + 12: preparedTransfer.reason = "Net Debit Cap exceeded by this request at this time, please try again later" + + + 13: end if + + + 14: runningPosition = currentPosition + sumReserved // the initial value of the Participants position plus the total value that has been accepted in the batch so far + + + 15: runningReservedValue = sumTransfersInBatch - sumProcessed + reservedPosition + + + (NEED TO UPDATE IN CODE) + + + // the running down of the total reserved value at the begining of the batch. + + + 16: Add transfer to the processedTransfer list recording the transfer state and running position and reserved values { transferState, transfer, rawMessage, transferAmount, runningPosition, runningReservedValue } + + + 16: end foreach + + + + + Once the outcome for all transfers is known,update the Participant's position and remove the reserved amount associated with the batch + + + (If there are any alarm limits, process those returning limits in which the threshold has been breached) + + + Do a bulk insert of the trasnferStateChanges associated with processing, using the result to complete the participantPositionChange and bulk insert of these to persist the running position + + + + + 15 + + + Assess any limit thresholds on the final position + + + adding to alarm list if triggered + + + + + 16 + + + Persist latest position + + + value + + + and + + + reservedValue + + + to DB for Payer + + + + UPDATE + + + participantPosition + + + SET value += sumRESERVED, + + + reservedValue -= sumTransfersInBatch + + + + + 17 + + + Bulk persist transferStateChange for all processedTransfers + + + + batch INSERT + + + transferStateChange + + + select for update from transfer table where transferId in ([transferBatch.transferId,...]) + + + build list of transferStateChanges from transferBatch + + + + + 18 + + + Populate batchParticipantPositionChange from the resultant transferStateChange and the earlier processedTransfer list + + + + + Effectively: + + + SET transferStateChangeId = processedTransfer.transferStateChangeId, + + + participantPositionId = preparedTransfer.participantPositionId, + + + value = preparedTransfer.positionValue, + + + reservedValue = preparedTransfer.positionReservedValue + + + + + 19 + + + Bulk persist the participant position change for all processedTransfers + + + + batch INSERT + + + participantPositionChange + + + + + 20 + + + Return a map of transferIds and their transferStateChanges + + + + + alt + + + [Calculate & Validate Latest Position Prepare (success)] + + + + + 21 + + + Notifications for Position Validation Success + + + Reference: Position Validation Success case (Prepare) + + + + [Calculate & Validate Latest Position Prepare (failure)] + + + + + Validation failure! + + + + + Persist Transfer State (with transferState='ABORTED' on position check fail) + + + + + 22 + + + Request to persist transfer + + + Error code: + + + 2003 + + + + + transferStateChange.state = "ABORTED", + + + transferStateChange.reason = "Net Debit Cap exceeded by this request at this time, please try again later" + + + + + 23 + + + Persist transfer state + + + + transferStateChange + + + + + 24 + + + Return success + + + + + 25 + + + Notifications for failures + + + Reference: Failure in Position Validation (Prepare) + + + + + Reference: Failure in Position Validation (Prepare) + + + + + alt + + + [If action == 'bulk-prepare'] + + + + + Message: + + + { + + + id: "<messageId>" + + + from: <ledgerName>, + + + to: <transferMessage.payerFsp>, + + + type: "application/json" + + + content: { + + + headers: <transferHeaders>, + + + payload: { + + + "errorInformation": { + + + "errorCode": 4001, + + + "errorDescription": "Payer FSP insufficient liquidity", + + + "extensionList": <transferMessage.extensionList> + + + } + + + }, + + + metadata: { + + + event: { + + + id: <uuid>, + + + responseTo: <previous.uuid>, + + + type: "bulk-processing", + + + action: "bulk-prepare", + + + createdAt: <timestamp>, + + + state: { + + + status: "error", + + + code: <errorInformation.errorCode> + + + description: <errorInformation.errorDescription> + + + } + + + } + + + } + + + } + + + + + 26 + + + Publish Position failure event (in Prepare) to Bulk Processing Topic (for Payer) + + + Error codes: + + + 2003 + + + + [If action == 'prepare'] + + + + + Message: + + + { + + + id: "<messageId>" + + + from: <ledgerName>, + + + to: <transferMessage.payerFsp>, + + + type: "application/json" + + + content: { + + + headers: <transferHeaders>, + + + payload: { + + + "errorInformation": { + + + "errorCode": 4001, + + + "errorDescription": "Payer FSP insufficient liquidity", + + + "extensionList": <transferMessage.extensionList> + + + } + + + }, + + + metadata: { + + + event: { + + + id: <uuid>, + + + responseTo: <previous.uuid>, + + + type: "notification", + + + action: "position", + + + createdAt: <timestamp>, + + + state: { + + + status: "error", + + + code: <errorInformation.errorCode> + + + description: <errorInformation.errorDescription> + + + } + + + } + + + } + + + } + + + + + 27 + + + Publish Notification (failure) event for Payer + + + Error code: + + + 2003 + + + + + Reference: Position Validation Success case (Prepare) + + + + + alt + + + [If action == 'bulk-prepare'] + + + + + Message: + + + { + + + id: "<messageId>" + + + from: <transferMessage.payerFsp>, + + + to: <transferMessage.payeeFsp>, + + + type: "application/json" + + + content: { + + + headers: <transferHeaders>, + + + payload: <transferMessage> + + + }, + + + metadata: { + + + event: { + + + id: <uuid>, + + + responseTo: <previous.uuid>, + + + type: "bulk-processing", + + + action: "bulk-prepare", + + + createdAt: <timestamp>, + + + state: { + + + status: "success", + + + code: 0 + + + } + + + } + + + } + + + } + + + + + 28 + + + Publish Position Success event (in Prepare) to Bulk Processing Topic + + + Error codes: + + + 2003 + + + + [If action == 'prepare'] + + + + + Message: + + + { + + + id: "<messageId>" + + + from: <transferMessage.payerFsp>, + + + to: <transferMessage.payeeFsp>, + + + type: "application/json" + + + content: { + + + headers: <transferHeaders>, + + + payload: <transferMessage> + + + }, + + + metadata: { + + + event: { + + + id: <uuid>, + + + responseTo: <previous.uuid>, + + + type: "notification", + + + action: "abort", + + + createdAt: <timestamp>, + + + state: { + + + status: "success", + + + code: 0 + + + } + + + } + + + } + + + } + + + + + 29 + + + Publish Notification event + + + Error code: + + + 2003 + + diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.4.1-bulk-processing-handler.plantuml b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.4.1-bulk-processing-handler.plantuml new file mode 100644 index 000000000..35108f2bf --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.4.1-bulk-processing-handler.plantuml @@ -0,0 +1,332 @@ +/'***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + * Georgi Georgiev + -------------- + ******'/ + +@startuml +' declare title +title 1.4.0. Bulk Processing Handler Consume + +autonumber + +/'***** + Diagram notes + -------------- + RECEIVED/RECEIVED + from: prepare-handler , action: prepare-duplicate/success, result: PENDING_PREPARE/ACCEPTED & RECEIVED_DUPLICATE + from: prepare-handler , action: prepare-duplicate/error , result: PENDING_PREPARE/ACCEPTED & RECEIVED_DUPLICATE + from: prepare-handler , action: prepare/error , result: PENDING_PREPARE/ACCEPTED & RECEIVED_INVALID + from: position-handler, action: prepare/error , result: PENDING_PREPARE/ACCEPTED & RECEIVED_INVALID + from: position-handler, action: prepare/success , result: PENDING_PREPARE/ACCEPTED & ACCEPTED + from: timeout-handler , action: timeout-received/error , result: unchanged/COMPLETED & EXPIRED + -------------- + ACCEPTED/ACCEPTED + from: position-handler, action: timeout-reserved/error , result: unchanged/COMPLETED & EXPIRED + -------------- + PROCESSING/ACCEPTED + from: fulfil-handler , action: fulfil-duplicate/success , result: PENDING_FULFIL/COMPLETED & FULFIL_DUPLICATE + from: fulfil-handler , action: fulfil-duplicate/error , result: PENDING_FULFIL/COMPLETED & FULFIL_DUPLICATE + from: position-handler, action: commit/success , result: PENDING_FULFIL/COMPLETED & COMPLETED + from: position-handler, action: reject/success , result: PENDING_FULFIL/COMPLETED & REJECTED + from: position-handler, action: abort/error , result: PENDING_FULFIL/COMPLETED & FULFIL_INVALID + from: fulfil-handler , action: commit/error , result: PENDING_FULFIL/COMPLETED & FULFIL_INVALID + from: position-handler, action: timeout-reserved/error , result: unchanged/COMPLETED & EXPIRED + -------------- + COMPLETED/EXPIRED + -------------- + ******'/ + +' Actor Keys: +' boundary - APIs/Interfaces, etc +' collections - Kafka Topics +' control - Kafka Consumers +' entity - Database Access Objects +' database - Database Persistance Store + +' declare actors +collections "topic-bulk-\nprocessing" as TOPIC_BULK_PROCESSING +control "Bulk Processing\nHandler" as BULK_PROC_HANDLER +collections "topic-event" as TOPIC_EVENTS +collections "mojaloop-\nobject-store\n(**MLOS**)" as OBJECT_STORE +collections "topic-notification" as TOPIC_NOTIFICATION +entity "Bulk DAO" as BULK_DAO +database "Central Store" as DB + +box "Central Service" #LightYellow + participant TOPIC_BULK_PROCESSING + participant BULK_PROC_HANDLER + participant TOPIC_EVENTS + participant OBJECT_STORE + participant TOPIC_NOTIFICATION + participant BULK_DAO + participant DB +end box + +' start flow +activate BULK_PROC_HANDLER +group Bulk Processing Handler Consume + TOPIC_BULK_PROCESSING <- BULK_PROC_HANDLER: Consume message + activate TOPIC_BULK_PROCESSING + deactivate TOPIC_BULK_PROCESSING + + break + group Validate Event + BULK_PROC_HANDLER <-> BULK_PROC_HANDLER: Validate event - Rule:\ntype == 'bulk-processing' && action IN\n['prepare-duplicate', 'bulk-prepare',\n'bulk-timeout-received', 'fulfil-duplicate',\n'bulk-commit', 'bulk-timeout-reserved']\nError codes: 2001 + end + end + + group Persist Event Information + ||| + BULK_PROC_HANDLER -> TOPIC_EVENTS: Publish event information + ref over BULK_PROC_HANDLER, TOPIC_EVENTS: Event Handler Consume\n + ||| + end + + group Process Message + BULK_PROC_HANDLER -> BULK_DAO: Retrieve current state of Bulk Transfer + activate BULK_DAO + BULK_DAO -> DB: Retrieve current state of Bulk Transfer + activate DB + hnote over DB #lightyellow + bulkTransfer + bulkTransferStateChange + end note + BULK_DAO <-- DB: Return **bulkTransferInfo** + deactivate DB + BULK_PROC_HANDLER <-- BULK_DAO: Return **bulkTransferInfo** + deactivate BULK_DAO + + group Validate Bulk Transfer State + note right of BULK_PROC_HANDLER #lightgrey + **Initialize variables**: + let criteriaState + let incompleteBulkState + let completedBulkState + let bulkTransferState + let processingState + let errorCode, errorMessage + let produceNotification = false + end note + alt bulkTransferInfo.bulkTransferState IN ['RECEIVED', 'PENDING_PREPARE'] + note right of BULK_PROC_HANDLER #lightgrey + criteriaState = 'RECEIVED' + incompleteBulkState = 'PENDING_PREPARE' + completedBulkState = 'ACCEPTED' + end note + alt action == 'prepare-duplicate' AND state.status == 'error' + note right of BULK_PROC_HANDLER #lightgrey + processingState = 'RECEIVED_DUPLICATE' + errorCode = payload.errorInformation.errorCode + errorDescription = payload.errorInformation.errorDescription + end note + else action == 'bulk-prepare' AND state.status == 'error' + note right of BULK_PROC_HANDLER #lightgrey + processingState = 'RECEIVED_INVALID' + errorCode = payload.errorInformation.errorCode + errorDescription = payload.errorInformation.errorDescription + end note + else action == 'bulk-prepare' AND state.status == 'success' + note right of BULK_PROC_HANDLER #lightgrey + processingState = 'ACCEPTED' + end note + else action IN ['bulk-timeout-received', 'bulk-timeout-reserved'] + note right of BULK_PROC_HANDLER #lightgrey + incompleteBulkState = 'EXPIRING' + completedBulkState = 'COMPLETED' + processingState = 'EXPIRED' + errorCode = payload.errorInformation.errorCode + errorDescription = payload.errorInformation.errorDescription + end note + else all other actions + note right of BULK_PROC_HANDLER #lightgrey + throw ErrorHandler.Factory.createFSPIOPError(INTERNAL_SERVER_ERROR) + end note + end + else bulkTransferInfo.bulkTransferState IN ['ACCEPTED'] + alt action == 'bulk-timeout-reserved' + note right of BULK_PROC_HANDLER #lightgrey + criteriaState = 'ACCEPTED' + incompleteBulkState = 'EXPIRING' + completedBulkState = 'COMPLETED' + processingState = 'EXPIRED' + end note + else all other actions + note right of BULK_PROC_HANDLER #lightgrey + throw ErrorHandler.Factory.createFSPIOPError(INTERNAL_SERVER_ERROR) + end note + end + else bulkTransferInfo.bulkTransferState IN ['PROCESSING', 'PENDING_FULFIL', 'EXPIRING'] + note right of BULK_PROC_HANDLER #lightgrey + criteriaState = 'PROCESSING' + incompleteBulkState = 'PENDING_FULFIL' + completedBulkState = 'COMPLETED' + end note + alt action == 'fulfil-duplicate' + note right of BULK_PROC_HANDLER #lightgrey + processingState = 'FULFIL_DUPLICATE' + end note + else action == 'bulk-commit' AND state.status == 'success' + note right of BULK_PROC_HANDLER #lightgrey + processingState = 'COMPLETED' + end note + else action == 'reject' AND state.status == 'success' + note right of BULK_PROC_HANDLER #lightgrey + processingState = 'REJECTED' + end note + else action IN ['commit', 'abort'] AND state.status == 'error' + note right of BULK_PROC_HANDLER #lightgrey + processingState = 'FULFIL_INVALID' + end note + else action == 'bulk-timeout-reserved' + note right of BULK_PROC_HANDLER #lightgrey + incompleteBulkState = 'EXPIRING' + completedBulkState = 'COMPLETED' + processingState = 'EXPIRED' + errorCode = payload.errorInformation.errorCode + errorDescription = payload.errorInformation.errorDescription + end note + else all other actions + note right of BULK_PROC_HANDLER #lightgrey + throw ErrorHandler.Factory.createFSPIOPError(INTERNAL_SERVER_ERROR) + end note + end + else all other ['PENDING_INVALID', 'COMPLETED', 'REJECTED', 'INVALID'] + note right of BULK_PROC_HANDLER #lightgrey + throw ErrorHandler.Factory.createFSPIOPError(INTERNAL_SERVER_ERROR) + end note + end + end + + BULK_PROC_HANDLER -> BULK_DAO: Persist individual transfer processing state + activate BULK_DAO + BULK_DAO -> DB: Persist individual transfer processing state\n-- store errorCode/errorMessage when\nstate.status == 'error' + activate DB + hnote over DB #lightyellow + bulkTransferAssociation + end note + deactivate DB + BULK_PROC_HANDLER <-- BULK_DAO: Return success + deactivate BULK_DAO + + BULK_PROC_HANDLER -> BULK_DAO: Check previously defined completion criteria + activate BULK_DAO + BULK_DAO -> DB: Select EXISTS (LIMIT 1) in criteriaState + activate DB + hnote over DB #lightyellow + bulkTransferAssociation + end note + BULK_DAO <-- DB: Return **existingIndividualTransfer** + deactivate DB + BULK_PROC_HANDLER <-- BULK_DAO: Return **existingIndividualTransfer** + deactivate BULK_DAO + + alt individual transfer exists + note right of BULK_PROC_HANDLER #lightgrey + bulkTransferState = incompleteBulkState + end note + else no transfer in criteriaState exists + note right of BULK_PROC_HANDLER #lightgrey + bulkTransferState = completedBulkState + produceNotification = true + end note + end + + BULK_PROC_HANDLER -> BULK_DAO: Persist bulkTransferState from previous step + activate BULK_DAO + BULK_DAO -> DB: Persist bulkTransferState + activate DB + deactivate DB + hnote over DB #lightyellow + bulkTransferStateChange + end note + BULK_PROC_HANDLER <-- BULK_DAO: Return success + deactivate BULK_DAO + + + alt produceNotification == true + BULK_PROC_HANDLER -> BULK_DAO: Request to retrieve all bulk transfer and individual transfer results + activate BULK_DAO + BULK_DAO -> DB: Get bulkTransferResult + activate DB + hnote over DB #lightyellow + bulkTransfer + bulkTransferStateChange + bulkTransferAssociation + end note + BULK_DAO <-- DB: Return **bulkTransferResult** + deactivate DB + BULK_PROC_HANDLER <-- BULK_DAO: Return **bulkTransferResult** + deactivate BULK_DAO + + group Send Bulk Notification(s) + note right of BULK_PROC_HANDLER #lightgrey + Depending on the action decide where to + send notification: payer, payee OR both + end note + + BULK_PROC_HANDLER -> OBJECT_STORE: Generate & Persist bulk message to object store:\n**MLOS.bulkTransferResults** by destination + activate OBJECT_STORE + OBJECT_STORE --> BULK_PROC_HANDLER: Return reference to the stored object(s)\n**MLOS.bulkTransferResults.messageId** + deactivate OBJECT_STORE + note right of BULK_PROC_HANDLER #yellow + Message: + { + id: + from: , + to: , + type: "application/json" + content: { + headers: , + payload: { + bulkTransferId: , + bulkTransferState: + } + }, + metadata: { + event: { + id: , + responseTo: , + type: "notification", + action: "bulk-[prepare | commit | abort | processing]", + createdAt: , + state: { + status: state.status, + code: state.code + } + } + } + } + end note + + BULK_PROC_HANDLER -> TOPIC_NOTIFICATION: Publish Notification event for Payer/Payee\nError codes: 2003 + activate TOPIC_NOTIFICATION + deactivate TOPIC_NOTIFICATION + end + else produceNotification == false + note right of BULK_PROC_HANDLER #lightgrey + Do nothing (awaitAllTransfers) + end note + end + end +end +deactivate BULK_PROC_HANDLER +@enduml diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.4.1-bulk-processing-handler.svg b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.4.1-bulk-processing-handler.svg new file mode 100644 index 000000000..fa4186d7d --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-1.4.1-bulk-processing-handler.svg @@ -0,0 +1,860 @@ + + + + + + + + + + + 1.4.0. Bulk Processing Handler Consume + + + + Central Service + + + + + + + + + + + + + + + + + + + + + + + + + topic-bulk- + + + processing + + + + + topic-bulk- + + + processing + + + Bulk Processing + + + Handler + + + + + Bulk Processing + + + Handler + + + + + + + topic-event + + + + + topic-event + + + + + mojaloop- + + + object-store + + + ( + + + MLOS + + + ) + + + + + mojaloop- + + + object-store + + + ( + + + MLOS + + + ) + + + + + topic-notification + + + + + topic-notification + + + Bulk DAO + + + + + Bulk DAO + + + + + Central Store + + + + + Central Store + + + + + + + + Bulk Processing Handler Consume + + + + + 1 + + + Consume message + + + + + break + + + + + Validate Event + + + + + 2 + + + Validate event - Rule: + + + type == 'bulk-processing' && action IN + + + ['prepare-duplicate', 'bulk-prepare', + + + 'bulk-timeout-received', 'fulfil-duplicate', + + + 'bulk-commit', 'bulk-timeout-reserved'] + + + Error codes: + + + 2001 + + + + + Persist Event Information + + + + + 3 + + + Publish event information + + + + + ref + + + Event Handler Consume + + + + + Process Message + + + + + 4 + + + Retrieve current state of Bulk Transfer + + + + + 5 + + + Retrieve current state of Bulk Transfer + + + + bulkTransfer + + + bulkTransferStateChange + + + + + 6 + + + Return + + + bulkTransferInfo + + + + + 7 + + + Return + + + bulkTransferInfo + + + + + Validate Bulk Transfer State + + + + + Initialize variables + + + : + + + let criteriaState + + + let incompleteBulkState + + + let completedBulkState + + + let bulkTransferState + + + let processingState + + + let errorCode, errorMessage + + + let produceNotification = false + + + + + alt + + + [bulkTransferInfo.bulkTransferState IN ['RECEIVED', 'PENDING_PREPARE']] + + + + + criteriaState = 'RECEIVED' + + + incompleteBulkState = 'PENDING_PREPARE' + + + completedBulkState = 'ACCEPTED' + + + + + alt + + + [action == 'prepare-duplicate' AND state.status == 'error'] + + + + + processingState = 'RECEIVED_DUPLICATE' + + + errorCode = payload.errorInformation.errorCode + + + errorDescription = payload.errorInformation.errorDescription + + + + [action == 'bulk-prepare' AND state.status == 'error'] + + + + + processingState = 'RECEIVED_INVALID' + + + errorCode = payload.errorInformation.errorCode + + + errorDescription = payload.errorInformation.errorDescription + + + + [action == 'bulk-prepare' AND state.status == 'success'] + + + + + processingState = 'ACCEPTED' + + + + [action IN ['bulk-timeout-received', 'bulk-timeout-reserved']] + + + + + incompleteBulkState = 'EXPIRING' + + + completedBulkState = 'COMPLETED' + + + processingState = 'EXPIRED' + + + errorCode = payload.errorInformation.errorCode + + + errorDescription = payload.errorInformation.errorDescription + + + + [all other actions] + + + + + throw + + + ErrorHandler.Factory.createFSPIOPError(INTERNAL_SERVER_ERROR) + + + + [bulkTransferInfo.bulkTransferState IN ['ACCEPTED']] + + + + + alt + + + [action == 'bulk-timeout-reserved'] + + + + + criteriaState = 'ACCEPTED' + + + incompleteBulkState = 'EXPIRING' + + + completedBulkState = 'COMPLETED' + + + processingState = 'EXPIRED' + + + + [all other actions] + + + + + throw + + + ErrorHandler.Factory.createFSPIOPError(INTERNAL_SERVER_ERROR) + + + + [bulkTransferInfo.bulkTransferState IN ['PROCESSING', 'PENDING_FULFIL', 'EXPIRING']] + + + + + criteriaState = 'PROCESSING' + + + incompleteBulkState = 'PENDING_FULFIL' + + + completedBulkState = 'COMPLETED' + + + + + alt + + + [action == 'fulfil-duplicate'] + + + + + processingState = 'FULFIL_DUPLICATE' + + + + [action == 'bulk-commit' AND state.status == 'success'] + + + + + processingState = 'COMPLETED' + + + + [action == 'reject' AND state.status == 'success'] + + + + + processingState = 'REJECTED' + + + + [action IN ['commit', 'abort'] AND state.status == 'error'] + + + + + processingState = 'FULFIL_INVALID' + + + + [action == 'bulk-timeout-reserved'] + + + + + incompleteBulkState = 'EXPIRING' + + + completedBulkState = 'COMPLETED' + + + processingState = 'EXPIRED' + + + errorCode = payload.errorInformation.errorCode + + + errorDescription = payload.errorInformation.errorDescription + + + + [all other actions] + + + + + throw + + + ErrorHandler.Factory.createFSPIOPError(INTERNAL_SERVER_ERROR) + + + + [all other ['PENDING_INVALID', 'COMPLETED', 'REJECTED', 'INVALID']] + + + + + throw + + + ErrorHandler.Factory.createFSPIOPError(INTERNAL_SERVER_ERROR) + + + + + 8 + + + Persist individual transfer processing state + + + + + 9 + + + Persist individual transfer processing state + + + -- store errorCode/errorMessage when + + + state.status == 'error' + + + + bulkTransferAssociation + + + + + 10 + + + Return success + + + + + 11 + + + Check previously defined completion criteria + + + + + 12 + + + Select EXISTS (LIMIT 1) in criteriaState + + + + bulkTransferAssociation + + + + + 13 + + + Return + + + existingIndividualTransfer + + + + + 14 + + + Return + + + existingIndividualTransfer + + + + + alt + + + [individual transfer exists] + + + + + bulkTransferState = incompleteBulkState + + + + [no transfer in criteriaState exists] + + + + + bulkTransferState = completedBulkState + + + produceNotification = true + + + + + 15 + + + Persist bulkTransferState from previous step + + + + + 16 + + + Persist bulkTransferState + + + + bulkTransferStateChange + + + + + 17 + + + Return success + + + + + alt + + + [produceNotification == true] + + + + + 18 + + + Request to retrieve all bulk transfer and individual transfer results + + + + + 19 + + + Get bulkTransferResult + + + + bulkTransfer + + + bulkTransferStateChange + + + bulkTransferAssociation + + + + + 20 + + + Return + + + bulkTransferResult + + + + + 21 + + + Return + + + bulkTransferResult + + + + + Send Bulk Notification(s) + + + + + Depending on the action decide where to + + + send notification: payer, payee OR both + + + + + 22 + + + Generate & Persist bulk message to object store: + + + MLOS.bulkTransferResults + + + by destination + + + + + 23 + + + Return reference to the stored object(s) + + + MLOS.bulkTransferResults.messageId + + + + + Message: + + + { + + + id: <messageId> + + + from: <source>, + + + to: <destination>, + + + type: "application/json" + + + content: { + + + headers: <bulkTransferHeaders>, + + + payload: { + + + bulkTransferId: <uuid>, + + + bulkTransferState: <string> + + + } + + + }, + + + metadata: { + + + event: { + + + id: <uuid>, + + + responseTo: <previous.uuid>, + + + type: "notification", + + + action: "bulk-[prepare | commit | abort | processing]", + + + createdAt: <timestamp>, + + + state: { + + + status: state.status, + + + code: state.code + + + } + + + } + + + } + + + } + + + + + 24 + + + Publish Notification event for Payer/Payee + + + Error codes: + + + 2003 + + + + [produceNotification == false] + + + + + Do nothing (awaitAllTransfers) + + diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-2.1.0-bulk-fulfil-overview.plantuml b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-2.1.0-bulk-fulfil-overview.plantuml new file mode 100644 index 000000000..01e413e4d --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-2.1.0-bulk-fulfil-overview.plantuml @@ -0,0 +1,226 @@ +/'***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + * Georgi Georgiev + -------------- + ******'/ + +@startuml +' declate title +title 2.1.0. DFSP2 sends a Bulk Fulfil Success Transfer request + +autonumber + +' Actor Keys: +' boundary - APIs/Interfaces, etc +' collections - Kafka Topics +' control - Kafka Consumers +' entity - Database Access Objects +' database - Database Persistance Store + +' declare actors +actor "DFSP1\nPayer" as DFSP1 +actor "DFSP2\nPayee" as DFSP2 +boundary "ML API Adapter" as MLAPI +control "ML API \nNotification Handler" as NOTIFY_HANDLER +collections "mongo-\nobject-store" as OBJECT_STORE +boundary "Central Service API" as CSAPI +collections "topic-\nbulk-fulfil" as TOPIC_BULK_FULFIL +control "Bulk Fulfil\nHandler" as BULK_FULFIL_HANDLER +collections "topic-\nfulfil" as TOPIC_FULFIL +control "Fulfil \nHandler" as FULF_HANDLER +collections "topic-\ntransfer-position" as TOPIC_TRANSFER_POSITION +control "Position \nHandler" as POS_HANDLER +collections "topic-\nbulk-processing" as TOPIC_BULK_PROCESSING +control "Bulk Processing\nHandler" as BULK_PROC_HANDLER +collections "topic-\nnotification" as TOPIC_NOTIFICATIONS + +box "Financial Service Providers" #lightGray + participant DFSP1 + participant DFSP2 +end box + +box "ML API Adapter Service" #LightBlue + participant MLAPI + participant NOTIFY_HANDLER +end box + +box "Central Service" #LightYellow + participant OBJECT_STORE + participant CSAPI + participant TOPIC_BULK_FULFIL + participant BULK_FULFIL_HANDLER + participant TOPIC_FULFIL + participant FULF_HANDLER + participant TOPIC_TRANSFER_POSITION + participant POS_HANDLER + participant TOPIC_BULK_PROCESSING + participant BULK_PROC_HANDLER + participant TOPIC_NOTIFICATIONS +end box + +' start flow +activate NOTIFY_HANDLER +activate BULK_FULFIL_HANDLER +activate FULF_HANDLER +activate POS_HANDLER +activate BULK_PROC_HANDLER +group DFSP2 sends a Bulk Fulfil Success Transfer request to DFSP1 + note right of DFSP2 #yellow + Headers - transferHeaders: { + Content-Length: , + Content-Type: , + Date: , + FSPIOP-Source: , + FSPIOP-Destination: , + FSPIOP-Encryption: , + FSPIOP-Signature: , + FSPIOP-URI: , + FSPIOP-HTTP-Method: + } + + Payload - bulkTransferMessage: + { + bulkTransferState: , + completedTimestamp: , + individualTransferResults: + [ + { + transferId: , + fulfilment: , + extensionList: { extension: [ + { key: , value: } + ] } + } + ], + extensionList: { extension: [ + { key: , value: } + ] } + } + end note + DFSP2 ->> MLAPI: PUT - /bulkTransfers/ + activate MLAPI + MLAPI -> MLAPI: Validate incoming message\nError codes: 3000-3002, 3100-3107 + MLAPI -> OBJECT_STORE: Persist incoming bulk message to\nobject store: **MLOS.individualTransferFulfils** + activate OBJECT_STORE + OBJECT_STORE --> MLAPI: Return messageId reference to the stored object(s) + deactivate OBJECT_STORE + note right of MLAPI #yellow + Message: + { + id: , + from: , + to: , + type: "application/json", + content: { + headers: , + payload: { + bulkTransferId: , + bulkTransferState: "COMPLETED", + completedTimestamp: , + extensionList: { extension: [ + { key: , value: } + ] }, + count: , + hash: + } + }, + metadata: { + event: { + id: , + type: "bulk-fulfil", + action: "bulk-commit", + createdAt: , + state: { + status: "success", + code: 0 + } + } + } + } + end note + MLAPI -> TOPIC_BULK_FULFIL: Route & Publish Bulk Fulfil event for Payee\nError code: 2003 + activate TOPIC_BULK_FULFIL + TOPIC_BULK_FULFIL <-> TOPIC_BULK_FULFIL: Ensure event is replicated \nas configured (ACKS=all)\nError code: 2003 + TOPIC_BULK_FULFIL --> MLAPI: Respond replication acknowledgements \nhave been received + deactivate TOPIC_BULK_FULFIL + MLAPI -->> DFSP2: Respond HTTP - 200 (OK) + deactivate MLAPI + TOPIC_BULK_FULFIL <- BULK_FULFIL_HANDLER: Consume message + BULK_FULFIL_HANDLER -> OBJECT_STORE: Retrieve individual transfers by key:\n**MLOS.individualTransferFulfils.messageId** + activate OBJECT_STORE + OBJECT_STORE --> BULK_FULFIL_HANDLER: Stream bulk's individual transfers + deactivate OBJECT_STORE + ref over TOPIC_BULK_FULFIL, TOPIC_FULFIL: Bulk Prepare Handler Consume \n + alt Success + BULK_FULFIL_HANDLER -> TOPIC_FULFIL: Produce (stream) single transfer message\nfor each individual transfer [loop] + else Failure + BULK_FULFIL_HANDLER --> TOPIC_NOTIFICATIONS: Produce single message for the entire bulk + end + ||| + TOPIC_FULFIL <- FULF_HANDLER: Consume message + ref over TOPIC_FULFIL, TOPIC_TRANSFER_POSITION: Fulfil Handler Consume (Success)\n + alt Success + FULF_HANDLER -> TOPIC_TRANSFER_POSITION: Produce message + else Failure + FULF_HANDLER --> TOPIC_BULK_PROCESSING: Produce message + end + ||| + TOPIC_TRANSFER_POSITION <- POS_HANDLER: Consume message + ref over TOPIC_TRANSFER_POSITION, TOPIC_BULK_PROCESSING: Position Handler Consume (Success)\n + POS_HANDLER -> TOPIC_BULK_PROCESSING: Produce message + ||| + TOPIC_BULK_PROCESSING <- BULK_PROC_HANDLER: Consume message + ref over TOPIC_BULK_PROCESSING, TOPIC_NOTIFICATIONS: Bulk Processing Handler Consume (Success)\n + BULK_PROC_HANDLER -> OBJECT_STORE: Persist bulk message by destination to the\nobject store: **MLOS.bulkTransferResults** + activate OBJECT_STORE + OBJECT_STORE --> BULK_PROC_HANDLER: Return the reference to the stored \nnotification object(s): **messageId** + deactivate OBJECT_STORE + BULK_PROC_HANDLER -> TOPIC_NOTIFICATIONS: Send Bulk Commit notification + ||| + TOPIC_NOTIFICATIONS <- NOTIFY_HANDLER: Consume message + NOTIFY_HANDLER -> OBJECT_STORE: Retrieve bulk notification(s) by reference & destination:\n**MLOS.bulkTransferResults.messageId + destination** + activate OBJECT_STORE + OBJECT_STORE --> NOTIFY_HANDLER: Return notification payload + deactivate OBJECT_STORE + opt action == 'bulk-commit' + ||| + ref over DFSP1, TOPIC_NOTIFICATIONS: Send notification to Participant (Payer)\n + NOTIFY_HANDLER -> DFSP1: Send callback notification + end + ||| + TOPIC_NOTIFICATIONS <- NOTIFY_HANDLER: Consume message + NOTIFY_HANDLER -> OBJECT_STORE: Retrieve bulk notification(s) by reference & destination:\n**MLOS.bulkTransferResults.messageId + destination** + activate OBJECT_STORE + OBJECT_STORE --> NOTIFY_HANDLER: Return notification payload + deactivate OBJECT_STORE + opt action == 'bulk-commit' + ||| + ref over DFSP2, TOPIC_NOTIFICATIONS: Send notification to Participant (Payee)\n + NOTIFY_HANDLER -> DFSP2: Send callback notification + end + ||| +end +deactivate POS_HANDLER +activate BULK_FULFIL_HANDLER +deactivate FULF_HANDLER +deactivate BULK_PROC_HANDLER +deactivate NOTIFY_HANDLER +@enduml diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-2.1.0-bulk-fulfil-overview.svg b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-2.1.0-bulk-fulfil-overview.svg new file mode 100644 index 000000000..90d6889fc --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-2.1.0-bulk-fulfil-overview.svg @@ -0,0 +1,835 @@ + + + + + + + + + + + 2.1.0. DFSP2 sends a Bulk Fulfil Success Transfer request + + + + Financial Service Providers + + + + ML API Adapter Service + + + + Central Service + + + + + + + + + + + DFSP1 + + + Payer + + + + + DFSP1 + + + Payer + + + + + DFSP2 + + + Payee + + + + + DFSP2 + + + Payee + + + + + ML API Adapter + + + + + ML API Adapter + + + + + ML API + + + Notification Handler + + + + + ML API + + + Notification Handler + + + + + + + mongo- + + + object-store + + + + + mongo- + + + object-store + + + Central Service API + + + + + Central Service API + + + + + + + topic- + + + bulk-fulfil + + + + + topic- + + + bulk-fulfil + + + Bulk Fulfil + + + Handler + + + + + Bulk Fulfil + + + Handler + + + + + + + topic- + + + fulfil + + + + + topic- + + + fulfil + + + Fulfil + + + Handler + + + + + Fulfil + + + Handler + + + + + + + topic- + + + transfer-position + + + + + topic- + + + transfer-position + + + Position + + + Handler + + + + + Position + + + Handler + + + + + + + topic- + + + bulk-processing + + + + + topic- + + + bulk-processing + + + Bulk Processing + + + Handler + + + + + Bulk Processing + + + Handler + + + + + + + topic- + + + notification + + + + + topic- + + + notification + + + + + + DFSP2 sends a Bulk Fulfil Success Transfer request to DFSP1 + + + + + Headers - transferHeaders: { + + + Content-Length: <int>, + + + Content-Type: <string>, + + + Date: <date>, + + + FSPIOP-Source: <string>, + + + FSPIOP-Destination: <string>, + + + FSPIOP-Encryption: <string>, + + + FSPIOP-Signature: <string>, + + + FSPIOP-URI: <uri>, + + + FSPIOP-HTTP-Method: <string> + + + } + + + Payload - bulkTransferMessage: + + + { + + + bulkTransferState: <bulkTransferState>, + + + completedTimestamp: <completedTimeStamp>, + + + individualTransferResults: + + + [ + + + { + + + transferId: <uuid>, + + + fulfilment: <ilpCondition>, + + + extensionList: { extension: [ + + + { key: <string>, value: <string> } + + + ] } + + + } + + + ], + + + extensionList: { extension: [ + + + { key: <string>, value: <string> } + + + ] } + + + } + + + + 1 + + + PUT - /bulkTransfers/<ID> + + + + + 2 + + + Validate incoming message + + + Error codes: + + + 3000-3002, 3100-3107 + + + + + 3 + + + Persist incoming bulk message to + + + object store: + + + MLOS.individualTransferFulfils + + + + + 4 + + + Return messageId reference to the stored object(s) + + + + + Message: + + + { + + + id: <messageId>, + + + from: <payeeFspName>, + + + to: <payerFspName>, + + + type: "application/json", + + + content: { + + + headers: <bulkTransferHeaders>, + + + payload: { + + + bulkTransferId: <uuid>, + + + bulkTransferState: "COMPLETED", + + + completedTimestamp: <timestamp>, + + + extensionList: { extension: [ + + + { key: <string>, value: <string> } + + + ] }, + + + count: <int>, + + + hash: <string> + + + } + + + }, + + + metadata: { + + + event: { + + + id: <uuid>, + + + type: "bulk-fulfil", + + + action: "bulk-commit", + + + createdAt: <timestamp>, + + + state: { + + + status: "success", + + + code: 0 + + + } + + + } + + + } + + + } + + + + + 5 + + + Route & Publish Bulk Fulfil event for Payee + + + Error code: + + + 2003 + + + + + 6 + + + Ensure event is replicated + + + as configured (ACKS=all) + + + Error code: + + + 2003 + + + + + 7 + + + Respond replication acknowledgements + + + have been received + + + + + 8 + + + Respond HTTP - 200 (OK) + + + + + 9 + + + Consume message + + + + + 10 + + + Retrieve individual transfers by key: + + + MLOS.individualTransferFulfils.messageId + + + + + 11 + + + Stream bulk's individual transfers + + + + + ref + + + Bulk Prepare Handler Consume + + + + + alt + + + [Success] + + + + + 12 + + + Produce (stream) single transfer message + + + for each individual transfer [loop] + + + + [Failure] + + + + + 13 + + + Produce single message for the entire bulk + + + + + 14 + + + Consume message + + + + + ref + + + Fulfil Handler Consume (Success) + + + + + alt + + + [Success] + + + + + 15 + + + Produce message + + + + [Failure] + + + + + 16 + + + Produce message + + + + + 17 + + + Consume message + + + + + ref + + + Position Handler Consume (Success) + + + + + 18 + + + Produce message + + + + + 19 + + + Consume message + + + + + ref + + + Bulk Processing Handler Consume (Success) + + + + + 20 + + + Persist bulk message by destination to the + + + object store: + + + MLOS.bulkTransferResults + + + + + 21 + + + Return the reference to the stored + + + notification object(s): + + + messageId + + + + + 22 + + + Send Bulk Commit notification + + + + + 23 + + + Consume message + + + + + 24 + + + Retrieve bulk notification(s) by reference & destination: + + + MLOS.bulkTransferResults.messageId + destination + + + + + 25 + + + Return notification payload + + + + + opt + + + [action == 'bulk-commit'] + + + + + ref + + + Send notification to Participant (Payer) + + + + + 26 + + + Send callback notification + + + + + 27 + + + Consume message + + + + + 28 + + + Retrieve bulk notification(s) by reference & destination: + + + MLOS.bulkTransferResults.messageId + destination + + + + + 29 + + + Return notification payload + + + + + opt + + + [action == 'bulk-commit'] + + + + + ref + + + Send notification to Participant (Payee) + + + + + 30 + + + Send callback notification + + diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-2.1.1-bulk-fulfil-handler.plantuml b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-2.1.1-bulk-fulfil-handler.plantuml new file mode 100644 index 000000000..b5b87dfc6 --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-2.1.1-bulk-fulfil-handler.plantuml @@ -0,0 +1,311 @@ +/'***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + * Georgi Georgiev + -------------- + ******'/ + +@startuml +' declare title +title 2.1.1. Bulk Fulfil Handler Consume + +autonumber + +' Actor Keys: +' boundary - APIs/Interfaces, etc +' collections - Kafka Topics +' control - Kafka Consumers +' entity - Database Access Objects +' database - Database Persistance Store + +' declare actors +collections "mongo-\nobject-store" as OBJECT_STORE +collections "topic-bulk-\nfulfil" as TOPIC_BULK_FULFIL +collections "topic-bulk-\nprocessing" as TOPIC_BULK_PROCESSING +control "Bulk Fulfil\nHandler" as BULK_FULF_HANDLER +collections "topic-transfer-\nfulfil" as TOPIC_TRANSFER_FULFIL +collections "topic-event" as TOPIC_EVENTS +collections "topic-notification" as TOPIC_NOTIFICATION +entity "Bulk DAO" as BULK_DAO +database "Central Store" as DB + +box "Central Service" #LightYellow + participant OBJECT_STORE + participant TOPIC_BULK_FULFIL + participant BULK_FULF_HANDLER + participant TOPIC_TRANSFER_FULFIL + participant TOPIC_BULK_PROCESSING + participant TOPIC_EVENTS + participant TOPIC_NOTIFICATION + participant BULK_DAO + participant DB +end box + +' start flow +activate BULK_FULF_HANDLER +group Bulk Fulfil Handler Consume + TOPIC_BULK_FULFIL <- BULK_FULF_HANDLER: Consume message + activate TOPIC_BULK_FULFIL + deactivate TOPIC_BULK_FULFIL + + break + group Validate Event + BULK_FULF_HANDLER <-> BULK_FULF_HANDLER: Validate event - Rule:\ntype == 'bulk-fulfil' && action == 'bulk-commit'\nError codes: 2001 + end + end + + group Persist Event Information + ||| + BULK_FULF_HANDLER -> TOPIC_EVENTS: Publish event information + ref over BULK_FULF_HANDLER, TOPIC_EVENTS: Event Handler Consume \n + ||| + end + + group Validate FSPIOP-Signature + ||| + ref over BULK_FULF_HANDLER, TOPIC_NOTIFICATION: Validate message.content.headers.**FSPIOP-Signature**\nError codes: 3105/3106\n + ||| + end + + group Validate Bulk Fulfil Transfer + BULK_FULF_HANDLER <-> BULK_FULF_HANDLER: Schema validation of the incoming message + BULK_FULF_HANDLER <-> BULK_FULF_HANDLER: Verify the message's signature\n(to be confirmed in future requirement) + note right of BULK_FULF_HANDLER #lightgrey + The above validation steps are already handled by the + Bulk-API-Adapter for the open source implementation. + It may need to be added in future for custom adapters. + end note + + group Validate Duplicate Check + ||| + BULK_FULF_HANDLER -> DB: Request Duplicate Check + ref over BULK_FULF_HANDLER, DB: Request Duplicate Check\n + DB --> BULK_FULF_HANDLER: Return { hasDuplicateId: Boolean, hasDuplicateHash: Boolean } + end + + alt hasDuplicateId == TRUE && hasDuplicateHash == TRUE + break + BULK_FULF_HANDLER -> BULK_DAO: Request to retrieve Bulk Transfer state & completedTimestamp\nError code: 2003 + activate BULK_DAO + BULK_DAO -> DB: Query database + hnote over DB #lightyellow + bulkTransfer + bulkTransferFulfilment + bulkTransferStateChange + end note + activate DB + BULK_DAO <-- DB: Return resultset + deactivate DB + BULK_DAO --> BULK_FULF_HANDLER: Return **bulkTransferStateId** & **completedTimestamp** (not null when completed) + deactivate BULK_DAO + + note right of BULK_FULF_HANDLER #yellow + Message: + { + id: + from: , + to: , + type: application/json + content: { + headers: , + payload: { + bulkTransferState: , + completedTimestamp: + } + }, + metadata: { + event: { + id: , + responseTo: , + type: "notification", + action: "bulk-fulfil-duplicate", + createdAt: , + state: { + status: "success", + code: 0 + } + } + } + } + end note + BULK_FULF_HANDLER -> TOPIC_NOTIFICATION: Publish Notification event for Payee + activate TOPIC_NOTIFICATION + deactivate TOPIC_NOTIFICATION + end + else hasDuplicateId == TRUE && hasDuplicateHash == FALSE + note right of BULK_FULF_HANDLER #yellow + { + id: , + from: , + type: "application/json", + content: { + headers: , + payload: { + errorInformation: { + errorCode: "3106", + errorDescription: "Modified request", + extensionList: { + extension: [ + { + key: "_cause", + value: + } + ] + } + }, + uriParams: { + id: + } + } + }, + metadata: { + correlationId: , + event: { + id: , + type: "notification", + action: "bulk-commit", + createdAt: , + state: { + status: "error", + code: "3106", + description: "Modified request" + }, + responseTo: + } + } + } + end note + BULK_FULF_HANDLER -> TOPIC_NOTIFICATION: Publish Notification (failure) event for Payer\nError codes: 3106 + activate TOPIC_NOTIFICATION + deactivate TOPIC_NOTIFICATION + else hasDuplicateId == FALSE + alt Validate Bulk Transfer Fulfil (success) + group Persist Bulk Transfer State (with bulktransferState='PROCESSING') + BULK_FULF_HANDLER -> BULK_DAO: Request to persist bulk transfer fulfil\nError codes: 2003 + activate BULK_DAO + BULK_DAO -> DB: Persist bulkTransferFulfilment + hnote over DB #lightyellow + bulkTransferFulfilment + bulkTransferStateChange + bulkTransferExtension + end note + activate DB + deactivate DB + BULK_DAO --> BULK_FULF_HANDLER: Return success + deactivate BULK_DAO + end + else Validate Bulk Transfer Fulfil (failure) + group Persist Bulk Transfer State (with bulkTransferState='INVALID/REJECTED') + BULK_FULF_HANDLER -> BULK_DAO: Request to persist bulk\ntransfer fulfil failure\nError codes: 2003 + activate BULK_DAO + BULK_DAO -> DB: Persist transfer + hnote over DB #lightyellow + bulkTransferFulfilment + bulkTransferStateChange + bulkTransferExtension + bulkTransferError + end note + activate DB + deactivate DB + BULK_DAO --> BULK_FULF_HANDLER: Return success + deactivate BULK_DAO + end + end + end + end + alt Validate Bulk Transfer Fulfil (success) + loop for every individual transfer in the bulk fulfil list + BULK_FULF_HANDLER -> OBJECT_STORE: Retrieve individual transfers from the bulk using\nreference: **MLOS.individualTransferFulfils.messageId** + activate OBJECT_STORE + OBJECT_STORE --> BULK_FULF_HANDLER: Stream bulk's individual transfer fulfils + deactivate OBJECT_STORE + + note right of BULK_FULF_HANDLER #yellow + Message: + { + id: + from: , + to: , + type: "application/json" + content: { + headers: , + payload: + }, + metadata: { + event: { + id: , + responseTo: , + type: "fulfil", + action: "bulk-commit", + createdAt: , + state: { + status: "success", + code: 0 + } + } + } + } + end note + BULK_FULF_HANDLER -> TOPIC_TRANSFER_FULFIL: Route & Publish Prepare event to the Payer for the Individual Transfer\nError codes: 2003 + activate TOPIC_TRANSFER_FULFIL + deactivate TOPIC_TRANSFER_FULFIL + end + else Validate Bulk Transfer Fulfil (failure) + note right of BULK_FULF_HANDLER #yellow + Message: + { + id: + from: , + to: , + type: "application/json" + content: { + headers: , + payload: { + "errorInformation": { + "errorCode": + "errorDescription": "", + "extensionList": + } + }, + metadata: { + event: { + id: , + responseTo: , + type: "notification", + action: "bulk-abort", + createdAt: , + state: { + status: 'error', + code: + description: + } + } + } + } + end note + BULK_FULF_HANDLER -> TOPIC_NOTIFICATION: Publish Notification (failure) event for Payer\nError codes: 2003 + activate TOPIC_NOTIFICATION + deactivate TOPIC_NOTIFICATION + end +end +deactivate BULK_FULF_HANDLER +@enduml + diff --git a/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-2.1.1-bulk-fulfil-handler.svg b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-2.1.1-bulk-fulfil-handler.svg new file mode 100644 index 000000000..bb328dce5 --- /dev/null +++ b/docs/technical/central-bulk-transfers/assets/diagrams/sequence/seq-bulk-2.1.1-bulk-fulfil-handler.svg @@ -0,0 +1,954 @@ + + +