summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations/pay.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-03-22 21:16:38 +0100
committerFlorian Dold <florian@dold.me>2022-03-22 21:16:38 +0100
commit5d23eb36354d07508a015531f298b3e261bbafce (patch)
treefae0d2599c94d88c9264bb63a301adb1706824c1 /packages/taler-wallet-core/src/operations/pay.ts
parentf8d12f7b0d4af1b1769b89e80c87f9c169678564 (diff)
downloadwallet-core-5d23eb36354d07508a015531f298b3e261bbafce.tar.gz
wallet-core-5d23eb36354d07508a015531f298b3e261bbafce.tar.bz2
wallet-core-5d23eb36354d07508a015531f298b3e261bbafce.zip
wallet: improve error handling and error codes
Diffstat (limited to 'packages/taler-wallet-core/src/operations/pay.ts')
-rw-r--r--packages/taler-wallet-core/src/operations/pay.ts106
1 files changed, 60 insertions, 46 deletions
diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts
index 9521d544f..ce3a26c34 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -25,6 +25,7 @@
* Imports.
*/
import {
+ AbsoluteTime,
AmountJson,
Amounts,
codecForContractTerms,
@@ -34,7 +35,6 @@ import {
ConfirmPayResult,
ConfirmPayResultType,
ContractTerms,
- decodeCrock,
Duration,
durationMax,
durationMin,
@@ -43,19 +43,17 @@ import {
getRandomBytes,
HttpStatusCode,
j2s,
- kdf,
Logger,
NotificationType,
parsePayUri,
PreparePayResult,
PreparePayResultType,
RefreshReason,
- stringToBytes,
TalerErrorCode,
- TalerErrorDetails,
- AbsoluteTime,
- URL,
+ TalerErrorDetail,
TalerProtocolTimestamp,
+ TransactionType,
+ URL,
} from "@gnu-taler/taler-util";
import { EXCHANGE_COINS_LOCK, InternalWalletState } from "../common.js";
import {
@@ -74,9 +72,9 @@ import {
} from "../db.js";
import {
guardOperationException,
- makeErrorDetails,
- OperationFailedAndReportedError,
- OperationFailedError,
+ makeErrorDetail,
+ makePendingOperationFailedError,
+ TalerError,
} from "../errors.js";
import {
AvailableCoinInfo,
@@ -467,7 +465,7 @@ async function recordConfirmPay(
async function reportProposalError(
ws: InternalWalletState,
proposalId: string,
- err: TalerErrorDetails,
+ err: TalerErrorDetail,
): Promise<void> {
await ws.db
.mktx((x) => ({ proposals: x.proposals }))
@@ -550,7 +548,7 @@ async function incrementPurchasePayRetry(
async function reportPurchasePayError(
ws: InternalWalletState,
proposalId: string,
- err: TalerErrorDetails,
+ err: TalerErrorDetail,
): Promise<void> {
await ws.db
.mktx((x) => ({ purchases: x.purchases }))
@@ -575,7 +573,7 @@ export async function processDownloadProposal(
proposalId: string,
forceNow = false,
): Promise<void> {
- const onOpErr = (err: TalerErrorDetails): Promise<void> =>
+ const onOpErr = (err: TalerErrorDetail): Promise<void> =>
reportProposalError(ws, proposalId, err);
await guardOperationException(
() => processDownloadProposalImpl(ws, proposalId, forceNow),
@@ -602,7 +600,7 @@ async function resetDownloadProposalRetry(
async function failProposalPermanently(
ws: InternalWalletState,
proposalId: string,
- err: TalerErrorDetails,
+ err: TalerErrorDetail,
): Promise<void> {
await ws.db
.mktx((x) => ({ proposals: x.proposals }))
@@ -727,13 +725,13 @@ async function processDownloadProposalImpl(
if (r.isError) {
switch (r.talerErrorResponse.code) {
case TalerErrorCode.MERCHANT_POST_ORDERS_ID_CLAIM_ALREADY_CLAIMED:
- throw OperationFailedError.fromCode(
+ throw TalerError.fromDetail(
TalerErrorCode.WALLET_ORDER_ALREADY_CLAIMED,
- "order already claimed (likely by other wallet)",
{
orderId: proposal.orderId,
claimUrl: orderClaimUrl,
},
+ "order already claimed (likely by other wallet)",
);
default:
throwUnexpectedRequestError(httpResponse, r.talerErrorResponse);
@@ -758,13 +756,17 @@ async function processDownloadProposalImpl(
logger.trace(
`malformed contract terms: ${j2s(proposalResp.contract_terms)}`,
);
- const err = makeErrorDetails(
+ const err = makeErrorDetail(
TalerErrorCode.WALLET_CONTRACT_TERMS_MALFORMED,
- "validation for well-formedness failed",
{},
+ "validation for well-formedness failed",
);
await failProposalPermanently(ws, proposalId, err);
- throw new OperationFailedAndReportedError(err);
+ throw makePendingOperationFailedError(
+ err,
+ TransactionType.Payment,
+ proposalId,
+ );
}
const contractTermsHash = ContractTermsUtil.hashContractTerms(
@@ -780,13 +782,17 @@ async function processDownloadProposalImpl(
proposalResp.contract_terms,
);
} catch (e) {
- const err = makeErrorDetails(
+ const err = makeErrorDetail(
TalerErrorCode.WALLET_CONTRACT_TERMS_MALFORMED,
- `schema validation failed: ${e}`,
{},
+ `schema validation failed: ${e}`,
);
await failProposalPermanently(ws, proposalId, err);
- throw new OperationFailedAndReportedError(err);
+ throw makePendingOperationFailedError(
+ err,
+ TransactionType.Payment,
+ proposalId,
+ );
}
const sigValid = await ws.cryptoApi.isValidContractTermsSignature(
@@ -796,16 +802,20 @@ async function processDownloadProposalImpl(
);
if (!sigValid) {
- const err = makeErrorDetails(
+ const err = makeErrorDetail(
TalerErrorCode.WALLET_CONTRACT_TERMS_SIGNATURE_INVALID,
- "merchant's signature on contract terms is invalid",
{
merchantPub: parsedContractTerms.merchant_pub,
orderId: parsedContractTerms.order_id,
},
+ "merchant's signature on contract terms is invalid",
);
await failProposalPermanently(ws, proposalId, err);
- throw new OperationFailedAndReportedError(err);
+ throw makePendingOperationFailedError(
+ err,
+ TransactionType.Payment,
+ proposalId,
+ );
}
const fulfillmentUrl = parsedContractTerms.fulfillment_url;
@@ -814,16 +824,20 @@ async function processDownloadProposalImpl(
const baseUrlFromContractTerms = parsedContractTerms.merchant_base_url;
if (baseUrlForDownload !== baseUrlFromContractTerms) {
- const err = makeErrorDetails(
+ const err = makeErrorDetail(
TalerErrorCode.WALLET_CONTRACT_TERMS_BASE_URL_MISMATCH,
- "merchant base URL mismatch",
{
baseUrlForDownload,
baseUrlFromContractTerms,
},
+ "merchant base URL mismatch",
);
await failProposalPermanently(ws, proposalId, err);
- throw new OperationFailedAndReportedError(err);
+ throw makePendingOperationFailedError(
+ err,
+ TransactionType.Payment,
+ proposalId,
+ );
}
const contractData = extractContractData(
@@ -895,10 +909,8 @@ async function startDownloadProposal(
]);
});
- /**
- * If we have already claimed this proposal with the same sessionId
- * nonce and claim token, reuse it.
- */
+ /* If we have already claimed this proposal with the same sessionId
+ * nonce and claim token, reuse it. */
if (
oldProposal &&
oldProposal.downloadSessionId === sessionId &&
@@ -1029,7 +1041,7 @@ async function storePayReplaySuccess(
async function handleInsufficientFunds(
ws: InternalWalletState,
proposalId: string,
- err: TalerErrorDetails,
+ err: TalerErrorDetail,
): Promise<void> {
logger.trace("handling insufficient funds, trying to re-select coins");
@@ -1319,12 +1331,12 @@ export async function preparePayForUri(
const uriResult = parsePayUri(talerPayUri);
if (!uriResult) {
- throw OperationFailedError.fromCode(
+ throw TalerError.fromDetail(
TalerErrorCode.WALLET_INVALID_TALER_PAY_URI,
- `invalid taler://pay URI (${talerPayUri})`,
{
talerPayUri,
},
+ `invalid taler://pay URI (${talerPayUri})`,
);
}
@@ -1503,7 +1515,7 @@ export async function processPurchasePay(
proposalId: string,
forceNow = false,
): Promise<ConfirmPayResult> {
- const onOpErr = (e: TalerErrorDetails): Promise<void> =>
+ const onOpErr = (e: TalerErrorDetail): Promise<void> =>
reportPurchasePayError(ws, proposalId, e);
return await guardOperationException(
() => processPurchasePayImpl(ws, proposalId, forceNow),
@@ -1527,9 +1539,8 @@ async function processPurchasePayImpl(
lastError: {
// FIXME: allocate more specific error code
code: TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
- message: `trying to pay for purchase that is not in the database`,
- hint: `proposal ID is ${proposalId}`,
- details: {},
+ hint: `trying to pay for purchase that is not in the database`,
+ proposalId: proposalId,
},
};
}
@@ -1594,10 +1605,10 @@ async function processPurchasePayImpl(
resp.status <= 599
) {
logger.trace("treating /pay error as transient");
- const err = makeErrorDetails(
+ const err = makeErrorDetail(
TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
- "/pay failed",
getHttpResponseErrorDetails(resp),
+ "/pay failed",
);
return {
type: ConfirmPayResultType.Pending,
@@ -1621,8 +1632,11 @@ async function processPurchasePayImpl(
delete purch.payRetryInfo;
await tx.purchases.put(purch);
});
- // FIXME: Maybe introduce a new return type for this instead of throwing?
- throw new OperationFailedAndReportedError(errDetails);
+ throw makePendingOperationFailedError(
+ errDetails,
+ TransactionType.Payment,
+ proposalId,
+ );
}
if (resp.status === HttpStatusCode.Conflict) {
@@ -1692,10 +1706,10 @@ async function processPurchasePayImpl(
resp.status >= 500 &&
resp.status <= 599
) {
- const err = makeErrorDetails(
+ const err = makeErrorDetail(
TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
- "/paid failed",
getHttpResponseErrorDetails(resp),
+ "/paid failed",
);
return {
type: ConfirmPayResultType.Pending,
@@ -1703,10 +1717,10 @@ async function processPurchasePayImpl(
};
}
if (resp.status !== 204) {
- throw OperationFailedError.fromCode(
+ throw TalerError.fromDetail(
TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
- "/paid failed",
getHttpResponseErrorDetails(resp),
+ "/paid failed",
);
}
await storePayReplaySuccess(ws, proposalId, sessionId);