@mojaloop/central-ledger 19.14.0 → 20.1.0

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 (394) hide show
  1. package/.grype.yaml +3 -0
  2. package/.license-scanner.json +16 -0
  3. package/.ncurc.yaml +19 -13
  4. package/.nvmrc +1 -1
  5. package/.nycrc.yml +14 -2
  6. package/CHANGELOG.md +51 -0
  7. package/Dockerfile +13 -10
  8. package/README.md +39 -89
  9. package/audit-ci.jsonc +1 -1
  10. package/config/default.json +60 -3
  11. package/config/deprecated.default-settlement.json +160 -0
  12. package/docker/ml-api-adapter/default.json +28 -0
  13. package/docker-compose.yml +6 -0
  14. package/nodemon.json +6 -0
  15. package/package.json +128 -64
  16. package/{sbom-v19.13.2.csv → sbom-v20.0.0.csv} +886 -736
  17. package/scripts/_wait4_all.js +5 -5
  18. package/scripts/transferSettlementTemp/interchangeFeeCalculation.js +51 -0
  19. package/src/api/index.ts +30 -0
  20. package/src/api/interface/swagger.json +2249 -2054
  21. package/src/api/participants/routes.js +1 -1
  22. package/src/api/root/handler.int.ts +55 -0
  23. package/src/api/routes.js +7 -6
  24. package/src/api/server.ts +27 -0
  25. package/src/api/server.unit.ts +16 -0
  26. package/src/api/settlementModels/routes.js +4 -4
  27. package/src/domain/participant/index.d.ts +47 -0
  28. package/src/domain/participant/index.int.ts +724 -0
  29. package/src/domain/participant/index.js +4 -5
  30. package/src/domain/position/fx-prepare.js +1 -1
  31. package/src/domain/position/fx-timeout-reserved.js +6 -0
  32. package/src/domain/position/prepare.js +1 -1
  33. package/src/handlers/admin/handler.js +1 -1
  34. package/src/handlers/index.js +30 -0
  35. package/src/handlers/positions/handler.js +0 -2
  36. package/src/handlers/positions/handlerBatch.js +5 -5
  37. package/src/handlers/register.js +44 -29
  38. package/src/handlers/timeouts/handler.js +1 -4
  39. package/src/handlers/transfers/fx-abort.int.ts +209 -0
  40. package/src/handlers/transfers/fx-handlers.int.ts +275 -0
  41. package/src/handlers/transfers/fx-timeout.int.ts +284 -0
  42. package/src/handlers/transfers/handler.js +1 -3
  43. package/src/handlers/transfers/handlers.int.ts +538 -0
  44. package/src/handlers/transfers/prepare.int.ts +1283 -0
  45. package/src/handlers/transfers/prepare.js +2 -2
  46. package/src/knexfile.js +19 -0
  47. package/src/lib/cache.js +1 -1
  48. package/src/lib/config/index.ts +60 -0
  49. package/src/lib/config/resolver.ts +231 -0
  50. package/src/lib/config/resolver.unit.ts +72 -0
  51. package/src/lib/config/types.ts +372 -0
  52. package/src/lib/config/util.ts +523 -0
  53. package/src/lib/config/util.unit.ts +825 -0
  54. package/src/lib/migrator.js +3 -3
  55. package/src/lib/proxyCache.js +3 -3
  56. package/{migrations → src/migrations}/100100_event.js +1 -1
  57. package/{migrations → src/migrations}/110100_contactType.js +1 -1
  58. package/{migrations → src/migrations}/110200_currency.js +1 -1
  59. package/{migrations → src/migrations}/110201_currency-scale.js +1 -1
  60. package/{migrations → src/migrations}/110300_endpointType.js +1 -1
  61. package/{migrations → src/migrations}/110400_ledgerEntryType.js +1 -1
  62. package/{migrations → src/migrations}/110450_ledgerAccountType.js +1 -1
  63. package/{migrations → src/migrations}/110500_participantLimitType.js +1 -1
  64. package/{migrations → src/migrations}/110600_transferParticipantRoleType.js +1 -1
  65. package/{migrations → src/migrations}/110700_transferState.js +1 -1
  66. package/{migrations → src/migrations}/110900_settlementState.js +1 -1
  67. package/{migrations → src/migrations}/112000_bulkTransferState.js +1 -1
  68. package/{migrations → src/migrations}/112100_bulkProcessingState.js +1 -1
  69. package/{migrations → src/migrations}/200100_participant.js +1 -1
  70. package/{migrations → src/migrations}/200200_participantContact.js +1 -1
  71. package/{migrations → src/migrations}/200300_participantEndpoint.js +1 -1
  72. package/{migrations → src/migrations}/200400_participantParty.js +1 -1
  73. package/{migrations → src/migrations}/200600_token.js +1 -1
  74. package/{migrations → src/migrations}/200601_token-indexes.js +1 -1
  75. package/{migrations → src/migrations}/300100_transferDuplicateCheck.js +2 -2
  76. package/{migrations → src/migrations}/300150_bulkTransferDuplicateCheck.js +2 -2
  77. package/{migrations → src/migrations}/300200_transfer.js +2 -2
  78. package/{migrations → src/migrations}/300202_transfer-decimal184.js +1 -1
  79. package/{migrations → src/migrations}/300250_bulkTransfer.js +2 -3
  80. package/{migrations → src/migrations}/300275_bulkTransferAssociation.js +2 -3
  81. package/{migrations → src/migrations}/300300_ilpPacket.js +1 -1
  82. package/{migrations → src/migrations}/300400_transferStateChange.js +1 -1
  83. package/{migrations → src/migrations}/300450_bulkTransferStateChange.js +1 -1
  84. package/{migrations → src/migrations}/300500_segment.js +1 -1
  85. package/{migrations → src/migrations}/310100_participantCurrency.js +1 -1
  86. package/{migrations → src/migrations}/310200_transferParticipant.js +1 -1
  87. package/{migrations → src/migrations}/310202_transferParticipant-decimal184.js +1 -1
  88. package/{migrations → src/migrations}/310204_transferParticipant-participantId.js +2 -2
  89. package/{migrations → src/migrations}/310300_participantPosition.js +1 -1
  90. package/{migrations → src/migrations}/310302_participantPosition-decimal184.js +1 -1
  91. package/{migrations → src/migrations}/310400_participantPositionChange.js +1 -1
  92. package/{migrations → src/migrations}/310402_participantPositionChange-decimal184.js +1 -1
  93. package/{migrations → src/migrations}/310403_participantPositionChange-participantCurrencyId.js +2 -2
  94. package/{migrations → src/migrations}/310404_participantPositionChange-change.js +2 -2
  95. package/{migrations → src/migrations}/310500_participantLimit.js +1 -1
  96. package/{migrations → src/migrations}/310502_participantLimit-decimal184.js +1 -1
  97. package/{migrations → src/migrations}/400100_settlementWindow.js +1 -1
  98. package/{migrations → src/migrations}/400200_settlement.js +1 -1
  99. package/{migrations → src/migrations}/400300_settlementSettlementWindow.js +1 -1
  100. package/{migrations → src/migrations}/400301_settlementSettlementWindow-indexes.js +0 -1
  101. package/{migrations → src/migrations}/400400_settlementWindowStateChange.js +1 -1
  102. package/{migrations → src/migrations}/400500_settlementTransferParticipant.js +1 -1
  103. package/{migrations → src/migrations}/400502_settlementTransferParticipant-decimal184.js +1 -1
  104. package/{migrations → src/migrations}/400600_settlementParticipantCurrency.js +1 -1
  105. package/{migrations → src/migrations}/400602_settlementParticipantCurrency-decimal184.js +1 -1
  106. package/{migrations → src/migrations}/400700_settlementParticipantCurrencyStateChange.js +1 -1
  107. package/{migrations → src/migrations}/400800_settlementStateChange.js +1 -1
  108. package/{migrations → src/migrations}/400900_settlementWindowContent.js +1 -1
  109. package/{migrations → src/migrations}/401000_settlementWindowContentStateChange.js +1 -1
  110. package/{migrations → src/migrations}/401001_settlementWindowContentStateChange-indexes.js +6 -6
  111. package/{migrations → src/migrations}/401200_settlementContentAggregation.js +1 -1
  112. package/{migrations → src/migrations}/401300_settlementModel.js +1 -1
  113. package/{migrations → src/migrations}/410100_transferFulfilmentDuplicateCheck.js +1 -1
  114. package/{migrations → src/migrations}/410102_transferFulfilmentDuplicateCheck-dropForeignTransferId.js +1 -1
  115. package/{migrations → src/migrations}/410103_transferFulfilmentDuplicateCheck-hashNullable.js +1 -1
  116. package/{migrations → src/migrations}/410200_transferFulfilment.js +1 -1
  117. package/{migrations → src/migrations}/410202_transferFulfilment-ilpFulfilment.js +1 -1
  118. package/{migrations → src/migrations}/410203_transferFulfilment-foreignTransferFulfilmentId.js +1 -1
  119. package/{migrations → src/migrations}/410240_bulkTransferFulfilmentDuplicateCheck.js +1 -1
  120. package/{migrations → src/migrations}/410250_bulkTransferFulfilment.js +1 -1
  121. package/{migrations → src/migrations}/410300_transferExtension.js +1 -1
  122. package/{migrations → src/migrations}/410302_transferExtension-transferErrorId.js +1 -1
  123. package/{migrations → src/migrations}/410350_bulkTransferExtension.js +1 -1
  124. package/{migrations → src/migrations}/410400_transferTimeout.js +1 -1
  125. package/{migrations → src/migrations}/410450_transferErrorDuplicateCheck.js +1 -1
  126. package/{migrations → src/migrations}/410500_transferError.js +1 -1
  127. package/{migrations → src/migrations}/410502_transferError-foreignTransferErrorDuplicateCheckId.js +1 -1
  128. package/{migrations → src/migrations}/410550_bulkTransferError.js +1 -1
  129. package/{migrations → src/migrations}/410600_expiringTransfer.js +1 -1
  130. package/{migrations → src/migrations}/500401_quote-hotfix-2719.js +8 -8
  131. package/{migrations → src/migrations}/500501_feature-fixSubIdRef.js +12 -12
  132. package/{migrations → src/migrations}/500601_party-2480.js +3 -3
  133. package/{migrations → src/migrations}/501001_quoteExtension-quote-service-174.js +1 -2
  134. package/{migrations → src/migrations}/501002_quoteExtension-2522.js +3 -3
  135. package/{migrations → src/migrations}/600010_fxTransferType.js +1 -1
  136. package/{migrations → src/migrations}/600012_fxParticipantCurrencyType.js +1 -1
  137. package/{migrations → src/migrations}/600100_fxTransferDuplicateCheck.js +2 -2
  138. package/{migrations → src/migrations}/600110_fxTransferErrorDuplicateCheck.js.js +1 -1
  139. package/{migrations → src/migrations}/600200_fxTransfer.js +2 -2
  140. package/{migrations → src/migrations}/600400_fxTransferStateChange.js +1 -1
  141. package/{migrations → src/migrations}/600501_fxWatchList.js +2 -2
  142. package/{migrations → src/migrations}/600600_fxTransferFulfilmentDuplicateCheck.js +1 -1
  143. package/{migrations → src/migrations}/600700_fxTransferFulfilment.js +1 -1
  144. package/{migrations → src/migrations}/600800_fxTransferExtension.js +1 -1
  145. package/{migrations → src/migrations}/601400_fxTransferTimeout.js +1 -1
  146. package/{migrations → src/migrations}/601500_fxTransferError.js +1 -1
  147. package/{migrations → src/migrations}/610200_fxTransferParticipant.js +1 -1
  148. package/{migrations → src/migrations}/610202_fxTransferParticipant-participantId.js +2 -2
  149. package/{migrations → src/migrations}/610403_participantPositionChange-fxTransfer.js +1 -1
  150. package/{migrations → src/migrations}/800100_quotePartyView.js +1 -1
  151. package/{migrations → src/migrations}/800101_feature-fixSubIdRef.js +1 -1
  152. package/{migrations → src/migrations}/800200_quoteView.js +1 -1
  153. package/{migrations → src/migrations}/800300_quoteResponseView.js +1 -1
  154. package/{migrations → src/migrations}/900100_foreign-keys.js +18 -18
  155. package/{migrations → src/migrations}/900101_foreign-keys.js +6 -6
  156. package/{migrations → src/migrations}/910100_feature904DDL.js +7 -7
  157. package/{migrations → src/migrations}/910101_feature904DataMigration.js +1 -1
  158. package/{migrations → src/migrations}/910102_feature949DataMigration.js +1 -1
  159. package/{migrations → src/migrations}/910103_feature1093SettlementByCurrency.js +3 -3
  160. package/{migrations → src/migrations}/940101_quotePartyIdInfoExtension-2522.js +25 -25
  161. package/{migrations → src/migrations}/950101_transferParticipantStateChange.js +2 -2
  162. package/{migrations → src/migrations}/950102_settlementModel-adjustPosition.js +2 -2
  163. package/{migrations → src/migrations}/950103_dropTransferParticipantStateChange.js +3 -3
  164. package/{migrations → src/migrations}/950104_settlementModel-settlementAccountTypeId.js +3 -3
  165. package/src/migrations/950107_settlementWindowContent_foreign_settlementModel.js +63 -0
  166. package/{migrations → src/migrations}/950108_participantProxy.js +1 -2
  167. package/{migrations → src/migrations}/950120_fxQuoteConversionTerms_sourceAmount_nullable.js +1 -1
  168. package/{migrations → src/migrations}/960100_create_externalParticipant.js +2 -2
  169. package/{migrations → src/migrations}/960110_alter_transferParticipant__addFiled_externalParticipantId.js +2 -2
  170. package/{migrations → src/migrations}/961001_transferForwarded.js +1 -1
  171. package/{migrations → src/migrations}/961002_fxTransferForwarded.js +1 -1
  172. package/src/models/participant/externalParticipant.int.ts +85 -0
  173. package/src/models/participant/facade.js +4 -2
  174. package/src/models/participant/participantCurrencyCached.js +1 -1
  175. package/src/models/position/facade.js +8 -2
  176. package/src/models/transfer/facade.int.ts +139 -0
  177. package/src/models/transfer/facade.js +8 -3
  178. package/src/models/transfer/transferDuplicateCheck.js +2 -2
  179. package/src/models/transfer/transferError.int.ts +112 -0
  180. package/src/models/transfer/transferExtension.int.ts +137 -0
  181. package/src/models/transfer/transferStateChange.int.ts +117 -0
  182. package/{seeds → src/seeds}/participant.js +1 -1
  183. package/{seeds → src/seeds}/settlementInterchange.js +1 -0
  184. package/src/settlement/api/handlerRoutes.js +71 -0
  185. package/src/settlement/api/handlers/health.js +10 -0
  186. package/src/settlement/api/handlers/index.js +57 -0
  187. package/src/settlement/api/handlers/settlementWindows/{id}.js +103 -0
  188. package/src/settlement/api/handlers/settlementWindows.js +77 -0
  189. package/src/settlement/api/handlers/settlements/{id}.js +133 -0
  190. package/src/settlement/api/handlers/settlements/{sid}/participants/{pid}/accounts/{aid}.js +127 -0
  191. package/src/settlement/api/handlers/settlements/{sid}/participants/{pid}.js +127 -0
  192. package/src/settlement/api/handlers/settlements.js +114 -0
  193. package/src/settlement/api/openapiRouting.js +87 -0
  194. package/src/settlement/api/routes.js +176 -0
  195. package/src/settlement/domain/rules/index.js +46 -0
  196. package/src/settlement/domain/settlement/index.js +405 -0
  197. package/src/settlement/domain/settlementWindow/index.js +117 -0
  198. package/src/settlement/domain/transactions/index.js +83 -0
  199. package/src/settlement/domain/transferSettlement/index.js +57 -0
  200. package/src/settlement/handlers/deferredSettlement/handler.js +180 -0
  201. package/src/settlement/handlers/grossSettlement/handler.js +180 -0
  202. package/src/settlement/handlers/index.js +101 -0
  203. package/src/settlement/handlers/register.js +92 -0
  204. package/src/settlement/handlers/rules/handler.js +164 -0
  205. package/src/settlement/interface/swagger-handler.json +34 -0
  206. package/src/settlement/interface/swagger.json +1560 -0
  207. package/src/settlement/lib/db.js +32 -0
  208. package/src/settlement/lib/kafka/index.js +41 -0
  209. package/src/settlement/lib/kafka/producer.js +142 -0
  210. package/src/settlement/lib/scriptEngine.js +99 -0
  211. package/src/settlement/lib/scriptsLoader.js +175 -0
  212. package/src/settlement/lib/utility.js +243 -0
  213. package/src/settlement/models/ilpPackets/ilpPacket.js +42 -0
  214. package/src/settlement/models/lib/enums.js +192 -0
  215. package/src/settlement/models/misc/migrationLock.js +53 -0
  216. package/src/settlement/models/settlement/facade.js +1652 -0
  217. package/src/settlement/models/settlement/index.js +55 -0
  218. package/src/settlement/models/settlement/participantCurrency.js +48 -0
  219. package/src/settlement/models/settlement/settlement.js +49 -0
  220. package/src/settlement/models/settlement/settlementModel.js +44 -0
  221. package/src/settlement/models/settlement/settlementParticipantCurrency.js +61 -0
  222. package/src/settlement/models/settlement/settlementStateChange.js +44 -0
  223. package/src/settlement/models/settlement/settlementTransferParticipant.js +46 -0
  224. package/src/settlement/models/settlementWindow/facade.js +345 -0
  225. package/src/settlement/models/settlementWindow/index.js +47 -0
  226. package/src/settlement/models/settlementWindow/settlementWindowStateChange.js +55 -0
  227. package/src/settlement/models/settlementWindowContent/facade.js +79 -0
  228. package/src/settlement/models/settlementWindowContent/index.js +40 -0
  229. package/src/settlement/models/settlementWindowContent/settlementWindowContentStateChange.js +53 -0
  230. package/src/settlement/models/transferSettlement/facade.js +363 -0
  231. package/src/settlement/models/transferSettlement/index.js +42 -0
  232. package/src/settlement/shared/logger/index.js +7 -0
  233. package/src/settlement/shared/plugins.js +88 -0
  234. package/src/settlement/shared/setup.js +224 -0
  235. package/src/settlement/utils/truthyProperty.js +37 -0
  236. package/src/shared/legacyUtilShim.js +20 -0
  237. package/src/shared/plugins.js +3 -3
  238. package/src/shared/results.ts +22 -0
  239. package/src/shared/results.unit.ts +38 -0
  240. package/src/shared/setup.js +14 -1
  241. package/src/testing/api-helpers.ts +1222 -0
  242. package/src/testing/exec-async.ts +67 -0
  243. package/src/testing/harness.snapshot.sql +3895 -0
  244. package/src/testing/harness.test.ts +15 -0
  245. package/src/testing/harness.ts +1160 -0
  246. package/src/testing/kafka.ts +221 -0
  247. package/src/testing/prng.ts +82 -0
  248. package/src/testing/rdkafka.test.ts +263 -0
  249. package/src/testing/run.ts +559 -0
  250. package/src/testing/snapshot.test.ts +92 -0
  251. package/src/testing/snapshot.ts +549 -0
  252. package/src/testing/tap-stream.ts +164 -0
  253. package/src/testing/types.ts +95 -0
  254. package/src/testing/util.ts +333 -0
  255. package/src/types/central-services-error-handling.d.ts +72 -0
  256. package/src/types/central-services-stream.d.ts +148 -0
  257. package/test-integration.Dockerfile +0 -2
  258. package/test.Dockerfile +0 -2
  259. package/tsconfig.json +40 -0
  260. package/config/knexfile.js +0 -19
  261. package/migrations/950107_settlementWindowContent_foreign_settlementModel.js +0 -63
  262. package/src/api/index.js +0 -16
  263. package/src/lib/config.js +0 -81
  264. /package/{migrations → src/migrations}/110101_contactType-indexes.js +0 -0
  265. /package/{migrations → src/migrations}/110301_endpointType-indexes.js +0 -0
  266. /package/{migrations → src/migrations}/110401_ledgerEntryType-indexes.js +0 -0
  267. /package/{migrations → src/migrations}/110451_ledgerAccountType-indexes.js +0 -0
  268. /package/{migrations → src/migrations}/110501_participantLimitType-indexes.js +0 -0
  269. /package/{migrations → src/migrations}/110601_transferParticipantRoleType-indexes.js +0 -0
  270. /package/{migrations → src/migrations}/110800_settlementWindowState.js +0 -0
  271. /package/{migrations → src/migrations}/111000_amountType.js +0 -0
  272. /package/{migrations → src/migrations}/111001_amountType-indexes.js +0 -0
  273. /package/{migrations → src/migrations}/111100_balanceOfPayments.js +0 -0
  274. /package/{migrations → src/migrations}/111101_balanceOfPayments-indexes.js +0 -0
  275. /package/{migrations → src/migrations}/111200_partyIdentifierType.js +0 -0
  276. /package/{migrations → src/migrations}/111201_partyIdentifierType-indexes.js +0 -0
  277. /package/{migrations → src/migrations}/111300_partyType.js +0 -0
  278. /package/{migrations → src/migrations}/111301_partyType-indexes.js +0 -0
  279. /package/{migrations → src/migrations}/111400_quoteDuplicateCheck.js +0 -0
  280. /package/{migrations → src/migrations}/111500_transactionInitiator.js +0 -0
  281. /package/{migrations → src/migrations}/111501_transactionInitiator-indexes.js +0 -0
  282. /package/{migrations → src/migrations}/111600_transactionInitiatorType.js +0 -0
  283. /package/{migrations → src/migrations}/111601_transactionInitiatorType-indexes.js +0 -0
  284. /package/{migrations → src/migrations}/111700_settlementDelay.js +0 -0
  285. /package/{migrations → src/migrations}/111701_settlementDelay-indexes.js +0 -0
  286. /package/{migrations → src/migrations}/111800_settlementInterchange.js +0 -0
  287. /package/{migrations → src/migrations}/111801_settlementInterchange-indexes.js +0 -0
  288. /package/{migrations → src/migrations}/111900_settlementGranularity.js +0 -0
  289. /package/{migrations → src/migrations}/111901_settlementGranularity-indexes.js +0 -0
  290. /package/{migrations → src/migrations}/112101_bulkProcessingState-indexes.js +0 -0
  291. /package/{migrations → src/migrations}/200101_participant-indexes.js +0 -0
  292. /package/{migrations → src/migrations}/200201_participantContact-indexes.js +0 -0
  293. /package/{migrations → src/migrations}/200301_participantEndpoint-indexes.js +0 -0
  294. /package/{migrations → src/migrations}/200401_participantParty-indexes.js +0 -0
  295. /package/{migrations → src/migrations}/300201_transfer-indexes.js +0 -0
  296. /package/{migrations → src/migrations}/300251_bulkTransfer-indexes.js +0 -0
  297. /package/{migrations → src/migrations}/300276_bulkTransferAssociation-indexes.js +0 -0
  298. /package/{migrations → src/migrations}/300401_transferStateChange-indexes.js +0 -0
  299. /package/{migrations → src/migrations}/300451_bulkTransferStateChange-indexes.js +0 -0
  300. /package/{migrations → src/migrations}/300501_segment-indexes.js +0 -0
  301. /package/{migrations → src/migrations}/310101_participantCurrency-indexes.js +0 -0
  302. /package/{migrations → src/migrations}/310201_transferParticipant-indexes.js +0 -0
  303. /package/{migrations → src/migrations}/310203_transferParticipant-indexes.js +0 -0
  304. /package/{migrations → src/migrations}/310301_participantPosition-indexes.js +0 -0
  305. /package/{migrations → src/migrations}/310401_participantPositionChange-indexes.js +0 -0
  306. /package/{migrations → src/migrations}/310405_participantPositionChange-positionChange.js +0 -0
  307. /package/{migrations → src/migrations}/310406_participantPositionChange-positionChange-revert.js +0 -0
  308. /package/{migrations → src/migrations}/310501_participantLimit-indexes.js +0 -0
  309. /package/{migrations → src/migrations}/310503_participantLimit-indexes-composite.js +0 -0
  310. /package/{migrations → src/migrations}/400401_settlmentWindowStateChange-indexes.js +0 -0
  311. /package/{migrations → src/migrations}/400501_settlementTransferParticipant-indexes.js +0 -0
  312. /package/{migrations → src/migrations}/400601_settlementParticipantCurrency-indexes.js +0 -0
  313. /package/{migrations → src/migrations}/400701_settlementParticipantCurrencyStateChange-indexes.js +0 -0
  314. /package/{migrations → src/migrations}/400801_settlementStateChange-indexes.js +0 -0
  315. /package/{migrations → src/migrations}/401002_settlementWindowContent-indexes.js +0 -0
  316. /package/{migrations → src/migrations}/401201_settlementContentAggregation-indexes.js +0 -0
  317. /package/{migrations → src/migrations}/401301_settlementModel-indexes.js +0 -0
  318. /package/{migrations → src/migrations}/410101_transferFulfilmentDuplicateCheck-indexes.js +0 -0
  319. /package/{migrations → src/migrations}/410201_transferFulfilment-indexes.js +0 -0
  320. /package/{migrations → src/migrations}/410301_transferExtension-indexes.js +0 -0
  321. /package/{migrations → src/migrations}/410351_bulkTransferExtension-indexes.js +0 -0
  322. /package/{migrations → src/migrations}/410401_transferTimeout-indexes.js +0 -0
  323. /package/{migrations → src/migrations}/410501_transferError-indexes.js +0 -0
  324. /package/{migrations → src/migrations}/410551_bulkTransferError-indexes.js +0 -0
  325. /package/{migrations → src/migrations}/410601_expiringTransfer-indexes.js +0 -0
  326. /package/{migrations → src/migrations}/500100_transactionReference.js +0 -0
  327. /package/{migrations → src/migrations}/500101_transactionReference-indexes.js +0 -0
  328. /package/{migrations → src/migrations}/500200_transactionScenario.js +0 -0
  329. /package/{migrations → src/migrations}/500201_transactionScenario-indexes.js +0 -0
  330. /package/{migrations → src/migrations}/500300_transactionSubScenario.js +0 -0
  331. /package/{migrations → src/migrations}/500400_quote.js +0 -0
  332. /package/{migrations → src/migrations}/500500_quoteParty.js +0 -0
  333. /package/{migrations → src/migrations}/500600_party.js +0 -0
  334. /package/{migrations → src/migrations}/500700_quoteResponse.js +0 -0
  335. /package/{migrations → src/migrations}/500800_quoteResponseIlpPacket.js +0 -0
  336. /package/{migrations → src/migrations}/500900_geoCode.js +0 -0
  337. /package/{migrations → src/migrations}/501000_quoteExtension.js +0 -0
  338. /package/{migrations → src/migrations}/501100_quoteResponseDuplicateCheck.js +0 -0
  339. /package/{migrations → src/migrations}/501200_quoteError.js +0 -0
  340. /package/{migrations → src/migrations}/501300_transferRules.js +0 -0
  341. /package/{migrations → src/migrations}/600011_fxTransferType-indexes.js +0 -0
  342. /package/{migrations → src/migrations}/600013_fxParticipantCurrencyType-indexes.js +0 -0
  343. /package/{migrations → src/migrations}/600201_fxTransfer-indexes.js +0 -0
  344. /package/{migrations → src/migrations}/600401_fxTransferStateChange-indexes.js +0 -0
  345. /package/{migrations → src/migrations}/600502_fxWatchList-indexes.js +0 -0
  346. /package/{migrations → src/migrations}/600601_fxTransferFulfilmentDuplicateCheck-indexes.js +0 -0
  347. /package/{migrations → src/migrations}/600701_fxTransferFulfilment-indexes.js +0 -0
  348. /package/{migrations → src/migrations}/601401_fxTransferTimeout-indexes.js +0 -0
  349. /package/{migrations → src/migrations}/601501_fxTransferError-indexes.js +0 -0
  350. /package/{migrations → src/migrations}/610201_fxTransferParticipant-indexes.js +0 -0
  351. /package/{migrations → src/migrations}/910102_hotfix954.js +0 -0
  352. /package/{migrations → src/migrations}/940100_quotePartyIdInfoExtension.js +0 -0
  353. /package/{migrations → src/migrations}/950100_settlementContentAggregation-decimal184.js +0 -0
  354. /package/{migrations → src/migrations}/950106_unique_settlement_model_ledger_account_currency.js +0 -0
  355. /package/{migrations → src/migrations}/950109_fxQuote.js +0 -0
  356. /package/{migrations → src/migrations}/950110_fxQuoteResponse.js +0 -0
  357. /package/{migrations → src/migrations}/950111_fxQuoteError.js +0 -0
  358. /package/{migrations → src/migrations}/950113_fxQuoteDuplicateCheck.js +0 -0
  359. /package/{migrations → src/migrations}/950114_fxQuoteResponseDuplicateCheck.js +0 -0
  360. /package/{migrations → src/migrations}/950115_fxQuoteConversionTerms.js +0 -0
  361. /package/{migrations → src/migrations}/950116_fxQuoteConversionTermsExtension.js +0 -0
  362. /package/{migrations → src/migrations}/950117_fxQuoteResponseConversionTerms.js +0 -0
  363. /package/{migrations → src/migrations}/950118_fxQuoteResponseConversionTermsExtension.js +0 -0
  364. /package/{migrations → src/migrations}/950119_fxCharge.js +0 -0
  365. /package/{migrations → src/migrations}/960111_alter_fxTransferParticipant__addFiled_externalParticipantId.js +0 -0
  366. /package/{migrations → src/migrations}/960112_fxTransfer-indexes2.js +0 -0
  367. /package/{migrations → src/migrations}/960113_fxTransferExtension-indexes.js +0 -0
  368. /package/{migrations → src/migrations}/960114_fxTransferTimeout-indexes2.js +0 -0
  369. /package/{migrations → src/migrations}/961000_participantEndpoint_unique.js +0 -0
  370. /package/{migrations → src/migrations}/migration.template +0 -0
  371. /package/{seeds → src/seeds}/amountType.js +0 -0
  372. /package/{seeds → src/seeds}/balanceOfPayments.js +0 -0
  373. /package/{seeds → src/seeds}/bulkProcessingState.js +0 -0
  374. /package/{seeds → src/seeds}/bulkTransferState.js +0 -0
  375. /package/{seeds → src/seeds}/currency.js +0 -0
  376. /package/{seeds → src/seeds}/endpointType.js +0 -0
  377. /package/{seeds → src/seeds}/fxParticipantCurrencyType.js +0 -0
  378. /package/{seeds → src/seeds}/fxTransferType.js +0 -0
  379. /package/{seeds → src/seeds}/ledgerAccountType.js +0 -0
  380. /package/{seeds → src/seeds}/ledgerEntryType.js +0 -0
  381. /package/{seeds → src/seeds}/participantLimitType.js +0 -0
  382. /package/{seeds → src/seeds}/partyIdentifierType.js +0 -0
  383. /package/{seeds → src/seeds}/partyType.js +0 -0
  384. /package/{seeds → src/seeds}/settlementDelay.js +0 -0
  385. /package/{seeds → src/seeds}/settlementGranularity.js +0 -0
  386. /package/{seeds → src/seeds}/settlementState.js +0 -0
  387. /package/{seeds → src/seeds}/settlementWindow1State.js +0 -0
  388. /package/{seeds → src/seeds}/settlementWindow2Open.js +0 -0
  389. /package/{seeds → src/seeds}/transactionInitiator.js +0 -0
  390. /package/{seeds → src/seeds}/transactionInitiatorType.js +0 -0
  391. /package/{seeds → src/seeds}/transactionScenario.js +0 -0
  392. /package/{seeds → src/seeds}/transferParticipantRoleType.js +0 -0
  393. /package/{seeds → src/seeds}/transferState.js +0 -0
  394. /package/{seeds → src/seeds}/z1000_settlementModel-deprecated.js- +0 -0
