implement freezing for payments

This commit is contained in:
Florian Dold 2021-08-24 15:08:34 +02:00
parent 408d8e9fc8
commit a09359bd39
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
7 changed files with 45 additions and 8 deletions

View File

@ -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;

View File

@ -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, {

View File

@ -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";

View File

@ -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 (

View File

@ -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,

View File

@ -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,

View File

@ -64,6 +64,7 @@ export enum HttpResponseStatus {
NoContent = 204,
Gone = 210,
NotModified = 304,
BadRequest = 400,
PaymentRequired = 402,
NotFound = 404,
Conflict = 409,