aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2019-12-07 18:42:18 +0100
committerFlorian Dold <florian.dold@gmail.com>2019-12-07 18:42:18 +0100
commit165486a11268ab3d8009506916cd22d233cd248b (patch)
treeb90db78eafd4eb12b84ea2e8f4dc0353a315ab7f
parentd634626d7f3a179613a86eedb2f1c7a917ce65ba (diff)
downloadwallet-core-165486a11268ab3d8009506916cd22d233cd248b.tar.gz
wallet-core-165486a11268ab3d8009506916cd22d233cd248b.tar.bz2
wallet-core-165486a11268ab3d8009506916cd22d233cd248b.zip
auto-refund
-rw-r--r--src/dbTypes.ts7
-rw-r--r--src/util/helpers.ts36
-rw-r--r--src/wallet-impl/pay.ts45
-rw-r--r--src/wallet-impl/tip.ts4
4 files changed, 72 insertions, 20 deletions
diff --git a/src/dbTypes.ts b/src/dbTypes.ts
index 553040614..3ffade4e3 100644
--- a/src/dbTypes.ts
+++ b/src/dbTypes.ts
@@ -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;
}
/**
diff --git a/src/util/helpers.ts b/src/util/helpers.ts
index eb8a1c7b2..3831e84af 100644
--- a/src/util/helpers.ts
+++ b/src/util/helpers.ts
@@ -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.
@@ -152,25 +152,37 @@ export function extractTalerStampOrThrow(stamp: string): Timestamp {
}
/**
- * Check if a timestamp is in the right format.
+ * Extract a duration from a Taler duration string.
*/
-export function timestampCheck(stamp: string): boolean {
- return getTalerStampSec(stamp) !== null;
+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,
+ };
}
-
/**
- * Get a JavaScript Date object from a Taler date string.
- * Returns null if input is not in the right format.
+ * Extract a duration from a Taler duration string.
*/
-export function getTalerStampDate(stamp: string): Date | null {
- const sec = getTalerStampSec(stamp);
- if (sec == null) {
- return null;
+export function extractTalerDurationOrThrow(duration: string): Duration {
+ const r = extractTalerDuration(duration);
+ if (!r) {
+ throw Error("invalid duration");
}
- return new Date(sec * 1000);
+ return r;
+}
+
+/**
+ * Check if a timestamp is in the right format.
+ */
+export function timestampCheck(stamp: string): boolean {
+ return getTalerStampSec(stamp) !== null;
}
+
/**
* Compute the hash function of a JSON object.
*/
diff --git a/src/wallet-impl/pay.ts b/src/wallet-impl/pay.ts
index 7076f905d..c39feeec3 100644
--- a/src/wallet-impl/pay.ts
+++ b/src/wallet-impl/pay.ts
@@ -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();
diff --git a/src/wallet-impl/tip.ts b/src/wallet-impl/tip.ts
index 9cfaed930..11e029fcd 100644
--- a/src/wallet-impl/tip.ts
+++ b/src/wallet-impl/tip.ts
@@ -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,