@@ -0,0 +1,405 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2020-2025 Mojaloop Foundation
5
+ The Mojaloop files are made available by the Mojaloop 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
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ 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.
10
+
11
+ Contributors
12
+ --------------
13
+ This is the official list of the Mojaloop project contributors for this file.
14
+ Names of the original copyright holders (individuals or organizations)
15
+ should be listed with a '*' in the first column. People who have
16
+ contributed from an organization can be listed under the organization
17
+ that actually holds the copyright for their contributions (see the
18
+ Mojaloop Foundation for an example). Those individuals should have
19
+ their names indented and be marked with a '-'. Email address can be added
20
+ optionally within square brackets <email>.
21
+
22
+ * Mojaloop Foundation
23
+ - Name Surname <name.surname@mojaloop.io>
24
+ - Shashikant Hirugade <shashi.mojaloop@gmail.com>
25
+ * ModusBox
26
+ - Deon Botha <deon.botha@modusbox.com>
27
+ - Georgi Georgiev <georgi.georgiev@modusbox.com>
28
+ - Miguel de Barros <miguel.debarros@modusbox.com>
29
+ - Rajiv Mothilal <rajiv.mothilal@modusbox.com>
30
+ - Valentin Genev <valentin.genev@modusbox.com>
31
+ --------------
32
+ ******/
33
+ 'use strict'
34
+
35
+ const arrayDiff = require('lodash').difference
36
+ const SettlementModel = require('../../models/settlement')
37
+ const SettlementModelModel = require('../../models/settlement/settlementModel')
38
+ const SettlementWindowContentModel = require('../../models/settlementWindowContent')
39
+ const SettlementWindowModel = require('../../models/settlementWindow')
40
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
41
+ const { logger } = require('../../shared/logger')
42
+ const MLNumber = require('@mojaloop/ml-number')
43
+
44
+ const prepareParticipantsResult = (participantCurrenciesList) => {
45
+ const participantAccounts = {}
46
+ for (const account of participantCurrenciesList) {
47
+ const { id } = account
48
+ const formattedAccount = {
49
+ id: account.participantCurrencyId,
50
+ state: account.state,
51
+ reason: account.reason,
52
+ netSettlementAmount: {
53
+ amount: new MLNumber(account.netAmount).toNumber(),
54
+ currency: account.currency
55
+ }
56
+ }
57
+ if (id in participantAccounts) {
58
+ const accountList = participantAccounts[id].accounts
59
+ accountList.push(formattedAccount)
60
+ participantAccounts[id] = {
61
+ id,
62
+ accounts: accountList
63
+ }
64
+ } else {
65
+ participantAccounts[id] = {
66
+ id,
67
+ accounts: [formattedAccount]
68
+ }
69
+ }
70
+ }
71
+ return Array.from(Object.keys(participantAccounts).map(participantId => participantAccounts[participantId]))
72
+ }
73
+
74
+ const groupSettlementWindowContentBySettlementWindow = (records) => {
75
+ const settlementWindows = {}
76
+ for (const record of records) {
77
+ const id = record.settlementWindowId
78
+ delete record.settlementWindowId
79
+ if (id in settlementWindows) {
80
+ settlementWindows[id].push(record)
81
+ } else {
82
+ settlementWindows[id] = [record]
83
+ }
84
+ }
85
+ return settlementWindows
86
+ }
87
+
88
+ module.exports = {
89
+ getById: async function ({ settlementId }, enums) {
90
+ const settlement = await SettlementModel.getById({ settlementId }, enums)
91
+ if (settlement) {
92
+ const settlementWindowsList = await SettlementWindowModel.getBySettlementId({ settlementId }, enums)
93
+ const participantCurrenciesList = await SettlementModel.settlementParticipantCurrency.getParticipantCurrencyBySettlementId({ settlementId }, enums)
94
+ const participants = prepareParticipantsResult(participantCurrenciesList)
95
+
96
+ // Build settlement window content array and insert into settlement window list object
97
+ const windowContentRecords = []
98
+ let windowContentResponseData = {}
99
+
100
+ for (const key of Object.keys(settlementWindowsList)) {
101
+ const windowContentRecord = await SettlementWindowContentModel.getBySettlementAndWindowId(settlementId, settlementWindowsList[key].id)
102
+ windowContentResponseData = {
103
+ id: windowContentRecord[0].id,
104
+ state: windowContentRecord[0].state,
105
+ ledgerAccountType: windowContentRecord[0].ledgerAccountType,
106
+ currencyId: windowContentRecord[0].currencyId,
107
+ createdDate: windowContentRecord[0].createdDate,
108
+ changedDate: windowContentRecord[0].changedDate
109
+ }
110
+ windowContentRecords.push(windowContentResponseData)
111
+ settlementWindowsList[key].content = windowContentRecords
112
+ }
113
+
114
+ return {
115
+ id: settlement.settlementId,
116
+ state: settlement.state,
117
+ reason: settlement.reason,
118
+ createdDate: settlement.createdDate,
119
+ changedDate: settlement.changedDate,
120
+ settlementWindows: settlementWindowsList,
121
+ participants
122
+ }
123
+ } else {
124
+ const error = ErrorHandler.Factory.createFSPIOPError(
125
+ ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
126
+ `Settlement with ID '${settlementId}' not found`
127
+ )
128
+ logger.error(error)
129
+ throw error
130
+ }
131
+ },
132
+
133
+ putById: SettlementModel.putById,
134
+ abortById: async function (settlementId, payload, enums) {
135
+ // seq-settlement-6.2.6, step 3
136
+ const settlementData = await SettlementModel.getById({ settlementId })
137
+
138
+ if (!settlementData) {
139
+ const error = ErrorHandler.Factory.createFSPIOPError(
140
+ ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
141
+ `Settlement with ID '${settlementId}' not found`
142
+ )
143
+ logger.error(error)
144
+ throw error
145
+ }
146
+ if (settlementData.state === enums.settlementStates.PS_TRANSFERS_COMMITTED ||
147
+ settlementData.state === enums.settlementStates.SETTLING ||
148
+ settlementData.state === enums.settlementStates.SETTLED) {
149
+ const error = ErrorHandler.Factory.createFSPIOPError(
150
+ ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
151
+ `State change is not allowed for settlement '${settlementId}' in state '${settlementData.state}'`
152
+ )
153
+ logger.error(error)
154
+ throw error
155
+ } else if (settlementData.state === enums.settlementStates.ABORTED) {
156
+ return SettlementModel.abortByIdStateAborted(settlementId, payload, enums)
157
+ } else if (settlementData.state === enums.settlementStates.PS_TRANSFERS_RESERVED) {
158
+ const transferCommittedAccount = await SettlementModel.getTransferCommitedAccount(settlementId, enums)
159
+ if (transferCommittedAccount !== undefined) {
160
+ const error = ErrorHandler.Factory.createFSPIOPError(
161
+ ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
162
+ `At least one settlement transfer is committed for settlement '${settlementId}'. Aborting is not allowed.`
163
+ )
164
+ logger.error(error)
165
+ throw error
166
+ }
167
+ }
168
+ },
169
+
170
+ getSettlementsByParams: async function (params, enums) {
171
+ // 7 filters - at least one should be used
172
+ Object.keys(params.query).forEach(key => params.query[key] === undefined && delete params.query[key])
173
+ if (Object.keys(params.query).length && Object.keys(params.query).length < 10) {
174
+ const settlements = {}
175
+ let settlement
176
+ let participant
177
+ const settlementsData = await SettlementModel.getByParams(params.query, enums)
178
+ if (settlementsData && settlementsData.length > 0) {
179
+ for (const s of settlementsData) {
180
+ if (!settlements[s.settlementId]) {
181
+ const settlementId = s.settlementId
182
+ const settlementWindowsList = await SettlementWindowModel.getBySettlementId({ settlementId })
183
+ for (const key of Object.keys(settlementWindowsList)) {
184
+ settlementWindowsList[key].content = await SettlementWindowContentModel.getBySettlementAndWindowId(s.settlementId, settlementWindowsList[key].id)
185
+ }
186
+ settlements[s.settlementId] = {
187
+ id: s.settlementId,
188
+ state: s.settlementStateId,
189
+ reason: s.settlementWindowReason,
190
+ createdDate: s.createdDate,
191
+ changedDate: s.changedDate,
192
+ settlementWindows: settlementWindowsList
193
+ }
194
+ }
195
+ settlement = settlements[s.settlementId]
196
+ if (!settlement.settlementWindows) {
197
+ settlement.settlementWindows = {}
198
+ }
199
+ if (!settlement.participants) {
200
+ settlement.participants = {}
201
+ }
202
+ if (!settlement.participants[s.participantId]) {
203
+ settlement.participants[s.participantId] = {
204
+ id: s.participantId
205
+ }
206
+ }
207
+ participant = settlement.participants[s.participantId]
208
+ if (!participant.accounts) {
209
+ participant.accounts = {}
210
+ }
211
+ participant.accounts[s.participantCurrencyId] = {
212
+ id: s.participantCurrencyId,
213
+ state: s.accountState,
214
+ reason: s.accountReason,
215
+ netSettlementAmount: {
216
+ amount: new MLNumber(s.accountAmount).toNumber(),
217
+ currency: s.accountCurrency
218
+ }
219
+ }
220
+ }
221
+ // transform settlements map to result array
222
+ const result = Object.keys(settlements).map((i) => {
223
+ let settlementWindows = settlements[i].settlementWindows
224
+ settlementWindows = Object.keys(settlementWindows).map((j) => {
225
+ return settlementWindows[j]
226
+ })
227
+ settlements[i].settlementWindows = settlementWindows
228
+ let participants = settlements[i].participants
229
+ participants = Object.keys(participants).map((j) => {
230
+ let accounts = participants[j].accounts
231
+ accounts = Object.keys(accounts).map((k) => {
232
+ return accounts[k]
233
+ })
234
+ participants[j].accounts = accounts
235
+ return participants[j]
236
+ })
237
+ settlements[i].participants = participants
238
+ return settlements[i]
239
+ })
240
+ return result
241
+ } else {
242
+ const error = ErrorHandler.Factory.createFSPIOPError(
243
+ ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
244
+ `No settlements found matching the provided parameters: ${JSON.stringify(params.query)}`
245
+ )
246
+ logger.error(error)
247
+ throw error
248
+ }
249
+ } else {
250
+ const error = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'Use at least one parameter: state, fromDateTime, toDateTime, currency, settlementWindowId, fromSettlementWindowDateTime, toSettlementWindowDateTime, participantId, accountId')
251
+ logger.error(error)
252
+ throw error
253
+ }
254
+ },
255
+
256
+ settlementEventTrigger: async function (params, enums) {
257
+ // validate settlement model
258
+ const { settlementModel, reason, settlementWindows } = params
259
+ const settlementModelData = await SettlementModelModel.getByName(settlementModel)
260
+ if (!settlementModelData) {
261
+ const error = ErrorHandler.Factory.createFSPIOPError(
262
+ ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
263
+ `Settlement model not found: ${settlementModel}`
264
+ )
265
+ logger.error(error)
266
+ throw error
267
+ } else if (settlementModelData.settlementGranularityId === enums.settlementGranularity.GROSS ||
268
+ settlementModelData.settlementDelayId === enums.settlementDelay.IMMEDIATE) {
269
+ const error = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'Settlement can not be created for GROSS or IMMEDIATE models')
270
+ logger.error(error)
271
+ throw error
272
+ }
273
+
274
+ // validate windows content
275
+ const idList = settlementWindows.map(v => v.id)
276
+ const applicableWindows = await SettlementWindowModel.getByListOfIds(idList, settlementModelData, enums.settlementWindowStates)
277
+ const applicableIdList = applicableWindows.map(v => v.settlementWindowId)
278
+ const nonApplicableIdList = arrayDiff(idList, applicableIdList)
279
+ if (nonApplicableIdList.length) {
280
+ const error = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, `Inapplicable windows ${nonApplicableIdList.join(', ')}`)
281
+ logger.error(error)
282
+ throw error
283
+ }
284
+
285
+ // settlement event trigger
286
+ const settlementId = await SettlementModel.triggerSettlementEvent({ idList, reason }, settlementModelData, enums)
287
+
288
+ // retrieve resulting data for response
289
+ const settlement = await SettlementModel.getById({ settlementId })
290
+ const settlementWindowsList = await SettlementWindowModel.getBySettlementId({ settlementId })
291
+ const settlementWindowContentAll = await SettlementWindowContentModel.getBySettlementId(settlementId)
292
+ const settlementWindowsContent = groupSettlementWindowContentBySettlementWindow(settlementWindowContentAll)
293
+ const settlementWindowsWithContent = settlementWindowsList.map(record => {
294
+ record.content = settlementWindowsContent[record.id]
295
+ return record
296
+ })
297
+ const participantCurrenciesList = await SettlementModel.settlementParticipantCurrency.getParticipantCurrencyBySettlementId({ settlementId })
298
+ const participants = prepareParticipantsResult(participantCurrenciesList)
299
+ return {
300
+ id: settlement.settlementId,
301
+ settlementModel,
302
+ state: settlement.state,
303
+ reason: settlement.reason,
304
+ createdDate: settlement.createdDate,
305
+ changedDate: settlement.changedDate,
306
+ settlementWindows: settlementWindowsWithContent,
307
+ participants
308
+ }
309
+ },
310
+
311
+ getByIdParticipantAccount: async function ({ settlementId, participantId, accountId = null }, enums) {
312
+ let participantFoundInSettlement = false
313
+ const accountProvided = accountId > 0
314
+ let participantAndAccountMatched = !accountProvided
315
+ let accountFoundInSettlement = !accountProvided
316
+
317
+ const settlement = await SettlementModel.getById({ settlementId }, enums) // 3
318
+ const settlementFound = !!settlement
319
+
320
+ let settlementParticipantCurrencyIdList, account, settlementAccount
321
+
322
+ if (settlementFound) {
323
+ settlementParticipantCurrencyIdList = await SettlementModel.settlementParticipantCurrency.getAccountsInSettlementByIds({
324
+ settlementId,
325
+ participantId
326
+ }, enums) // 6
327
+ participantFoundInSettlement = settlementParticipantCurrencyIdList.length > 0
328
+
329
+ if (participantFoundInSettlement && accountProvided) {
330
+ account = await SettlementModel.checkParticipantAccountExists({
331
+ participantId,
332
+ accountId
333
+ }, enums) // 9
334
+ participantAndAccountMatched = !!account
335
+
336
+ if (participantAndAccountMatched) {
337
+ settlementAccount = await SettlementModel.getAccountInSettlement({
338
+ settlementId,
339
+ accountId
340
+ }, enums) // 12
341
+ accountFoundInSettlement = !!settlementAccount
342
+ }
343
+ }
344
+ }
345
+
346
+ let settlementWindows
347
+ let accounts
348
+ let participants
349
+ if (settlementFound && participantFoundInSettlement && participantAndAccountMatched && accountFoundInSettlement) {
350
+ if (accountProvided) { // 16
351
+ settlementWindows = await SettlementModel.settlementSettlementWindow.getWindowsBySettlementIdAndAccountId({
352
+ settlementId,
353
+ accountId
354
+ }, enums)
355
+ accounts = await SettlementModel.settlementParticipantCurrency.getSettlementAccountById(settlementAccount.settlementParticipantCurrencyId, enums)
356
+ participants = prepareParticipantsResult(accounts)
357
+ } else {
358
+ settlementWindows = await SettlementModel.settlementSettlementWindow.getWindowsBySettlementIdAndParticipantId({
359
+ settlementId,
360
+ participantId
361
+ }, enums)
362
+ const ids = settlementParticipantCurrencyIdList.map(record => record.settlementParticipantCurrencyId)
363
+ accounts = await SettlementModel.settlementParticipantCurrency.getSettlementAccountsByListOfIds(ids, enums)
364
+ participants = prepareParticipantsResult(accounts)
365
+ }
366
+ } else {
367
+ if (!settlementFound) {
368
+ const error = ErrorHandler.Factory.createFSPIOPError(
369
+ ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
370
+ `Settlement with ID '${settlementId}' not found`
371
+ )
372
+ logger.error(error)
373
+ throw error
374
+ } else if (!participantFoundInSettlement) {
375
+ const error = ErrorHandler.Factory.createFSPIOPError(
376
+ ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
377
+ `Participant with ID '${participantId}' is not part of settlement '${settlementId}'`
378
+ )
379
+ logger.error(error)
380
+ throw error
381
+ } else if (!participantAndAccountMatched) {
382
+ const error = ErrorHandler.Factory.createFSPIOPError(
383
+ ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
384
+ `Provided account ID '${accountId}' does not match any position account for participant '${participantId}' in settlement '${settlementId}'`
385
+ )
386
+ logger.error(error)
387
+ throw error
388
+ } else {
389
+ const error = ErrorHandler.Factory.createFSPIOPError(
390
+ ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
391
+ `Account ID '${accountId}' is not part of settlement '${settlementId}'`
392
+ )
393
+ logger.error(error)
394
+ throw error
395
+ }
396
+ }
397
+
398
+ return {
399
+ id: settlement.settlementId,
400
+ state: settlement.state,
401
+ settlementWindows,
402
+ participants
403
+ }
404
+ }
405
+ }
@@ -0,0 +1,117 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2020-2025 Mojaloop Foundation
5
+ The Mojaloop files are made available by the Mojaloop 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
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ 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.
10
+
11
+ Contributors
12
+ --------------
13
+ This is the official list of the Mojaloop project contributors for this file.
14
+ Names of the original copyright holders (individuals or organizations)
15
+ should be listed with a '*' in the first column. People who have
16
+ contributed from an organization can be listed under the organization
17
+ that actually holds the copyright for their contributions (see the
18
+ Mojaloop Foundation for an example). Those individuals should have
19
+ their names indented and be marked with a '-'. Email address can be added
20
+ optionally within square brackets <email>.
21
+
22
+ * Mojaloop Foundation
23
+ - Name Surname <name.surname@mojaloop.io>
24
+
25
+ * ModusBox
26
+ - Deon Botha <deon.botha@modusbox.com>
27
+ - Georgi Georgiev <georgi.georgiev@modusbox.com>
28
+ - Miguel de Barros <miguel.debarros@modusbox.com>
29
+ - Rajiv Mothilal <rajiv.mothilal@modusbox.com>
30
+ - Valentin Genev <valentin.genev@modusbox.com>
31
+ - Lazola Lucas <lazola.lucas@modusbox.com>
32
+ --------------
33
+ ******/
34
+
35
+ const Config = require('../../../lib/config')
36
+ const Enum = require('@mojaloop/central-services-shared').Enum
37
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
38
+ const hasFilters = require('./../../utils/truthyProperty')
39
+ const Producer = require('@mojaloop/central-services-stream').Util.Producer
40
+ const KafkaUtil = require('@mojaloop/central-services-shared').Util.Kafka
41
+ const SettlementWindowModel = require('../../models/settlementWindow')
42
+ const SettlementWindowContentModel = require('../../models/settlementWindowContent')
43
+ const StreamingProtocol = require('@mojaloop/central-services-shared').Util.StreamingProtocol
44
+ const { logger } = require('../../shared/logger')
45
+ const idGenerator = require('@mojaloop/central-services-shared').Util.id
46
+ const generateULID = idGenerator({ type: 'ulid' })
47
+
48
+ module.exports = {
49
+ getById: async function (params, enums, options) {
50
+ const settlementWindow = await SettlementWindowModel.getById(params)
51
+
52
+ if (settlementWindow) {
53
+ const settlementWindowContent = await SettlementWindowContentModel.getBySettlementWindowId(settlementWindow.settlementWindowId)
54
+
55
+ if (settlementWindowContent) {
56
+ settlementWindow.content = settlementWindowContent
57
+ return settlementWindow
58
+ } else {
59
+ const error = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `No records for settlementWidowContentId : ${params.settlementWindowId} found`)
60
+ logger.error(error)
61
+ throw error
62
+ }
63
+ } else {
64
+ const error = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `No record for settlementWindowId: ${params.settlementWindowId} found`)
65
+ logger.error(error)
66
+ throw error
67
+ }
68
+ },
69
+
70
+ getByParams: async function (params, enums) {
71
+ // 4 filters - at least one should be used
72
+ if (hasFilters(params.query) && Object.keys(params.query).length < 6) {
73
+ const settlementWindows = await SettlementWindowModel.getByParams(params, enums)
74
+ if (settlementWindows && settlementWindows.length > 0) {
75
+ for (const settlementWindow of settlementWindows) {
76
+ const settlementWindowContent = await SettlementWindowContentModel.getBySettlementWindowId(settlementWindow.settlementWindowId)
77
+ if (settlementWindowContent) {
78
+ settlementWindow.content = settlementWindowContent
79
+ } else {
80
+ const error = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `No records for settlementWidowContentId : ${settlementWindow.settlementWindowId} found`)
81
+ logger.error(error)
82
+ throw error
83
+ }
84
+ }
85
+ return settlementWindows
86
+ } else {
87
+ const error = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, `settlementWindow by filters: ${JSON.stringify(params.query).replace(/"/g, '')} not found`)
88
+ logger.error(error)
89
+ throw error
90
+ }
91
+ } else {
92
+ const error = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'Use at least one parameter: participantId, state, fromDateTime, toDateTime, currency')
93
+ logger.error(error)
94
+ throw error
95
+ }
96
+ },
97
+
98
+ process: async function (params, enums) {
99
+ const settlementWindowId = await SettlementWindowModel.process(params, enums)
100
+ const messageId = generateULID()
101
+ const eventId = generateULID()
102
+ const state = StreamingProtocol.createEventState(Enum.Events.EventStatus.SUCCESS.status, Enum.Events.EventStatus.SUCCESS.code, Enum.Events.EventStatus.SUCCESS.description)
103
+ const event = StreamingProtocol.createEventMetadata(Enum.Events.Event.Type.DEFERRED_SETTLEMENT, Enum.Events.Event.Action.CLOSE, state)
104
+ const metadata = StreamingProtocol.createMetadata(eventId, event)
105
+ const messageProtocol = StreamingProtocol.createMessage(messageId, Config.HUB_NAME, Config.HUB_NAME, metadata, params.headers, params)
106
+ const topicConfig = KafkaUtil.createGeneralTopicConf(Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE, Enum.Events.Event.Type.DEFERRED_SETTLEMENT, Enum.Events.Event.Action.CLOSE)
107
+ const kafkaConfig = KafkaUtil.getKafkaConfig(Config.KAFKA_CONFIG, Enum.Kafka.Config.PRODUCER, Enum.Events.Event.Type.DEFERRED_SETTLEMENT.toUpperCase(), Enum.Events.Event.Action.CLOSE.toUpperCase())
108
+ await Producer.produceMessage(messageProtocol, topicConfig, kafkaConfig)
109
+
110
+ return SettlementWindowModel.getById({ settlementWindowId }, enums)
111
+ },
112
+
113
+ close: async function (settlementWindowId, reason) {
114
+ await SettlementWindowModel.close(settlementWindowId, reason)
115
+ return SettlementWindowModel.getById({ settlementWindowId })
116
+ }
117
+ }
@@ -0,0 +1,83 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2020-2025 Mojaloop Foundation
5
+ The Mojaloop files are made available by the Mojaloop 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
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ 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.
10
+
11
+ Contributors
12
+ --------------
13
+ This is the official list of the Mojaloop project contributors for this file.
14
+ Names of the original copyright holders (individuals or organizations)
15
+ should be listed with a '*' in the first column. People who have
16
+ contributed from an organization can be listed under the organization
17
+ that actually holds the copyright for their contributions (see the
18
+ Mojaloop Foundation for an example). Those individuals should have
19
+ their names indented and be marked with a '-'. Email address can be added
20
+ optionally within square brackets <email>.
21
+
22
+ * Mojaloop Foundation
23
+ - Name Surname <name.surname@mojaloop.io>
24
+
25
+ * ModusBox
26
+ - Lazola Lucas <lazola.lucas@modusbox.com>
27
+ --------------
28
+ ******/
29
+ 'use strict'
30
+
31
+ const IlpPacket = require('../../models/ilpPackets/ilpPacket')
32
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
33
+
34
+ const ilpPacket = require('ilp-packet')
35
+ const base64url = require('base64url')
36
+ const { logger } = require('../../shared/logger')
37
+
38
+ const getById = async (id) => {
39
+ try {
40
+ return await IlpPacket.getById(id)
41
+ } catch (err) {
42
+ logger.error(err)
43
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
44
+ }
45
+ }
46
+
47
+ const decodeIlpPacket = async (inputIlpPacket) => {
48
+ const binaryPacket = Buffer.from(inputIlpPacket, 'base64')
49
+
50
+ // Try ILPv4 first
51
+ try {
52
+ return ilpPacket.deserializeIlpPrepare(binaryPacket)
53
+ } catch (v4Error) {
54
+ logger.debug('Failed to decode as ILPv4, attempting ILPv1')
55
+ // Fall back to ILPv1
56
+ try {
57
+ return ilpPacket.deserializeIlpPayment(binaryPacket)
58
+ } catch (v1Error) {
59
+ logger.error('Failed to decode ILP packet as both v4 and v1', { v4Error, v1Error })
60
+ throw v1Error
61
+ }
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Get the transaction object in the data field of an Ilp packet
67
+ *
68
+ * @returns {object} - Transaction Object
69
+ */
70
+ const getTransactionObject = async function (inputIlpPacket) {
71
+ try {
72
+ const jsonPacket = await decodeIlpPacket(inputIlpPacket)
73
+ const decodedData = base64url.decode(jsonPacket.data.toString())
74
+ return JSON.parse(decodedData)
75
+ } catch (err) {
76
+ logger.error(err)
77
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
78
+ }
79
+ }
80
+ module.exports = {
81
+ getById,
82
+ getTransactionObject
83
+ }
@@ -0,0 +1,57 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2020-2025 Mojaloop Foundation
5
+ The Mojaloop files are made available by the Mojaloop 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
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ 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.
10
+
11
+ Contributors
12
+ --------------
13
+ This is the official list of the Mojaloop project contributors for this file.
14
+ Names of the original copyright holders (individuals or organizations)
15
+ should be listed with a '*' in the first column. People who have
16
+ contributed from an organization can be listed under the organization
17
+ that actually holds the copyright for their contributions (see the
18
+ Mojaloop Foundation for an example). Those individuals should have
19
+ their names indented and be marked with a '-'. Email address can be added
20
+ optionally within square brackets <email>.
21
+
22
+ * Mojaloop Foundation
23
+ - Name Surname <name.surname@mojaloop.io>
24
+
25
+ * ModusBox
26
+ - Deon Botha <deon.botha@modusbox.com>
27
+ - Lazola Lucas <lazola.lucas@modusbox.com>
28
+ - Claudio Viola <claudio.viola@modusbox.com>
29
+ --------------
30
+ ******/
31
+
32
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
33
+ const TransferSettlementModel = require('../../models/transferSettlement')
34
+ const { logger } = require('../../shared/logger')
35
+ const Enum = require('@mojaloop/central-services-shared').Enum.Settlements
36
+
37
+ module.exports = {
38
+ processMsgFulfil: async function (transferEventId, transferEventStateStatus, trx) {
39
+ logger.debug(`transferSettlement::processMsgFulfil(transferEventId=${transferEventId}, transferEventStateStatus=${transferEventStateStatus}) - start`)
40
+ try {
41
+ // Get the 'GROSS' settlement model by transfer
42
+ const grossSettlementModel = await TransferSettlementModel.getSettlementModelByTransferId(transferEventId, Enum.settlementGranularityName.GROSS)
43
+ logger.debug(`transferSettlement::processMsgFulfil - result grossSettlementModel=${JSON.stringify(grossSettlementModel)}`)
44
+ logger.debug(`transferSettlement::processMsgFulfil - grossSettlementModel.length=${grossSettlementModel.length}`)
45
+ if (grossSettlementModel.length > 0) {
46
+ logger.debug(`transferSettlement::processMsgFulfil - updateStateChange(transferEventId=${transferEventId}, transferEventStateStatus=${transferEventStateStatus}) - start`)
47
+ await TransferSettlementModel.updateStateChange(transferEventId, transferEventStateStatus, trx)
48
+ logger.debug('transferSettlement::processMsgFulfil - updateStateChange - end')
49
+ }
50
+ logger.debug('transferSettlement::processMsgFulfil - end')
51
+ return true
52
+ } catch (err) {
53
+ logger.error('transferSettlement::processMsgFulfil - error!', err)
54
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
55
+ }
56
+ }
57
+ }