aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts')
-rw-r--r--packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts256
1 files changed, 168 insertions, 88 deletions
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
index 447ffce8f..48b81d6c2 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
@@ -91,7 +91,7 @@ import {
const logger = new Logger("pay-peer-pull-credit.ts");
-export async function queryPurseForPeerPullCredit(
+async function queryPurseForPeerPullCredit(
ws: InternalWalletState,
pullIni: PeerPullPaymentInitiationRecord,
cancellationToken: CancellationToken,
@@ -102,7 +102,7 @@ export async function queryPurseForPeerPullCredit(
);
purseDepositUrl.searchParams.set("timeout_ms", "30000");
logger.info(`querying purse status via ${purseDepositUrl.href}`);
- const resp = await ws.http.get(purseDepositUrl.href, {
+ const resp = await ws.http.fetch(purseDepositUrl.href, {
timeout: { d_ms: 60000 },
cancellationToken,
});
@@ -153,8 +153,11 @@ export async function queryPurseForPeerPullCredit(
pub: reserve.reservePub,
},
});
-
- await ws.db
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.PeerPullCredit,
+ pursePub: pullIni.pursePub,
+ });
+ const transitionInfo = await ws.db
.mktx((x) => [x.peerPullPaymentInitiations])
.runReadWrite(async (tx) => {
const finPi = await tx.peerPullPaymentInitiations.get(pullIni.pursePub);
@@ -162,11 +165,15 @@ export async function queryPurseForPeerPullCredit(
logger.warn("peerPullPaymentInitiation not found anymore");
return;
}
+ const oldTxState = computePeerPullCreditTransactionState(finPi);
if (finPi.status === PeerPullPaymentInitiationStatus.PendingReady) {
- finPi.status = PeerPullPaymentInitiationStatus.DonePurseDeposited;
+ finPi.status = PeerPullPaymentInitiationStatus.PendingWithdrawing;
}
await tx.peerPullPaymentInitiations.put(finPi);
+ const newTxState = computePeerPullCreditTransactionState(finPi);
+ return { oldTxState, newTxState };
});
+ notifyTransition(ws, transactionId, transitionInfo);
return {
ready: true,
};
@@ -293,91 +300,68 @@ async function processPeerPullCreditAbortingDeletePurse(
return OperationAttemptResult.pendingEmpty();
}
-export async function processPeerPullCredit(
+async function handlePeerPullCreditWithdrawing(
ws: InternalWalletState,
- pursePub: string,
+ pullIni: PeerPullPaymentInitiationRecord,
): Promise<OperationAttemptResult> {
- const pullIni = await ws.db
- .mktx((x) => [x.peerPullPaymentInitiations])
- .runReadOnly(async (tx) => {
- return tx.peerPullPaymentInitiations.get(pursePub);
- });
- if (!pullIni) {
- throw Error("peer pull payment initiation not found in database");
+ if (!pullIni.withdrawalGroupId) {
+ throw Error("invalid db state (withdrawing, but no withdrawal group ID");
}
-
- const retryTag = constructTaskIdentifier({
- tag: PendingTaskType.PeerPullCredit,
- pursePub,
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.PeerPullCredit,
+ pursePub: pullIni.pursePub,
});
-
- // We're already running!
- if (ws.activeLongpoll[retryTag]) {
- logger.info("peer-pull-credit already in long-polling, returning!");
- return {
- type: OperationAttemptResultType.Longpoll,
- };
- }
-
- logger.trace(`processing ${retryTag}, status=${pullIni.status}`);
-
- switch (pullIni.status) {
- case PeerPullPaymentInitiationStatus.DonePurseDeposited: {
- // We implement this case so that the "retry" action on a peer-pull-credit transaction
- // also retries the withdrawal task.
-
- logger.warn(
- "peer pull payment initiation is already finished, retrying withdrawal",
+ const wgId = pullIni.withdrawalGroupId;
+ let finished: boolean = false;
+ const transitionInfo = await ws.db
+ .mktx((x) => [x.peerPullPaymentInitiations, x.withdrawalGroups])
+ .runReadWrite(async (tx) => {
+ const ppi = await tx.peerPullPaymentInitiations.get(
+ pullIni.pursePub,
);
-
- const withdrawalGroupId = pullIni.withdrawalGroupId;
-
- if (withdrawalGroupId) {
- const taskId = constructTaskIdentifier({
- tag: PendingTaskType.Withdraw,
- withdrawalGroupId,
- });
- stopLongpolling(ws, taskId);
- await resetOperationTimeout(ws, taskId);
- await runOperationWithErrorReporting(ws, taskId, () =>
- processWithdrawalGroup(ws, withdrawalGroupId),
- );
+ if (!ppi) {
+ finished = true;
+ return;
}
+ if (ppi.status !== PeerPullPaymentInitiationStatus.PendingWithdrawing) {
+ finished = true;
+ return;
+ }
+ const oldTxState = computePeerPullCreditTransactionState(ppi);
+ const wg = await tx.withdrawalGroups.get(wgId);
+ if (!wg) {
+ // FIXME: Fail the operation instead?
+ return undefined;
+ }
+ switch (wg.status) {
+ case WithdrawalGroupStatus.Finished:
+ finished = true;
+ ppi.status = PeerPullPaymentInitiationStatus.Done;
+ break;
+ // FIXME: Also handle other final states!
+ }
+ await tx.peerPullPaymentInitiations.put(ppi);
+ const newTxState = computePeerPullCreditTransactionState(ppi);
return {
- type: OperationAttemptResultType.Finished,
- result: undefined,
- };
- }
- case PeerPullPaymentInitiationStatus.PendingReady:
- runLongpollAsync(ws, retryTag, async (cancellationToken) =>
- queryPurseForPeerPullCredit(ws, pullIni, cancellationToken),
- );
- logger.trace(
- "returning early from processPeerPullCredit for long-polling in background",
- );
- return {
- type: OperationAttemptResultType.Longpoll,
+ oldTxState,
+ newTxState,
};
- case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: {
- if (!pullIni.kycInfo) {
- throw Error("invalid state, kycInfo required");
- }
- return await longpollKycStatus(
- ws,
- pursePub,
- pullIni.exchangeBaseUrl,
- pullIni.kycInfo,
- "individual",
- );
- }
- case PeerPullPaymentInitiationStatus.PendingCreatePurse:
- break;
- case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
- return await processPeerPullCreditAbortingDeletePurse(ws, pullIni);
- default:
- throw Error(`unknown PeerPullPaymentInitiationStatus ${pullIni.status}`);
+ });
+ notifyTransition(ws, transactionId, transitionInfo);
+ if (finished) {
+ return OperationAttemptResult.finishedEmpty();
+ } else {
+ // FIXME: Return indicator that we depend on the other operation!
+ return OperationAttemptResult.pendingEmpty();
}
+}
+async function handlePeerPullCreditCreatePurse(
+ ws: InternalWalletState,
+ pullIni: PeerPullPaymentInitiationRecord,
+): Promise<OperationAttemptResult> {
+ const purseFee = Amounts.stringify(Amounts.zeroOfAmount(pullIni.amount));
+ const pursePub = pullIni.pursePub;
const mergeReserve = await ws.db
.mktx((x) => [x.reserves])
.runReadOnly(async (tx) => {
@@ -388,8 +372,6 @@ export async function processPeerPullCredit(
throw Error("merge reserve for peer pull payment not found in database");
}
- const purseFee = Amounts.stringify(Amounts.zeroOfAmount(pullIni.amount));
-
const reservePayto = talerPaytoFromExchangeReserve(
pullIni.exchangeBaseUrl,
mergeReserve.reservePub,
@@ -474,6 +456,104 @@ export async function processPeerPullCredit(
};
}
+export async function processPeerPullCredit(
+ ws: InternalWalletState,
+ pursePub: string,
+): Promise<OperationAttemptResult> {
+ const pullIni = await ws.db
+ .mktx((x) => [x.peerPullPaymentInitiations])
+ .runReadOnly(async (tx) => {
+ return tx.peerPullPaymentInitiations.get(pursePub);
+ });
+ if (!pullIni) {
+ throw Error("peer pull payment initiation not found in database");
+ }
+
+ const retryTag = constructTaskIdentifier({
+ tag: PendingTaskType.PeerPullCredit,
+ pursePub,
+ });
+
+ // We're already running!
+ if (ws.activeLongpoll[retryTag]) {
+ logger.info("peer-pull-credit already in long-polling, returning!");
+ return {
+ type: OperationAttemptResultType.Longpoll,
+ };
+ }
+
+ logger.trace(`processing ${retryTag}, status=${pullIni.status}`);
+
+ switch (pullIni.status) {
+ case PeerPullPaymentInitiationStatus.Done: {
+ // We implement this case so that the "retry" action on a peer-pull-credit transaction
+ // also retries the withdrawal task.
+
+ logger.warn(
+ "peer pull payment initiation is already finished, retrying withdrawal",
+ );
+
+ const withdrawalGroupId = pullIni.withdrawalGroupId;
+
+ if (withdrawalGroupId) {
+ const taskId = constructTaskIdentifier({
+ tag: PendingTaskType.Withdraw,
+ withdrawalGroupId,
+ });
+ stopLongpolling(ws, taskId);
+ await resetOperationTimeout(ws, taskId);
+ await runOperationWithErrorReporting(ws, taskId, () =>
+ processWithdrawalGroup(ws, withdrawalGroupId),
+ );
+ }
+ return {
+ type: OperationAttemptResultType.Finished,
+ result: undefined,
+ };
+ }
+ case PeerPullPaymentInitiationStatus.PendingReady:
+ runLongpollAsync(ws, retryTag, async (cancellationToken) =>
+ queryPurseForPeerPullCredit(ws, pullIni, cancellationToken),
+ );
+ logger.trace(
+ "returning early from processPeerPullCredit for long-polling in background",
+ );
+ return {
+ type: OperationAttemptResultType.Longpoll,
+ };
+ case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: {
+ if (!pullIni.kycInfo) {
+ throw Error("invalid state, kycInfo required");
+ }
+ return await longpollKycStatus(
+ ws,
+ pursePub,
+ pullIni.exchangeBaseUrl,
+ pullIni.kycInfo,
+ "individual",
+ );
+ }
+ case PeerPullPaymentInitiationStatus.PendingCreatePurse:
+ return handlePeerPullCreditCreatePurse(ws, pullIni);
+ case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
+ return await processPeerPullCreditAbortingDeletePurse(ws, pullIni);
+ case PeerPullPaymentInitiationStatus.PendingWithdrawing:
+ return handlePeerPullCreditWithdrawing(ws, pullIni);
+ case PeerPullPaymentInitiationStatus.Aborted:
+ case PeerPullPaymentInitiationStatus.Failed:
+ case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+ case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
+ case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
+ case PeerPullPaymentInitiationStatus.SuspendedReady:
+ case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
+ break;
+ default:
+ assertUnreachable(pullIni.status);
+ }
+
+ return OperationAttemptResult.finishedEmpty();
+}
+
async function processPeerPullCreditKycRequired(
ws: InternalWalletState,
peerIni: PeerPullPaymentInitiationRecord,
@@ -789,7 +869,7 @@ export async function suspendPeerPullCreditTransaction(
newStatus =
PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse;
break;
- case PeerPullPaymentInitiationStatus.DonePurseDeposited:
+ case PeerPullPaymentInitiationStatus.Done:
case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
case PeerPullPaymentInitiationStatus.SuspendedReady:
@@ -848,7 +928,7 @@ export async function abortPeerPullCreditTransaction(
case PeerPullPaymentInitiationStatus.PendingReady:
newStatus = PeerPullPaymentInitiationStatus.AbortingDeletePurse;
break;
- case PeerPullPaymentInitiationStatus.DonePurseDeposited:
+ case PeerPullPaymentInitiationStatus.Done:
case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
case PeerPullPaymentInitiationStatus.SuspendedReady:
@@ -903,7 +983,7 @@ export async function failPeerPullCreditTransaction(
case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
case PeerPullPaymentInitiationStatus.PendingWithdrawing:
case PeerPullPaymentInitiationStatus.PendingReady:
- case PeerPullPaymentInitiationStatus.DonePurseDeposited:
+ case PeerPullPaymentInitiationStatus.Done:
case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
case PeerPullPaymentInitiationStatus.SuspendedReady:
@@ -961,7 +1041,7 @@ export async function resumePeerPullCreditTransaction(
case PeerPullPaymentInitiationStatus.PendingWithdrawing:
case PeerPullPaymentInitiationStatus.PendingReady:
case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
- case PeerPullPaymentInitiationStatus.DonePurseDeposited:
+ case PeerPullPaymentInitiationStatus.Done:
case PeerPullPaymentInitiationStatus.Failed:
case PeerPullPaymentInitiationStatus.Aborted:
break;
@@ -1018,7 +1098,7 @@ export function computePeerPullCreditTransactionState(
major: TransactionMajorState.Pending,
minor: TransactionMinorState.Ready,
};
- case PeerPullPaymentInitiationStatus.DonePurseDeposited:
+ case PeerPullPaymentInitiationStatus.Done:
return {
major: TransactionMajorState.Done,
};
@@ -1078,7 +1158,7 @@ export function computePeerPullCreditTransactionActions(
return [TransactionAction.Abort, TransactionAction.Suspend];
case PeerPullPaymentInitiationStatus.PendingReady:
return [TransactionAction.Abort, TransactionAction.Suspend];
- case PeerPullPaymentInitiationStatus.DonePurseDeposited:
+ case PeerPullPaymentInitiationStatus.Done:
return [TransactionAction.Delete];
case PeerPullPaymentInitiationStatus.PendingWithdrawing:
return [TransactionAction.Abort, TransactionAction.Suspend];