summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/taler-util/src/walletTypes.ts1
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts12
-rw-r--r--packages/taler-wallet-core/src/db.ts10
-rw-r--r--packages/taler-wallet-core/src/dbless.ts11
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts77
-rw-r--r--packages/taler-wallet-core/src/wallet.ts5
6 files changed, 78 insertions, 38 deletions
diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts
index 3a415b221..6d3837a62 100644
--- a/packages/taler-util/src/walletTypes.ts
+++ b/packages/taler-util/src/walletTypes.ts
@@ -452,7 +452,6 @@ export interface BankWithdrawDetails {
suggestedExchange?: string;
confirmTransferUrl?: string;
wireTypes: string[];
- extractedStatusUrl: string;
}
export interface AcceptWithdrawalResponse {
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts
index e8a8c5028..dc7298e5d 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts
@@ -24,6 +24,7 @@ import {
BankApi,
BankAccessApi,
} from "@gnu-taler/taler-wallet-core";
+import { j2s } from "@gnu-taler/taler-util";
/**
* Run test for basic, bank-integrated withdrawal.
@@ -62,6 +63,14 @@ export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
talerWithdrawUri: wop.taler_withdraw_uri,
},
);
+ // Do it twice to check idempotency
+ const r3 = await wallet.client.call(
+ WalletApiOperation.AcceptBankIntegratedWithdrawal,
+ {
+ exchangeBaseUrl: exchange.baseUrl,
+ talerWithdrawUri: wop.taler_withdraw_uri,
+ },
+ );
await wallet.runPending();
// Confirm it
@@ -75,7 +84,8 @@ export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
const balResp = await wallet.client.call(WalletApiOperation.GetBalances, {});
t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available);
- await t.shutdown();
+ const txn = await wallet.client.call(WalletApiOperation.GetTransactions, {});
+ console.log(`transactions: ${j2s(txn)}`);
}
runWithdrawalBankIntegratedTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index bc0bb4f65..3d59ce0a7 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -112,11 +112,7 @@ export enum ReserveRecordStatus {
* with a bank-integrated withdrawal.
*/
export interface ReserveBankInfo {
- /**
- * Status URL that the wallet will use to query the status
- * of the Taler withdrawal operation on the bank's side.
- */
- statusUrl: string;
+ talerWithdrawUri: string;
/**
* URL that the user can be redirected to, and allows
@@ -1799,6 +1795,10 @@ export const WalletStoresV1 = {
{
byReservePub: describeIndex("byReservePub", "reservePub"),
byStatus: describeIndex("byStatus", "operationStatus"),
+ byTalerWithdrawUri: describeIndex(
+ "byTalerWithdrawUri",
+ "bankInfo.talerWithdrawUri",
+ ),
},
),
planchets: describeStore(
diff --git a/packages/taler-wallet-core/src/dbless.ts b/packages/taler-wallet-core/src/dbless.ts
index 9bc9c36bf..4669b0be7 100644
--- a/packages/taler-wallet-core/src/dbless.ts
+++ b/packages/taler-wallet-core/src/dbless.ts
@@ -46,6 +46,8 @@ import {
parsePaytoUri,
AbsoluteTime,
UnblindedSignature,
+ BankWithdrawDetails,
+ parseWithdrawUri,
} from "@gnu-taler/taler-util";
import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
import { DenominationRecord } from "./db.js";
@@ -57,7 +59,12 @@ import {
isWithdrawableDenom,
readSuccessResponseJsonOrThrow,
} from "./index.browser.js";
-import { BankAccessApi, BankApi, BankServiceHandle } from "./index.js";
+import {
+ BankAccessApi,
+ BankApi,
+ BankServiceHandle,
+ getBankStatusUrl,
+} from "./index.js";
const logger = new Logger("dbless.ts");
@@ -119,7 +126,7 @@ export async function topupReserveWithDemobank(
amount,
);
const bankInfo = await getBankWithdrawalInfo(http, wopi.taler_withdraw_uri);
- const bankStatusUrl = bankInfo.extractedStatusUrl;
+ const bankStatusUrl = getBankStatusUrl(wopi.taler_withdraw_uri);
if (!bankInfo.suggestedExchange) {
throw Error("no suggested exchange");
}
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index 721a043d7..3c4e2d98c 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -36,7 +36,8 @@ import {
codecForWithdrawResponse,
DenomKeyType,
Duration,
- durationFromSpec, encodeCrock,
+ durationFromSpec,
+ encodeCrock,
ExchangeListItem,
ExchangeWithdrawRequest,
ForcedDenomSel,
@@ -54,7 +55,8 @@ import {
VersionMatchResult,
WithdrawBatchResponse,
WithdrawResponse,
- WithdrawUriInfoResponse
+ WithdrawUriInfoResponse,
+ WithdrawUriResult,
} from "@gnu-taler/taler-util";
import { EddsaKeypair } from "../crypto/cryptoImplementation.js";
import {
@@ -71,12 +73,12 @@ import {
ReserveBankInfo,
ReserveRecordStatus,
WalletStoresV1,
- WithdrawalGroupRecord
+ WithdrawalGroupRecord,
} from "../db.js";
import {
getErrorDetailFromException,
makeErrorDetail,
- TalerError
+ TalerError,
} from "../errors.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { assertUnreachable } from "../util/assertUnreachable.js";
@@ -85,24 +87,21 @@ import {
HttpRequestLibrary,
readSuccessResponseJsonOrErrorCode,
readSuccessResponseJsonOrThrow,
- throwUnexpectedRequestError
+ throwUnexpectedRequestError,
} from "../util/http.js";
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
-import {
- DbAccess,
- GetReadOnlyAccess
-} from "../util/query.js";
+import { DbAccess, GetReadOnlyAccess } from "../util/query.js";
import { RetryInfo } from "../util/retries.js";
import {
WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
- WALLET_EXCHANGE_PROTOCOL_VERSION
+ WALLET_EXCHANGE_PROTOCOL_VERSION,
} from "../versions.js";
import { guardOperationException } from "./common.js";
import {
getExchangeDetails,
getExchangePaytoUri,
getExchangeTrust,
- updateExchangeFromUrl
+ updateExchangeFromUrl,
} from "./exchanges.js";
/**
@@ -241,7 +240,7 @@ export function selectWithdrawalDenominations(
for (const d of denoms) {
let count = 0;
const cost = Amounts.add(d.value, d.feeWithdraw).amount;
- for (; ;) {
+ for (;;) {
if (Amounts.cmp(remaining, cost) < 0) {
break;
}
@@ -384,7 +383,6 @@ export async function getBankWithdrawalInfo(
return {
amount: Amounts.parseOrThrow(status.amount),
confirmTransferUrl: status.confirm_transfer_url,
- extractedStatusUrl: reqUrl.href,
selectionDone: status.selection_done,
senderWire: status.sender_wire,
suggestedExchange: status.suggested_exchange,
@@ -898,7 +896,8 @@ export async function updateWithdrawalDenoms(
denom.verificationStatus === DenominationVerificationStatus.Unverified
) {
logger.trace(
- `Validating denomination (${current + 1}/${denominations.length
+ `Validating denomination (${current + 1}/${
+ denominations.length
}) signature of ${denom.denomPubHash}`,
);
let valid = false;
@@ -1025,7 +1024,7 @@ async function queryReserve(
if (
resp.status === 404 &&
result.talerErrorResponse.code ===
- TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
+ TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
) {
ws.notify({
type: NotificationType.ReserveNotYetFound,
@@ -1315,7 +1314,7 @@ export async function getExchangeWithdrawalInfo(
) {
logger.warn(
`wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` +
- `(exchange has ${exchangeDetails.protocolVersion}), checking for updates`,
+ `(exchange has ${exchangeDetails.protocolVersion}), checking for updates`,
);
}
}
@@ -1400,11 +1399,10 @@ export async function getWithdrawalDetailsForUri(
const exchangeRecords = await tx.exchanges.iter().toArray();
for (const r of exchangeRecords) {
const details = await ws.exchangeOps.getExchangeDetails(tx, r.baseUrl);
- const denominations = await tx.denominations.indexes
- .byExchangeBaseUrl.iter(r.baseUrl).toArray();
+ const denominations = await tx.denominations.indexes.byExchangeBaseUrl
+ .iter(r.baseUrl)
+ .toArray();
if (details && denominations) {
-
-
exchanges.push({
exchangeBaseUrl: details.exchangeBaseUrl,
currency: details.currency,
@@ -1417,7 +1415,7 @@ export async function getWithdrawalDetailsForUri(
paytoUris: details.wireInfo.accounts.map((x) => x.payto_uri),
auditors: details.auditors,
wireInfo: details.wireInfo,
- denominations: denominations
+ denominations: denominations,
});
}
}
@@ -1502,6 +1500,18 @@ export function getReserveRequestTimeout(r: WithdrawalGroupRecord): Duration {
);
}
+export function getBankStatusUrl(talerWithdrawUri: string): string {
+ const uriResult = parseWithdrawUri(talerWithdrawUri);
+ if (!uriResult) {
+ throw Error(`can't parse withdrawal URL ${talerWithdrawUri}`);
+ }
+ const url = new URL(
+ `withdrawal-operation/${uriResult.withdrawalOperationId}`,
+ uriResult.bankIntegrationApiBaseUrl,
+ );
+ return url.href;
+}
+
async function registerReserveWithBank(
ws: InternalWalletState,
withdrawalGroupId: string,
@@ -1524,7 +1534,7 @@ async function registerReserveWithBank(
if (!bankInfo) {
return;
}
- const bankStatusUrl = bankInfo.statusUrl;
+ const bankStatusUrl = getBankStatusUrl(bankInfo.talerWithdrawUri);
const httpResp = await ws.http.postJson(
bankStatusUrl,
{
@@ -1584,10 +1594,12 @@ async function processReserveBankStatus(
default:
return;
}
- const bankStatusUrl = withdrawalGroup.bankInfo?.statusUrl;
- if (!bankStatusUrl) {
+ if (!withdrawalGroup.bankInfo) {
return;
}
+ const bankStatusUrl = getBankStatusUrl(
+ withdrawalGroup.bankInfo.talerWithdrawUri,
+ );
const statusResp = await ws.http.get(bankStatusUrl, {
timeout: getReserveRequestTimeout(withdrawalGroup),
@@ -1778,6 +1790,21 @@ export async function acceptWithdrawalFromUri(
restrictAge?: number;
},
): Promise<AcceptWithdrawalResponse> {
+ const existingWithdrawalGroup = await ws.db
+ .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
+ .runReadOnly(async (tx) => {
+ return await tx.withdrawalGroups.indexes.byTalerWithdrawUri.get(
+ req.talerWithdrawUri,
+ );
+ });
+
+ if (existingWithdrawalGroup) {
+ return {
+ reservePub: existingWithdrawalGroup.reservePub,
+ confirmTransferUrl: existingWithdrawalGroup.bankInfo?.confirmUrl,
+ };
+ }
+
await updateExchangeFromUrl(ws, req.selectedExchange);
const withdrawInfo = await getBankWithdrawalInfo(
ws.http,
@@ -1796,7 +1823,7 @@ export async function acceptWithdrawalFromUri(
reserveStatus: ReserveRecordStatus.RegisteringBank,
bankInfo: {
exchangePaytoUri,
- statusUrl: withdrawInfo.extractedStatusUrl,
+ talerWithdrawUri: req.talerWithdrawUri,
confirmUrl: withdrawInfo.confirmTransferUrl,
},
});
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index ac81660d2..3c83cea6e 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -24,7 +24,6 @@
*/
import {
AbsoluteTime,
- AcceptManualWithdrawalResult,
AmountJson,
Amounts,
BalancesResponse,
@@ -98,7 +97,6 @@ import {
CoinSourceType,
exportDb,
importDb,
- ReserveRecordStatus,
WalletStoresV1,
} from "./db.js";
import { getErrorDetailFromException, TalerError } from "./errors.js";
@@ -187,9 +185,8 @@ import {
acceptWithdrawalFromUri,
createManualWithdrawal,
getExchangeWithdrawalInfo,
- getFundingPaytoUrisTx,
getWithdrawalDetailsForUri,
- processWithdrawalGroup as processWithdrawalGroup,
+ processWithdrawalGroup,
} from "./operations/withdraw.js";
import {
PendingOperationsResponse,