fix refunds
This commit is contained in:
parent
f67d7f54f9
commit
8115ac660c
@ -980,7 +980,7 @@ export enum PurchaseStatus {
|
|||||||
QueryRefund = "query-refund",
|
QueryRefund = "query-refund",
|
||||||
ProcessRefund = "process-refund",
|
ProcessRefund = "process-refund",
|
||||||
Abort = "abort",
|
Abort = "abort",
|
||||||
Done = "done",
|
Dormant = "dormant",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,12 +89,15 @@ export class MerchantBackendConnection {
|
|||||||
summary: string,
|
summary: string,
|
||||||
fulfillmentUrl: string,
|
fulfillmentUrl: string,
|
||||||
): Promise<{ orderId: string }> {
|
): Promise<{ orderId: string }> {
|
||||||
|
const t = Math.floor(new Date().getTime() / 1000) + 15 * 60;
|
||||||
const reqUrl = new URL("order", this.merchantBaseUrl).href;
|
const reqUrl = new URL("order", this.merchantBaseUrl).href;
|
||||||
const orderReq = {
|
const orderReq = {
|
||||||
order: {
|
order: {
|
||||||
amount,
|
amount,
|
||||||
summary,
|
summary,
|
||||||
fulfillment_url: fulfillmentUrl,
|
fulfillment_url: fulfillmentUrl,
|
||||||
|
refund_deadline: `/Date(${t})/`,
|
||||||
|
wire_transfer_deadline: `/Date(${t})/`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const resp = await axios({
|
const resp = await axios({
|
||||||
|
@ -137,7 +137,7 @@ async function withWallet<T>(
|
|||||||
console.error("Operation failed: " + e.message);
|
console.error("Operation failed: " + e.message);
|
||||||
console.log("Hint: check pending operations for details.");
|
console.log("Hint: check pending operations for details.");
|
||||||
} else {
|
} else {
|
||||||
console.error("caught exception:", e);
|
console.error("caught unhandled exception (bug?):", e);
|
||||||
}
|
}
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -52,8 +52,9 @@ export async function guardOperationException<T>(
|
|||||||
onOpError: (e: OperationError) => Promise<void>,
|
onOpError: (e: OperationError) => Promise<void>,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
try {
|
try {
|
||||||
return op();
|
return await op();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.log("guard: caught exception");
|
||||||
if (e instanceof OperationFailedAndReportedError) {
|
if (e instanceof OperationFailedAndReportedError) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@ -62,6 +63,7 @@ export async function guardOperationException<T>(
|
|||||||
throw new OperationFailedAndReportedError(e.message);
|
throw new OperationFailedAndReportedError(e.message);
|
||||||
}
|
}
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
|
console.log("guard: caught Error");
|
||||||
await onOpError({
|
await onOpError({
|
||||||
type: "exception",
|
type: "exception",
|
||||||
message: e.message,
|
message: e.message,
|
||||||
@ -69,6 +71,7 @@ export async function guardOperationException<T>(
|
|||||||
});
|
});
|
||||||
throw new OperationFailedAndReportedError(e.message);
|
throw new OperationFailedAndReportedError(e.message);
|
||||||
}
|
}
|
||||||
|
console.log("guard: caught something else");
|
||||||
await onOpError({
|
await onOpError({
|
||||||
type: "exception",
|
type: "exception",
|
||||||
message: "non-error exception thrown",
|
message: "non-error exception thrown",
|
||||||
|
@ -365,6 +365,8 @@ async function recordConfirmPay(
|
|||||||
const p = await tx.get(Stores.proposals, proposal.proposalId);
|
const p = await tx.get(Stores.proposals, proposal.proposalId);
|
||||||
if (p) {
|
if (p) {
|
||||||
p.proposalStatus = ProposalStatus.ACCEPTED;
|
p.proposalStatus = ProposalStatus.ACCEPTED;
|
||||||
|
p.lastError = undefined;
|
||||||
|
p.retryInfo = initRetryInfo(false);
|
||||||
await tx.put(Stores.proposals, p);
|
await tx.put(Stores.proposals, p);
|
||||||
}
|
}
|
||||||
await tx.put(Stores.purchases, t);
|
await tx.put(Stores.purchases, t);
|
||||||
@ -467,6 +469,7 @@ async function incrementPurchaseRetry(
|
|||||||
proposalId: string,
|
proposalId: string,
|
||||||
err: OperationError | undefined,
|
err: OperationError | undefined,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
console.log("incrementing purchase retry with error", err);
|
||||||
await runWithWriteTransaction(ws.db, [Stores.purchases], async tx => {
|
await runWithWriteTransaction(ws.db, [Stores.purchases], async tx => {
|
||||||
const pr = await tx.get(Stores.purchases, proposalId);
|
const pr = await tx.get(Stores.purchases, proposalId);
|
||||||
if (!pr) {
|
if (!pr) {
|
||||||
@ -650,6 +653,8 @@ export async function submitPay(
|
|||||||
throw Error("merchant payment signature invalid");
|
throw Error("merchant payment signature invalid");
|
||||||
}
|
}
|
||||||
purchase.finished = true;
|
purchase.finished = true;
|
||||||
|
purchase.status = PurchaseStatus.Dormant;
|
||||||
|
purchase.lastError = undefined;
|
||||||
purchase.retryInfo = initRetryInfo(false);
|
purchase.retryInfo = initRetryInfo(false);
|
||||||
const modifiedCoins: CoinRecord[] = [];
|
const modifiedCoins: CoinRecord[] = [];
|
||||||
for (const pc of purchase.payReq.coins) {
|
for (const pc of purchase.payReq.coins) {
|
||||||
@ -992,6 +997,7 @@ async function submitRefundsToExchange(
|
|||||||
}
|
}
|
||||||
const pendingKeys = Object.keys(purchase.refundsPending);
|
const pendingKeys = Object.keys(purchase.refundsPending);
|
||||||
if (pendingKeys.length === 0) {
|
if (pendingKeys.length === 0) {
|
||||||
|
console.log("no pending refunds");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const pk of pendingKeys) {
|
for (const pk of pendingKeys) {
|
||||||
@ -1010,28 +1016,34 @@ async function submitRefundsToExchange(
|
|||||||
const exchangeUrl = purchase.payReq.coins[0].exchange_url;
|
const exchangeUrl = purchase.payReq.coins[0].exchange_url;
|
||||||
const reqUrl = new URL("refund", exchangeUrl);
|
const reqUrl = new URL("refund", exchangeUrl);
|
||||||
const resp = await ws.http.postJson(reqUrl.href, req);
|
const resp = await ws.http.postJson(reqUrl.href, req);
|
||||||
|
console.log("sent refund permission");
|
||||||
if (resp.status !== 200) {
|
if (resp.status !== 200) {
|
||||||
console.error("refund failed", resp);
|
console.error("refund failed", resp);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transactionally mark successful refunds as done
|
let allRefundsProcessed = false;
|
||||||
const transformPurchase = (
|
|
||||||
t: PurchaseRecord | undefined,
|
await runWithWriteTransaction(
|
||||||
): PurchaseRecord | undefined => {
|
ws.db,
|
||||||
if (!t) {
|
[Stores.purchases, Stores.coins],
|
||||||
console.warn("purchase not found, not updating refund");
|
async tx => {
|
||||||
|
const p = await tx.get(Stores.purchases, proposalId);
|
||||||
|
if (!p) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (t.refundsPending[pk]) {
|
if (p.refundsPending[pk]) {
|
||||||
t.refundsDone[pk] = t.refundsPending[pk];
|
p.refundsDone[pk] = p.refundsPending[pk];
|
||||||
delete t.refundsPending[pk];
|
delete p.refundsPending[pk];
|
||||||
}
|
}
|
||||||
return t;
|
if (Object.keys(p.refundsPending).length === 0) {
|
||||||
};
|
p.retryInfo = initRetryInfo();
|
||||||
const transformCoin = (
|
p.lastError = undefined;
|
||||||
c: CoinRecord | undefined,
|
p.status = PurchaseStatus.Dormant;
|
||||||
): CoinRecord | undefined => {
|
allRefundsProcessed = true;
|
||||||
|
}
|
||||||
|
await tx.put(Stores.purchases, p);
|
||||||
|
const c = await tx.get(Stores.coins, perm.coin_pub);
|
||||||
if (!c) {
|
if (!c) {
|
||||||
console.warn("coin not found, can't apply refund");
|
console.warn("coin not found, can't apply refund");
|
||||||
return;
|
return;
|
||||||
@ -1041,19 +1053,15 @@ async function submitRefundsToExchange(
|
|||||||
c.status = CoinStatus.Dirty;
|
c.status = CoinStatus.Dirty;
|
||||||
c.currentAmount = Amounts.add(c.currentAmount, refundAmount).amount;
|
c.currentAmount = Amounts.add(c.currentAmount, refundAmount).amount;
|
||||||
c.currentAmount = Amounts.sub(c.currentAmount, refundFee).amount;
|
c.currentAmount = Amounts.sub(c.currentAmount, refundFee).amount;
|
||||||
|
await tx.put(Stores.coins, c);
|
||||||
return c;
|
|
||||||
};
|
|
||||||
|
|
||||||
await runWithWriteTransaction(
|
|
||||||
ws.db,
|
|
||||||
[Stores.purchases, Stores.coins],
|
|
||||||
async tx => {
|
|
||||||
await tx.mutate(Stores.purchases, proposalId, transformPurchase);
|
|
||||||
await tx.mutate(Stores.coins, perm.coin_pub, transformCoin);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
refresh(ws, perm.coin_pub);
|
if (allRefundsProcessed) {
|
||||||
|
ws.notify({
|
||||||
|
type: NotificationType.RefundFinished,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
await refresh(ws, perm.coin_pub);
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.notify({
|
ws.notify({
|
||||||
@ -1062,7 +1070,6 @@ async function submitRefundsToExchange(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function acceptRefundResponse(
|
async function acceptRefundResponse(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
proposalId: string,
|
proposalId: string,
|
||||||
@ -1086,6 +1093,8 @@ async function acceptRefundResponse(
|
|||||||
|
|
||||||
t.lastRefundTimestamp = getTimestampNow();
|
t.lastRefundTimestamp = getTimestampNow();
|
||||||
t.status = PurchaseStatus.ProcessRefund;
|
t.status = PurchaseStatus.ProcessRefund;
|
||||||
|
t.lastError = undefined;
|
||||||
|
t.retryInfo = initRetryInfo();
|
||||||
|
|
||||||
for (const perm of refundPermissions) {
|
for (const perm of refundPermissions) {
|
||||||
if (
|
if (
|
||||||
@ -1102,14 +1111,21 @@ async function acceptRefundResponse(
|
|||||||
await submitRefundsToExchange(ws, proposalId);
|
await submitRefundsToExchange(ws, proposalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function queryRefund(
|
||||||
async function queryRefund(ws: InternalWalletState, proposalId: string): Promise<void> {
|
ws: InternalWalletState,
|
||||||
|
proposalId: string,
|
||||||
|
): Promise<void> {
|
||||||
const purchase = await oneShotGet(ws.db, Stores.purchases, proposalId);
|
const purchase = await oneShotGet(ws.db, Stores.purchases, proposalId);
|
||||||
if (purchase?.status !== PurchaseStatus.QueryRefund) {
|
if (purchase?.status !== PurchaseStatus.QueryRefund) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const refundUrl = new URL("refund", purchase.contractTerms.merchant_base_url).href
|
const refundUrlObj = new URL(
|
||||||
|
"refund",
|
||||||
|
purchase.contractTerms.merchant_base_url,
|
||||||
|
);
|
||||||
|
refundUrlObj.searchParams.set("order_id", purchase.contractTerms.order_id);
|
||||||
|
const refundUrl = refundUrlObj.href;
|
||||||
let resp;
|
let resp;
|
||||||
try {
|
try {
|
||||||
resp = await ws.http.get(refundUrl);
|
resp = await ws.http.get(refundUrl);
|
||||||
@ -1122,22 +1138,45 @@ async function queryRefund(ws: InternalWalletState, proposalId: string): Promise
|
|||||||
await acceptRefundResponse(ws, proposalId, refundResponse);
|
await acceptRefundResponse(ws, proposalId, refundResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startRefundQuery(ws: InternalWalletState, proposalId: string): Promise<void> {
|
async function startRefundQuery(
|
||||||
const success = await runWithWriteTransaction(ws.db, [Stores.purchases], async (tx) => {
|
ws: InternalWalletState,
|
||||||
|
proposalId: string,
|
||||||
|
): Promise<void> {
|
||||||
|
const success = await runWithWriteTransaction(
|
||||||
|
ws.db,
|
||||||
|
[Stores.purchases],
|
||||||
|
async tx => {
|
||||||
const p = await tx.get(Stores.purchases, proposalId);
|
const p = await tx.get(Stores.purchases, proposalId);
|
||||||
if (p?.status !== PurchaseStatus.Done) {
|
if (!p) {
|
||||||
|
console.log("no purchase found for refund URL");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
p.status = PurchaseStatus.QueryRefund;
|
if (p.status === PurchaseStatus.QueryRefund) {
|
||||||
return true;
|
return true;
|
||||||
});
|
}
|
||||||
|
if (p.status === PurchaseStatus.ProcessRefund) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (p.status !== PurchaseStatus.Dormant) {
|
||||||
|
console.log(
|
||||||
|
`can't apply refund, as payment isn't done (status ${p.status})`,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
p.lastError = undefined;
|
||||||
|
p.status = PurchaseStatus.QueryRefund;
|
||||||
|
p.retryInfo = initRetryInfo();
|
||||||
|
await tx.put(Stores.purchases, p);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await queryRefund(ws, proposalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
await processPurchase(ws, proposalId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accept a refund, return the contract hash for the contract
|
* Accept a refund, return the contract hash for the contract
|
||||||
@ -1149,6 +1188,8 @@ export async function applyRefund(
|
|||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const parseResult = parseRefundUri(talerRefundUri);
|
const parseResult = parseRefundUri(talerRefundUri);
|
||||||
|
|
||||||
|
console.log("applying refund");
|
||||||
|
|
||||||
if (!parseResult) {
|
if (!parseResult) {
|
||||||
throw Error("invalid refund URI");
|
throw Error("invalid refund URI");
|
||||||
}
|
}
|
||||||
@ -1163,6 +1204,7 @@ export async function applyRefund(
|
|||||||
throw Error("no purchase for the taler://refund/ URI was found");
|
throw Error("no purchase for the taler://refund/ URI was found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("processing purchase for refund");
|
||||||
await startRefundQuery(ws, purchase.proposalId);
|
await startRefundQuery(ws, purchase.proposalId);
|
||||||
|
|
||||||
return purchase.contractTermsHash;
|
return purchase.contractTermsHash;
|
||||||
@ -1180,7 +1222,7 @@ export async function processPurchase(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function processPurchaseImpl(
|
async function processPurchaseImpl(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
proposalId: string,
|
proposalId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@ -1188,8 +1230,9 @@ export async function processPurchaseImpl(
|
|||||||
if (!purchase) {
|
if (!purchase) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
logger.trace(`processing purchase ${proposalId}`);
|
||||||
switch (purchase.status) {
|
switch (purchase.status) {
|
||||||
case PurchaseStatus.Done:
|
case PurchaseStatus.Dormant:
|
||||||
return;
|
return;
|
||||||
case PurchaseStatus.Abort:
|
case PurchaseStatus.Abort:
|
||||||
// FIXME
|
// FIXME
|
||||||
@ -1200,7 +1243,9 @@ export async function processPurchaseImpl(
|
|||||||
await queryRefund(ws, proposalId);
|
await queryRefund(ws, proposalId);
|
||||||
break;
|
break;
|
||||||
case PurchaseStatus.ProcessRefund:
|
case PurchaseStatus.ProcessRefund:
|
||||||
|
console.log("submitting refunds to exchange (toplvl)");
|
||||||
await submitRefundsToExchange(ws, proposalId);
|
await submitRefundsToExchange(ws, proposalId);
|
||||||
|
console.log("after submitting refunds to exchange (toplvl)");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw assertUnreachable(purchase.status);
|
throw assertUnreachable(purchase.status);
|
||||||
|
@ -32,7 +32,9 @@ import {
|
|||||||
ReserveRecordStatus,
|
ReserveRecordStatus,
|
||||||
CoinStatus,
|
CoinStatus,
|
||||||
ProposalStatus,
|
ProposalStatus,
|
||||||
|
PurchaseStatus,
|
||||||
} from "../dbTypes";
|
} from "../dbTypes";
|
||||||
|
import { assertUnreachable } from "../util/assertUnreachable";
|
||||||
|
|
||||||
function updateRetryDelay(
|
function updateRetryDelay(
|
||||||
oldDelay: Duration,
|
oldDelay: Duration,
|
||||||
@ -353,7 +355,7 @@ async function gatherPurchasePending(
|
|||||||
onlyDue: boolean = false,
|
onlyDue: boolean = false,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await tx.iter(Stores.purchases).forEach((pr) => {
|
await tx.iter(Stores.purchases).forEach((pr) => {
|
||||||
if (pr.finished) {
|
if (pr.status === PurchaseStatus.Dormant) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resp.nextRetryDelay = updateRetryDelay(
|
resp.nextRetryDelay = updateRetryDelay(
|
||||||
@ -369,6 +371,9 @@ async function gatherPurchasePending(
|
|||||||
givesLifeness: true,
|
givesLifeness: true,
|
||||||
isReplay: false,
|
isReplay: false,
|
||||||
proposalId: pr.proposalId,
|
proposalId: pr.proposalId,
|
||||||
|
status: pr.status,
|
||||||
|
retryInfo: pr.retryInfo,
|
||||||
|
lastError: pr.lastError,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -282,6 +282,7 @@ async function processPlanchet(
|
|||||||
}
|
}
|
||||||
if (numDone === ws.denoms.length) {
|
if (numDone === ws.denoms.length) {
|
||||||
ws.finishTimestamp = getTimestampNow();
|
ws.finishTimestamp = getTimestampNow();
|
||||||
|
ws.lastError = undefined;
|
||||||
ws.retryInfo = initRetryInfo(false);
|
ws.retryInfo = initRetryInfo(false);
|
||||||
withdrawSessionFinished = true;
|
withdrawSessionFinished = true;
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ import {
|
|||||||
processDownloadProposal,
|
processDownloadProposal,
|
||||||
applyRefund,
|
applyRefund,
|
||||||
getFullRefundFees,
|
getFullRefundFees,
|
||||||
processPurchaseImpl,
|
processPurchase,
|
||||||
} from "./wallet-impl/pay";
|
} from "./wallet-impl/pay";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -180,6 +180,7 @@ export class Wallet {
|
|||||||
pending: PendingOperationInfo,
|
pending: PendingOperationInfo,
|
||||||
forceNow: boolean = false,
|
forceNow: boolean = false,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
console.log("running pending", pending);
|
||||||
switch (pending.type) {
|
switch (pending.type) {
|
||||||
case "bug":
|
case "bug":
|
||||||
// Nothing to do, will just be displayed to the user
|
// Nothing to do, will just be displayed to the user
|
||||||
@ -209,7 +210,7 @@ export class Wallet {
|
|||||||
await processTip(this.ws, pending.tipId);
|
await processTip(this.ws, pending.tipId);
|
||||||
break;
|
break;
|
||||||
case "pay":
|
case "pay":
|
||||||
await processPurchaseImpl(this.ws, pending.proposalId);
|
await processPurchase(this.ws, pending.proposalId);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assertUnreachable(pending);
|
assertUnreachable(pending);
|
||||||
|
@ -37,6 +37,7 @@ import {
|
|||||||
ExchangeWireInfo,
|
ExchangeWireInfo,
|
||||||
WithdrawalSource,
|
WithdrawalSource,
|
||||||
RetryInfo,
|
RetryInfo,
|
||||||
|
PurchaseStatus,
|
||||||
} from "./dbTypes";
|
} from "./dbTypes";
|
||||||
import { CoinPaySig, ContractTerms, PayReq } from "./talerTypes";
|
import { CoinPaySig, ContractTerms, PayReq } from "./talerTypes";
|
||||||
|
|
||||||
@ -520,6 +521,7 @@ export const enum NotificationType {
|
|||||||
ReserveDepleted = "reserve-depleted",
|
ReserveDepleted = "reserve-depleted",
|
||||||
WithdrawSessionFinished = "withdraw-session-finished",
|
WithdrawSessionFinished = "withdraw-session-finished",
|
||||||
WaitingForRetry = "waiting-for-retry",
|
WaitingForRetry = "waiting-for-retry",
|
||||||
|
RefundFinished = "refund-finished",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProposalAcceptedNotification {
|
export interface ProposalAcceptedNotification {
|
||||||
@ -585,6 +587,10 @@ export interface WaitingForRetryNotification {
|
|||||||
numGivingLiveness: number;
|
numGivingLiveness: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RefundFinishedNotification {
|
||||||
|
type: NotificationType.RefundFinished;
|
||||||
|
}
|
||||||
|
|
||||||
export type WalletNotification =
|
export type WalletNotification =
|
||||||
| ProposalAcceptedNotification
|
| ProposalAcceptedNotification
|
||||||
| ProposalDownloadedNotification
|
| ProposalDownloadedNotification
|
||||||
@ -599,7 +605,8 @@ export type WalletNotification =
|
|||||||
| ReserveConfirmedNotification
|
| ReserveConfirmedNotification
|
||||||
| WithdrawSessionFinishedNotification
|
| WithdrawSessionFinishedNotification
|
||||||
| ReserveDepletedNotification
|
| ReserveDepletedNotification
|
||||||
| WaitingForRetryNotification;
|
| WaitingForRetryNotification
|
||||||
|
| RefundFinishedNotification;
|
||||||
|
|
||||||
export interface OperationError {
|
export interface OperationError {
|
||||||
type: string;
|
type: string;
|
||||||
@ -612,7 +619,7 @@ export interface PendingExchangeUpdateOperation {
|
|||||||
stage: string;
|
stage: string;
|
||||||
reason: string;
|
reason: string;
|
||||||
exchangeBaseUrl: string;
|
exchangeBaseUrl: string;
|
||||||
lastError?: OperationError;
|
lastError: OperationError | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PendingBugOperation {
|
export interface PendingBugOperation {
|
||||||
@ -674,6 +681,9 @@ export interface PendingPayOperation {
|
|||||||
type: "pay";
|
type: "pay";
|
||||||
proposalId: string;
|
proposalId: string;
|
||||||
isReplay: boolean;
|
isReplay: boolean;
|
||||||
|
status: PurchaseStatus;
|
||||||
|
retryInfo: RetryInfo,
|
||||||
|
lastError: OperationError | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PendingOperationInfoCommon {
|
export interface PendingOperationInfoCommon {
|
||||||
|
Loading…
Reference in New Issue
Block a user