diff options
Diffstat (limited to 'src/walletCoreApiHandler.ts')
-rw-r--r-- | src/walletCoreApiHandler.ts | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/src/walletCoreApiHandler.ts b/src/walletCoreApiHandler.ts new file mode 100644 index 000000000..6c32fd888 --- /dev/null +++ b/src/walletCoreApiHandler.ts @@ -0,0 +1,295 @@ +/* + This file is part of GNU Taler + (C) 2020 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import { Wallet } from "./wallet"; +import { + OperationFailedError, + OperationFailedAndReportedError, + makeErrorDetails, +} from "./operations/errors"; +import { TalerErrorCode } from "./TalerErrorCode"; +import { withdrawTestBalance } from "./headless/helpers"; +import { codecForTransactionsRequest } from "./types/transactions"; +import { + makeCodecForObject, + codecForString, + Codec, + makeCodecOptional, +} from "./util/codec"; +import { Amounts } from "./util/amounts"; + +interface AddExchangeRequest { + exchangeBaseUrl: string; +} + +const codecForAddExchangeRequest = (): Codec<AddExchangeRequest> => + makeCodecForObject<AddExchangeRequest>() + .property("exchangeBaseUrl", codecForString) + .build("AddExchangeRequest"); + +interface GetExchangeTosRequest { + exchangeBaseUrl: string; +} + +const codecForGetExchangeTosRequest = (): Codec<GetExchangeTosRequest> => + makeCodecForObject<GetExchangeTosRequest>() + .property("exchangeBaseUrl", codecForString) + .build("GetExchangeTosRequest"); + +interface AcceptManualWithdrawalRequest { + exchangeBaseUrl: string; + amount: string; +} + +const codecForAcceptManualWithdrawalRequet = (): Codec< + AcceptManualWithdrawalRequest +> => + makeCodecForObject<AcceptManualWithdrawalRequest>() + .property("exchangeBaseUrl", codecForString) + .property("amount", codecForString) + .build("AcceptManualWithdrawalRequest"); + +interface GetWithdrawalDetailsForAmountRequest { + exchangeBaseUrl: string; + amount: string; +} + +interface AcceptBankIntegratedWithdrawalRequest { + talerWithdrawUri: string; + exchangeBaseUrl: string; +} + +const codecForAcceptBankIntegratedWithdrawalRequest = (): Codec< + AcceptBankIntegratedWithdrawalRequest +> => + makeCodecForObject<AcceptBankIntegratedWithdrawalRequest>() + .property("exchangeBaseUrl", codecForString) + .property("talerWithdrawUri", codecForString) + .build("AcceptBankIntegratedWithdrawalRequest"); + +const codecForGetWithdrawalDetailsForAmountRequest = (): Codec< + GetWithdrawalDetailsForAmountRequest +> => + makeCodecForObject<GetWithdrawalDetailsForAmountRequest>() + .property("exchangeBaseUrl", codecForString) + .property("amount", codecForString) + .build("GetWithdrawalDetailsForAmountRequest"); + +interface AcceptExchangeTosRequest { + exchangeBaseUrl: string; + etag: string; +} + +const codecForAcceptExchangeTosRequest = (): Codec<AcceptExchangeTosRequest> => + makeCodecForObject<AcceptExchangeTosRequest>() + .property("exchangeBaseUrl", codecForString) + .property("etag", codecForString) + .build("AcceptExchangeTosRequest"); + +interface ApplyRefundRequest { + talerRefundUri: string; +} + +const codecForApplyRefundRequest = (): Codec<ApplyRefundRequest> => + makeCodecForObject<ApplyRefundRequest>() + .property("talerRefundUri", codecForString) + .build("ApplyRefundRequest"); + +interface GetWithdrawUriInfoRequest { + talerWithdrawUri: string; +} + +const codecForGetWithdrawUriInfoRequest = (): Codec< + GetWithdrawUriInfoRequest +> => + makeCodecForObject<GetWithdrawUriInfoRequest>() + .property("talerWithdrawUri", codecForString) + .build("GetWithdrawUriInfoRequest"); + +interface AbortProposalRequest { + proposalId: string; +} + +const codecForAbortProposalRequest = (): Codec<AbortProposalRequest> => + makeCodecForObject<AbortProposalRequest>() + .property("proposalId", codecForString) + .build("AbortProposalRequest"); + +interface PreparePayRequest { + talerPayUri: string; +} + +const codecForPreparePayRequest = (): Codec<PreparePayRequest> => + makeCodecForObject<PreparePayRequest>() + .property("talerPayUri", codecForString) + .build("PreparePay"); + +interface ConfirmPayRequest { + proposalId: string; + sessionId?: string; +} + +const codecForConfirmPayRequest = (): Codec<ConfirmPayRequest> => + makeCodecForObject<ConfirmPayRequest>() + .property("proposalId", codecForString) + .property("sessionId", makeCodecOptional(codecForString)) + .build("ConfirmPay"); + +/** + * Implementation of the "wallet-core" API. + */ + +async function dispatchRequestInternal( + wallet: Wallet, + operation: string, + payload: unknown, +): Promise<unknown> { + switch (operation) { + case "withdrawTestkudos": + return await withdrawTestBalance(wallet); + case "getTransactions": { + const req = codecForTransactionsRequest().decode(payload); + return await wallet.getTransactions(req); + } + case "addExchange": { + const req = codecForAddExchangeRequest().decode(payload); + await wallet.updateExchangeFromUrl(req.exchangeBaseUrl); + return {}; + } + case "listExchanges": { + return await wallet.getExchanges(); + } + case "getWithdrawUriInfo": { + const req = codecForGetWithdrawUriInfoRequest().decode(payload); + // FIXME: implement "natively" + throw Error("not implemented"); + } + case "acceptManualWithdrawal": { + const req = codecForAcceptManualWithdrawalRequet().decode(payload); + const res = await wallet.acceptManualWithdrawal( + req.exchangeBaseUrl, + Amounts.parseOrThrow(req.amount), + ); + return res; + } + case "getWithdrawalDetailsForAmount": { + const req = codecForGetWithdrawalDetailsForAmountRequest().decode( + payload, + ); + return await wallet.getWithdrawalDetailsForAmount( + req.exchangeBaseUrl, + Amounts.parseOrThrow(req.amount), + ); + } + case "getBalances": { + return await wallet.getBalances(); + } + case "getPendingOperations": { + return await wallet.getPendingOperations(); + } + case "acceptExchangeTermsOfService": { + const req = codecForAcceptExchangeTosRequest().decode(payload); + return await wallet.acceptExchangeTermsOfService( + req.exchangeBaseUrl, + req.etag, + ); + } + case "applyRefund": { + const req = codecForApplyRefundRequest().decode(payload); + return await wallet.applyRefund(req.talerRefundUri); + } + case "acceptBankIntegratedWithdrawal": { + const req = codecForAcceptBankIntegratedWithdrawalRequest().decode( + payload, + ); + return await wallet.acceptWithdrawal( + req.talerWithdrawUri, + req.exchangeBaseUrl, + ); + } + case "getExchangeTos": { + const req = codecForGetExchangeTosRequest().decode(payload); + return wallet.getExchangeTos(req.exchangeBaseUrl); + } + case "abortProposal": { + const req = codecForAbortProposalRequest().decode(payload); + return await wallet.refuseProposal(req.proposalId); + } + case "retryPendingNow": { + await wallet.runPending(true); + return {}; + } + case "preparePay": { + const req = codecForPreparePayRequest().decode(payload); + return await wallet.preparePayForUri(req.talerPayUri); + } + case "confirmPay": { + const req = codecForConfirmPayRequest().decode(payload); + return await wallet.confirmPay(req.proposalId, req.sessionId); + } + } + throw OperationFailedError.fromCode( + TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN, + "unknown operation", + { + operation, + }, + ); +} + +/** + * Handle a request to the wallet-core API. + */ +export async function handleCoreApiRequest( + w: Wallet, + operation: string, + id: string, + payload: unknown, +): Promise<unknown> { + try { + const result = await dispatchRequestInternal(w, operation, payload); + const respMsg = { + isError: false, + operation, + id, + result, + }; + return respMsg; + } catch (e) { + if ( + e instanceof OperationFailedError || + e instanceof OperationFailedAndReportedError + ) { + return { + isError: true, + operation, + id, + error: e.operationError, + }; + } else { + return { + isError: true, + operation, + id, + error: makeErrorDetails( + TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION, + `unexpected exception: ${e}`, + {}, + ), + }; + } + } +} |