auto-refund
This commit is contained in:
parent
d634626d7f
commit
165486a112
@ -788,7 +788,7 @@ export interface TipRecord {
|
||||
/**
|
||||
* Timestamp, the tip can't be picked up anymore after this deadline.
|
||||
*/
|
||||
deadline: number;
|
||||
deadline: Timestamp;
|
||||
|
||||
/**
|
||||
* The exchange that will sign our coins, chosen by the merchant.
|
||||
@ -1066,6 +1066,11 @@ export interface PurchaseRecord {
|
||||
* Last error (or undefined) for querying the refund status with the merchant.
|
||||
*/
|
||||
lastRefundApplyError: OperationError | undefined;
|
||||
|
||||
/**
|
||||
* Continue querying the refund status until this deadline has expired.
|
||||
*/
|
||||
autoRefundDeadline: Timestamp | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,7 +24,7 @@
|
||||
import { AmountJson } from "./amounts";
|
||||
import * as Amounts from "./amounts";
|
||||
|
||||
import { Timestamp } from "../walletTypes";
|
||||
import { Timestamp, Duration } from "../walletTypes";
|
||||
|
||||
/**
|
||||
* Show an amount in a form suitable for the user.
|
||||
@ -151,6 +151,30 @@ export function extractTalerStampOrThrow(stamp: string): Timestamp {
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a duration from a Taler duration string.
|
||||
*/
|
||||
export function extractTalerDuration(duration: string): Duration | undefined {
|
||||
const m = duration.match(/\/?Delay\(([0-9]*)\)\/?/);
|
||||
if (!m || !m[1]) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
d_ms: parseInt(m[1], 10) * 1000,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a duration from a Taler duration string.
|
||||
*/
|
||||
export function extractTalerDurationOrThrow(duration: string): Duration {
|
||||
const r = extractTalerDuration(duration);
|
||||
if (!r) {
|
||||
throw Error("invalid duration");
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a timestamp is in the right format.
|
||||
*/
|
||||
@ -159,18 +183,6 @@ export function timestampCheck(stamp: string): boolean {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a JavaScript Date object from a Taler date string.
|
||||
* Returns null if input is not in the right format.
|
||||
*/
|
||||
export function getTalerStampDate(stamp: string): Date | null {
|
||||
const sec = getTalerStampSec(stamp);
|
||||
if (sec == null) {
|
||||
return null;
|
||||
}
|
||||
return new Date(sec * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the hash function of a JSON object.
|
||||
*/
|
||||
|
@ -62,6 +62,8 @@ import {
|
||||
strcmp,
|
||||
canonicalJson,
|
||||
extractTalerStampOrThrow,
|
||||
extractTalerDurationOrThrow,
|
||||
extractTalerDuration,
|
||||
} from "../util/helpers";
|
||||
import { Logger } from "../util/logging";
|
||||
import { InternalWalletState } from "./state";
|
||||
@ -359,6 +361,7 @@ async function recordConfirmPay(
|
||||
lastRefundApplyError: undefined,
|
||||
refundApplyRetryInfo: initRetryInfo(),
|
||||
firstSuccessfulPayTimestamp: undefined,
|
||||
autoRefundDeadline: undefined,
|
||||
};
|
||||
|
||||
await runWithWriteTransaction(
|
||||
@ -704,9 +707,23 @@ export async function submitPay(
|
||||
// FIXME: properly display error
|
||||
throw Error("merchant payment signature invalid");
|
||||
}
|
||||
const isFirst = purchase.firstSuccessfulPayTimestamp === undefined;
|
||||
purchase.firstSuccessfulPayTimestamp = getTimestampNow();
|
||||
purchase.lastPayError = undefined;
|
||||
purchase.payRetryInfo = initRetryInfo(false);
|
||||
if (isFirst) {
|
||||
const ar = purchase.contractTerms.auto_refund;
|
||||
if (ar) {
|
||||
const autoRefundDelay = extractTalerDuration(ar);
|
||||
if (autoRefundDelay) {
|
||||
purchase.refundStatusRequested = true;
|
||||
purchase.autoRefundDeadline = {
|
||||
t_ms: getTimestampNow().t_ms + autoRefundDelay.d_ms,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const modifiedCoins: CoinRecord[] = [];
|
||||
for (const pc of purchase.payReq.coins) {
|
||||
const c = await oneShotGet(ws.db, Stores.coins, pc.coin_pub);
|
||||
@ -1064,11 +1081,6 @@ async function acceptRefundResponse(
|
||||
return;
|
||||
}
|
||||
|
||||
p.lastRefundStatusTimestamp = getTimestampNow();
|
||||
p.lastRefundStatusError = undefined;
|
||||
p.refundStatusRetryInfo = initRetryInfo();
|
||||
p.refundStatusRequested = false;
|
||||
|
||||
for (const perm of refundPermissions) {
|
||||
if (
|
||||
!p.refundsPending[perm.merchant_sig] &&
|
||||
@ -1079,6 +1091,29 @@ async function acceptRefundResponse(
|
||||
}
|
||||
}
|
||||
|
||||
// Are we done with querying yet, or do we need to do another round
|
||||
// after a retry delay?
|
||||
let queryDone = true;
|
||||
|
||||
if (numNewRefunds === 0) {
|
||||
if (p.autoRefundDeadline && p.autoRefundDeadline.t_ms < getTimestampNow().t_ms) {
|
||||
queryDone = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (queryDone) {
|
||||
p.lastRefundStatusTimestamp = getTimestampNow();
|
||||
p.lastRefundStatusError = undefined;
|
||||
p.refundStatusRetryInfo = initRetryInfo();
|
||||
p.refundStatusRequested = false;
|
||||
} else {
|
||||
// No error, but we need to try again!
|
||||
p.lastRefundStatusTimestamp = getTimestampNow();
|
||||
p.refundStatusRetryInfo.retryCounter++;
|
||||
updateRetryInfoTimeout(p.refundStatusRetryInfo);
|
||||
p.lastRefundStatusError = undefined;
|
||||
}
|
||||
|
||||
if (numNewRefunds) {
|
||||
p.lastRefundApplyError = undefined;
|
||||
p.refundApplyRetryInfo = initRetryInfo();
|
||||
|
@ -23,7 +23,7 @@ import { TipPickupGetResponse, TipPlanchetDetail, TipResponse } from "../talerTy
|
||||
import * as Amounts from "../util/amounts";
|
||||
import { Stores, PlanchetRecord, WithdrawalSessionRecord, initRetryInfo, updateRetryInfoTimeout } from "../dbTypes";
|
||||
import { getWithdrawDetailsForAmount, getVerifiedWithdrawDenomList, processWithdrawSession } from "./withdraw";
|
||||
import { getTalerStampSec } from "../util/helpers";
|
||||
import { getTalerStampSec, extractTalerStampOrThrow } from "../util/helpers";
|
||||
import { updateExchangeFromUrl } from "./exchanges";
|
||||
import { getRandomBytes, encodeCrock } from "../crypto/talerCrypto";
|
||||
import { guardOperationException } from "./errors";
|
||||
@ -68,7 +68,7 @@ export async function getTipStatus(
|
||||
tipId,
|
||||
accepted: false,
|
||||
amount,
|
||||
deadline: getTalerStampSec(tipPickupStatus.stamp_expire)!,
|
||||
deadline: extractTalerStampOrThrow(tipPickupStatus.stamp_expire),
|
||||
exchangeUrl: tipPickupStatus.exchange_url,
|
||||
merchantBaseUrl: res.merchantBaseUrl,
|
||||
nextUrl: undefined,
|
||||
|
Loading…
Reference in New Issue
Block a user