@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.
- package/.env.example +140 -0
- package/.eslintignore +2 -0
- package/.eslintrc.json +30 -0
- package/.nvmrc +1 -0
- package/.versionrc +15 -0
- package/CHANGELOG.md +118 -0
- package/InboundServer/api.yaml +3594 -0
- package/InboundServer/api_template.yaml +69 -0
- package/InboundServer/handlers.js +940 -0
- package/InboundServer/index.js +205 -0
- package/InboundServer/middlewares.js +426 -0
- package/OAuthTestServer/index.js +66 -0
- package/OAuthTestServer/model.js +70 -0
- package/OutboundServer/api.yaml +2732 -0
- package/OutboundServer/api_interfaces/index.d.ts +117 -0
- package/OutboundServer/api_interfaces/openapi.d.ts +1475 -0
- package/OutboundServer/api_template/components/parameters/bulkQuoteId.yaml +9 -0
- package/OutboundServer/api_template/components/parameters/bulkTransferId.yaml +9 -0
- package/OutboundServer/api_template/components/parameters/requestToPayTransactionId.yaml +9 -0
- package/OutboundServer/api_template/components/parameters/transferId.yaml +9 -0
- package/OutboundServer/api_template/components/responses/accountsCreationCompleted.yaml +5 -0
- package/OutboundServer/api_template/components/responses/accountsCreationError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/accountsCreationTimeout.yaml +5 -0
- package/OutboundServer/api_template/components/responses/authorizationPostSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/authorizationsServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkQuoteBadRequest.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkQuoteServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkQuoteSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkQuoteTimeout.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkTransferBadRequest.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkTransferServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkTransferSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkTransferTimeout.yaml +5 -0
- package/OutboundServer/api_template/components/responses/partiesByIdError404.yaml +9 -0
- package/OutboundServer/api_template/components/responses/partiesByIdSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/quotesPostSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/quotesServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/requestToPaySuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/requestToPayTransferBadRequest.yaml +5 -0
- package/OutboundServer/api_template/components/responses/requestToPayTransferSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/simpleTransfersPostSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/simpleTransfersServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/transferBadRequest.yaml +5 -0
- package/OutboundServer/api_template/components/responses/transferServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/transferSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/transferTimeout.yaml +5 -0
- package/OutboundServer/api_template/components/schemas/accountCreationStatus.yaml +18 -0
- package/OutboundServer/api_template/components/schemas/accountsCreationState.yaml +4 -0
- package/OutboundServer/api_template/components/schemas/accountsRequest.yaml +20 -0
- package/OutboundServer/api_template/components/schemas/accountsResponse.yaml +15 -0
- package/OutboundServer/api_template/components/schemas/async2SyncCurrentState.yaml +5 -0
- package/OutboundServer/api_template/components/schemas/authorizationsPostRequest.yaml +15 -0
- package/OutboundServer/api_template/components/schemas/authorizationsPostResponse.yaml +19 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteErrorResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteRequest.yaml +26 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteResponse.yaml +21 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteStatus.yaml +4 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteStatusResponse.yaml +17 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferErrorResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferRequest.yaml +26 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferResponse.yaml +16 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferStatus.yaml +4 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferStatusResponse.yaml +17 -0
- package/OutboundServer/api_template/components/schemas/errorAccountsResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/errorAuthorizationsResponse.yaml +3 -0
- package/OutboundServer/api_template/components/schemas/errorQuotesResponse.yaml +9 -0
- package/OutboundServer/api_template/components/schemas/errorResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/errorSimpleTransfersResponse.yaml +3 -0
- package/OutboundServer/api_template/components/schemas/errorTransferResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/extensionListEmptiable.yaml +6 -0
- package/OutboundServer/api_template/components/schemas/individualQuote.yaml +32 -0
- package/OutboundServer/api_template/components/schemas/individualQuoteResult.yaml +28 -0
- package/OutboundServer/api_template/components/schemas/individualTransfer.yaml +32 -0
- package/OutboundServer/api_template/components/schemas/individualTransferFulfilment.yaml +13 -0
- package/OutboundServer/api_template/components/schemas/individualTransferResult.yaml +41 -0
- package/OutboundServer/api_template/components/schemas/mojaloopError.yaml +5 -0
- package/OutboundServer/api_template/components/schemas/mojaloopTransactionRequestState.yaml +2 -0
- package/OutboundServer/api_template/components/schemas/partiesByIdResponse.yaml +13 -0
- package/OutboundServer/api_template/components/schemas/quote.yaml +3 -0
- package/OutboundServer/api_template/components/schemas/quoteError.yaml +16 -0
- package/OutboundServer/api_template/components/schemas/quotesPostRequest.yaml +13 -0
- package/OutboundServer/api_template/components/schemas/quotesPostResponse.yaml +48 -0
- package/OutboundServer/api_template/components/schemas/requestToPayRequest.yaml +39 -0
- package/OutboundServer/api_template/components/schemas/requestToPayResponse.yaml +41 -0
- package/OutboundServer/api_template/components/schemas/requestToPayTransferRequest.yaml +42 -0
- package/OutboundServer/api_template/components/schemas/requestToPayTransferResponse.yaml +58 -0
- package/OutboundServer/api_template/components/schemas/simpleTransferServerError.yaml +5 -0
- package/OutboundServer/api_template/components/schemas/simpleTransfersPostRequest.yaml +12 -0
- package/OutboundServer/api_template/components/schemas/simpleTransfersPostResponse.yaml +11 -0
- package/OutboundServer/api_template/components/schemas/transactionType.yaml +4 -0
- package/OutboundServer/api_template/components/schemas/transferContinuationAcceptOTP.yaml +9 -0
- package/OutboundServer/api_template/components/schemas/transferContinuationAcceptParty.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/transferContinuationAcceptQuote.yaml +9 -0
- package/OutboundServer/api_template/components/schemas/transferError.yaml +16 -0
- package/OutboundServer/api_template/components/schemas/transferFulfilment.yaml +3 -0
- package/OutboundServer/api_template/components/schemas/transferParty.yaml +40 -0
- package/OutboundServer/api_template/components/schemas/transferRequest.yaml +37 -0
- package/OutboundServer/api_template/components/schemas/transferResponse.yaml +58 -0
- package/OutboundServer/api_template/components/schemas/transferStatus.yaml +6 -0
- package/OutboundServer/api_template/components/schemas/transferStatusResponse.yaml +13 -0
- package/OutboundServer/api_template/health.yaml +12 -0
- package/OutboundServer/api_template/openapi.yaml +55 -0
- package/OutboundServer/api_template/paths/accounts.yaml +26 -0
- package/OutboundServer/api_template/paths/authorizations.yaml +19 -0
- package/OutboundServer/api_template/paths/bulkQuotes.yaml +23 -0
- package/OutboundServer/api_template/paths/bulkQuotes_bulkQuoteId.yaml +24 -0
- package/OutboundServer/api_template/paths/bulkTransfers.yaml +23 -0
- package/OutboundServer/api_template/paths/bulkTransfers_bulkTransferId.yaml +24 -0
- package/OutboundServer/api_template/paths/parties_Type_ID.yaml +20 -0
- package/OutboundServer/api_template/paths/parties_Type_ID_SubId.yaml +22 -0
- package/OutboundServer/api_template/paths/quotes.yaml +20 -0
- package/OutboundServer/api_template/paths/requestToPay.yaml +22 -0
- package/OutboundServer/api_template/paths/requestToPayTransfer.yaml +57 -0
- package/OutboundServer/api_template/paths/requestToPayTransfer_requestToPayTransactionId.yaml +34 -0
- package/OutboundServer/api_template/paths/simpleTransfers.yaml +19 -0
- package/OutboundServer/api_template/paths/transfers.yaml +55 -0
- package/OutboundServer/api_template/paths/transfers_transferId.yaml +58 -0
- package/OutboundServer/handlers.js +622 -0
- package/OutboundServer/index.js +137 -0
- package/OutboundServer/middlewares.js +67 -0
- package/TestServer/api.yaml +62 -0
- package/TestServer/handlers.js +63 -0
- package/TestServer/index.js +215 -0
- package/audit-resolve.json +65 -0
- package/babel.config.js +3 -0
- package/config.js +158 -0
- package/index.d.ts +1 -0
- package/index.js +149 -0
- package/jest.config.js +15 -0
- package/lib/api/index.js +12 -0
- package/lib/cache.js +352 -0
- package/lib/check.js +25 -0
- package/lib/model/AccountsModel.js +396 -0
- package/lib/model/Async2SyncModel.js +283 -0
- package/lib/model/AuthorizationsModel.js +86 -0
- package/lib/model/InboundTransfersModel.js +730 -0
- package/lib/model/OutboundBulkQuotesModel.js +485 -0
- package/lib/model/OutboundBulkTransfersModel.js +479 -0
- package/lib/model/OutboundRequestToPayModel.js +517 -0
- package/lib/model/OutboundRequestToPayTransferModel.js +893 -0
- package/lib/model/OutboundTransfersModel.js +823 -0
- package/lib/model/PartiesModel.js +70 -0
- package/lib/model/ProxyModel/MatchRules/Expression.js +48 -0
- package/lib/model/ProxyModel/MatchRules/Headers.js +65 -0
- package/lib/model/ProxyModel/MatchRules/MatchRule.js +27 -0
- package/lib/model/ProxyModel/MatchRules/Path.js +36 -0
- package/lib/model/ProxyModel/MatchRules/Query.js +65 -0
- package/lib/model/ProxyModel/MatchRules/index.js +19 -0
- package/lib/model/ProxyModel/Route.js +82 -0
- package/lib/model/ProxyModel/configSchema.json +118 -0
- package/lib/model/ProxyModel/index.js +138 -0
- package/lib/model/QuotesModel.js +94 -0
- package/lib/model/TransfersModel.js +81 -0
- package/lib/model/common/BackendError.js +26 -0
- package/lib/model/common/PersistentStateMachine.js +93 -0
- package/lib/model/common/index.js +18 -0
- package/lib/model/index.js +43 -0
- package/lib/model/lib/deferredJob.js +113 -0
- package/lib/model/lib/index.js +9 -0
- package/lib/model/lib/requests/backendRequests.js +227 -0
- package/lib/model/lib/requests/common.js +76 -0
- package/lib/model/lib/requests/index.js +19 -0
- package/lib/model/lib/shared.js +468 -0
- package/lib/randomphrase/index.js +21 -0
- package/lib/randomphrase/words.json +3397 -0
- package/lib/router.js +28 -0
- package/lib/validate.js +205 -0
- package/package.json +102 -0
- package/test/__mocks__/@mojaloop/sdk-standard-components.js +152 -0
- package/test/__mocks__/javascript-state-machine.js +21 -0
- package/test/__mocks__/redis.js +49 -0
- package/test/__mocks__/uuidv4.js +16 -0
- package/test/config/integration.env +136 -0
- package/test/integration/lib/Outbound/authorizations.test.js +58 -0
- package/test/integration/lib/Outbound/data/authorizationsPostRequest.json +43 -0
- package/test/integration/lib/Outbound/data/quotesPostRequest.json +52 -0
- package/test/integration/lib/Outbound/data/transfersPostRequest.json +24 -0
- package/test/integration/lib/Outbound/parties.test.js +28 -0
- package/test/integration/lib/Outbound/quotes.test.js +58 -0
- package/test/integration/lib/Outbound/simpleTransfers.test.js +67 -0
- package/test/integration/lib/cache.test.js +80 -0
- package/test/integration/testEnv.js +7 -0
- package/test/unit/InboundServer.test.js +443 -0
- package/test/unit/TestServer.test.js +394 -0
- package/test/unit/api/accounts/accounts.test.js +128 -0
- package/test/unit/api/accounts/data/postAccountsBody.json +7 -0
- package/test/unit/api/accounts/data/postAccountsErrorMojaloopResponse.json +25 -0
- package/test/unit/api/accounts/data/postAccountsErrorTimeoutResponse.json +19 -0
- package/test/unit/api/accounts/data/postAccountsSuccessResponse.json +17 -0
- package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError1.json +21 -0
- package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError2.json +21 -0
- package/test/unit/api/accounts/utils.js +65 -0
- package/test/unit/api/proxy/data/proxyConfig.yaml +82 -0
- package/test/unit/api/proxy/data/requestBody.json +22 -0
- package/test/unit/api/proxy/data/requestHeaders.json +5 -0
- package/test/unit/api/proxy/data/requestQuery.json +6 -0
- package/test/unit/api/proxy/data/responseBody.json +21 -0
- package/test/unit/api/proxy/data/responseHeaders.json +5 -0
- package/test/unit/api/proxy/proxy.test.js +220 -0
- package/test/unit/api/proxy/utils.js +79 -0
- package/test/unit/api/transfers/data/getTransfersCommittedResponse.json +21 -0
- package/test/unit/api/transfers/data/getTransfersErrorNotFound.json +17 -0
- package/test/unit/api/transfers/data/postQuotesBody.json +52 -0
- package/test/unit/api/transfers/data/postTransfersBadBody.json +17 -0
- package/test/unit/api/transfers/data/postTransfersBody.json +24 -0
- package/test/unit/api/transfers/data/postTransfersErrorMojaloopResponse.json +53 -0
- package/test/unit/api/transfers/data/postTransfersErrorTimeoutResponse.json +47 -0
- package/test/unit/api/transfers/data/postTransfersSimpleBody.json +26 -0
- package/test/unit/api/transfers/data/postTransfersSuccessResponse.json +101 -0
- package/test/unit/api/transfers/data/putPartiesBody.json +20 -0
- package/test/unit/api/transfers/data/putQuotesBody.json +37 -0
- package/test/unit/api/transfers/data/putTransfersBody.json +17 -0
- package/test/unit/api/transfers/transfers.test.js +191 -0
- package/test/unit/api/transfers/utils.js +183 -0
- package/test/unit/api/utils.js +75 -0
- package/test/unit/config.test.js +119 -0
- package/test/unit/data/commonHttpHeaders.json +6 -0
- package/test/unit/data/defaultConfig.json +58 -0
- package/test/unit/data/postQuotesBody.json +52 -0
- package/test/unit/data/putParticipantsBody.json +12 -0
- package/test/unit/data/putPartiesBody.json +20 -0
- package/test/unit/data/testFile.json +29 -0
- package/test/unit/data/testFile.yaml +14 -0
- package/test/unit/inboundApi/data/mockArguments.json +117 -0
- package/test/unit/inboundApi/data/mockTransactionRequest.json +42 -0
- package/test/unit/inboundApi/handlers.test.js +799 -0
- package/test/unit/index.test.js +55 -0
- package/test/unit/lib/cache.test.js +146 -0
- package/test/unit/lib/model/AccountsModel.test.js +121 -0
- package/test/unit/lib/model/AuthorizationsModel.test.js +460 -0
- package/test/unit/lib/model/InboundTransfersModel.test.js +628 -0
- package/test/unit/lib/model/OutboundBulkQuotesModel.test.js +249 -0
- package/test/unit/lib/model/OutboundBulkTransfersModel.test.js +244 -0
- package/test/unit/lib/model/OutboundRequestToPayModel.test.js +166 -0
- package/test/unit/lib/model/OutboundRequestToPayTransferModel.test.js +245 -0
- package/test/unit/lib/model/OutboundTransfersModel.test.js +836 -0
- package/test/unit/lib/model/PartiesModel.test.js +468 -0
- package/test/unit/lib/model/QuotesModel.test.js +470 -0
- package/test/unit/lib/model/TransfersModel.test.js +474 -0
- package/test/unit/lib/model/common/PersistentStateMachine.test.js +179 -0
- package/test/unit/lib/model/data/authorizationsResponse.json +13 -0
- package/test/unit/lib/model/data/bulkQuoteRequest.json +27 -0
- package/test/unit/lib/model/data/bulkQuoteResponse.json +35 -0
- package/test/unit/lib/model/data/bulkTransferFulfil.json +13 -0
- package/test/unit/lib/model/data/bulkTransferRequest.json +29 -0
- package/test/unit/lib/model/data/defaultConfig.json +47 -0
- package/test/unit/lib/model/data/getBulkTransfersBackendResponse.json +42 -0
- package/test/unit/lib/model/data/getBulkTransfersMojaloopResponse.json +22 -0
- package/test/unit/lib/model/data/getTransfersBackendResponse.json +34 -0
- package/test/unit/lib/model/data/getTransfersMojaloopResponse.json +17 -0
- package/test/unit/lib/model/data/mockArguments.json +131 -0
- package/test/unit/lib/model/data/mockTxnRequestsArguments.json +63 -0
- package/test/unit/lib/model/data/notificationToPayee.json +10 -0
- package/test/unit/lib/model/data/payeeParty.json +16 -0
- package/test/unit/lib/model/data/putAuthorizationsResponse.json +10 -0
- package/test/unit/lib/model/data/putQuotesResponse.json +33 -0
- package/test/unit/lib/model/data/putTransfersResponse.json +5 -0
- package/test/unit/lib/model/data/quoteResponse.json +31 -0
- package/test/unit/lib/model/data/requestToPayRequest.json +20 -0
- package/test/unit/lib/model/data/requestToPayTransferRequest.json +27 -0
- package/test/unit/lib/model/data/transactionRequestResponse.json +18 -0
- package/test/unit/lib/model/data/transferFulfil.json +8 -0
- package/test/unit/lib/model/data/transferRequest.json +26 -0
- package/test/unit/lib/model/mockedLibRequests.js +74 -0
- package/test/unit/mockLogger.js +39 -0
- package/test/unit/outboundApi/data/bulkQuoteRequest.json +28 -0
- package/test/unit/outboundApi/data/bulkTransferRequest.json +28 -0
- package/test/unit/outboundApi/data/mockBulkQuoteError.json +45 -0
- package/test/unit/outboundApi/data/mockBulkTransferError.json +48 -0
- package/test/unit/outboundApi/data/mockError.json +41 -0
- package/test/unit/outboundApi/data/mockGetPartiesError.json +4 -0
- package/test/unit/outboundApi/data/mockRequestToPayError.json +32 -0
- package/test/unit/outboundApi/data/mockRequestToPayTransferError.json +39 -0
- package/test/unit/outboundApi/data/requestToPay.json +21 -0
- package/test/unit/outboundApi/data/requestToPayTransferRequest.json +20 -0
- package/test/unit/outboundApi/data/transferRequest.json +21 -0
- 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;
|