@mojaloop/sdk-scheme-adapter 11.18.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/.env.example +140 -0
  2. package/.eslintignore +2 -0
  3. package/.eslintrc.json +30 -0
  4. package/.nvmrc +1 -0
  5. package/.versionrc +15 -0
  6. package/CHANGELOG.md +118 -0
  7. package/InboundServer/api.yaml +3594 -0
  8. package/InboundServer/api_template.yaml +69 -0
  9. package/InboundServer/handlers.js +940 -0
  10. package/InboundServer/index.js +205 -0
  11. package/InboundServer/middlewares.js +426 -0
  12. package/OAuthTestServer/index.js +66 -0
  13. package/OAuthTestServer/model.js +70 -0
  14. package/OutboundServer/api.yaml +2732 -0
  15. package/OutboundServer/api_interfaces/index.d.ts +117 -0
  16. package/OutboundServer/api_interfaces/openapi.d.ts +1475 -0
  17. package/OutboundServer/api_template/components/parameters/bulkQuoteId.yaml +9 -0
  18. package/OutboundServer/api_template/components/parameters/bulkTransferId.yaml +9 -0
  19. package/OutboundServer/api_template/components/parameters/requestToPayTransactionId.yaml +9 -0
  20. package/OutboundServer/api_template/components/parameters/transferId.yaml +9 -0
  21. package/OutboundServer/api_template/components/responses/accountsCreationCompleted.yaml +5 -0
  22. package/OutboundServer/api_template/components/responses/accountsCreationError.yaml +5 -0
  23. package/OutboundServer/api_template/components/responses/accountsCreationTimeout.yaml +5 -0
  24. package/OutboundServer/api_template/components/responses/authorizationPostSuccess.yaml +5 -0
  25. package/OutboundServer/api_template/components/responses/authorizationsServerError.yaml +5 -0
  26. package/OutboundServer/api_template/components/responses/bulkQuoteBadRequest.yaml +5 -0
  27. package/OutboundServer/api_template/components/responses/bulkQuoteServerError.yaml +5 -0
  28. package/OutboundServer/api_template/components/responses/bulkQuoteSuccess.yaml +5 -0
  29. package/OutboundServer/api_template/components/responses/bulkQuoteTimeout.yaml +5 -0
  30. package/OutboundServer/api_template/components/responses/bulkTransferBadRequest.yaml +5 -0
  31. package/OutboundServer/api_template/components/responses/bulkTransferServerError.yaml +5 -0
  32. package/OutboundServer/api_template/components/responses/bulkTransferSuccess.yaml +5 -0
  33. package/OutboundServer/api_template/components/responses/bulkTransferTimeout.yaml +5 -0
  34. package/OutboundServer/api_template/components/responses/partiesByIdError404.yaml +9 -0
  35. package/OutboundServer/api_template/components/responses/partiesByIdSuccess.yaml +5 -0
  36. package/OutboundServer/api_template/components/responses/quotesPostSuccess.yaml +5 -0
  37. package/OutboundServer/api_template/components/responses/quotesServerError.yaml +5 -0
  38. package/OutboundServer/api_template/components/responses/requestToPaySuccess.yaml +5 -0
  39. package/OutboundServer/api_template/components/responses/requestToPayTransferBadRequest.yaml +5 -0
  40. package/OutboundServer/api_template/components/responses/requestToPayTransferSuccess.yaml +5 -0
  41. package/OutboundServer/api_template/components/responses/simpleTransfersPostSuccess.yaml +5 -0
  42. package/OutboundServer/api_template/components/responses/simpleTransfersServerError.yaml +5 -0
  43. package/OutboundServer/api_template/components/responses/transferBadRequest.yaml +5 -0
  44. package/OutboundServer/api_template/components/responses/transferServerError.yaml +5 -0
  45. package/OutboundServer/api_template/components/responses/transferSuccess.yaml +5 -0
  46. package/OutboundServer/api_template/components/responses/transferTimeout.yaml +5 -0
  47. package/OutboundServer/api_template/components/schemas/accountCreationStatus.yaml +18 -0
  48. package/OutboundServer/api_template/components/schemas/accountsCreationState.yaml +4 -0
  49. package/OutboundServer/api_template/components/schemas/accountsRequest.yaml +20 -0
  50. package/OutboundServer/api_template/components/schemas/accountsResponse.yaml +15 -0
  51. package/OutboundServer/api_template/components/schemas/async2SyncCurrentState.yaml +5 -0
  52. package/OutboundServer/api_template/components/schemas/authorizationsPostRequest.yaml +15 -0
  53. package/OutboundServer/api_template/components/schemas/authorizationsPostResponse.yaml +19 -0
  54. package/OutboundServer/api_template/components/schemas/bulkQuoteErrorResponse.yaml +8 -0
  55. package/OutboundServer/api_template/components/schemas/bulkQuoteRequest.yaml +26 -0
  56. package/OutboundServer/api_template/components/schemas/bulkQuoteResponse.yaml +21 -0
  57. package/OutboundServer/api_template/components/schemas/bulkQuoteStatus.yaml +4 -0
  58. package/OutboundServer/api_template/components/schemas/bulkQuoteStatusResponse.yaml +17 -0
  59. package/OutboundServer/api_template/components/schemas/bulkTransferErrorResponse.yaml +8 -0
  60. package/OutboundServer/api_template/components/schemas/bulkTransferRequest.yaml +26 -0
  61. package/OutboundServer/api_template/components/schemas/bulkTransferResponse.yaml +16 -0
  62. package/OutboundServer/api_template/components/schemas/bulkTransferStatus.yaml +4 -0
  63. package/OutboundServer/api_template/components/schemas/bulkTransferStatusResponse.yaml +17 -0
  64. package/OutboundServer/api_template/components/schemas/errorAccountsResponse.yaml +8 -0
  65. package/OutboundServer/api_template/components/schemas/errorAuthorizationsResponse.yaml +3 -0
  66. package/OutboundServer/api_template/components/schemas/errorQuotesResponse.yaml +9 -0
  67. package/OutboundServer/api_template/components/schemas/errorResponse.yaml +8 -0
  68. package/OutboundServer/api_template/components/schemas/errorSimpleTransfersResponse.yaml +3 -0
  69. package/OutboundServer/api_template/components/schemas/errorTransferResponse.yaml +8 -0
  70. package/OutboundServer/api_template/components/schemas/extensionListEmptiable.yaml +6 -0
  71. package/OutboundServer/api_template/components/schemas/individualQuote.yaml +32 -0
  72. package/OutboundServer/api_template/components/schemas/individualQuoteResult.yaml +28 -0
  73. package/OutboundServer/api_template/components/schemas/individualTransfer.yaml +32 -0
  74. package/OutboundServer/api_template/components/schemas/individualTransferFulfilment.yaml +13 -0
  75. package/OutboundServer/api_template/components/schemas/individualTransferResult.yaml +41 -0
  76. package/OutboundServer/api_template/components/schemas/mojaloopError.yaml +5 -0
  77. package/OutboundServer/api_template/components/schemas/mojaloopTransactionRequestState.yaml +2 -0
  78. package/OutboundServer/api_template/components/schemas/partiesByIdResponse.yaml +13 -0
  79. package/OutboundServer/api_template/components/schemas/quote.yaml +3 -0
  80. package/OutboundServer/api_template/components/schemas/quoteError.yaml +16 -0
  81. package/OutboundServer/api_template/components/schemas/quotesPostRequest.yaml +13 -0
  82. package/OutboundServer/api_template/components/schemas/quotesPostResponse.yaml +48 -0
  83. package/OutboundServer/api_template/components/schemas/requestToPayRequest.yaml +39 -0
  84. package/OutboundServer/api_template/components/schemas/requestToPayResponse.yaml +41 -0
  85. package/OutboundServer/api_template/components/schemas/requestToPayTransferRequest.yaml +42 -0
  86. package/OutboundServer/api_template/components/schemas/requestToPayTransferResponse.yaml +58 -0
  87. package/OutboundServer/api_template/components/schemas/simpleTransferServerError.yaml +5 -0
  88. package/OutboundServer/api_template/components/schemas/simpleTransfersPostRequest.yaml +12 -0
  89. package/OutboundServer/api_template/components/schemas/simpleTransfersPostResponse.yaml +11 -0
  90. package/OutboundServer/api_template/components/schemas/transactionType.yaml +4 -0
  91. package/OutboundServer/api_template/components/schemas/transferContinuationAcceptOTP.yaml +9 -0
  92. package/OutboundServer/api_template/components/schemas/transferContinuationAcceptParty.yaml +8 -0
  93. package/OutboundServer/api_template/components/schemas/transferContinuationAcceptQuote.yaml +9 -0
  94. package/OutboundServer/api_template/components/schemas/transferError.yaml +16 -0
  95. package/OutboundServer/api_template/components/schemas/transferFulfilment.yaml +3 -0
  96. package/OutboundServer/api_template/components/schemas/transferParty.yaml +40 -0
  97. package/OutboundServer/api_template/components/schemas/transferRequest.yaml +37 -0
  98. package/OutboundServer/api_template/components/schemas/transferResponse.yaml +58 -0
  99. package/OutboundServer/api_template/components/schemas/transferStatus.yaml +6 -0
  100. package/OutboundServer/api_template/components/schemas/transferStatusResponse.yaml +13 -0
  101. package/OutboundServer/api_template/health.yaml +12 -0
  102. package/OutboundServer/api_template/openapi.yaml +55 -0
  103. package/OutboundServer/api_template/paths/accounts.yaml +26 -0
  104. package/OutboundServer/api_template/paths/authorizations.yaml +19 -0
  105. package/OutboundServer/api_template/paths/bulkQuotes.yaml +23 -0
  106. package/OutboundServer/api_template/paths/bulkQuotes_bulkQuoteId.yaml +24 -0
  107. package/OutboundServer/api_template/paths/bulkTransfers.yaml +23 -0
  108. package/OutboundServer/api_template/paths/bulkTransfers_bulkTransferId.yaml +24 -0
  109. package/OutboundServer/api_template/paths/parties_Type_ID.yaml +20 -0
  110. package/OutboundServer/api_template/paths/parties_Type_ID_SubId.yaml +22 -0
  111. package/OutboundServer/api_template/paths/quotes.yaml +20 -0
  112. package/OutboundServer/api_template/paths/requestToPay.yaml +22 -0
  113. package/OutboundServer/api_template/paths/requestToPayTransfer.yaml +57 -0
  114. package/OutboundServer/api_template/paths/requestToPayTransfer_requestToPayTransactionId.yaml +34 -0
  115. package/OutboundServer/api_template/paths/simpleTransfers.yaml +19 -0
  116. package/OutboundServer/api_template/paths/transfers.yaml +55 -0
  117. package/OutboundServer/api_template/paths/transfers_transferId.yaml +58 -0
  118. package/OutboundServer/handlers.js +622 -0
  119. package/OutboundServer/index.js +137 -0
  120. package/OutboundServer/middlewares.js +67 -0
  121. package/TestServer/api.yaml +62 -0
  122. package/TestServer/handlers.js +63 -0
  123. package/TestServer/index.js +215 -0
  124. package/audit-resolve.json +65 -0
  125. package/babel.config.js +3 -0
  126. package/config.js +158 -0
  127. package/index.d.ts +1 -0
  128. package/index.js +149 -0
  129. package/jest.config.js +15 -0
  130. package/lib/api/index.js +12 -0
  131. package/lib/cache.js +352 -0
  132. package/lib/check.js +25 -0
  133. package/lib/model/AccountsModel.js +396 -0
  134. package/lib/model/Async2SyncModel.js +283 -0
  135. package/lib/model/AuthorizationsModel.js +86 -0
  136. package/lib/model/InboundTransfersModel.js +730 -0
  137. package/lib/model/OutboundBulkQuotesModel.js +485 -0
  138. package/lib/model/OutboundBulkTransfersModel.js +479 -0
  139. package/lib/model/OutboundRequestToPayModel.js +517 -0
  140. package/lib/model/OutboundRequestToPayTransferModel.js +893 -0
  141. package/lib/model/OutboundTransfersModel.js +823 -0
  142. package/lib/model/PartiesModel.js +70 -0
  143. package/lib/model/ProxyModel/MatchRules/Expression.js +48 -0
  144. package/lib/model/ProxyModel/MatchRules/Headers.js +65 -0
  145. package/lib/model/ProxyModel/MatchRules/MatchRule.js +27 -0
  146. package/lib/model/ProxyModel/MatchRules/Path.js +36 -0
  147. package/lib/model/ProxyModel/MatchRules/Query.js +65 -0
  148. package/lib/model/ProxyModel/MatchRules/index.js +19 -0
  149. package/lib/model/ProxyModel/Route.js +82 -0
  150. package/lib/model/ProxyModel/configSchema.json +118 -0
  151. package/lib/model/ProxyModel/index.js +138 -0
  152. package/lib/model/QuotesModel.js +94 -0
  153. package/lib/model/TransfersModel.js +81 -0
  154. package/lib/model/common/BackendError.js +26 -0
  155. package/lib/model/common/PersistentStateMachine.js +93 -0
  156. package/lib/model/common/index.js +18 -0
  157. package/lib/model/index.js +43 -0
  158. package/lib/model/lib/deferredJob.js +113 -0
  159. package/lib/model/lib/index.js +9 -0
  160. package/lib/model/lib/requests/backendRequests.js +227 -0
  161. package/lib/model/lib/requests/common.js +76 -0
  162. package/lib/model/lib/requests/index.js +19 -0
  163. package/lib/model/lib/shared.js +468 -0
  164. package/lib/randomphrase/index.js +21 -0
  165. package/lib/randomphrase/words.json +3397 -0
  166. package/lib/router.js +28 -0
  167. package/lib/validate.js +205 -0
  168. package/package.json +102 -0
  169. package/test/__mocks__/@mojaloop/sdk-standard-components.js +152 -0
  170. package/test/__mocks__/javascript-state-machine.js +21 -0
  171. package/test/__mocks__/redis.js +49 -0
  172. package/test/__mocks__/uuidv4.js +16 -0
  173. package/test/config/integration.env +136 -0
  174. package/test/integration/lib/Outbound/authorizations.test.js +58 -0
  175. package/test/integration/lib/Outbound/data/authorizationsPostRequest.json +43 -0
  176. package/test/integration/lib/Outbound/data/quotesPostRequest.json +52 -0
  177. package/test/integration/lib/Outbound/data/transfersPostRequest.json +24 -0
  178. package/test/integration/lib/Outbound/parties.test.js +28 -0
  179. package/test/integration/lib/Outbound/quotes.test.js +58 -0
  180. package/test/integration/lib/Outbound/simpleTransfers.test.js +67 -0
  181. package/test/integration/lib/cache.test.js +80 -0
  182. package/test/integration/testEnv.js +7 -0
  183. package/test/unit/InboundServer.test.js +443 -0
  184. package/test/unit/TestServer.test.js +394 -0
  185. package/test/unit/api/accounts/accounts.test.js +128 -0
  186. package/test/unit/api/accounts/data/postAccountsBody.json +7 -0
  187. package/test/unit/api/accounts/data/postAccountsErrorMojaloopResponse.json +25 -0
  188. package/test/unit/api/accounts/data/postAccountsErrorTimeoutResponse.json +19 -0
  189. package/test/unit/api/accounts/data/postAccountsSuccessResponse.json +17 -0
  190. package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError1.json +21 -0
  191. package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError2.json +21 -0
  192. package/test/unit/api/accounts/utils.js +65 -0
  193. package/test/unit/api/proxy/data/proxyConfig.yaml +82 -0
  194. package/test/unit/api/proxy/data/requestBody.json +22 -0
  195. package/test/unit/api/proxy/data/requestHeaders.json +5 -0
  196. package/test/unit/api/proxy/data/requestQuery.json +6 -0
  197. package/test/unit/api/proxy/data/responseBody.json +21 -0
  198. package/test/unit/api/proxy/data/responseHeaders.json +5 -0
  199. package/test/unit/api/proxy/proxy.test.js +220 -0
  200. package/test/unit/api/proxy/utils.js +79 -0
  201. package/test/unit/api/transfers/data/getTransfersCommittedResponse.json +21 -0
  202. package/test/unit/api/transfers/data/getTransfersErrorNotFound.json +17 -0
  203. package/test/unit/api/transfers/data/postQuotesBody.json +52 -0
  204. package/test/unit/api/transfers/data/postTransfersBadBody.json +17 -0
  205. package/test/unit/api/transfers/data/postTransfersBody.json +24 -0
  206. package/test/unit/api/transfers/data/postTransfersErrorMojaloopResponse.json +53 -0
  207. package/test/unit/api/transfers/data/postTransfersErrorTimeoutResponse.json +47 -0
  208. package/test/unit/api/transfers/data/postTransfersSimpleBody.json +26 -0
  209. package/test/unit/api/transfers/data/postTransfersSuccessResponse.json +101 -0
  210. package/test/unit/api/transfers/data/putPartiesBody.json +20 -0
  211. package/test/unit/api/transfers/data/putQuotesBody.json +37 -0
  212. package/test/unit/api/transfers/data/putTransfersBody.json +17 -0
  213. package/test/unit/api/transfers/transfers.test.js +191 -0
  214. package/test/unit/api/transfers/utils.js +183 -0
  215. package/test/unit/api/utils.js +75 -0
  216. package/test/unit/config.test.js +119 -0
  217. package/test/unit/data/commonHttpHeaders.json +6 -0
  218. package/test/unit/data/defaultConfig.json +58 -0
  219. package/test/unit/data/postQuotesBody.json +52 -0
  220. package/test/unit/data/putParticipantsBody.json +12 -0
  221. package/test/unit/data/putPartiesBody.json +20 -0
  222. package/test/unit/data/testFile.json +29 -0
  223. package/test/unit/data/testFile.yaml +14 -0
  224. package/test/unit/inboundApi/data/mockArguments.json +117 -0
  225. package/test/unit/inboundApi/data/mockTransactionRequest.json +42 -0
  226. package/test/unit/inboundApi/handlers.test.js +799 -0
  227. package/test/unit/index.test.js +55 -0
  228. package/test/unit/lib/cache.test.js +146 -0
  229. package/test/unit/lib/model/AccountsModel.test.js +121 -0
  230. package/test/unit/lib/model/AuthorizationsModel.test.js +460 -0
  231. package/test/unit/lib/model/InboundTransfersModel.test.js +628 -0
  232. package/test/unit/lib/model/OutboundBulkQuotesModel.test.js +249 -0
  233. package/test/unit/lib/model/OutboundBulkTransfersModel.test.js +244 -0
  234. package/test/unit/lib/model/OutboundRequestToPayModel.test.js +166 -0
  235. package/test/unit/lib/model/OutboundRequestToPayTransferModel.test.js +245 -0
  236. package/test/unit/lib/model/OutboundTransfersModel.test.js +836 -0
  237. package/test/unit/lib/model/PartiesModel.test.js +468 -0
  238. package/test/unit/lib/model/QuotesModel.test.js +470 -0
  239. package/test/unit/lib/model/TransfersModel.test.js +474 -0
  240. package/test/unit/lib/model/common/PersistentStateMachine.test.js +179 -0
  241. package/test/unit/lib/model/data/authorizationsResponse.json +13 -0
  242. package/test/unit/lib/model/data/bulkQuoteRequest.json +27 -0
  243. package/test/unit/lib/model/data/bulkQuoteResponse.json +35 -0
  244. package/test/unit/lib/model/data/bulkTransferFulfil.json +13 -0
  245. package/test/unit/lib/model/data/bulkTransferRequest.json +29 -0
  246. package/test/unit/lib/model/data/defaultConfig.json +47 -0
  247. package/test/unit/lib/model/data/getBulkTransfersBackendResponse.json +42 -0
  248. package/test/unit/lib/model/data/getBulkTransfersMojaloopResponse.json +22 -0
  249. package/test/unit/lib/model/data/getTransfersBackendResponse.json +34 -0
  250. package/test/unit/lib/model/data/getTransfersMojaloopResponse.json +17 -0
  251. package/test/unit/lib/model/data/mockArguments.json +131 -0
  252. package/test/unit/lib/model/data/mockTxnRequestsArguments.json +63 -0
  253. package/test/unit/lib/model/data/notificationToPayee.json +10 -0
  254. package/test/unit/lib/model/data/payeeParty.json +16 -0
  255. package/test/unit/lib/model/data/putAuthorizationsResponse.json +10 -0
  256. package/test/unit/lib/model/data/putQuotesResponse.json +33 -0
  257. package/test/unit/lib/model/data/putTransfersResponse.json +5 -0
  258. package/test/unit/lib/model/data/quoteResponse.json +31 -0
  259. package/test/unit/lib/model/data/requestToPayRequest.json +20 -0
  260. package/test/unit/lib/model/data/requestToPayTransferRequest.json +27 -0
  261. package/test/unit/lib/model/data/transactionRequestResponse.json +18 -0
  262. package/test/unit/lib/model/data/transferFulfil.json +8 -0
  263. package/test/unit/lib/model/data/transferRequest.json +26 -0
  264. package/test/unit/lib/model/mockedLibRequests.js +74 -0
  265. package/test/unit/mockLogger.js +39 -0
  266. package/test/unit/outboundApi/data/bulkQuoteRequest.json +28 -0
  267. package/test/unit/outboundApi/data/bulkTransferRequest.json +28 -0
  268. package/test/unit/outboundApi/data/mockBulkQuoteError.json +45 -0
  269. package/test/unit/outboundApi/data/mockBulkTransferError.json +48 -0
  270. package/test/unit/outboundApi/data/mockError.json +41 -0
  271. package/test/unit/outboundApi/data/mockGetPartiesError.json +4 -0
  272. package/test/unit/outboundApi/data/mockRequestToPayError.json +32 -0
  273. package/test/unit/outboundApi/data/mockRequestToPayTransferError.json +39 -0
  274. package/test/unit/outboundApi/data/requestToPay.json +21 -0
  275. package/test/unit/outboundApi/data/requestToPayTransferRequest.json +20 -0
  276. package/test/unit/outboundApi/data/transferRequest.json +21 -0
  277. package/test/unit/outboundApi/handlers.test.js +986 -0
