wallet-core: add missing resume/suspend implementations
This commit is contained in:
parent
246f914ca6
commit
0323067c07
@ -863,11 +863,22 @@ export interface TipRecord {
|
||||
* The url to be redirected after the tip is accepted.
|
||||
*/
|
||||
next_url: string | undefined;
|
||||
|
||||
/**
|
||||
* Timestamp for when the wallet finished picking up the tip
|
||||
* from the merchant.
|
||||
*/
|
||||
pickedUpTimestamp: TalerPreciseTimestamp | undefined;
|
||||
|
||||
status: TipRecordStatus;
|
||||
}
|
||||
|
||||
export enum TipRecordStatus {
|
||||
PendingPickup = 10,
|
||||
|
||||
SuspendidPickup = 21,
|
||||
|
||||
Done = 50,
|
||||
}
|
||||
|
||||
export enum RefreshCoinStatus {
|
||||
@ -1078,12 +1089,12 @@ export enum PurchaseStatus {
|
||||
/**
|
||||
* Not downloaded yet.
|
||||
*/
|
||||
DownloadingProposal = 10,
|
||||
PendingDownloadingProposal = 10,
|
||||
|
||||
/**
|
||||
* The user has accepted the proposal.
|
||||
*/
|
||||
Paying = 11,
|
||||
PendingPaying = 11,
|
||||
|
||||
/**
|
||||
* Currently in the process of aborting with a refund.
|
||||
@ -1093,17 +1104,17 @@ export enum PurchaseStatus {
|
||||
/**
|
||||
* Paying a second time, likely with different session ID
|
||||
*/
|
||||
PayingReplay = 13,
|
||||
PendingPayingReplay = 13,
|
||||
|
||||
/**
|
||||
* Query for refunds (until query succeeds).
|
||||
*/
|
||||
QueryingRefund = 14,
|
||||
PendingQueryingRefund = 14,
|
||||
|
||||
/**
|
||||
* Query for refund (until auto-refund deadline is reached).
|
||||
*/
|
||||
QueryingAutoRefund = 15,
|
||||
PendingQueryingAutoRefund = 15,
|
||||
|
||||
PendingAcceptRefund = 16,
|
||||
|
||||
@ -1902,7 +1913,7 @@ export interface PeerPullPaymentInitiationRecord {
|
||||
|
||||
export enum PeerPushPaymentIncomingStatus {
|
||||
PendingMerge = 10 /* ACTIVE_START */,
|
||||
MergeKycRequired = 11 /* ACTIVE_START + 1 */,
|
||||
PendingMergeKycRequired = 11 /* ACTIVE_START + 1 */,
|
||||
/**
|
||||
* Merge was successful and withdrawal group has been created, now
|
||||
* everything is in the hand of the withdrawal group.
|
||||
@ -2829,6 +2840,22 @@ export const walletDbFixups: FixupDescription[] = [
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TipRecordRecord_status_add",
|
||||
async fn(tx): Promise<void> {
|
||||
await tx.tips.iter().forEachAsync(async (r) => {
|
||||
// Remove legacy transactions that don't have the totalCost field yet.
|
||||
if (r.status == null) {
|
||||
if (r.pickedUpTimestamp) {
|
||||
r.status = TipRecordStatus.Done;
|
||||
} else {
|
||||
r.status = TipRecordStatus.PendingPickup;
|
||||
}
|
||||
await tx.tips.put(r);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const logger = new Logger("db.ts");
|
||||
|
@ -412,14 +412,14 @@ export async function exportBackup(
|
||||
let propStatus: BackupProposalStatus;
|
||||
switch (purch.purchaseStatus) {
|
||||
case PurchaseStatus.Done:
|
||||
case PurchaseStatus.QueryingAutoRefund:
|
||||
case PurchaseStatus.QueryingRefund:
|
||||
case PurchaseStatus.PendingQueryingAutoRefund:
|
||||
case PurchaseStatus.PendingQueryingRefund:
|
||||
propStatus = BackupProposalStatus.Paid;
|
||||
break;
|
||||
case PurchaseStatus.PayingReplay:
|
||||
case PurchaseStatus.DownloadingProposal:
|
||||
case PurchaseStatus.PendingPayingReplay:
|
||||
case PurchaseStatus.PendingDownloadingProposal:
|
||||
case PurchaseStatus.Proposed:
|
||||
case PurchaseStatus.Paying:
|
||||
case PurchaseStatus.PendingPaying:
|
||||
propStatus = BackupProposalStatus.Proposed;
|
||||
break;
|
||||
case PurchaseStatus.FailedClaim:
|
||||
|
@ -131,6 +131,7 @@ import {
|
||||
import {
|
||||
constructTransactionIdentifier,
|
||||
notifyTransition,
|
||||
stopLongpolling,
|
||||
} from "./transactions.js";
|
||||
|
||||
/**
|
||||
@ -339,7 +340,7 @@ async function processDownloadProposal(
|
||||
};
|
||||
}
|
||||
|
||||
if (proposal.purchaseStatus != PurchaseStatus.DownloadingProposal) {
|
||||
if (proposal.purchaseStatus != PurchaseStatus.PendingDownloadingProposal) {
|
||||
return {
|
||||
type: OperationAttemptResultType.Finished,
|
||||
result: undefined,
|
||||
@ -516,7 +517,7 @@ async function processDownloadProposal(
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
if (p.purchaseStatus !== PurchaseStatus.DownloadingProposal) {
|
||||
if (p.purchaseStatus !== PurchaseStatus.PendingDownloadingProposal) {
|
||||
return;
|
||||
}
|
||||
const oldTxState = computePayMerchantTransactionState(p);
|
||||
@ -626,7 +627,7 @@ async function createPurchase(
|
||||
merchantBaseUrl,
|
||||
orderId,
|
||||
proposalId: proposalId,
|
||||
purchaseStatus: PurchaseStatus.DownloadingProposal,
|
||||
purchaseStatus: PurchaseStatus.PendingDownloadingProposal,
|
||||
repurchaseProposalId: undefined,
|
||||
downloadSessionId: sessionId,
|
||||
autoRefundDeadline: undefined,
|
||||
@ -699,7 +700,7 @@ async function storeFirstPaySuccess(
|
||||
return;
|
||||
}
|
||||
const oldTxState = computePayMerchantTransactionState(purchase);
|
||||
if (purchase.purchaseStatus === PurchaseStatus.Paying) {
|
||||
if (purchase.purchaseStatus === PurchaseStatus.PendingPaying) {
|
||||
purchase.purchaseStatus = PurchaseStatus.Done;
|
||||
}
|
||||
purchase.timestampFirstSuccessfulPay = now;
|
||||
@ -721,7 +722,7 @@ async function storeFirstPaySuccess(
|
||||
if (protoAr) {
|
||||
const ar = Duration.fromTalerProtocolDuration(protoAr);
|
||||
logger.info("auto_refund present");
|
||||
purchase.purchaseStatus = PurchaseStatus.QueryingAutoRefund;
|
||||
purchase.purchaseStatus = PurchaseStatus.PendingQueryingAutoRefund;
|
||||
purchase.autoRefundDeadline = AbsoluteTime.toProtocolTimestamp(
|
||||
AbsoluteTime.addDuration(AbsoluteTime.now(), ar),
|
||||
);
|
||||
@ -760,8 +761,8 @@ async function storePayReplaySuccess(
|
||||
}
|
||||
const oldTxState = computePayMerchantTransactionState(purchase);
|
||||
if (
|
||||
purchase.purchaseStatus === PurchaseStatus.Paying ||
|
||||
purchase.purchaseStatus === PurchaseStatus.PayingReplay
|
||||
purchase.purchaseStatus === PurchaseStatus.PendingPaying ||
|
||||
purchase.purchaseStatus === PurchaseStatus.PendingPayingReplay
|
||||
) {
|
||||
purchase.purchaseStatus = PurchaseStatus.Done;
|
||||
}
|
||||
@ -1058,7 +1059,7 @@ export async function checkPaymentByProposalId(
|
||||
}
|
||||
const oldTxState = computePayMerchantTransactionState(p);
|
||||
p.lastSessionId = sessionId;
|
||||
p.purchaseStatus = PurchaseStatus.PayingReplay;
|
||||
p.purchaseStatus = PurchaseStatus.PendingPayingReplay;
|
||||
await tx.purchases.put(p);
|
||||
const newTxState = computePayMerchantTransactionState(p);
|
||||
return { oldTxState, newTxState };
|
||||
@ -1098,8 +1099,8 @@ export async function checkPaymentByProposalId(
|
||||
} else {
|
||||
const paid =
|
||||
purchase.purchaseStatus === PurchaseStatus.Done ||
|
||||
purchase.purchaseStatus === PurchaseStatus.QueryingRefund ||
|
||||
purchase.purchaseStatus === PurchaseStatus.QueryingAutoRefund;
|
||||
purchase.purchaseStatus === PurchaseStatus.PendingQueryingRefund ||
|
||||
purchase.purchaseStatus === PurchaseStatus.PendingQueryingAutoRefund;
|
||||
const download = await expectProposalDownload(ws, purchase);
|
||||
return {
|
||||
status: PreparePayResultType.AlreadyConfirmed,
|
||||
@ -1348,7 +1349,7 @@ export async function confirmPay(
|
||||
logger.trace(`changing session ID to ${sessionIdOverride}`);
|
||||
purchase.lastSessionId = sessionIdOverride;
|
||||
if (purchase.purchaseStatus === PurchaseStatus.Done) {
|
||||
purchase.purchaseStatus = PurchaseStatus.PayingReplay;
|
||||
purchase.purchaseStatus = PurchaseStatus.PendingPayingReplay;
|
||||
}
|
||||
await tx.purchases.put(purchase);
|
||||
}
|
||||
@ -1424,7 +1425,7 @@ export async function confirmPay(
|
||||
};
|
||||
p.lastSessionId = sessionId;
|
||||
p.timestampAccept = TalerPreciseTimestamp.now();
|
||||
p.purchaseStatus = PurchaseStatus.Paying;
|
||||
p.purchaseStatus = PurchaseStatus.PendingPaying;
|
||||
await tx.purchases.put(p);
|
||||
await spendCoins(ws, tx, {
|
||||
//`txn:proposal:${p.proposalId}`
|
||||
@ -1440,7 +1441,7 @@ export async function confirmPay(
|
||||
});
|
||||
break;
|
||||
case PurchaseStatus.Done:
|
||||
case PurchaseStatus.Paying:
|
||||
case PurchaseStatus.PendingPaying:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1481,14 +1482,14 @@ export async function processPurchase(
|
||||
}
|
||||
|
||||
switch (purchase.purchaseStatus) {
|
||||
case PurchaseStatus.DownloadingProposal:
|
||||
case PurchaseStatus.PendingDownloadingProposal:
|
||||
return processDownloadProposal(ws, proposalId);
|
||||
case PurchaseStatus.Paying:
|
||||
case PurchaseStatus.PayingReplay:
|
||||
case PurchaseStatus.PendingPaying:
|
||||
case PurchaseStatus.PendingPayingReplay:
|
||||
return processPurchasePay(ws, proposalId);
|
||||
case PurchaseStatus.QueryingRefund:
|
||||
case PurchaseStatus.PendingQueryingRefund:
|
||||
return processPurchaseQueryRefund(ws, purchase);
|
||||
case PurchaseStatus.QueryingAutoRefund:
|
||||
case PurchaseStatus.PendingQueryingAutoRefund:
|
||||
return processPurchaseAutoRefund(ws, purchase);
|
||||
case PurchaseStatus.AbortingWithRefund:
|
||||
return processPurchaseAbortingRefund(ws, purchase);
|
||||
@ -1540,8 +1541,8 @@ export async function processPurchasePay(
|
||||
};
|
||||
}
|
||||
switch (purchase.purchaseStatus) {
|
||||
case PurchaseStatus.Paying:
|
||||
case PurchaseStatus.PayingReplay:
|
||||
case PurchaseStatus.PendingPaying:
|
||||
case PurchaseStatus.PendingPayingReplay:
|
||||
break;
|
||||
default:
|
||||
return OperationAttemptResult.finishedEmpty();
|
||||
@ -1757,11 +1758,11 @@ export async function abortPayMerchant(
|
||||
logger.warn(`tried to abort successful payment`);
|
||||
return;
|
||||
}
|
||||
if (oldStatus === PurchaseStatus.Paying) {
|
||||
if (oldStatus === PurchaseStatus.PendingPaying) {
|
||||
purchase.purchaseStatus = PurchaseStatus.AbortingWithRefund;
|
||||
}
|
||||
await tx.purchases.put(purchase);
|
||||
if (oldStatus === PurchaseStatus.Paying) {
|
||||
if (oldStatus === PurchaseStatus.PendingPaying) {
|
||||
if (purchase.payInfo) {
|
||||
const coinSel = purchase.payInfo.payCoinSelection;
|
||||
const currency = Amounts.currencyOf(purchase.payInfo.totalPayCost);
|
||||
@ -1789,32 +1790,146 @@ export async function abortPayMerchant(
|
||||
ws.workAvailable.trigger();
|
||||
}
|
||||
|
||||
|
||||
const transitionSuspend: { [x in PurchaseStatus]?: {
|
||||
next: PurchaseStatus | undefined,
|
||||
} } = {
|
||||
[PurchaseStatus.PendingDownloadingProposal]: {
|
||||
next: PurchaseStatus.SuspendedDownloadingProposal,
|
||||
},
|
||||
[PurchaseStatus.AbortingWithRefund]: {
|
||||
next: PurchaseStatus.SuspendedAbortingWithRefund,
|
||||
},
|
||||
[PurchaseStatus.PendingPaying]: {
|
||||
next: PurchaseStatus.SuspendedPaying,
|
||||
},
|
||||
[PurchaseStatus.PendingPayingReplay]: {
|
||||
next: PurchaseStatus.SuspendedPayingReplay,
|
||||
},
|
||||
[PurchaseStatus.PendingQueryingAutoRefund]: {
|
||||
next: PurchaseStatus.SuspendedQueryingAutoRefund,
|
||||
}
|
||||
}
|
||||
|
||||
const transitionResume: { [x in PurchaseStatus]?: {
|
||||
next: PurchaseStatus | undefined,
|
||||
} } = {
|
||||
[PurchaseStatus.SuspendedDownloadingProposal]: {
|
||||
next: PurchaseStatus.PendingDownloadingProposal,
|
||||
},
|
||||
[PurchaseStatus.SuspendedAbortingWithRefund]: {
|
||||
next: PurchaseStatus.AbortingWithRefund,
|
||||
},
|
||||
[PurchaseStatus.SuspendedPaying]: {
|
||||
next: PurchaseStatus.PendingPaying,
|
||||
},
|
||||
[PurchaseStatus.SuspendedPayingReplay]: {
|
||||
next: PurchaseStatus.PendingPayingReplay,
|
||||
},
|
||||
[PurchaseStatus.SuspendedQueryingAutoRefund]: {
|
||||
next: PurchaseStatus.PendingQueryingAutoRefund,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export async function suspendPayMerchant(
|
||||
ws: InternalWalletState,
|
||||
proposalId: string,
|
||||
): Promise<void> {
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Payment,
|
||||
proposalId,
|
||||
});
|
||||
const opId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.Purchase,
|
||||
proposalId,
|
||||
});
|
||||
stopLongpolling(ws, opId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [
|
||||
x.purchases,
|
||||
])
|
||||
.runReadWrite(async (tx) => {
|
||||
const purchase = await tx.purchases.get(proposalId);
|
||||
if (!purchase) {
|
||||
throw Error("purchase not found");
|
||||
}
|
||||
const oldTxState = computePayMerchantTransactionState(purchase);
|
||||
let newStatus = transitionSuspend[purchase.purchaseStatus];
|
||||
if (!newStatus) {
|
||||
return undefined;
|
||||
}
|
||||
await tx.purchases.put(purchase);
|
||||
const newTxState = computePayMerchantTransactionState(purchase);
|
||||
return { oldTxState, newTxState };
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
ws.workAvailable.trigger();
|
||||
}
|
||||
|
||||
|
||||
export async function resumePayMerchant(
|
||||
ws: InternalWalletState,
|
||||
proposalId: string,
|
||||
): Promise<void> {
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Payment,
|
||||
proposalId,
|
||||
});
|
||||
const opId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.Purchase,
|
||||
proposalId,
|
||||
});
|
||||
stopLongpolling(ws, opId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [
|
||||
x.purchases,
|
||||
])
|
||||
.runReadWrite(async (tx) => {
|
||||
const purchase = await tx.purchases.get(proposalId);
|
||||
if (!purchase) {
|
||||
throw Error("purchase not found");
|
||||
}
|
||||
const oldTxState = computePayMerchantTransactionState(purchase);
|
||||
let newStatus = transitionResume[purchase.purchaseStatus];
|
||||
if (!newStatus) {
|
||||
return undefined;
|
||||
}
|
||||
await tx.purchases.put(purchase);
|
||||
const newTxState = computePayMerchantTransactionState(purchase);
|
||||
return { oldTxState, newTxState };
|
||||
});
|
||||
ws.workAvailable.trigger();
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
ws.workAvailable.trigger();
|
||||
}
|
||||
|
||||
export function computePayMerchantTransactionState(
|
||||
purchaseRecord: PurchaseRecord,
|
||||
): TransactionState {
|
||||
switch (purchaseRecord.purchaseStatus) {
|
||||
// Pending States
|
||||
case PurchaseStatus.DownloadingProposal:
|
||||
case PurchaseStatus.PendingDownloadingProposal:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.ClaimProposal,
|
||||
};
|
||||
case PurchaseStatus.Paying:
|
||||
case PurchaseStatus.PendingPaying:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.SubmitPayment,
|
||||
};
|
||||
case PurchaseStatus.PayingReplay:
|
||||
case PurchaseStatus.PendingPayingReplay:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.RebindSession,
|
||||
};
|
||||
case PurchaseStatus.QueryingAutoRefund:
|
||||
case PurchaseStatus.PendingQueryingAutoRefund:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.AutoRefund,
|
||||
};
|
||||
case PurchaseStatus.QueryingRefund:
|
||||
case PurchaseStatus.PendingQueryingRefund:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.CheckRefund,
|
||||
@ -1937,7 +2052,7 @@ async function processPurchaseAutoRefund(
|
||||
logger.warn("purchase does not exist anymore");
|
||||
return;
|
||||
}
|
||||
if (p.purchaseStatus !== PurchaseStatus.QueryingRefund) {
|
||||
if (p.purchaseStatus !== PurchaseStatus.PendingQueryingRefund) {
|
||||
return;
|
||||
}
|
||||
const oldTxState = computePayMerchantTransactionState(p);
|
||||
@ -1982,7 +2097,7 @@ async function processPurchaseAutoRefund(
|
||||
logger.warn("purchase does not exist anymore");
|
||||
return;
|
||||
}
|
||||
if (p.purchaseStatus !== PurchaseStatus.QueryingAutoRefund) {
|
||||
if (p.purchaseStatus !== PurchaseStatus.PendingQueryingAutoRefund) {
|
||||
return;
|
||||
}
|
||||
const oldTxState = computePayMerchantTransactionState(p);
|
||||
@ -2118,7 +2233,7 @@ async function processPurchaseQueryRefund(
|
||||
logger.warn("purchase does not exist anymore");
|
||||
return undefined;
|
||||
}
|
||||
if (p.purchaseStatus !== PurchaseStatus.QueryingRefund) {
|
||||
if (p.purchaseStatus !== PurchaseStatus.PendingQueryingRefund) {
|
||||
return undefined;
|
||||
}
|
||||
const oldTxState = computePayMerchantTransactionState(p);
|
||||
@ -2143,7 +2258,7 @@ async function processPurchaseQueryRefund(
|
||||
logger.warn("purchase does not exist anymore");
|
||||
return;
|
||||
}
|
||||
if (p.purchaseStatus !== PurchaseStatus.QueryingRefund) {
|
||||
if (p.purchaseStatus !== PurchaseStatus.PendingQueryingRefund) {
|
||||
return;
|
||||
}
|
||||
const oldTxState = computePayMerchantTransactionState(p);
|
||||
@ -2242,7 +2357,7 @@ export async function startQueryRefund(
|
||||
return;
|
||||
}
|
||||
const oldTxState = computePayMerchantTransactionState(p);
|
||||
p.purchaseStatus = PurchaseStatus.QueryingRefund;
|
||||
p.purchaseStatus = PurchaseStatus.PendingQueryingRefund;
|
||||
const newTxState = computePayMerchantTransactionState(p);
|
||||
await tx.purchases.put(p);
|
||||
return { oldTxState, newTxState };
|
||||
|
@ -1008,7 +1008,7 @@ export async function processPeerPushCredit(
|
||||
const amount = Amounts.parseOrThrow(contractTerms.amount);
|
||||
|
||||
if (
|
||||
peerInc.status === PeerPushPaymentIncomingStatus.MergeKycRequired &&
|
||||
peerInc.status === PeerPushPaymentIncomingStatus.PendingMergeKycRequired &&
|
||||
peerInc.kycInfo
|
||||
) {
|
||||
const txId = constructTransactionIdentifier({
|
||||
@ -1080,7 +1080,7 @@ export async function processPeerPushCredit(
|
||||
paytoHash: kycPending.h_payto,
|
||||
requirementRow: kycPending.requirement_row,
|
||||
};
|
||||
peerInc.status = PeerPushPaymentIncomingStatus.MergeKycRequired;
|
||||
peerInc.status = PeerPushPaymentIncomingStatus.PendingMergeKycRequired;
|
||||
await tx.peerPushPaymentIncoming.put(peerInc);
|
||||
});
|
||||
return {
|
||||
@ -1122,7 +1122,7 @@ export async function processPeerPushCredit(
|
||||
}
|
||||
if (
|
||||
peerInc.status === PeerPushPaymentIncomingStatus.PendingMerge ||
|
||||
peerInc.status === PeerPushPaymentIncomingStatus.MergeKycRequired
|
||||
peerInc.status === PeerPushPaymentIncomingStatus.PendingMergeKycRequired
|
||||
) {
|
||||
peerInc.status = PeerPushPaymentIncomingStatus.Done;
|
||||
}
|
||||
@ -2186,6 +2186,345 @@ export async function suspendPeerPushDebitTransaction(
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export async function suspendPeerPullDebitTransaction(
|
||||
ws: InternalWalletState,
|
||||
peerPullPaymentIncomingId: string,
|
||||
) {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPullDebit,
|
||||
peerPullPaymentIncomingId,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPullDebit,
|
||||
peerPullPaymentIncomingId,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.peerPullPaymentIncoming])
|
||||
.runReadWrite(async (tx) => {
|
||||
const pullDebitRec = await tx.peerPullPaymentIncoming.get(
|
||||
peerPullPaymentIncomingId,
|
||||
);
|
||||
if (!pullDebitRec) {
|
||||
logger.warn(`peer pull debit ${peerPullPaymentIncomingId} not found`);
|
||||
return;
|
||||
}
|
||||
let newStatus: PeerPullDebitRecordStatus | undefined = undefined;
|
||||
switch (pullDebitRec.status) {
|
||||
case PeerPullDebitRecordStatus.DialogProposed:
|
||||
break;
|
||||
case PeerPullDebitRecordStatus.DonePaid:
|
||||
break;
|
||||
case PeerPullDebitRecordStatus.PendingDeposit:
|
||||
newStatus = PeerPullDebitRecordStatus.SuspendedDeposit;
|
||||
break;
|
||||
case PeerPullDebitRecordStatus.SuspendedDeposit:
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(pullDebitRec.status);
|
||||
}
|
||||
if (newStatus != null) {
|
||||
const oldTxState = computePeerPullDebitTransactionState(pullDebitRec);
|
||||
pullDebitRec.status = newStatus;
|
||||
const newTxState = computePeerPullDebitTransactionState(pullDebitRec);
|
||||
await tx.peerPullPaymentIncoming.put(pullDebitRec);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export async function resumePeerPullDebitTransaction(
|
||||
ws: InternalWalletState,
|
||||
peerPullPaymentIncomingId: string,
|
||||
) {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPullDebit,
|
||||
peerPullPaymentIncomingId,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPullDebit,
|
||||
peerPullPaymentIncomingId,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.peerPullPaymentIncoming])
|
||||
.runReadWrite(async (tx) => {
|
||||
const pullDebitRec = await tx.peerPullPaymentIncoming.get(
|
||||
peerPullPaymentIncomingId,
|
||||
);
|
||||
if (!pullDebitRec) {
|
||||
logger.warn(`peer pull debit ${peerPullPaymentIncomingId} not found`);
|
||||
return;
|
||||
}
|
||||
let newStatus: PeerPullDebitRecordStatus | undefined = undefined;
|
||||
switch (pullDebitRec.status) {
|
||||
case PeerPullDebitRecordStatus.DialogProposed:
|
||||
case PeerPullDebitRecordStatus.DonePaid:
|
||||
case PeerPullDebitRecordStatus.PendingDeposit:
|
||||
break;
|
||||
case PeerPullDebitRecordStatus.SuspendedDeposit:
|
||||
newStatus = PeerPullDebitRecordStatus.PendingDeposit;
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(pullDebitRec.status);
|
||||
}
|
||||
if (newStatus != null) {
|
||||
const oldTxState = computePeerPullDebitTransactionState(pullDebitRec);
|
||||
pullDebitRec.status = newStatus;
|
||||
const newTxState = computePeerPullDebitTransactionState(pullDebitRec);
|
||||
await tx.peerPullPaymentIncoming.put(pullDebitRec);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
ws.workAvailable.trigger();
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export async function suspendPeerPushCreditTransaction(
|
||||
ws: InternalWalletState,
|
||||
peerPushPaymentIncomingId: string,
|
||||
) {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPushCredit,
|
||||
peerPushPaymentIncomingId,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPushCredit,
|
||||
peerPushPaymentIncomingId,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.peerPushPaymentIncoming])
|
||||
.runReadWrite(async (tx) => {
|
||||
const pushCreditRec = await tx.peerPushPaymentIncoming.get(
|
||||
peerPushPaymentIncomingId,
|
||||
);
|
||||
if (!pushCreditRec) {
|
||||
logger.warn(`peer push credit ${peerPushPaymentIncomingId} not found`);
|
||||
return;
|
||||
}
|
||||
let newStatus: PeerPushPaymentIncomingStatus | undefined = undefined;
|
||||
switch (pushCreditRec.status) {
|
||||
case PeerPushPaymentIncomingStatus.DialogProposed:
|
||||
case PeerPushPaymentIncomingStatus.Done:
|
||||
case PeerPushPaymentIncomingStatus.SuspendedMerge:
|
||||
case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired:
|
||||
case PeerPushPaymentIncomingStatus.SuspendedWithdrawing:
|
||||
break;
|
||||
case PeerPushPaymentIncomingStatus.PendingMergeKycRequired:
|
||||
newStatus = PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired;
|
||||
break;
|
||||
case PeerPushPaymentIncomingStatus.PendingMerge:
|
||||
newStatus = PeerPushPaymentIncomingStatus.SuspendedMerge;
|
||||
break;
|
||||
case PeerPushPaymentIncomingStatus.PendingWithdrawing:
|
||||
// FIXME: Suspend internal withdrawal transaction!
|
||||
newStatus = PeerPushPaymentIncomingStatus.SuspendedWithdrawing;
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(pushCreditRec.status);
|
||||
}
|
||||
if (newStatus != null) {
|
||||
const oldTxState = computePeerPushCreditTransactionState(pushCreditRec);
|
||||
pushCreditRec.status = newStatus;
|
||||
const newTxState = computePeerPushCreditTransactionState(pushCreditRec);
|
||||
await tx.peerPushPaymentIncoming.put(pushCreditRec);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export async function resumePeerPushCreditTransaction(
|
||||
ws: InternalWalletState,
|
||||
peerPushPaymentIncomingId: string,
|
||||
) {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPushCredit,
|
||||
peerPushPaymentIncomingId,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPushCredit,
|
||||
peerPushPaymentIncomingId,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.peerPushPaymentIncoming])
|
||||
.runReadWrite(async (tx) => {
|
||||
const pushCreditRec = await tx.peerPushPaymentIncoming.get(
|
||||
peerPushPaymentIncomingId,
|
||||
);
|
||||
if (!pushCreditRec) {
|
||||
logger.warn(`peer push credit ${peerPushPaymentIncomingId} not found`);
|
||||
return;
|
||||
}
|
||||
let newStatus: PeerPushPaymentIncomingStatus | undefined = undefined;
|
||||
switch (pushCreditRec.status) {
|
||||
case PeerPushPaymentIncomingStatus.DialogProposed:
|
||||
case PeerPushPaymentIncomingStatus.Done:
|
||||
case PeerPushPaymentIncomingStatus.PendingMergeKycRequired:
|
||||
case PeerPushPaymentIncomingStatus.PendingMerge:
|
||||
case PeerPushPaymentIncomingStatus.PendingWithdrawing:
|
||||
case PeerPushPaymentIncomingStatus.SuspendedMerge:
|
||||
newStatus = PeerPushPaymentIncomingStatus.PendingMerge;
|
||||
break;
|
||||
case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired:
|
||||
newStatus = PeerPushPaymentIncomingStatus.PendingMergeKycRequired;
|
||||
break;
|
||||
case PeerPushPaymentIncomingStatus.SuspendedWithdrawing:
|
||||
// FIXME: resume underlying "internal-withdrawal" transaction.
|
||||
newStatus = PeerPushPaymentIncomingStatus.PendingWithdrawing;
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(pushCreditRec.status);
|
||||
}
|
||||
if (newStatus != null) {
|
||||
const oldTxState = computePeerPushCreditTransactionState(pushCreditRec);
|
||||
pushCreditRec.status = newStatus;
|
||||
const newTxState = computePeerPushCreditTransactionState(pushCreditRec);
|
||||
await tx.peerPushPaymentIncoming.put(pushCreditRec);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
ws.workAvailable.trigger();
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export async function suspendPeerPullCreditTransaction(
|
||||
ws: InternalWalletState,
|
||||
pursePub: string,
|
||||
) {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPullCredit,
|
||||
pursePub,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPullCredit,
|
||||
pursePub,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.peerPullPaymentInitiations])
|
||||
.runReadWrite(async (tx) => {
|
||||
const pullCreditRec = await tx.peerPullPaymentInitiations.get(pursePub);
|
||||
if (!pullCreditRec) {
|
||||
logger.warn(`peer pull credit ${pursePub} not found`);
|
||||
return;
|
||||
}
|
||||
let newStatus: PeerPullPaymentInitiationStatus | undefined = undefined;
|
||||
switch (pullCreditRec.status) {
|
||||
case PeerPullPaymentInitiationStatus.PendingCreatePurse:
|
||||
newStatus = PeerPullPaymentInitiationStatus.SuspendedCreatePurse;
|
||||
break;
|
||||
case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
|
||||
newStatus = PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired;
|
||||
break;
|
||||
case PeerPullPaymentInitiationStatus.PendingWithdrawing:
|
||||
newStatus = PeerPullPaymentInitiationStatus.SuspendedWithdrawing;
|
||||
break;
|
||||
case PeerPullPaymentInitiationStatus.PendingReady:
|
||||
newStatus = PeerPullPaymentInitiationStatus.SuspendedReady;
|
||||
break;
|
||||
case PeerPullPaymentInitiationStatus.DonePurseDeposited:
|
||||
case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
|
||||
case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
|
||||
case PeerPullPaymentInitiationStatus.SuspendedReady:
|
||||
case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(pullCreditRec.status);
|
||||
}
|
||||
if (newStatus != null) {
|
||||
const oldTxState = computePeerPullCreditTransactionState(pullCreditRec);
|
||||
pullCreditRec.status = newStatus;
|
||||
const newTxState = computePeerPullCreditTransactionState(pullCreditRec);
|
||||
await tx.peerPullPaymentInitiations.put(pullCreditRec);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export async function resumePeerPullCreditTransaction(
|
||||
ws: InternalWalletState,
|
||||
pursePub: string,
|
||||
) {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPullCredit,
|
||||
pursePub,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPullCredit,
|
||||
pursePub,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.peerPullPaymentInitiations])
|
||||
.runReadWrite(async (tx) => {
|
||||
const pullCreditRec = await tx.peerPullPaymentInitiations.get(pursePub);
|
||||
if (!pullCreditRec) {
|
||||
logger.warn(`peer pull credit ${pursePub} not found`);
|
||||
return;
|
||||
}
|
||||
let newStatus: PeerPullPaymentInitiationStatus | undefined = undefined;
|
||||
switch (pullCreditRec.status) {
|
||||
case PeerPullPaymentInitiationStatus.PendingCreatePurse:
|
||||
case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
|
||||
case PeerPullPaymentInitiationStatus.PendingWithdrawing:
|
||||
case PeerPullPaymentInitiationStatus.PendingReady:
|
||||
case PeerPullPaymentInitiationStatus.DonePurseDeposited:
|
||||
case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
|
||||
newStatus = PeerPullPaymentInitiationStatus.PendingCreatePurse;
|
||||
break;
|
||||
case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
|
||||
newStatus = PeerPullPaymentInitiationStatus.PendingMergeKycRequired;
|
||||
break;
|
||||
case PeerPullPaymentInitiationStatus.SuspendedReady:
|
||||
newStatus = PeerPullPaymentInitiationStatus.PendingReady;
|
||||
break;
|
||||
case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
|
||||
newStatus = PeerPullPaymentInitiationStatus.PendingWithdrawing;
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(pullCreditRec.status);
|
||||
}
|
||||
if (newStatus != null) {
|
||||
const oldTxState = computePeerPullCreditTransactionState(pullCreditRec);
|
||||
pullCreditRec.status = newStatus;
|
||||
const newTxState = computePeerPullCreditTransactionState(pullCreditRec);
|
||||
await tx.peerPullPaymentInitiations.put(pullCreditRec);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
ws.workAvailable.trigger();
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export async function resumePeerPushDebitTransaction(
|
||||
ws: InternalWalletState,
|
||||
pursePub: string,
|
||||
@ -2244,6 +2583,7 @@ export async function resumePeerPushDebitTransaction(
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
ws.workAvailable.trigger();
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
@ -2265,7 +2605,7 @@ export function computePeerPushCreditTransactionState(
|
||||
return {
|
||||
major: TransactionMajorState.Done,
|
||||
};
|
||||
case PeerPushPaymentIncomingStatus.MergeKycRequired:
|
||||
case PeerPushPaymentIncomingStatus.PendingMergeKycRequired:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.KycRequired,
|
||||
|
@ -48,6 +48,7 @@ import {
|
||||
CoinSourceType,
|
||||
DenominationRecord,
|
||||
TipRecord,
|
||||
TipRecordStatus,
|
||||
} from "../db.js";
|
||||
import { makeErrorDetail } from "@gnu-taler/taler-util";
|
||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||
@ -57,6 +58,7 @@ import {
|
||||
} from "@gnu-taler/taler-util/http";
|
||||
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
|
||||
import {
|
||||
constructTaskIdentifier,
|
||||
OperationAttemptResult,
|
||||
OperationAttemptResultType,
|
||||
} from "../util/retries.js";
|
||||
@ -68,7 +70,13 @@ import {
|
||||
updateWithdrawalDenoms,
|
||||
} from "./withdraw.js";
|
||||
import { selectWithdrawalDenominations } from "../util/coinSelection.js";
|
||||
import { constructTransactionIdentifier } from "./transactions.js";
|
||||
import {
|
||||
constructTransactionIdentifier,
|
||||
notifyTransition,
|
||||
stopLongpolling,
|
||||
} from "./transactions.js";
|
||||
import { PendingTaskType } from "../pending-types.js";
|
||||
import { assertUnreachable } from "../util/assertUnreachable.js";
|
||||
|
||||
const logger = new Logger("operations/tip.ts");
|
||||
|
||||
@ -156,6 +164,7 @@ export async function prepareTip(
|
||||
const newTipRecord: TipRecord = {
|
||||
walletTipId: walletTipId,
|
||||
acceptedTimestamp: undefined,
|
||||
status: TipRecordStatus.PendingPickup,
|
||||
tipAmountRaw: Amounts.stringify(amount),
|
||||
tipExpiration: tipPickupStatus.expiration,
|
||||
exchangeBaseUrl: tipPickupStatus.exchange_url,
|
||||
@ -180,7 +189,7 @@ export async function prepareTip(
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Tip,
|
||||
walletTipId: tipRecord.walletTipId,
|
||||
})
|
||||
});
|
||||
|
||||
const tipStatus: PrepareTipResult = {
|
||||
accepted: !!tipRecord && !!tipRecord.acceptedTimestamp,
|
||||
@ -410,3 +419,98 @@ export async function acceptTip(
|
||||
next_url: found?.next_url,
|
||||
};
|
||||
}
|
||||
|
||||
export async function suspendTipTransaction(
|
||||
ws: InternalWalletState,
|
||||
walletTipId: string,
|
||||
): Promise<void> {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.TipPickup,
|
||||
walletTipId,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Tip,
|
||||
walletTipId,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.tips])
|
||||
.runReadWrite(async (tx) => {
|
||||
const tipRec = await tx.tips.get(walletTipId);
|
||||
if (!tipRec) {
|
||||
logger.warn(`transaction tip ${walletTipId} not found`);
|
||||
return;
|
||||
}
|
||||
let newStatus: TipRecordStatus | undefined = undefined;
|
||||
switch (tipRec.status) {
|
||||
case TipRecordStatus.Done:
|
||||
case TipRecordStatus.SuspendidPickup:
|
||||
break;
|
||||
case TipRecordStatus.PendingPickup:
|
||||
newStatus = TipRecordStatus.SuspendidPickup;
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(tipRec.status);
|
||||
}
|
||||
if (newStatus != null) {
|
||||
const oldTxState = computeTipTransactionStatus(tipRec);
|
||||
tipRec.status = newStatus;
|
||||
const newTxState = computeTipTransactionStatus(tipRec);
|
||||
await tx.tips.put(tipRec);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
ws.workAvailable.trigger();
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export async function resumeTipTransaction(
|
||||
ws: InternalWalletState,
|
||||
walletTipId: string,
|
||||
): Promise<void> {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.TipPickup,
|
||||
walletTipId,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Tip,
|
||||
walletTipId,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.tips])
|
||||
.runReadWrite(async (tx) => {
|
||||
const tipRec = await tx.tips.get(walletTipId);
|
||||
if (!tipRec) {
|
||||
logger.warn(`transaction tip ${walletTipId} not found`);
|
||||
return;
|
||||
}
|
||||
let newStatus: TipRecordStatus | undefined = undefined;
|
||||
switch (tipRec.status) {
|
||||
case TipRecordStatus.Done:
|
||||
case TipRecordStatus.SuspendidPickup:
|
||||
newStatus = TipRecordStatus.PendingPickup;
|
||||
break;
|
||||
case TipRecordStatus.PendingPickup:
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(tipRec.status);
|
||||
}
|
||||
if (newStatus != null) {
|
||||
const oldTxState = computeTipTransactionStatus(tipRec);
|
||||
tipRec.status = newStatus;
|
||||
const newTxState = computeTipTransactionStatus(tipRec);
|
||||
await tx.tips.put(tipRec);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
@ -86,20 +86,40 @@ import {
|
||||
computeRefundTransactionState,
|
||||
expectProposalDownload,
|
||||
extractContractData,
|
||||
resumePayMerchant,
|
||||
suspendPayMerchant,
|
||||
} from "./pay-merchant.js";
|
||||
import {
|
||||
computePeerPullCreditTransactionState,
|
||||
computePeerPullDebitTransactionState,
|
||||
computePeerPushCreditTransactionState,
|
||||
computePeerPushDebitTransactionState,
|
||||
resumePeerPullCreditTransaction,
|
||||
resumePeerPullDebitTransaction,
|
||||
resumePeerPushCreditTransaction,
|
||||
resumePeerPushDebitTransaction,
|
||||
suspendPeerPullCreditTransaction,
|
||||
suspendPeerPullDebitTransaction,
|
||||
suspendPeerPushCreditTransaction,
|
||||
suspendPeerPushDebitTransaction,
|
||||
} from "./pay-peer.js";
|
||||
import { computeRefreshTransactionState } from "./refresh.js";
|
||||
import { computeTipTransactionStatus } from "./tip.js";
|
||||
import {
|
||||
computeRefreshTransactionState,
|
||||
resumeRefreshGroup,
|
||||
suspendRefreshGroup,
|
||||
} from "./refresh.js";
|
||||
import {
|
||||
computeTipTransactionStatus,
|
||||
resumeTipTransaction,
|
||||
suspendTipTransaction,
|
||||
} from "./tip.js";
|
||||
import {
|
||||
abortWithdrawalTransaction,
|
||||
augmentPaytoUrisForWithdrawal,
|
||||
cancelAbortingWithdrawalTransaction,
|
||||
computeWithdrawalTransactionStatus,
|
||||
resumeWithdrawalTransaction,
|
||||
suspendWithdrawalTransaction,
|
||||
} from "./withdraw.js";
|
||||
|
||||
const logger = new Logger("taler-wallet-core:transactions.ts");
|
||||
@ -159,6 +179,7 @@ export async function getTransactionById(
|
||||
}
|
||||
|
||||
switch (parsedTx.tag) {
|
||||
case TransactionType.InternalWithdrawal:
|
||||
case TransactionType.Withdrawal: {
|
||||
const withdrawalGroupId = parsedTx.withdrawalGroupId;
|
||||
return await ws.db
|
||||
@ -844,7 +865,7 @@ async function buildTransactionForPurchase(
|
||||
proposalId: purchaseRecord.proposalId,
|
||||
info,
|
||||
refundQueryActive:
|
||||
purchaseRecord.purchaseStatus === PurchaseStatus.QueryingRefund,
|
||||
purchaseRecord.purchaseStatus === PurchaseStatus.PendingQueryingRefund,
|
||||
...(ort?.lastError ? { error: ort.lastError } : {}),
|
||||
};
|
||||
}
|
||||
@ -1197,7 +1218,8 @@ export type ParsedTransactionIdentifier =
|
||||
| { tag: TransactionType.Refresh; refreshGroupId: string }
|
||||
| { tag: TransactionType.Refund; refundGroupId: string }
|
||||
| { tag: TransactionType.Tip; walletTipId: string }
|
||||
| { tag: TransactionType.Withdrawal; withdrawalGroupId: string };
|
||||
| { tag: TransactionType.Withdrawal; withdrawalGroupId: string }
|
||||
| { tag: TransactionType.InternalWithdrawal; withdrawalGroupId: string };
|
||||
|
||||
export function constructTransactionIdentifier(
|
||||
pTxId: ParsedTransactionIdentifier,
|
||||
@ -1223,6 +1245,8 @@ export function constructTransactionIdentifier(
|
||||
return `txn:${pTxId.tag}:${pTxId.walletTipId}` as TransactionIdStr;
|
||||
case TransactionType.Withdrawal:
|
||||
return `txn:${pTxId.tag}:${pTxId.withdrawalGroupId}` as TransactionIdStr;
|
||||
case TransactionType.InternalWithdrawal:
|
||||
return `txn:${pTxId.tag}:${pTxId.withdrawalGroupId}` as TransactionIdStr;
|
||||
default:
|
||||
assertUnreachable(pTxId);
|
||||
}
|
||||
@ -1242,6 +1266,10 @@ export function parseTransactionIdentifier(
|
||||
|
||||
const [prefix, type, ...rest] = txnParts;
|
||||
|
||||
if (prefix != "txn") {
|
||||
throw Error("invalid transaction identifier");
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case TransactionType.Deposit:
|
||||
return { tag: TransactionType.Deposit, depositGroupId: rest[0] };
|
||||
@ -1329,6 +1357,7 @@ export async function retryTransaction(
|
||||
stopLongpolling(ws, taskId);
|
||||
break;
|
||||
}
|
||||
case TransactionType.InternalWithdrawal:
|
||||
case TransactionType.Withdrawal: {
|
||||
// FIXME: Abort current long-poller!
|
||||
const taskId = constructTaskIdentifier({
|
||||
@ -1366,8 +1395,38 @@ export async function retryTransaction(
|
||||
stopLongpolling(ws, taskId);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case TransactionType.PeerPullDebit: {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPullDebit,
|
||||
peerPullPaymentIncomingId: parsedTx.peerPullPaymentIncomingId,
|
||||
});
|
||||
await resetOperationTimeout(ws, taskId);
|
||||
stopLongpolling(ws, taskId);
|
||||
break;
|
||||
}
|
||||
case TransactionType.PeerPushCredit: {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPushCredit,
|
||||
peerPushPaymentIncomingId: parsedTx.peerPushPaymentIncomingId,
|
||||
});
|
||||
await resetOperationTimeout(ws, taskId);
|
||||
stopLongpolling(ws, taskId);
|
||||
break;
|
||||
}
|
||||
case TransactionType.PeerPushDebit: {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPushDebit,
|
||||
pursePub: parsedTx.pursePub,
|
||||
});
|
||||
await resetOperationTimeout(ws, taskId);
|
||||
stopLongpolling(ws, taskId);
|
||||
break;
|
||||
}
|
||||
case TransactionType.Refund:
|
||||
// Nothing to do for a refund transaction.
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(parsedTx);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1389,8 +1448,35 @@ export async function suspendTransaction(
|
||||
case TransactionType.Deposit:
|
||||
await suspendDepositGroup(ws, tx.depositGroupId);
|
||||
return;
|
||||
case TransactionType.Refresh:
|
||||
await suspendRefreshGroup(ws, tx.refreshGroupId);
|
||||
return;
|
||||
case TransactionType.InternalWithdrawal:
|
||||
case TransactionType.Withdrawal:
|
||||
await suspendWithdrawalTransaction(ws, tx.withdrawalGroupId);
|
||||
return;
|
||||
case TransactionType.Payment:
|
||||
await suspendPayMerchant(ws, tx.proposalId);
|
||||
return;
|
||||
case TransactionType.PeerPullCredit:
|
||||
await suspendPeerPullCreditTransaction(ws, tx.pursePub);
|
||||
break;
|
||||
case TransactionType.PeerPushDebit:
|
||||
await suspendPeerPushDebitTransaction(ws, tx.pursePub);
|
||||
break;
|
||||
case TransactionType.PeerPullDebit:
|
||||
await suspendPeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId);
|
||||
break;
|
||||
case TransactionType.PeerPushCredit:
|
||||
await suspendPeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId);
|
||||
break;
|
||||
case TransactionType.Refund:
|
||||
throw Error("refund transactions can't be suspended or resumed");
|
||||
case TransactionType.Tip:
|
||||
await suspendTipTransaction(ws, tx.walletTipId);
|
||||
break;
|
||||
default:
|
||||
logger.warn(`unable to suspend transaction of type '${tx.tag}'`);
|
||||
assertUnreachable(tx);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1429,8 +1515,33 @@ export async function resumeTransaction(
|
||||
case TransactionType.Deposit:
|
||||
await resumeDepositGroup(ws, tx.depositGroupId);
|
||||
return;
|
||||
default:
|
||||
logger.warn(`unable to resume transaction of type '${tx.tag}'`);
|
||||
case TransactionType.Refresh:
|
||||
await resumeRefreshGroup(ws, tx.refreshGroupId);
|
||||
return;
|
||||
case TransactionType.InternalWithdrawal:
|
||||
case TransactionType.Withdrawal:
|
||||
await resumeWithdrawalTransaction(ws, tx.withdrawalGroupId);
|
||||
return;
|
||||
case TransactionType.Payment:
|
||||
await resumePayMerchant(ws, tx.proposalId);
|
||||
return;
|
||||
case TransactionType.PeerPullCredit:
|
||||
await resumePeerPullCreditTransaction(ws, tx.pursePub);
|
||||
break;
|
||||
case TransactionType.PeerPushDebit:
|
||||
await resumePeerPushDebitTransaction(ws, tx.pursePub);
|
||||
break;
|
||||
case TransactionType.PeerPullDebit:
|
||||
await resumePeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId);
|
||||
break;
|
||||
case TransactionType.PeerPushCredit:
|
||||
await resumePeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId);
|
||||
break;
|
||||
case TransactionType.Refund:
|
||||
throw Error("refund transactions can't be suspended or resumed");
|
||||
case TransactionType.Tip:
|
||||
await resumeTipTransaction(ws, tx.walletTipId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,7 +259,7 @@ export async function resumeWithdrawalTransaction(
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
ws.workAvailable.trigger();
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
|
Loading…
Reference in New Issue
Block a user