aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-08-24 15:08:34 +0200
committerFlorian Dold <florian@dold.me>2021-08-24 15:08:34 +0200
commita09359bd3930f2a4550de22bd018122a2c7846e6 (patch)
tree9adad799168353bad6e0eb9e6df8744463ba2f56
parent408d8e9fc896193fbcff1afd12aa04ab6d513798 (diff)
implement freezing for payments
-rw-r--r--packages/taler-util/src/transactionsTypes.ts6
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts7
-rw-r--r--packages/taler-wallet-core/src/db.ts6
-rw-r--r--packages/taler-wallet-core/src/operations/pay.ts21
-rw-r--r--packages/taler-wallet-core/src/operations/pending.ts6
-rw-r--r--packages/taler-wallet-core/src/operations/transactions.ts6
-rw-r--r--packages/taler-wallet-core/src/util/http.ts1
7 files changed, 45 insertions, 8 deletions
diff --git a/packages/taler-util/src/transactionsTypes.ts b/packages/taler-util/src/transactionsTypes.ts
index e29a5549d..2ee34022f 100644
--- a/packages/taler-util/src/transactionsTypes.ts
+++ b/packages/taler-util/src/transactionsTypes.ts
@@ -80,6 +80,12 @@ export interface TransactionCommon {
// but its transactionId will remain unchanged
pending: boolean;
+ /**
+ * True if the transaction encountered a problem that might be
+ * permanent. A frozen transaction won't be automatically retried.
+ */
+ frozen: boolean;
+
// Raw amount of the transaction (exclusive of fees or other extra costs)
amountRaw: AmountString;
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts b/packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts
index 7a1ff669a..430a1ac93 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts
@@ -112,13 +112,6 @@ export async function runDenomUnofferedTest(t: GlobalTestState) {
merchantErrorCode,
TalerErrorCode.MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND,
);
-
- const purchId = makeEventId(TransactionType.Payment, preparePayResult.proposalId);
- await wallet.client.call(WalletApiOperation.DeleteTransaction, {
- transactionId: purchId,
- });
-
- // Now, delete the purchase and refresh operation.
}
await wallet.client.call(WalletApiOperation.AddExchange, {
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index ef6b45c11..66d79ebc0 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -1276,6 +1276,12 @@ export interface PurchaseRecord {
* Continue querying the refund status until this deadline has expired.
*/
autoRefundDeadline: Timestamp | undefined;
+
+ /**
+ * Is the payment frozen? I.e. did we encounter
+ * an error where it doesn't make sense to retry.
+ */
+ payFrozen?: boolean;
}
export const WALLET_BACKUP_STATE_KEY = "walletBackupState";
diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts
index a201e6f8d..9a7b0d069 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -93,6 +93,7 @@ import {
readSuccessResponseJsonOrErrorCode,
readSuccessResponseJsonOrThrow,
readTalerErrorResponse,
+ readUnexpectedResponseDetails,
throwUnexpectedRequestError,
} from "../util/http.js";
import {
@@ -1209,6 +1210,26 @@ async function submitPay(
};
}
+ if (resp.status === HttpResponseStatus.BadRequest) {
+ const errDetails = await readUnexpectedResponseDetails(resp);
+ logger.warn("unexpected 400 response for /pay");
+ logger.warn(j2s(errDetails));
+ await ws.db
+ .mktx((x) => ({ purchases: x.purchases }))
+ .runReadWrite(async (tx) => {
+ const purch = await tx.purchases.get(proposalId);
+ if (!purch) {
+ return;
+ }
+ purch.payFrozen = true;
+ purch.lastPayError = errDetails;
+ delete purch.payRetryInfo;
+ await tx.purchases.put(purch);
+ });
+ // FIXME: Maybe introduce a new return type for this instead of throwing?
+ throw new OperationFailedAndReportedError(errDetails);
+ }
+
if (resp.status === HttpResponseStatus.Conflict) {
const err = await readTalerErrorResponse(resp);
if (
diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts
index a4ca972a7..a87b1c8b1 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -235,7 +235,11 @@ async function gatherPurchasePending(
resp: PendingOperationsResponse,
): Promise<void> {
await tx.purchases.iter().forEach((pr) => {
- if (pr.paymentSubmitPending && pr.abortStatus === AbortStatus.None) {
+ if (
+ pr.paymentSubmitPending &&
+ pr.abortStatus === AbortStatus.None &&
+ !pr.payFrozen
+ ) {
const timestampDue = pr.payRetryInfo?.nextRetry ?? getTimestampNow();
resp.pendingOperations.push({
type: PendingTaskType.Pay,
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts
index a21dbe8b8..dc738b77f 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -168,6 +168,7 @@ export async function getTransactions(
TransactionType.Withdrawal,
wsr.withdrawalGroupId,
),
+ frozen: false,
...(wsr.lastError ? { error: wsr.lastError } : {}),
});
});
@@ -215,6 +216,7 @@ export async function getTransactions(
TransactionType.Withdrawal,
r.initialWithdrawalGroupId,
),
+ frozen: false,
...(r.lastError ? { error: r.lastError } : {}),
});
});
@@ -230,6 +232,7 @@ export async function getTransactions(
amountRaw: Amounts.stringify(dg.effectiveDepositAmount),
amountEffective: Amounts.stringify(dg.totalPayCost),
pending: !dg.timestampFinished,
+ frozen: false,
timestamp: dg.timestampCreated,
targetPaytoUri: dg.wire.payto_uri,
transactionId: makeEventId(
@@ -288,6 +291,7 @@ export async function getTransactions(
transactionId: paymentTransactionId,
proposalId: pr.proposalId,
info: info,
+ frozen: pr.payFrozen ?? false,
...(err ? { error: err } : {}),
});
@@ -351,6 +355,7 @@ export async function getTransactions(
amountEffective: Amounts.stringify(amountEffective),
amountRaw: Amounts.stringify(amountRaw),
pending: false,
+ frozen: false,
});
}
});
@@ -372,6 +377,7 @@ export async function getTransactions(
amountEffective: Amounts.stringify(tipRecord.tipAmountEffective),
amountRaw: Amounts.stringify(tipRecord.tipAmountRaw),
pending: !tipRecord.pickedUpTimestamp,
+ frozen: false,
timestamp: tipRecord.acceptedTimestamp,
transactionId: makeEventId(
TransactionType.Tip,
diff --git a/packages/taler-wallet-core/src/util/http.ts b/packages/taler-wallet-core/src/util/http.ts
index ce507465a..e056ffcee 100644
--- a/packages/taler-wallet-core/src/util/http.ts
+++ b/packages/taler-wallet-core/src/util/http.ts
@@ -64,6 +64,7 @@ export enum HttpResponseStatus {
NoContent = 204,
Gone = 210,
NotModified = 304,
+ BadRequest = 400,
PaymentRequired = 402,
NotFound = 404,
Conflict = 409,