@@ -0,0 +1,86 @@
1
+ /**************************************************************************
2
+ * (C) Copyright ModusBox Inc. 2020 - All rights reserved. *
3
+ * *
4
+ * This file is made available under the terms of the license agreement *
5
+ * specified in the corresponding source code repository. *
6
+ * *
7
+ * ORIGINAL AUTHOR: *
8
+ * Sridhar Voruganti - sridhar.voruganti@modusbox.com *
9
+ **************************************************************************/
10
+
11
+ 'use strict';
12
+
13
+ const Async2SyncModel = require('./Async2SyncModel');
14
+
15
+
16
+ // delegated methods for AuthorizationsModel
17
+
18
+ /**
19
+ * @name channelName
20
+ * @description generates the pub/sub channel name
21
+ * @param {object} - args
22
+ * @param {string} args.transactionRequestId - the transactionRequestId
23
+ * @param {string} [args.fspId] - ignored if passed - the destination fsp id
24
+ * @param {string} [args.authorization] - ignored if passed - the authorization payload
25
+ * @returns {string} - the pub/sub channel name
26
+ */
27
+ function channelName ({ transactionRequestId /* ,fspId, authorization - are not used here */ }) {
28
+ const tokens = ['authorizations', transactionRequestId];
29
+ return tokens.map(x => `${x}`).join('-');
30
+ }
31
+
32
+ /**
33
+ * @name requestAction
34
+ * @description invokes the call to switch
35
+ * @param {object} requests - MojaloopRequests instance
36
+ * @param {array} args - the arguments passed as object to `run` method
37
+ * @param {string} [args.transactionRequestId] - the transactionRequestId
38
+ * @param {string} args.fspId - the destination fsp id
39
+ * @param {string} args.authorization - the authorization
40
+ */
41
+ function requestAction (requests, { /* transactionRequestId, not used here */ fspId, authorization }) {
42
+ if (!fspId) {
43
+ throw new Error('AuthorizationsModel args requires \'fspId\' to be nonempty string');
44
+ }
45
+ return requests.postAuthorizations(authorization, fspId);
46
+ }
47
+
48
+ /**
49
+ * @name argsValidationMethod
50
+ * @description makes validation of args object, invoked in `run, triggerDeferredJob, generateKey` methods to ensure everything is going well
51
+ * @param {array} args - the arguments passed as object to `run` method
52
+ * @param {string} args.transactionRequestId - the transactionRequestId
53
+ * @param {string} args.fspId - the destination fsp id
54
+ * @param {string} [args.authorization] - ignored if passed - the authorization payload
55
+ */
56
+ function argsValidation ({ transactionRequestId, fspId /* ,authorization not used here */ }) {
57
+ if (!(transactionRequestId && typeof (transactionRequestId) === 'string' && transactionRequestId.length > 0)) {
58
+ throw new Error('AuthorizationsModel args requires \'transactionRequestId\' is nonempty string and mandatory property');
59
+ }
60
+ if (fspId && !(typeof (fspId) === 'string' && fspId.length > 0)) {
61
+ throw new Error('AuthorizationsModel args requires \'fspId\' to be nonempty string');
62
+ }
63
+ }
64
+
65
+ /**
66
+ * @name reformatMessage
67
+ * @description reformats message received from PUB/SUB channel, it is optional method, if not specified identify function is used by default
68
+ * @param {object} message - message received
69
+ * @returns {object} - reformatted message
70
+ */
71
+ function reformatMessage (message) {
72
+ return {
73
+ authorizations: { ...message }
74
+ };
75
+ }
76
+
77
+ // generate model
78
+ const AuthorizationsModel = Async2SyncModel.generate({
79
+ modelName: 'AuthorizationsModel',
80
+ channelNameMethod: channelName,
81
+ requestActionMethod: requestAction,
82
+ argsValidationMethod: argsValidation,
83
+ reformatMessageMethod: reformatMessage
84
+ });
85
+
86
+ module.exports = AuthorizationsModel;
@@ -0,0 +1,730 @@
1
+ /**************************************************************************
2
+ * (C) Copyright ModusBox Inc. 2019 - All rights reserved. *
3
+ * *
4
+ * This file is made available under the terms of the license agreement *
5
+ * specified in the corresponding source code repository. *
6
+ * *
7
+ * ORIGINAL AUTHOR: *
8
+ * James Bush - james.bush@modusbox.com *
9
+ **************************************************************************/
10
+
11
+ 'use strict';
12
+
13
+
14
+ const {
15
+ BackendRequests,
16
+ HTTPResponseError,
17
+ } = require('./lib/requests');
18
+ const {
19
+ MojaloopRequests,
20
+ Ilp,
21
+ Errors,
22
+ } = require('@mojaloop/sdk-standard-components');
23
+ const shared = require('./lib/shared');
24
+
25
+ /**
26
+ * Models the operations required for performing inbound transfers
27
+ */
28
+ class InboundTransfersModel {
29
+ constructor(config) {
30
+ this._cache = config.cache;
31
+ this._logger = config.logger;
32
+ this._dfspId = config.dfspId;
33
+ this._expirySeconds = config.expirySeconds;
34
+ this._rejectTransfersOnExpiredQuotes = config.rejectTransfersOnExpiredQuotes;
35
+ this._allowTransferWithoutQuote = config.allowTransferWithoutQuote;
36
+ this._reserveNotification = config.reserveNotification;
37
+
38
+ this._mojaloopRequests = new MojaloopRequests({
39
+ logger: this._logger,
40
+ peerEndpoint: config.peerEndpoint,
41
+ alsEndpoint: config.alsEndpoint,
42
+ quotesEndpoint: config.quotesEndpoint,
43
+ transfersEndpoint: config.transfersEndpoint,
44
+ bulkTransfersEndpoint: config.bulkTransfersEndpoint,
45
+ transactionRequestsEndpoint: config.transactionRequestsEndpoint,
46
+ bulkQuotesEndpoint: config.bulkQuotesEndpoint,
47
+ dfspId: config.dfspId,
48
+ tls: config.tls,
49
+ jwsSign: config.jwsSign,
50
+ jwsSigningKey: config.jwsSigningKey,
51
+ wso2: config.wso2,
52
+ resourceVersions: config.resourceVersions
53
+ });
54
+
55
+ this._backendRequests = new BackendRequests({
56
+ logger: this._logger,
57
+ backendEndpoint: config.backendEndpoint,
58
+ dfspId: config.dfspId
59
+ });
60
+
61
+ this._checkIlp = config.checkIlp;
62
+
63
+ this._ilp = new Ilp({
64
+ secret: config.ilpSecret,
65
+ logger: this._logger,
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Queries the backend API for the specified party and makes a callback to the originator with the result
71
+ */
72
+ async getAuthorizations(transactionRequestId, sourceFspId) {
73
+ try {
74
+ // make a call to the backend to resolve the party lookup
75
+ const response = await this._backendRequests.getOTP(transactionRequestId, sourceFspId);
76
+
77
+ if(!response) {
78
+ return 'No response from backend';
79
+ }
80
+
81
+ // project our internal otp representation into a mojaloop authorization response body
82
+ const mlAuthorization = {
83
+ authenticationInfo : {
84
+ authentication: 'OTP',
85
+ authenticationValue: `${response.otpValue}`
86
+ },
87
+ responseType: 'ENTERED'
88
+ };
89
+ // make a callback to the source fsp with the party info
90
+ return this._mojaloopRequests.putAuthorizations(transactionRequestId, mlAuthorization, sourceFspId);
91
+ }
92
+ catch(err) {
93
+ this._logger.push({ err }).log('Error in getOTP');
94
+ const mojaloopError = await this._handleError(err);
95
+ this._logger.push({ mojaloopError }).log(`Sending error response to ${sourceFspId}`);
96
+ return this._mojaloopRequests.putAuthorizationsError(transactionRequestId,
97
+ mojaloopError, sourceFspId);
98
+ }
99
+ }
100
+
101
+
102
+ /**
103
+ * Queries the backend API for the specified party and makes a callback to the originator with our dfspId if found
104
+ */
105
+ async getParticipants(idType, idValue, idSubValue, sourceFspId) {
106
+ try {
107
+ // make a call to the backend to resolve the party lookup
108
+ const response = await this._backendRequests.getParties(idType, idValue, idSubValue);
109
+
110
+ if(!response) {
111
+ return 'No response from backend';
112
+ }
113
+
114
+ // make a callback to the source fsp with our dfspId indicating we own the party
115
+ return this._mojaloopRequests.putParticipants(idType, idValue, idSubValue, { fspId: this._dfspId },
116
+ sourceFspId);
117
+ }
118
+ catch(err) {
119
+ this._logger.push({ err }).log('Error in getParticipants');
120
+ const mojaloopError = await this._handleError(err);
121
+ this._logger.push({ mojaloopError }).log(`Sending error response to ${sourceFspId}`);
122
+ return this._mojaloopRequests.putParticipantsError(idType, idValue, idSubValue,
123
+ mojaloopError, sourceFspId);
124
+ }
125
+ }
126
+
127
+
128
+ /**
129
+ * Queries the backend API for the specified party and makes a callback to the originator with the result
130
+ */
131
+ async getParties(idType, idValue, idSubValue, sourceFspId) {
132
+ try {
133
+ // make a call to the backend to resolve the party lookup
134
+ const response = await this._backendRequests.getParties(idType, idValue, idSubValue);
135
+
136
+ if(!response) {
137
+ return 'No response from backend';
138
+ }
139
+
140
+ // project our internal party representation into a mojaloop partyies request body
141
+ const mlParty = {
142
+ party: shared.internalPartyToMojaloopParty(response, this._dfspId)
143
+ };
144
+
145
+ // make a callback to the source fsp with the party info
146
+ return this._mojaloopRequests.putParties(idType, idValue, idSubValue, mlParty, sourceFspId);
147
+ }
148
+ catch(err) {
149
+ this._logger.push({ err }).log('Error in getParties');
150
+ const mojaloopError = await this._handleError(err);
151
+ this._logger.push({ mojaloopError }).log(`Sending error response to ${sourceFspId}`);
152
+ return this._mojaloopRequests.putPartiesError(idType, idValue, idSubValue,
153
+ mojaloopError, sourceFspId);
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Asks the backend for a response to an incoming quote request and makes a callback to the originator with
159
+ * the result
160
+ */
161
+ async quoteRequest(quoteRequest, sourceFspId) {
162
+ try {
163
+ const internalForm = shared.mojaloopQuoteRequestToInternal(quoteRequest);
164
+
165
+ // make a call to the backend to ask for a quote response
166
+ const response = await this._backendRequests.postQuoteRequests(internalForm);
167
+
168
+ if(!response) {
169
+ // make an error callback to the source fsp
170
+ return 'No response from backend';
171
+ }
172
+
173
+ if(!response.expiration) {
174
+ const expiration = new Date().getTime() + (this._expirySeconds * 1000);
175
+ response.expiration = new Date(expiration).toISOString();
176
+ }
177
+
178
+ // project our internal quote reponse into mojaloop quote response form
179
+ const mojaloopResponse = shared.internalQuoteResponseToMojaloop(response);
180
+
181
+ // create our ILP packet and condition and tag them on to our internal quote response
182
+ const { fulfilment, ilpPacket, condition } = this._ilp.getQuoteResponseIlp(quoteRequest, mojaloopResponse);
183
+
184
+ mojaloopResponse.ilpPacket = ilpPacket;
185
+ mojaloopResponse.condition = condition;
186
+
187
+ // now store the fulfilment and the quote data against the quoteId in our cache
188
+ await this._cache.set(`quote_${quoteRequest.transactionId}`, {
189
+ request: quoteRequest,
190
+ internalRequest: internalForm,
191
+ response: response,
192
+ mojaloopResponse: mojaloopResponse,
193
+ fulfilment: fulfilment
194
+ });
195
+
196
+ // now store the quoteRespnse data against the quoteId in our cache to be sent as a response to GET /quotes/{ID}
197
+ await this._cache.set(`quoteResponse_${quoteRequest.quoteId}`, mojaloopResponse);
198
+
199
+ // make a callback to the source fsp with the quote response
200
+ return this._mojaloopRequests.putQuotes(quoteRequest.quoteId, mojaloopResponse, sourceFspId);
201
+ }
202
+ catch(err) {
203
+ this._logger.push({ err }).log('Error in quoteRequest');
204
+ const mojaloopError = await this._handleError(err);
205
+ this._logger.push({ mojaloopError }).log(`Sending error response to ${sourceFspId}`);
206
+ return await this._mojaloopRequests.putQuotesError(quoteRequest.quoteId,
207
+ mojaloopError, sourceFspId);
208
+ }
209
+ }
210
+
211
+ /**
212
+ * This is executed as when GET /quotes/{ID} request is made to get the response of a previous POST /quotes request.
213
+ * Gets the quoteResponse from the cache and makes a callback to the originator with result
214
+ */
215
+ async getQuoteRequest(quoteId, sourceFspId) {
216
+ try {
217
+ // Get the quoteRespnse data for the quoteId from the cache to be sent as a response to GET /quotes/{ID}
218
+ const quoteResponse = await this._cache.get(`quoteResponse_${quoteId}`);
219
+
220
+ // If no quoteResponse is found in the cache, make an error callback to the source fsp
221
+ if (!quoteResponse) {
222
+ const err = new Error('Quote Id not found');
223
+ const mojaloopError = await this._handleError(err, Errors.MojaloopApiErrorCodes.QUOTE_ID_NOT_FOUND);
224
+ this._logger.push({ mojaloopError }).log(`Sending error response to ${sourceFspId}`);
225
+ return await this._mojaloopRequests.putQuotesError(quoteId,
226
+ mojaloopError, sourceFspId);
227
+ }
228
+ // Make a PUT /quotes/{ID} callback to the source fsp with the quote response
229
+ return this._mojaloopRequests.putQuotes(quoteId, quoteResponse, sourceFspId);
230
+ }
231
+ catch(err) {
232
+ this._logger.push({ err }).log('Error in getQuoteRequest');
233
+ const mojaloopError = await this._handleError(err);
234
+ this._logger.push({ mojaloopError }).log(`Sending error response to ${sourceFspId}`);
235
+ return await this._mojaloopRequests.putQuotesError(quoteId,
236
+ mojaloopError, sourceFspId);
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Asks the backend for a response to an incoming transactoin request and makes a callback to the originator with
242
+ * the result
243
+ */
244
+ async transactionRequest(transactionRequest, sourceFspId) {
245
+ try {
246
+ const internalForm = shared.mojaloopTransactionRequestToInternal(transactionRequest);
247
+
248
+ // make a call to the backend to ask for a quote response
249
+ const response = await this._backendRequests.postTransactionRequests(internalForm);
250
+
251
+ if(!response) {
252
+ // make an error callback to the source fsp
253
+ return 'No response from backend';
254
+ }
255
+
256
+ // project our internal quote reponse into mojaloop quote response form
257
+ const mojaloopResponse = shared.internalTransactionRequestResponseToMojaloop(response);
258
+
259
+ // make a callback to the source fsp with the quote response
260
+ return this._mojaloopRequests.putTransactionRequests(transactionRequest.transactionRequestId, mojaloopResponse, sourceFspId);
261
+ }
262
+ catch(err) {
263
+ this._logger.push({ err }).log('Error in transactionRequest');
264
+ const mojaloopError = await this._handleError(err);
265
+ this._logger.push({ mojaloopError }).log(`Sending error response to ${sourceFspId}`);
266
+ return await this._mojaloopRequests.putTransactionRequestsError(transactionRequest.transactionRequestId,
267
+ mojaloopError, sourceFspId);
268
+ }
269
+ }
270
+
271
+
272
+ /**
273
+ * Validates an incoming transfer prepare request and makes a callback to the originator with
274
+ * the result
275
+ */
276
+ async prepareTransfer(prepareRequest, sourceFspId) {
277
+ try {
278
+
279
+ // retrieve our quote data
280
+ const quote = await this._cache.get(`quote_${prepareRequest.transferId}`);
281
+
282
+ if(!quote) {
283
+ // Check whether to allow transfers without a previous quote.
284
+ if(!this._allowTransferWithoutQuote) {
285
+ throw new Error(`Corresponding quote not found for transfer ${prepareRequest.transferId}`);
286
+ }
287
+ }
288
+
289
+ // Calculate or retrieve fulfilment and condition
290
+ let fulfilment = null;
291
+ let condition = null;
292
+ if(quote) {
293
+ fulfilment = quote.fulfilment;
294
+ condition = quote.mojaloopResponse.condition;
295
+ }
296
+ else {
297
+ fulfilment = this._ilp.calculateFulfil(prepareRequest.ilpPacket);
298
+ condition = this._ilp.calculateConditionFromFulfil(fulfilment);
299
+ }
300
+
301
+ // check incoming ILP matches our persisted values
302
+ if(this._checkIlp && (prepareRequest.condition !== condition)) {
303
+ throw new Error(`ILP condition in transfer prepare for ${prepareRequest.transferId} does not match quote`);
304
+ }
305
+
306
+
307
+ if (quote && this._rejectTransfersOnExpiredQuotes) {
308
+ const now = new Date().toISOString();
309
+ const expiration = quote.mojaloopResponse.expiration;
310
+ if (now > expiration) {
311
+ const error = Errors.MojaloopApiErrorObjectFromCode(Errors.MojaloopApiErrorCodes.QUOTE_EXPIRED);
312
+ this._logger.error(`Error in prepareTransfer: quote expired for transfer ${prepareRequest.transferId}, system time=${now} > quote time=${expiration}`);
313
+ return this._mojaloopRequests.putTransfersError(prepareRequest.transferId, error, sourceFspId);
314
+ }
315
+ }
316
+
317
+ // project the incoming transfer prepare into an internal transfer request
318
+ const internalForm = shared.mojaloopPrepareToInternalTransfer(prepareRequest, quote, this._ilp);
319
+
320
+ // make a call to the backend to inform it of the incoming transfer
321
+ const response = await this._backendRequests.postTransfers(internalForm);
322
+
323
+ if(!response) {
324
+ // make an error callback to the source fsp
325
+ return 'No response from backend';
326
+ }
327
+
328
+ this._logger.log(`Transfer accepted by backend returning homeTransactionId: ${response.homeTransactionId} for mojaloop transferId: ${prepareRequest.transferId}`);
329
+
330
+ // create a mojaloop transfer fulfil response
331
+ const mojaloopResponse = {
332
+ completedTimestamp: new Date(),
333
+ transferState: this._reserveNotification ? 'RESERVED' : 'COMMITTED',
334
+ fulfilment: fulfilment,
335
+ ...response.extensionList && {
336
+ extensionList: {
337
+ extension: response.extensionList,
338
+ },
339
+ },
340
+ };
341
+
342
+ // make a callback to the source fsp with the transfer fulfilment
343
+ return this._mojaloopRequests.putTransfers(prepareRequest.transferId, mojaloopResponse,
344
+ sourceFspId);
345
+ }
346
+ catch(err) {
347
+ this._logger.push({ err }).log('Error in prepareTransfer');
348
+ const mojaloopError = await this._handleError(err);
349
+ this._logger.push({ mojaloopError }).log(`Sending error response to ${sourceFspId}`);
350
+ return await this._mojaloopRequests.putTransfersError(prepareRequest.transferId,
351
+ mojaloopError, sourceFspId);
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Queries details of a transfer
357
+ */
358
+ async getTransfer(transferId, sourceFspId) {
359
+ try {
360
+ // make a call to the backend to get transfer details
361
+ const response = await this._backendRequests.getTransfers(transferId);
362
+
363
+ if (!response) {
364
+ return 'No response from backend';
365
+ }
366
+
367
+ const ilpPaymentData = {
368
+ transferId: transferId,
369
+ homeTransactionId: response.homeTransactionId,
370
+ from: shared.internalPartyToMojaloopParty(response.from, response.from.fspId),
371
+ to: shared.internalPartyToMojaloopParty(response.to, response.to.fspId),
372
+ amountType: response.amountType,
373
+ currency: response.currency,
374
+ amount: response.amount,
375
+ transactionType: response.transactionType,
376
+ note: response.note,
377
+ };
378
+
379
+ let fulfilment;
380
+ if (this._dfspId === response.to.fspId) {
381
+ fulfilment = this._ilp.getResponseIlp(ilpPaymentData).fulfilment;
382
+ }
383
+
384
+ // create a mojaloop transfer fulfil response
385
+ const mojaloopResponse = {
386
+ completedTimestamp: response.timestamp,
387
+ transferState: response.transferState,
388
+ fulfilment,
389
+ ...response.extensions && {
390
+ extensionList: {
391
+ extension: response.extensions,
392
+ },
393
+ },
394
+ };
395
+
396
+ // make a callback to the source fsp with the transfer fulfilment
397
+ return this._mojaloopRequests.putTransfers(transferId, mojaloopResponse,
398
+ sourceFspId);
399
+ }
400
+ catch (err) {
401
+ this._logger.push({ err }).log('Error in getTransfers');
402
+ const mojaloopError = await this._handleError(err);
403
+ this._logger.push({ mojaloopError }).log(`Sending error response to ${sourceFspId}`);
404
+ return this._mojaloopRequests.putTransfersError(transferId,
405
+ mojaloopError, sourceFspId);
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Asks the backend for a response to an incoming bulk quotes request and makes a callback to the originator with
411
+ * the results.
412
+ */
413
+ async bulkQuoteRequest(bulkQuoteRequest, sourceFspId) {
414
+ const { bulkQuoteId } = bulkQuoteRequest;
415
+ const fulfilments = {};
416
+ try {
417
+ const internalForm = shared.mojaloopBulkQuotesRequestToInternal(bulkQuoteRequest);
418
+
419
+ // make a call to the backend to ask for bulk quotes response
420
+ const response = await this._backendRequests.postBulkQuotes(internalForm);
421
+
422
+ if (!response) {
423
+ // make an error callback to the source fsp
424
+ return 'No response from backend';
425
+ }
426
+
427
+ if (!response.expiration) {
428
+ const expiration = new Date().getTime() + (this._expirySeconds * 1000);
429
+ response.expiration = new Date(expiration).toISOString();
430
+ }
431
+
432
+ // project our internal bulk quotes response into mojaloop bulk quotes response form
433
+ const mojaloopResponse = shared.internalBulkQuotesResponseToMojaloop(response);
434
+
435
+ // create our ILP packet and condition and tag them on to our internal quote response
436
+ bulkQuoteRequest.individualQuotes.map((quote) => {
437
+ const quoteRequest = {
438
+ transactionId: quote.transactionId,
439
+ quoteId: quote.quoteId,
440
+ payee: quote.payee,
441
+ payer: bulkQuoteRequest.payer,
442
+ transactionType: quote.transactionType,
443
+ };
444
+ // TODO: Optimize with a HashMap
445
+ const mojaloopIndividualQuote = mojaloopResponse.individualQuoteResults.find(
446
+ (quoteResult) => quoteResult.quoteId === quote.quoteId
447
+ );
448
+ const quoteResponse = {
449
+ transferAmount: mojaloopIndividualQuote.transferAmount,
450
+ note: mojaloopIndividualQuote.note || '',
451
+ };
452
+ const { fulfilment, ilpPacket, condition } = this._ilp.getQuoteResponseIlp(
453
+ quoteRequest, quoteResponse);
454
+
455
+ // mutate individual quotes in `mojaloopResponse`
456
+ mojaloopIndividualQuote.ilpPacket = ilpPacket;
457
+ mojaloopIndividualQuote.condition = condition;
458
+
459
+ fulfilments[quote.quoteId] = fulfilment;
460
+ });
461
+
462
+ // now store the fulfilments and the bulk quotes data against the bulkQuoteId in our cache
463
+ await this._cache.set(`bulkQuotes_${bulkQuoteId}`, {
464
+ request: bulkQuoteRequest,
465
+ internalRequest: internalForm,
466
+ mojaloopResponse: mojaloopResponse,
467
+ response,
468
+ fulfilments
469
+ });
470
+
471
+ // make a callback to the source fsp with the quote response
472
+ return this._mojaloopRequests.putBulkQuotes(bulkQuoteId, mojaloopResponse, sourceFspId);
473
+ }
474
+ catch (err) {
475
+ this._logger.push({ err }).log('Error in bulkQuotesRequest');
476
+ const mojaloopError = await this._handleError(err);
477
+ this._logger.push({ mojaloopError }).log(`Sending error response to ${sourceFspId}`);
478
+ return await this._mojaloopRequests.putBulkQuotesError(bulkQuoteId,
479
+ mojaloopError, sourceFspId);
480
+ }
481
+ }
482
+
483
+ /**
484
+ * Queries details of a bulk quote
485
+ */
486
+ async getBulkQuote(bulkQuoteId, sourceFspId) {
487
+ try {
488
+ // make a call to the backend to get bulk quote details
489
+ const response = await this._backendRequests.getBulkQuotes(bulkQuoteId);
490
+
491
+ if (!response) {
492
+ return 'No response from backend';
493
+ }
494
+
495
+ // project our internal quote reponse into mojaloop bulk quote response form
496
+ const mojaloopResponse = shared.internalBulkQuotesResponseToMojaloop(response);
497
+
498
+ // make a callback to the source fsp with the bulk quote response
499
+ return this._mojaloopRequests.putBulkQuotes(bulkQuoteId, mojaloopResponse,
500
+ sourceFspId);
501
+ }
502
+ catch (err) {
503
+ this._logger.push({ err }).log('Error in getBulkQuote');
504
+ const mojaloopError = await this._handleError(err);
505
+ this._logger.push({ mojaloopError }).log(`Sending error response to ${sourceFspId}`);
506
+ return this._mojaloopRequests.putBulkQuotesError(bulkQuoteId,
507
+ mojaloopError, sourceFspId);
508
+ }
509
+ }
510
+
511
+ /**
512
+ * Validates an incoming bulk transfer prepare request and makes a callback to the originator with
513
+ * the result
514
+ */
515
+ async prepareBulkTransfer(bulkPrepareRequest, sourceFspId) {
516
+ try {
517
+ // retrieve bulk quote data
518
+ const bulkQuote = await this._cache.get(`bulkQuotes_${bulkPrepareRequest.bulkQuoteId}`);
519
+
520
+ if (!bulkQuote) {
521
+ // Check whether to allow transfers without a previous quote.
522
+ if (!this._allowTransferWithoutQuote) {
523
+ throw new Error(`Corresponding bulk quotes not found for bulk transfers ${bulkPrepareRequest.bulkTransferId}`);
524
+ }
525
+ }
526
+
527
+ // create an index of individual quote results indexed by transactionId for faster lookups
528
+ const quoteResultsByTrxId = {};
529
+
530
+ if (bulkQuote && bulkQuote.mojaloopResponse && bulkQuote.mojaloopResponse.individualQuoteResults) {
531
+ for (const quoteResult of bulkQuote.mojaloopResponse.individualQuoteResults) {
532
+ quoteResultsByTrxId[quoteResult.transactionId] = quoteResult;
533
+ }
534
+ }
535
+
536
+ // transfer fulfilments
537
+ const fulfilments = {};
538
+
539
+ // collect errors for each transfer
540
+ let individualTransferErrors = [];
541
+
542
+ // validate individual transfer
543
+ for (const transfer of bulkPrepareRequest.individualTransfers) {
544
+ // decode ilpPacked for this transfer to get transaction object
545
+ const transactionObject = this._ilp.getTransactionObject(transfer.ilpPacket);
546
+
547
+ // we use the transactionId from the decoded ilpPacked in the transfer to match a corresponding quote
548
+ const quote = quoteResultsByTrxId[transactionObject.transactionId] || null;
549
+
550
+ // calculate or retrieve fulfilments and conditions
551
+ let fulfilment = null;
552
+ let condition = null;
553
+
554
+ if (quote) {
555
+ fulfilment = bulkQuote.fulfilments[quote.quoteId];
556
+ condition = quote.condition;
557
+ }
558
+ else {
559
+ fulfilment = this._ilp.calculateFulfil(transfer.ilpPacket);
560
+ condition = this._ilp.calculateConditionFromFulfil(fulfilment);
561
+ }
562
+
563
+ fulfilments[transfer.transferId] = fulfilment;
564
+
565
+ // check incoming ILP matches our persisted values
566
+ if (this._checkIlp && (transfer.condition !== condition)) {
567
+ const transferError = this._handleError(new Error(`ILP condition in bulk transfers prepare for ${transfer.transferId} does not match quote`));
568
+ individualTransferErrors.push({ transferId: transfer.transferId, transferError });
569
+ }
570
+ }
571
+
572
+ if (bulkQuote && this._rejectTransfersOnExpiredQuotes) {
573
+ const now = new Date();
574
+ const expiration = new Date(bulkQuote.mojaloopResponse.expiration);
575
+ if (now > expiration) {
576
+ // TODO: Verify and align with actual schema for bulk transfers error endpoint
577
+ const error = Errors.MojaloopApiErrorObjectFromCode(Errors.MojaloopApiErrorCodes.QUOTE_EXPIRED);
578
+ this._logger.error(`Error in prepareBulkTransfers: bulk quotes expired for bulk transfers ${bulkPrepareRequest.bulkTransferId}, system time=${now.toISOString()} > quote time=${expiration.toISOString()}`);
579
+ return this._mojaloopRequests.putBulkTransfersError(bulkPrepareRequest.bulkTransferId, error, sourceFspId);
580
+ }
581
+ }
582
+
583
+ if (individualTransferErrors.length) {
584
+ // TODO: Verify and align with actual schema for bulk transfers error endpoint
585
+ const mojaloopErrorResponse = {
586
+ bulkTransferState: 'REJECTED',
587
+ // eslint-disable-next-line no-unused-vars
588
+ individualTransferResults: individualTransferErrors.map(({ transferId, transferError }) => ({
589
+ transferId,
590
+ errorInformation: transferError,
591
+ }))
592
+ };
593
+ this._logger.push({ ...individualTransferErrors }).log('Error in prepareBulkTransfers');
594
+ this._logger.push({ ...individualTransferErrors }).log(`Sending error response to ${sourceFspId}`);
595
+
596
+ return await this._mojaloopRequests.putBulkTransfersError(bulkPrepareRequest.transferId,
597
+ mojaloopErrorResponse, sourceFspId);
598
+ }
599
+
600
+ // project the incoming bulk transfer prepare into an internal bulk transfer request
601
+ const internalForm = shared.mojaloopBulkPrepareToInternalBulkTransfer(bulkPrepareRequest, bulkQuote, this._ilp);
602
+
603
+ // make a call to the backend to inform it of the incoming bulk transfer
604
+ const response = await this._backendRequests.postBulkTransfers(internalForm);
605
+
606
+ if (!response) {
607
+ // make an error callback to the source fsp
608
+ return 'No response from backend';
609
+ }
610
+
611
+ this._logger.log(`Bulk transfer accepted by backend returning homeTransactionId: ${response.homeTransactionId} for mojaloop bulk transferId: ${bulkPrepareRequest.bulkTransferId}`);
612
+
613
+ // create a mojaloop transfer fulfil response
614
+ const mojaloopResponse = {
615
+ completedTimestamp: new Date(),
616
+ bulkTransferState: 'COMMITTED',
617
+ };
618
+
619
+ if (response.individualTransferResults && response.individualTransferResults.length) {
620
+ // eslint-disable-next-line no-unused-vars
621
+ mojaloopResponse.individualTransferResults = response.individualTransferResults.map((transfer) => {
622
+ return {
623
+ transferId: transfer.transferId,
624
+ fulfilment: fulfilments[transfer.transferId],
625
+ ...transfer.extensionList && {
626
+ extensionList: {
627
+ extension: transfer.extensionList,
628
+ },
629
+ }
630
+ };
631
+ });
632
+ }
633
+
634
+ // make a callback to the source fsp with the transfer fulfilment
635
+ return this._mojaloopRequests.putBulkTransfers(bulkPrepareRequest.bulkTransferId, mojaloopResponse, sourceFspId);
636
+ }
637
+ catch (err) {
638
+ this._logger.push({ err }).log('Error in prepareBulkTransfers');
639
+ const mojaloopError = await this._handleError(err);
640
+ this._logger.push({ mojaloopError }).log(`Sending error response to ${sourceFspId}`);
641
+ return await this._mojaloopRequests.putBulkTransfersError(bulkPrepareRequest.bulkTransferId,
642
+ mojaloopError, sourceFspId);
643
+ }
644
+ }
645
+
646
+ /**
647
+ * Queries details of a bulk transfer
648
+ */
649
+ async getBulkTransfer(bulkTransferId, sourceFspId) {
650
+ try {
651
+ // make a call to the backend to get bulk transfer details
652
+ const response = await this._backendRequests.getBulkTransfers(bulkTransferId);
653
+
654
+ if (!response) {
655
+ return 'No response from backend';
656
+ }
657
+
658
+ let individualTransferResults = [];
659
+
660
+ for (const transfer of response.internalRequest.individualTransfers) {
661
+ const ilpPaymentData = {
662
+ transferId: transfer.transferId,
663
+ to: shared.internalPartyToMojaloopParty(transfer.to, transfer.to.fspId),
664
+ amountType: transfer.amountType,
665
+ currency: transfer.currency,
666
+ amount: transfer.amount,
667
+ transactionType: transfer.transactionType,
668
+ note: transfer.note,
669
+ };
670
+ let fulfilment;
671
+ if (this._dfspId === transfer.to.fspId) {
672
+ fulfilment = this._ilp.getResponseIlp(ilpPaymentData).fulfilment;
673
+ }
674
+ const transferResult = { transferId: transfer.transferId, fulfilment };
675
+ transfer.errorInformation && (transferResult.errorInformation = transfer.errorInformation);
676
+ transfer.extensionList && (transferResult.extensionList = transfer.extensionList);
677
+ individualTransferResults.push(transferResult);
678
+ }
679
+
680
+ // create a mojaloop bulk transfer fulfil response
681
+ const mojaloopResponse = {
682
+ completedTimestamp: response.timestamp,
683
+ bulkTransferState: response.bulkTransferState,
684
+ individualTransferResults,
685
+ ...response.extensions && {
686
+ extensionList: {
687
+ extension: response.extensions,
688
+ },
689
+ },
690
+ };
691
+
692
+ // make a callback to the source fsp with the bulk transfer fulfilments
693
+ return this._mojaloopRequests.putBulkTransfers(bulkTransferId, mojaloopResponse,
694
+ sourceFspId);
695
+ }
696
+ catch (err) {
697
+ this._logger.push({ err }).log('Error in getBulkTransfer');
698
+ const mojaloopError = await this._handleError(err);
699
+ this._logger.push({ mojaloopError }).log(`Sending error response to ${sourceFspId}`);
700
+ return this._mojaloopRequests.putBulkTransfersError(bulkTransferId,
701
+ mojaloopError, sourceFspId);
702
+ }
703
+ }
704
+
705
+ /**
706
+ * Forwards Switch notification for fulfiled transfer to the DFSP backend, when acting as a payee
707
+ */
708
+ async sendNotificationToPayee(body, transferId) {
709
+ try {
710
+ const res = await this._backendRequests.putTransfersNotification(body, transferId);
711
+ return res;
712
+ } catch (err) {
713
+ this._logger.push({ err }).log('Error in sendNotificationToPayee');
714
+ }
715
+ }
716
+
717
+ async _handleError(err, mojaloopErrorCode = Errors.MojaloopApiErrorCodes.INTERNAL_SERVER_ERROR) {
718
+ if(err instanceof HTTPResponseError) {
719
+ const e = err.getData();
720
+ if(e.res && e.res.data) {
721
+ mojaloopErrorCode = Errors.MojaloopApiErrorCodeFromCode(`${e.res.data.statusCode}`);
722
+ }
723
+ }
724
+
725
+ return new Errors.MojaloopFSPIOPError(err, null, null, mojaloopErrorCode).toApiErrorObject();
726
+ }
727
+ }
728
+
729
+
730
+ module.exports = InboundTransfersModel;