handle permanent refund failure

This commit is contained in:
Florian Dold 2020-09-06 18:17:12 +05:30
parent cde4d13df8
commit c0861f0690
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
3 changed files with 72 additions and 8 deletions

View File

@ -899,10 +899,20 @@ export class ExchangeService implements ExchangeServiceInterface {
}
async runWirewatchOnce() {
await sh(
await runCommand(
this.globalState,
"wirewatch-test",
`taler-exchange-wirewatch ${this.timetravelArg} -c '${this.configFilename}' -t`,
`exchange-${this.name}-wirewatch-once`,
"taler-exchange-wirewatch",
[...this.timetravelArgArr, "-c", this.configFilename, "-t"],
);
}
async runAggregatorOnce() {
await runCommand(
this.globalState,
`exchange-${this.name}-aggregator-once`,
"taler-exchange-aggregator",
[...this.timetravelArgArr, "-c", this.configFilename, "-t"],
);
}

View File

@ -47,8 +47,6 @@ import {
MerchantCoinRefundStatus,
MerchantCoinRefundSuccessStatus,
MerchantCoinRefundFailureStatus,
codecForMerchantOrderStatusPaid,
AmountString,
codecForMerchantOrderRefundPickupResponse,
} from "../types/talerTypes";
import { guardOperationException } from "./errors";
@ -202,6 +200,56 @@ async function storePendingRefund(
};
}
async function storeFailedRefund(
tx: TransactionHandle,
p: PurchaseRecord,
r: MerchantCoinRefundFailureStatus,
): Promise<void> {
const refundKey = getRefundKey(r);
const coin = await tx.get(Stores.coins, r.coin_pub);
if (!coin) {
console.warn("coin not found, can't apply refund");
return;
}
const denom = await tx.getIndexed(
Stores.denominations.denomPubHashIndex,
coin.denomPubHash,
);
if (!denom) {
throw Error("inconsistent database");
}
const allDenoms = await tx
.iterIndexed(
Stores.denominations.exchangeBaseUrlIndex,
coin.exchangeBaseUrl,
)
.toArray();
const amountLeft = Amounts.sub(
Amounts.add(coin.currentAmount, Amounts.parseOrThrow(r.refund_amount))
.amount,
denom.feeRefund,
).amount;
const totalRefreshCostBound = getTotalRefreshCost(
allDenoms,
denom,
amountLeft,
);
p.refunds[refundKey] = {
type: RefundState.Failed,
obtainedTime: getTimestampNow(),
executionTime: r.execution_time,
refundAmount: Amounts.parseOrThrow(r.refund_amount),
refundFee: denom.feeRefund,
totalRefreshCostBound,
};
}
async function acceptRefunds(
ws: InternalWalletState,
proposalId: string,
@ -232,6 +280,10 @@ async function acceptRefunds(
const refundKey = getRefundKey(refundStatus);
const existingRefundInfo = p.refunds[refundKey];
const isPermanentFailure =
refundStatus.type === "failure" &&
refundStatus.exchange_status === 410;
// Already failed.
if (existingRefundInfo?.type === RefundState.Failed) {
continue;
@ -244,7 +296,7 @@ async function acceptRefunds(
// Still pending.
if (
refundStatus.type === "failure" &&
refundStatus.type === "failure" && !isPermanentFailure &&
existingRefundInfo?.type === RefundState.Pending
) {
continue;
@ -254,6 +306,8 @@ async function acceptRefunds(
if (refundStatus.type === "success") {
await applySuccessfulRefund(tx, p, refreshCoinsMap, refundStatus);
} else if (isPermanentFailure) {
await storeFailedRefund(tx, p, refundStatus);
} else {
await storePendingRefund(tx, p, refundStatus);
}

View File

@ -1325,13 +1325,13 @@ export const codecForMerchantCoinRefundFailureStatus = (): Codec<
buildCodecForObject<MerchantCoinRefundFailureStatus>()
.property("type", codecForConstString("failure"))
.property("coin_pub", codecForString())
.property("exchange_status", codecForConstNumber(200))
.property("exchange_status", codecForNumber())
.property("rtransaction_id", codecForNumber())
.property("refund_amount", codecForString())
.property("exchange_code", codecOptional(codecForNumber()))
.property("exchange_reply", codecOptional(codecForAny()))
.property("execution_time", codecForTimestamp)
.build("MerchantCoinRefundSuccessStatus");
.build("MerchantCoinRefundFailureStatus");
export const codecForMerchantCoinRefundStatus = (): Codec<
MerchantCoinRefundStatus