diff options
author | Florian Dold <florian@dold.me> | 2023-06-05 17:58:20 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2023-06-05 17:58:25 +0200 |
commit | 9fca44893a6f7fcee5c828da5fc10e7d76592b5d (patch) | |
tree | 634830c7a769302d5e107f2aee323c3f705cf113 | |
parent | 6e7c88a62073082b28ef563561d08f56acc0b017 (diff) |
wallet-core: handle more p2p abort cases nicely
11 files changed, 385 insertions, 100 deletions
diff --git a/packages/taler-util/src/http-common.ts b/packages/taler-util/src/http-common.ts index 4f8f12789..4f6aaaf44 100644 --- a/packages/taler-util/src/http-common.ts +++ b/packages/taler-util/src/http-common.ts @@ -45,7 +45,7 @@ export interface HttpResponse { export const DEFAULT_REQUEST_TIMEOUT_MS = 60000; export interface HttpRequestOptions { - method?: "POST" | "PUT" | "GET"; + method?: "POST" | "PUT" | "GET" | "DELETE"; headers?: { [name: string]: string }; /** diff --git a/packages/taler-util/src/taler-crypto.ts b/packages/taler-util/src/taler-crypto.ts index 4a657b621..c4eb925f7 100644 --- a/packages/taler-util/src/taler-crypto.ts +++ b/packages/taler-util/src/taler-crypto.ts @@ -958,6 +958,7 @@ export enum TalerSignaturePurpose { WALLET_PURSE_MERGE = 1213, WALLET_ACCOUNT_MERGE = 1214, WALLET_PURSE_ECONTRACT = 1216, + WALLET_PURSE_DELETE = 1220, EXCHANGE_CONFIRM_RECOUP = 1039, EXCHANGE_CONFIRM_RECOUP_REFRESH = 1041, ANASTASIS_POLICY_UPLOAD = 1400, diff --git a/packages/taler-util/src/transactions-types.ts b/packages/taler-util/src/transactions-types.ts index 3f3fe8ac0..576c8e335 100644 --- a/packages/taler-util/src/transactions-types.ts +++ b/packages/taler-util/src/transactions-types.ts @@ -83,6 +83,7 @@ export enum TransactionMajorState { Dialog = "dialog", SuspendedAborting = "suspended-aborting", Failed = "failed", + Expired = "expired", // Only used for the notification, never in the transaction history Deleted = "deleted", } diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts index 05ff8d891..af02807a6 100644 --- a/packages/taler-util/src/wallet-types.ts +++ b/packages/taler-util/src/wallet-types.ts @@ -734,6 +734,7 @@ export enum RefreshReason { Refund = "refund", AbortPay = "abort-pay", AbortDeposit = "abort-deposit", + AbortPeerPushDebit = "abort-peer-push-debit", Recoup = "recoup", BackupRestored = "backup-restored", Scheduled = "scheduled", diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts index 6b44c297d..c0c8e0d01 100644 --- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts +++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts @@ -106,6 +106,8 @@ import { EncryptContractRequest, EncryptContractResponse, EncryptedContract, + SignDeletePurseRequest, + SignDeletePurseResponse, SignPurseMergeRequest, SignPurseMergeResponse, SignRefundRequest, @@ -240,6 +242,8 @@ export interface TalerCryptoInterface { ): Promise<SignReservePurseCreateResponse>; signRefund(req: SignRefundRequest): Promise<SignRefundResponse>; + + signDeletePurse(req: SignDeletePurseRequest): Promise<SignDeletePurseResponse>; } /** @@ -419,6 +423,11 @@ export const nullCrypto: TalerCryptoInterface = { signRefund: function (req: SignRefundRequest): Promise<SignRefundResponse> { throw new Error("Function not implemented."); }, + signDeletePurse: function ( + req: SignDeletePurseRequest, + ): Promise<SignDeletePurseResponse> { + throw new Error("Function not implemented."); + }, }; export type WithArg<X> = X extends (req: infer T) => infer R @@ -1671,6 +1680,21 @@ export const nativeCryptoR: TalerCryptoInterfaceR = { sig: refundSigResp.sig, }; }, + async signDeletePurse( + tci: TalerCryptoInterfaceR, + req: SignDeletePurseRequest, + ): Promise<SignDeletePurseResponse> { + const deleteSigBlob = buildSigPS( + TalerSignaturePurpose.WALLET_PURSE_DELETE, + ).build(); + const sigResp = await tci.eddsaSign(tci, { + msg: encodeCrock(deleteSigBlob), + priv: req.pursePriv, + }); + return { + sig: sigResp.sig, + } + }, }; function amountToBuffer(amount: AmountLike): Uint8Array { diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts index 3b27db0c0..930db03a8 100644 --- a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts +++ b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts @@ -268,7 +268,14 @@ export interface SignRefundResponse { sig: string; } -export interface SignRefundResponse {} + +export interface SignDeletePurseRequest { + pursePriv: string; +} + +export interface SignDeletePurseResponse { + sig: EddsaSignatureString; +} export interface SignReservePurseCreateRequest { mergeTimestamp: TalerProtocolTimestamp; diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 3f19822c8..d64d1fbc6 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -1787,6 +1787,7 @@ export enum PeerPushPaymentInitiationStatus { Done = 50 /* DORMANT_START */, Aborted = 51, Failed = 52, + Expired = 53, } export interface PeerPushPaymentCoinSelection { @@ -1844,6 +1845,8 @@ export interface PeerPushPaymentInitiationRecord { timestampCreated: TalerPreciseTimestamp; + abortRefreshGroupId?: string; + /** * Status of the peer push payment initiation. */ 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 333202a69..e9c34cf73 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 @@ -83,7 +83,6 @@ import { stopLongpolling, } from "./transactions.js"; import { - checkWithdrawalKycStatus, getExchangeWithdrawalInfo, internalCreateWithdrawalGroup, processWithdrawalGroup, @@ -241,6 +240,62 @@ async function longpollKycStatus( }; } +async function processPeerPullCreditAbortingDeletePurse( + ws: InternalWalletState, + peerPullIni: PeerPullPaymentInitiationRecord, +): Promise<OperationAttemptResult> { + const { pursePub, pursePriv } = peerPullIni; + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.PeerPushDebit, + pursePub, + }); + + const sigResp = await ws.cryptoApi.signDeletePurse({ + pursePriv, + }); + const purseUrl = new URL( + `purses/${pursePub}`, + peerPullIni.exchangeBaseUrl, + ); + const resp = await ws.http.fetch(purseUrl.href, { + method: "DELETE", + headers: { + "taler-purse-signature": sigResp.sig, + }, + }); + logger.info(`deleted purse with response status ${resp.status}`); + + const transitionInfo = await ws.db + .mktx((x) => [ + x.peerPullPaymentInitiations, + x.refreshGroups, + x.denominations, + x.coinAvailability, + x.coins, + ]) + .runReadWrite(async (tx) => { + const ppiRec = await tx.peerPullPaymentInitiations.get(pursePub); + if (!ppiRec) { + return undefined; + } + if ( + ppiRec.status !== PeerPullPaymentInitiationStatus.AbortingDeletePurse + ) { + return undefined; + } + const oldTxState = computePeerPullCreditTransactionState(ppiRec); + ppiRec.status = PeerPullPaymentInitiationStatus.Aborted; + const newTxState = computePeerPullCreditTransactionState(ppiRec); + return { + oldTxState, + newTxState, + }; + }); + notifyTransition(ws, transactionId, transitionInfo); + + return OperationAttemptResult.pendingEmpty(); +} + export async function processPeerPullCredit( ws: InternalWalletState, pursePub: string, @@ -320,6 +375,8 @@ export async function processPeerPullCredit( } case PeerPullPaymentInitiationStatus.PendingCreatePurse: break; + case PeerPullPaymentInitiationStatus.AbortingDeletePurse: + return await processPeerPullCreditAbortingDeletePurse(ws, pullIni); default: throw Error(`unknown PeerPullPaymentInitiationStatus ${pullIni.status}`); } diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts b/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts index fdec42bbd..212d69eea 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts @@ -15,55 +15,152 @@ */ import { - ConfirmPeerPullDebitRequest, AcceptPeerPullPaymentResponse, Amounts, - j2s, - TalerError, - TalerErrorCode, - TransactionType, - RefreshReason, + ConfirmPeerPullDebitRequest, + ExchangePurseDeposits, Logger, PeerContractTerms, PreparePeerPullDebitRequest, PreparePeerPullDebitResponse, + RefreshReason, + TalerError, + TalerErrorCode, TalerPreciseTimestamp, + TransactionAction, + TransactionMajorState, + TransactionMinorState, + TransactionState, + TransactionType, + codecForAny, codecForExchangeGetContractResponse, codecForPeerContractTerms, decodeCrock, eddsaGetPublic, encodeCrock, getRandomBytes, + j2s, parsePayPullUri, - TransactionAction, - TransactionMajorState, - TransactionMinorState, - TransactionState, } from "@gnu-taler/taler-util"; +import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http"; import { InternalWalletState, PeerPullDebitRecordStatus, PeerPullPaymentIncomingRecord, PendingTaskType, } from "../index.js"; -import { TaskIdentifiers, constructTaskIdentifier } from "../util/retries.js"; -import { spendCoins, runOperationWithErrorReporting } from "./common.js"; +import { assertUnreachable } from "../util/assertUnreachable.js"; +import { + OperationAttemptResult, + OperationAttemptResultType, + TaskIdentifiers, + constructTaskIdentifier, +} from "../util/retries.js"; +import { runOperationWithErrorReporting, spendCoins } from "./common.js"; import { codecForExchangePurseStatus, getTotalPeerPaymentCost, + queryCoinInfosForSelection, selectPeerCoins, } from "./pay-peer-common.js"; -import { processPeerPullDebit } from "./pay-peer-push-credit.js"; import { constructTransactionIdentifier, notifyTransition, stopLongpolling, } from "./transactions.js"; -import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http"; -import { assertUnreachable } from "../util/assertUnreachable.js"; const logger = new Logger("pay-peer-pull-debit.ts"); +async function processPeerPullDebitPendingDeposit( + ws: InternalWalletState, + peerPullInc: PeerPullPaymentIncomingRecord, +): Promise<OperationAttemptResult> { + const peerPullPaymentIncomingId = peerPullInc.peerPullPaymentIncomingId; + const pursePub = peerPullInc.pursePub; + + const coinSel = peerPullInc.coinSel; + if (!coinSel) { + throw Error("invalid state, no coins selected"); + } + + const coins = await queryCoinInfosForSelection(ws, coinSel); + + const depositSigsResp = await ws.cryptoApi.signPurseDeposits({ + exchangeBaseUrl: peerPullInc.exchangeBaseUrl, + pursePub: peerPullInc.pursePub, + coins, + }); + + const purseDepositUrl = new URL( + `purses/${pursePub}/deposit`, + peerPullInc.exchangeBaseUrl, + ); + + const depositPayload: ExchangePurseDeposits = { + deposits: depositSigsResp.deposits, + }; + + if (logger.shouldLogTrace()) { + logger.trace(`purse deposit payload: ${j2s(depositPayload)}`); + } + + const httpResp = await ws.http.postJson(purseDepositUrl.href, depositPayload); + const resp = await readSuccessResponseJsonOrThrow(httpResp, codecForAny()); + logger.trace(`purse deposit response: ${j2s(resp)}`); + + await ws.db + .mktx((x) => [x.peerPullPaymentIncoming]) + .runReadWrite(async (tx) => { + const pi = await tx.peerPullPaymentIncoming.get( + peerPullPaymentIncomingId, + ); + if (!pi) { + throw Error("peer pull payment not found anymore"); + } + if (pi.status === PeerPullDebitRecordStatus.PendingDeposit) { + pi.status = PeerPullDebitRecordStatus.DonePaid; + } + await tx.peerPullPaymentIncoming.put(pi); + }); + + return { + type: OperationAttemptResultType.Finished, + result: undefined, + }; +} + +async function processPeerPullDebitAbortingRefresh( + ws: InternalWalletState, + peerPullInc: PeerPullPaymentIncomingRecord, +): Promise<OperationAttemptResult> { + throw Error("not implemented"); +} + +export async function processPeerPullDebit( + ws: InternalWalletState, + peerPullPaymentIncomingId: string, +): Promise<OperationAttemptResult> { + const peerPullInc = await ws.db + .mktx((x) => [x.peerPullPaymentIncoming]) + .runReadOnly(async (tx) => { + return tx.peerPullPaymentIncoming.get(peerPullPaymentIncomingId); + }); + if (!peerPullInc) { + throw Error("peer pull debit not found"); + } + + switch (peerPullInc.status) { + case PeerPullDebitRecordStatus.PendingDeposit: + return await processPeerPullDebitPendingDeposit(ws, peerPullInc); + case PeerPullDebitRecordStatus.AbortingRefresh: + return await processPeerPullDebitAbortingRefresh(ws, peerPullInc); + } + return { + type: OperationAttemptResultType.Finished, + result: undefined, + } +} + export async function confirmPeerPullDebit( ws: InternalWalletState, req: ConfirmPeerPullDebitRequest, diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts index 91b0b6022..1a79c7b87 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts @@ -553,76 +553,6 @@ export async function confirmPeerPushCredit( }; } -export async function processPeerPullDebit( - ws: InternalWalletState, - peerPullPaymentIncomingId: string, -): Promise<OperationAttemptResult> { - const peerPullInc = await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) - .runReadOnly(async (tx) => { - return tx.peerPullPaymentIncoming.get(peerPullPaymentIncomingId); - }); - if (!peerPullInc) { - throw Error("peer pull debit not found"); - } - if (peerPullInc.status === PeerPullDebitRecordStatus.PendingDeposit) { - const pursePub = peerPullInc.pursePub; - - const coinSel = peerPullInc.coinSel; - if (!coinSel) { - throw Error("invalid state, no coins selected"); - } - - const coins = await queryCoinInfosForSelection(ws, coinSel); - - const depositSigsResp = await ws.cryptoApi.signPurseDeposits({ - exchangeBaseUrl: peerPullInc.exchangeBaseUrl, - pursePub: peerPullInc.pursePub, - coins, - }); - - const purseDepositUrl = new URL( - `purses/${pursePub}/deposit`, - peerPullInc.exchangeBaseUrl, - ); - - const depositPayload: ExchangePurseDeposits = { - deposits: depositSigsResp.deposits, - }; - - if (logger.shouldLogTrace()) { - logger.trace(`purse deposit payload: ${j2s(depositPayload)}`); - } - - const httpResp = await ws.http.postJson( - purseDepositUrl.href, - depositPayload, - ); - const resp = await readSuccessResponseJsonOrThrow(httpResp, codecForAny()); - logger.trace(`purse deposit response: ${j2s(resp)}`); - } - - await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) - .runReadWrite(async (tx) => { - const pi = await tx.peerPullPaymentIncoming.get( - peerPullPaymentIncomingId, - ); - if (!pi) { - throw Error("peer pull payment not found anymore"); - } - if (pi.status === PeerPullDebitRecordStatus.PendingDeposit) { - pi.status = PeerPullDebitRecordStatus.DonePaid; - } - await tx.peerPullPaymentIncoming.put(pi); - }); - - return { - type: OperationAttemptResultType.Finished, - result: undefined, - }; -} - export async function suspendPeerPushCreditTransaction( ws: InternalWalletState, peerPushPaymentIncomingId: string, diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts index dead6313d..ac0aa9c87 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts @@ -18,6 +18,7 @@ import { Amounts, CheckPeerPushDebitRequest, CheckPeerPushDebitResponse, + CoinRefreshRequest, ContractTermsUtil, HttpStatusCode, InitiatePeerPushDebitRequest, @@ -27,13 +28,14 @@ import { TalerError, TalerErrorCode, TalerPreciseTimestamp, + TalerUriAction, TransactionAction, TransactionMajorState, TransactionMinorState, TransactionState, TransactionType, - constructPayPushUri, j2s, + stringifyTalerUri, } from "@gnu-taler/taler-util"; import { InternalWalletState } from "../internal-wallet-state.js"; import { @@ -46,6 +48,8 @@ import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http"; import { PeerPushPaymentInitiationRecord, PeerPushPaymentInitiationStatus, + RefreshOperationStatus, + createRefreshGroup, } from "../index.js"; import { PendingTaskType } from "../pending-types.js"; import { @@ -64,6 +68,7 @@ import { stopLongpolling, } from "./transactions.js"; import { assertUnreachable } from "../util/assertUnreachable.js"; +import { checkLogicInvariant } from "../util/invariants.js"; const logger = new Logger("pay-peer-push-debit.ts"); @@ -172,9 +177,89 @@ async function processPeerPushDebitCreateReserve( }; } -async function transitionPeerPushDebitFromReadyToDone( +async function processPeerPushDebitAbortingDeletePurse( + ws: InternalWalletState, + peerPushInitiation: PeerPushPaymentInitiationRecord, +): Promise<OperationAttemptResult> { + const { pursePub, pursePriv } = peerPushInitiation; + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.PeerPushDebit, + pursePub, + }); + + const sigResp = await ws.cryptoApi.signDeletePurse({ + pursePriv, + }); + const purseUrl = new URL( + `purses/${pursePub}`, + peerPushInitiation.exchangeBaseUrl, + ); + const resp = await ws.http.fetch(purseUrl.href, { + method: "DELETE", + headers: { + "taler-purse-signature": sigResp.sig, + }, + }); + logger.info(`deleted purse with response status ${resp.status}`); + + const transitionInfo = await ws.db + .mktx((x) => [ + x.peerPushPaymentInitiations, + x.refreshGroups, + x.denominations, + x.coinAvailability, + x.coins, + ]) + .runReadWrite(async (tx) => { + const ppiRec = await tx.peerPushPaymentInitiations.get(pursePub); + if (!ppiRec) { + return undefined; + } + if ( + ppiRec.status !== PeerPushPaymentInitiationStatus.AbortingDeletePurse + ) { + return undefined; + } + const currency = Amounts.currencyOf(ppiRec.amount); + const oldTxState = computePeerPushDebitTransactionState(ppiRec); + const coinPubs: CoinRefreshRequest[] = []; + + for (let i = 0; i < ppiRec.coinSel.coinPubs.length; i++) { + coinPubs.push({ + amount: ppiRec.coinSel.contributions[i], + coinPub: ppiRec.coinSel.coinPubs[i], + }); + } + + const refresh = await createRefreshGroup( + ws, + tx, + currency, + coinPubs, + RefreshReason.AbortPeerPushDebit, + ); + ppiRec.status = PeerPushPaymentInitiationStatus.AbortingRefresh; + ppiRec.abortRefreshGroupId = refresh.refreshGroupId; + const newTxState = computePeerPushDebitTransactionState(ppiRec); + return { + oldTxState, + newTxState, + }; + }); + notifyTransition(ws, transactionId, transitionInfo); + + return OperationAttemptResult.pendingEmpty(); +} + +interface SimpleTransition { + stFrom: PeerPushPaymentInitiationStatus; + stTo: PeerPushPaymentInitiationStatus; +} + +async function transitionPeerPushDebitTransaction( ws: InternalWalletState, pursePub: string, + transitionSpec: SimpleTransition, ): Promise<void> { const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPushDebit, @@ -187,11 +272,11 @@ async function transitionPeerPushDebitFromReadyToDone( if (!ppiRec) { return undefined; } - if (ppiRec.status !== PeerPushPaymentInitiationStatus.PendingReady) { + if (ppiRec.status !== transitionSpec.stFrom) { return undefined; } const oldTxState = computePeerPushDebitTransactionState(ppiRec); - ppiRec.status = PeerPushPaymentInitiationStatus.Done; + ppiRec.status = transitionSpec.stTo; const newTxState = computePeerPushDebitTransactionState(ppiRec); return { oldTxState, @@ -201,6 +286,54 @@ async function transitionPeerPushDebitFromReadyToDone( notifyTransition(ws, transactionId, transitionInfo); } +async function processPeerPushDebitAbortingRefresh( + ws: InternalWalletState, + peerPushInitiation: PeerPushPaymentInitiationRecord, +): Promise<OperationAttemptResult> { + const pursePub = peerPushInitiation.pursePub; + const abortRefreshGroupId = peerPushInitiation.abortRefreshGroupId; + checkLogicInvariant(!!abortRefreshGroupId); + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.PeerPushDebit, + pursePub: peerPushInitiation.pursePub, + }); + const transitionInfo = await ws.db + .mktx((x) => [x.refreshGroups, x.peerPushPaymentInitiations]) + .runReadWrite(async (tx) => { + const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId); + let newOpState: PeerPushPaymentInitiationStatus | undefined; + if (!refreshGroup) { + // Maybe it got manually deleted? Means that we should + // just go into failed. + logger.warn("no aborting refresh group found for deposit group"); + newOpState = PeerPushPaymentInitiationStatus.Failed; + } else { + if (refreshGroup.operationStatus === RefreshOperationStatus.Finished) { + newOpState = PeerPushPaymentInitiationStatus.Aborted; + } else if ( + refreshGroup.operationStatus === RefreshOperationStatus.Failed + ) { + newOpState = PeerPushPaymentInitiationStatus.Failed; + } + } + if (newOpState) { + const newDg = await tx.peerPushPaymentInitiations.get(pursePub); + if (!newDg) { + return; + } + const oldTxState = computePeerPushDebitTransactionState(newDg); + newDg.status = newOpState; + const newTxState = computePeerPushDebitTransactionState(newDg); + await tx.peerPushPaymentInitiations.put(newDg); + return { oldTxState, newTxState }; + } + return undefined; + }); + notifyTransition(ws, transactionId, transitionInfo); + // FIXME: Shouldn't this be finished in some cases?! + return OperationAttemptResult.pendingEmpty(); +} + /** * Process the "pending(ready)" state of a peer-push-debit transaction. */ @@ -214,7 +347,10 @@ async function processPeerPushDebitReady( pursePub, }); runLongpollAsync(ws, retryTag, async (ct) => { - const mergeUrl = new URL(`purses/${pursePub}/merge`); + const mergeUrl = new URL( + `purses/${pursePub}/merge`, + peerPushInitiation.exchangeBaseUrl, + ); mergeUrl.searchParams.set("timeout_ms", "30000"); const resp = await ws.http.fetch(mergeUrl.href, { // timeout: getReserveRequestTimeout(withdrawalGroup), @@ -226,16 +362,30 @@ async function processPeerPushDebitReady( codecForExchangePurseStatus(), ); if (purseStatus.deposit_timestamp) { - await transitionPeerPushDebitFromReadyToDone( + await transitionPeerPushDebitTransaction( ws, peerPushInitiation.pursePub, + { + stFrom: PeerPushPaymentInitiationStatus.PendingReady, + stTo: PeerPushPaymentInitiationStatus.Done, + }, ); return { ready: true, }; } } else if (resp.status === HttpStatusCode.Gone) { - // FIXME: transition the reserve into the expired state + await transitionPeerPushDebitTransaction( + ws, + peerPushInitiation.pursePub, + { + stFrom: PeerPushPaymentInitiationStatus.PendingReady, + stTo: PeerPushPaymentInitiationStatus.Expired, + }, + ); + return { + ready: true, + }; } return { ready: false, @@ -280,6 +430,10 @@ export async function processPeerPushDebit( return processPeerPushDebitCreateReserve(ws, peerPushInitiation); case PeerPushPaymentInitiationStatus.PendingReady: return processPeerPushDebitReady(ws, peerPushInitiation); + case PeerPushPaymentInitiationStatus.AbortingDeletePurse: + return processPeerPushDebitAbortingDeletePurse(ws, peerPushInitiation); + case PeerPushPaymentInitiationStatus.AbortingRefresh: + return processPeerPushDebitAbortingRefresh(ws, peerPushInitiation); } return { @@ -396,7 +550,8 @@ export async function initiatePeerPushDebit( mergePriv: mergePair.priv, pursePub: pursePair.pub, exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl, - talerUri: constructPayPushUri({ + talerUri: stringifyTalerUri({ + type: TalerUriAction.PayPush, exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl, contractPriv: contractKeyPair.priv, }), @@ -431,6 +586,8 @@ export function computePeerPushDebitTransactionActions( return [TransactionAction.Suspend, TransactionAction.Abort]; case PeerPushPaymentInitiationStatus.Done: return [TransactionAction.Delete]; + case PeerPushPaymentInitiationStatus.Expired: + return [TransactionAction.Delete]; case PeerPushPaymentInitiationStatus.Failed: return [TransactionAction.Delete]; } @@ -474,9 +631,9 @@ export async function abortPeerPushDebitTransaction( case PeerPushPaymentInitiationStatus.Done: case PeerPushPaymentInitiationStatus.AbortingDeletePurse: case PeerPushPaymentInitiationStatus.Aborted: - // Do nothing - break; + case PeerPushPaymentInitiationStatus.Expired: case PeerPushPaymentInitiationStatus.Failed: + // Do nothing break; default: assertUnreachable(pushDebitRec.status); @@ -535,6 +692,7 @@ export async function failPeerPushDebitTransaction( case PeerPushPaymentInitiationStatus.Done: case PeerPushPaymentInitiationStatus.Aborted: case PeerPushPaymentInitiationStatus.Failed: + case PeerPushPaymentInitiationStatus.Expired: // Do nothing break; default: @@ -598,6 +756,7 @@ export async function suspendPeerPushDebitTransaction( case PeerPushPaymentInitiationStatus.Done: case PeerPushPaymentInitiationStatus.Aborted: case PeerPushPaymentInitiationStatus.Failed: + case PeerPushPaymentInitiationStatus.Expired: // Do nothing break; default: @@ -660,6 +819,7 @@ export async function resumePeerPushDebitTransaction( case PeerPushPaymentInitiationStatus.Done: case PeerPushPaymentInitiationStatus.Aborted: case PeerPushPaymentInitiationStatus.Failed: + case PeerPushPaymentInitiationStatus.Expired: // Do nothing break; default: @@ -681,7 +841,6 @@ export async function resumePeerPushDebitTransaction( notifyTransition(ws, transactionId, transitionInfo); } - export function computePeerPushDebitTransactionState( ppiRecord: PeerPushPaymentInitiationRecord, ): TransactionState { @@ -738,5 +897,10 @@ export function computePeerPushDebitTransactionState( return { major: TransactionMajorState.Failed, }; + case PeerPushPaymentInitiationStatus.Expired: + return { + major: TransactionMajorState.Expired, + }; } -}
\ No newline at end of file +} + |