From fb3da3a28d6ed6a16ca7d0fa8ec775de51c7df6b Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 11 Mar 2021 13:08:41 +0100 Subject: towards recovering from accidental double spends --- packages/taler-wallet-core/src/operations/pay.ts | 44 ++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'packages/taler-wallet-core/src/operations/pay.ts') diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts index 03bf9e119..3add9bbbf 100644 --- a/packages/taler-wallet-core/src/operations/pay.ts +++ b/packages/taler-wallet-core/src/operations/pay.ts @@ -84,6 +84,8 @@ import { throwUnexpectedRequestError, getHttpResponseErrorDetails, readSuccessResponseJsonOrErrorCode, + HttpResponseStatus, + readTalerErrorResponse, } from "../util/http"; import { TalerErrorCode } from "../TalerErrorCode"; import { URL } from "../util/url"; @@ -1001,6 +1003,22 @@ async function storePayReplaySuccess( }); } +/** + * Handle a 409 Conflict response from the merchant. + * + * We do this by going through the coin history provided by the exchange and + * (1) verifying the signatures from the exchange + * (2) adjusting the remaining coin value + * (3) re-do coin selection. + */ +async function handleInsufficientFunds( + ws: InternalWalletState, + proposalId: string, + err: TalerErrorDetails, +): Promise { + throw Error("payment re-denomination not implemented yet"); +} + /** * Submit a payment to the merchant. * @@ -1078,6 +1096,32 @@ async function submitPay( }; } + if (resp.status === HttpResponseStatus.Conflict) { + const err = await readTalerErrorResponse(resp); + if ( + err.code === + TalerErrorCode.MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS + ) { + // Do this in the background, as it might take some time + handleInsufficientFunds(ws, proposalId, err).catch(async (e) => { + await incrementProposalRetry(ws, proposalId, { + code: TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION, + message: "unexpected exception", + hint: "unexpected exception", + details: { + exception: e, + }, + }); + }); + + return { + type: ConfirmPayResultType.Pending, + // FIXME: should we return something better here? + lastError: err, + }; + } + } + const merchantResp = await readSuccessResponseJsonOrThrow( resp, codecForMerchantPayResponse(), -- cgit v1.2.3