[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-wallet-core] branch master updated: wallet-core,harness: support
From: |
gnunet |
Subject: |
[taler-wallet-core] branch master updated: wallet-core,harness: support cash acceptor withdrawals, test |
Date: |
Tue, 07 Jan 2025 19:30:56 +0100 |
This is an automated email from the git hooks/post-receive script.
dold pushed a commit to branch master
in repository wallet-core.
The following commit(s) were added to refs/heads/master by this push:
new 15cd09e5c wallet-core,harness: support cash acceptor withdrawals, test
15cd09e5c is described below
commit 15cd09e5ce7293dc5ae370b67426a4b2cc5a219b
Author: Florian Dold <florian@dold.me>
AuthorDate: Tue Jan 7 19:30:53 2025 +0100
wallet-core,harness: support cash acceptor withdrawals, test
---
packages/taler-harness/src/harness/environments.ts | 54 ++++++-
.../src/integrationtests/testrunner.ts | 2 +
packages/taler-util/src/http-client/bank-core.ts | 33 +++-
.../taler-util/src/http-client/bank-integration.ts | 12 +-
.../taler-util/src/types-taler-bank-integration.ts | 59 +++++---
packages/taler-util/src/types-taler-common.ts | 83 -----------
packages/taler-util/src/types-taler-corebank.ts | 4 +-
packages/taler-util/src/types-taler-wallet.ts | 8 +-
packages/taler-wallet-core/src/balance.ts | 26 +++-
packages/taler-wallet-core/src/withdraw.ts | 166 +++++++++++++--------
10 files changed, 265 insertions(+), 182 deletions(-)
diff --git a/packages/taler-harness/src/harness/environments.ts
b/packages/taler-harness/src/harness/environments.ts
index ed3e34881..155dae463 100644
--- a/packages/taler-harness/src/harness/environments.ts
+++ b/packages/taler-harness/src/harness/environments.ts
@@ -24,6 +24,7 @@
* Imports
*/
import {
+ AccessToken,
AccountProperties,
AmlDecisionRequest,
AmlDecisionRequestWithoutSignature,
@@ -33,16 +34,19 @@ import {
decodeCrock,
Duration,
encodeCrock,
+ getRandomBytes,
HttpStatusCode,
j2s,
LegitimizationRuleSet,
Logger,
MerchantApiClient,
+ narrowOpSuccessOrThrow,
NotificationType,
PartialWalletRunConfig,
PreparePayResultType,
signAmlDecision,
TalerCorebankApiClient,
+ TalerCoreBankHttpClient,
TalerMerchantApi,
TalerProtocolTimestamp,
TransactionIdStr,
@@ -142,6 +146,11 @@ export interface EnvOptions {
accountRestrictions?: HarnessAccountRestriction[];
+ /**
+ * Force usage of libeufin for this particular test.
+ */
+ forceLibeufin?: boolean;
+
additionalExchangeConfig?(e: ExchangeService): void;
additionalMerchantConfig?(m: MerchantService): void;
additionalBankConfig?(b: BankService): void;
@@ -463,9 +472,10 @@ export async function createSimpleTestkudosEnvironmentV3(
httpPort: 8082,
};
- const bank: BankService = useLibeufinBank
- ? await LibeufinBankService.create(t, bc)
- : await FakebankService.create(t, bc);
+ const bank: BankService =
+ useLibeufinBank || opts.forceLibeufin
+ ? await LibeufinBankService.create(t, bc)
+ : await FakebankService.create(t, bc);
const exchange = ExchangeService.create(t, {
name: "testexchange-1",
@@ -1130,7 +1140,6 @@ export interface KycTestEnv {
wireGatewayApiClient: WireGatewayApiClient;
}
-
export async function createKycTestkudosEnvironment(
t: GlobalTestState,
opts: KycEnvOptions = {},
@@ -1219,7 +1228,6 @@ export async function createKycTestkudosEnvironment(
await walletService.start();
await walletService.pingUntilAvailable();
-
const walletClient = new WalletClient({
name: "wallet",
unixPath: walletService.socketPath,
@@ -1293,3 +1301,39 @@ export async function createKycTestkudosEnvironment(
wireGatewayApiClient,
};
}
+
+export interface TestUserResult {
+ username: string;
+ password: string;
+ token: AccessToken;
+}
+
+/**
+ * Register a new bank user with a random name and obtain a
+ * login token.
+ */
+export async function registerHarnessBankTestUser(
+ bankClient: TalerCoreBankHttpClient,
+): Promise<TestUserResult> {
+ const username = "user-" + encodeCrock(getRandomBytes(10)).toLowerCase();
+ const password = "pw-" + encodeCrock(getRandomBytes(10)).toLowerCase();
+ const createRes = await bankClient.createAccount(undefined, {
+ name: username,
+ username,
+ password,
+ });
+ narrowOpSuccessOrThrow("createAccount", createRes);
+ // It's a test account, so it's safe to log credentials.
+ logger.info(
+ `Created test bank account ${username} with password ${password}`,
+ );
+ const tokRes = await bankClient.createAccessTokenBasic(username, password, {
+ scope: "readwrite",
+ });
+ narrowOpSuccessOrThrow("token", tokRes);
+ return {
+ password,
+ username,
+ token: tokRes.body.access_token,
+ };
+}
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts
b/packages/taler-harness/src/integrationtests/testrunner.ts
index 6e5eb21fa..1a5c32764 100644
--- a/packages/taler-harness/src/integrationtests/testrunner.ts
+++ b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -143,6 +143,7 @@ import { runWallettestingTest } from
"./test-wallettesting.js";
import { runWithdrawalAbortBankTest } from "./test-withdrawal-abort-bank.js";
import { runWithdrawalAmountTest } from "./test-withdrawal-amount.js";
import { runWithdrawalBankIntegratedTest } from
"./test-withdrawal-bank-integrated.js";
+import { runWithdrawalCashacceptorTest } from
"./test-withdrawal-cashacceptor.js";
import { runWithdrawalConversionTest } from "./test-withdrawal-conversion.js";
import { runWithdrawalExternalTest } from "./test-withdrawal-external.js";
import { runWithdrawalFakebankTest } from "./test-withdrawal-fakebank.js";
@@ -296,6 +297,7 @@ const allTests: TestMainFunction[] = [
runKycAmpTimeoutTest,
runKycAmpFailureTest,
runPeerPushAbortTest,
+ runWithdrawalCashacceptorTest,
];
export interface TestRunSpec {
diff --git a/packages/taler-util/src/http-client/bank-core.ts
b/packages/taler-util/src/http-client/bank-core.ts
index ef17b32e1..208c05ce5 100644
--- a/packages/taler-util/src/http-client/bank-core.ts
+++ b/packages/taler-util/src/http-client/bank-core.ts
@@ -26,8 +26,10 @@ import {
PaginationParams,
TalerError,
TalerErrorCode,
+ TokenRequest,
UserAndToken,
codecForTalerCommonConfigResponse,
+ codecForTokenSuccessResponse,
opKnownAlternativeFailure,
opKnownHttpFailure,
opKnownTalerFailure,
@@ -46,7 +48,7 @@ import {
opSuccessFromHttp,
opUnknownFailure,
} from "../operation.js";
-import { WithdrawalOperationStatus } from "../types-taler-bank-integration.js";
+import { WithdrawalOperationStatusFlag } from
"../types-taler-bank-integration.js";
import {
codecForAccountData,
codecForBankAccountCreateWithdrawalResponse,
@@ -70,6 +72,7 @@ import {
CacheEvictor,
addLongPollingParam,
addPaginationParams,
+ makeBasicAuthHeader,
makeBearerTokenAuthHeader,
nullEvictor,
} from "./utils.js";
@@ -123,6 +126,32 @@ export class TalerCoreBankHttpClient {
return compare?.compatible ?? false;
}
+ async createAccessTokenBasic(
+ username: string,
+ password: string,
+ body: TokenRequest,
+ ) {
+ const url = new URL(`accounts/${username}/token`, this.baseUrl);
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "POST",
+ headers: {
+ Authorization: makeBasicAuthHeader(username, password),
+ },
+ body,
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(resp, codecForTokenSuccessResponse());
+ //FIXME: missing in docs
+ case HttpStatusCode.Unauthorized:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+
/**
* https://docs.taler.net/core/api-corebank.html#config
*
@@ -754,7 +783,7 @@ export class TalerCoreBankHttpClient {
async getWithdrawalById(
wid: string,
params?: {
- old_state?: WithdrawalOperationStatus;
+ old_state?: WithdrawalOperationStatusFlag;
} & LongPollParams,
) {
const url = new URL(`withdrawals/${wid}`, this.baseUrl);
diff --git a/packages/taler-util/src/http-client/bank-integration.ts
b/packages/taler-util/src/http-client/bank-integration.ts
index 9bcdac683..17c5337c2 100644
--- a/packages/taler-util/src/http-client/bank-integration.ts
+++ b/packages/taler-util/src/http-client/bank-integration.ts
@@ -21,6 +21,8 @@ import { LibtoolVersion } from "../libtool-version.js";
import { Logger } from "../logging.js";
import {
FailCasesByMethod,
+ OperationFail,
+ OperationOk,
ResultByMethod,
opEmptySuccess,
opKnownHttpFailure,
@@ -31,7 +33,8 @@ import {
import { TalerErrorCode } from "../taler-error-codes.js";
import {
BankWithdrawalOperationPostRequest,
- WithdrawalOperationStatus,
+ BankWithdrawalOperationStatus,
+ WithdrawalOperationStatusFlag,
codecForBankWithdrawalOperationPostResponse,
codecForBankWithdrawalOperationStatus,
} from "../types-taler-bank-integration.js";
@@ -96,9 +99,12 @@ export class TalerBankIntegrationHttpClient {
async getWithdrawalOperationById(
woid: string,
params?: {
- old_state?: WithdrawalOperationStatus;
+ old_state?: WithdrawalOperationStatusFlag;
} & LongPollParams,
- ) {
+ ): Promise<
+ | OperationOk<BankWithdrawalOperationStatus>
+ | OperationFail<HttpStatusCode.NotFound>
+ > {
const url = new URL(`withdrawal-operation/${woid}`, this.baseUrl);
addLongPollingParam(url, params);
if (params) {
diff --git a/packages/taler-util/src/types-taler-bank-integration.ts
b/packages/taler-util/src/types-taler-bank-integration.ts
index 517d59f38..6cec0fc9b 100644
--- a/packages/taler-util/src/types-taler-bank-integration.ts
+++ b/packages/taler-util/src/types-taler-bank-integration.ts
@@ -16,12 +16,30 @@
SPDX-License-Identifier: AGPL-3.0-or-later
*/
-import { Codec, buildCodecForObject, codecForConstString, codecForEither,
codecOptional } from "./codec.js";
-import { codecForAmountString, codecForList, codecForString } from
"./index.js";
+import {
+ Codec,
+ buildCodecForObject,
+ codecForConstString,
+ codecForEither,
+ codecOptional,
+} from "./codec.js";
+import {
+ codecForAmountString,
+ codecForBoolean,
+ codecForList,
+ codecForString,
+} from "./index.js";
import { PaytoString, codecForPaytoString } from "./payto.js";
-import { AmountString, CurrencySpecification, codecForCurrencyName,
codecForCurrencySpecificiation, codecForLibtoolVersion, codecForURLString }
from "./types-taler-common.js";
-
-export type WithdrawalOperationStatus =
+import {
+ AmountString,
+ CurrencySpecification,
+ codecForCurrencyName,
+ codecForCurrencySpecificiation,
+ codecForLibtoolVersion,
+ codecForURLString,
+} from "./types-taler-common.js";
+
+export type WithdrawalOperationStatusFlag =
| "pending"
| "selected"
| "aborted"
@@ -49,7 +67,7 @@ export interface BankWithdrawalOperationStatus {
// selected: the operations has been selected and is pending confirmation
// aborted: the operation has been aborted
// confirmed: the transfer has been confirmed and registered by the bank
- status: WithdrawalOperationStatus;
+ status: WithdrawalOperationStatusFlag;
// Currency used for the withdrawal.
// MUST be present when amount is absent.
@@ -116,6 +134,14 @@ export interface BankWithdrawalOperationStatus {
// only non-null if status is selected or confirmed.
// @since **v1**
selected_exchange_account?: string;
+
+ // If true, tells the wallet not to allow the user to
+ // specify an amount to withdraw and to not provide
+ // any amount when registering with the withdrawal
+ // operation. The amount to withdraw will be set
+ // by the final /withdrawals/$WITHDRAWAL_ID/confirm step.
+ // @since **v5**
+ no_amount_to_wallet?: boolean;
}
export interface BankWithdrawalOperationPostRequest {
@@ -138,7 +164,7 @@ export interface BankWithdrawalOperationPostResponse {
// selected: the operations has been selected and is pending confirmation
// aborted: the operation has been aborted
// confirmed: the transfer has been confirmed and registered by the bank
- status: Omit<"pending", WithdrawalOperationStatus>;
+ status: Omit<"pending", WithdrawalOperationStatusFlag>;
// URL that the user needs to navigate to in order to
// complete some final confirmation (e.g. 2FA).
@@ -148,15 +174,13 @@ export interface BankWithdrawalOperationPostResponse {
confirm_transfer_url?: string;
}
-
-export const codecForBankVersion =
- (): Codec<BankVersion> =>
- buildCodecForObject<BankVersion>()
- .property("currency", codecForCurrencyName())
- .property("currency_specification", codecForCurrencySpecificiation())
- .property("name", codecForConstString("taler-bank-integration"))
- .property("version", codecForLibtoolVersion())
- .build("TalerBankIntegrationApi.BankVersion");
+export const codecForBankVersion = (): Codec<BankVersion> =>
+ buildCodecForObject<BankVersion>()
+ .property("currency", codecForCurrencyName())
+ .property("currency_specification", codecForCurrencySpecificiation())
+ .property("name", codecForConstString("taler-bank-integration"))
+ .property("version", codecForLibtoolVersion())
+ .build("TalerBankIntegrationApi.BankVersion");
export const codecForBankWithdrawalOperationStatus =
(): Codec<BankWithdrawalOperationStatus> =>
@@ -183,6 +207,7 @@ export const codecForBankWithdrawalOperationStatus =
.property("wire_types", codecForList(codecForString()))
.property("selected_reserve_pub", codecOptional(codecForString()))
.property("selected_exchange_account", codecOptional(codecForString()))
+ .property("no_amount_to_wallet", codecOptional(codecForBoolean()))
.build("TalerBankIntegrationApi.BankWithdrawalOperationStatus");
export const codecForBankWithdrawalOperationPostResponse =
@@ -197,4 +222,4 @@ export const codecForBankWithdrawalOperationPostResponse =
),
)
.property("confirm_transfer_url", codecOptional(codecForURLString()))
- .build("TalerBankIntegrationApi.BankWithdrawalOperationPostResponse");
\ No newline at end of file
+ .build("TalerBankIntegrationApi.BankWithdrawalOperationPostResponse");
diff --git a/packages/taler-util/src/types-taler-common.ts
b/packages/taler-util/src/types-taler-common.ts
index 7a5f297d6..174626b02 100644
--- a/packages/taler-util/src/types-taler-common.ts
+++ b/packages/taler-util/src/types-taler-common.ts
@@ -177,61 +177,6 @@ export type ImageDataUrl = string;
*/
export type Cs25519Point = string;
-/**
- * Response from the bank.
- */
-export class WithdrawOperationStatusResponse {
- status: "selected" | "aborted" | "confirmed" | "pending";
-
- selection_done: boolean;
-
- transfer_done: boolean;
-
- aborted: boolean;
-
- amount: string | undefined;
-
- sender_wire?: string;
-
- suggested_exchange?: string;
-
- confirm_transfer_url?: string;
-
- wire_types: string[];
-
- // Currency used for the withdrawal.
- // MUST be present when amount is absent.
- // @since **v2**, may become mandatory in the future.
- currency?: string;
-
- // Minimum amount that the wallet can choose to withdraw.
- // Only applicable when the amount is not fixed.
- // @since **v4**.
- min_amount?: AmountString;
-
- // Maximum amount that the wallet can choose to withdraw.
- // Only applicable when the amount is not fixed.
- // @since **v4**.
- max_amount?: AmountString;
-
- // The non-Taler card fees the customer will have
- // to pay to the bank / payment service provider
- // they are using to make the withdrawal in addition
- // to the amount.
- // @since **v4**
- card_fees?: AmountString;
-
- // Exchange account selected by the wallet;
- // only non-null if status is selected or confirmed.
- // @since **v1**
- selected_exchange_account?: string;
-
- // Reserve public key selected by the exchange,
- // only non-null if status is selected or confirmed.
- // @since **v1**
- selected_reserve_pub?: EddsaPublicKey;
-}
-
export type LitAmountString = `${string}:${number}`;
export type LibtoolVersionString = string;
@@ -265,34 +210,6 @@ export const codecForEddsaSignature = codecForString;
export const codecForInternationalizedString =
(): Codec<InternationalizedString> => codecForMap(codecForString());
-export const codecForWithdrawOperationStatusResponse =
- (): Codec<WithdrawOperationStatusResponse> =>
- buildCodecForObject<WithdrawOperationStatusResponse>()
- .property(
- "status",
- codecForEither(
- codecForConstString("selected"),
- codecForConstString("confirmed"),
- codecForConstString("aborted"),
- codecForConstString("pending"),
- ),
- )
- .property("selection_done", codecForBoolean())
- .property("transfer_done", codecForBoolean())
- .property("aborted", codecForBoolean())
- .property("amount", codecOptional(codecForString()))
- .property("sender_wire", codecOptional(codecForString()))
- .property("suggested_exchange", codecOptional(codecForString()))
- .property("confirm_transfer_url", codecOptional(codecForString()))
- .property("wire_types", codecForList(codecForString()))
- .property("currency", codecOptional(codecForString()))
- .property("card_fees", codecOptional(codecForAmountString()))
- .property("min_amount", codecOptional(codecForAmountString()))
- .property("max_amount", codecOptional(codecForAmountString()))
- .property("selected_exchange_account", codecOptional(codecForString()))
- .property("selected_reserve_pub",
codecOptional(codecForEddsaPublicKey()))
- .build("WithdrawOperationStatusResponse");
-
export const codecForCurrencySpecificiation =
(): Codec<CurrencySpecification> =>
buildCodecForObject<CurrencySpecification>()
diff --git a/packages/taler-util/src/types-taler-corebank.ts
b/packages/taler-util/src/types-taler-corebank.ts
index e198e057f..310b0c2e4 100644
--- a/packages/taler-util/src/types-taler-corebank.ts
+++ b/packages/taler-util/src/types-taler-corebank.ts
@@ -36,7 +36,7 @@ import {
} from "./index.js";
import { PaytoString, codecForPaytoString } from "./payto.js";
import { TalerUriString } from "./taleruri.js";
-import { WithdrawalOperationStatus } from "./types-taler-bank-integration.js";
+import { WithdrawalOperationStatusFlag } from
"./types-taler-bank-integration.js";
import {
AmountString,
CurrencySpecification,
@@ -174,7 +174,7 @@ export interface WithdrawalPublicInfo {
// selected: the operations has been selected and is pending confirmation
// aborted: the operation has been aborted
// confirmed: the transfer has been confirmed and registered by the bank
- status: WithdrawalOperationStatus;
+ status: WithdrawalOperationStatusFlag;
// Amount that will be withdrawn with this operation
// (raw amount without fee considerations).
diff --git a/packages/taler-util/src/types-taler-wallet.ts
b/packages/taler-util/src/types-taler-wallet.ts
index 007b5e9af..0a3d49a49 100644
--- a/packages/taler-util/src/types-taler-wallet.ts
+++ b/packages/taler-util/src/types-taler-wallet.ts
@@ -54,7 +54,7 @@ import {
InternationalizedString,
TalerMerchantApi,
TemplateParams,
- WithdrawalOperationStatus,
+ WithdrawalOperationStatusFlag,
canonicalizeBaseUrl,
} from "./index.js";
import { PaytoString, codecForPaytoString } from "./payto.js";
@@ -953,7 +953,7 @@ export interface PreparePayResultAlreadyConfirmed {
}
export interface BankWithdrawDetails {
- status: WithdrawalOperationStatus;
+ status: WithdrawalOperationStatusFlag;
currency: string;
amount: AmountJson | undefined;
editableAmount: boolean;
@@ -1926,7 +1926,7 @@ export interface PrepareBankIntegratedWithdrawalResponse {
export interface ConfirmWithdrawalRequest {
transactionId: string;
exchangeBaseUrl: string;
- amount: AmountString;
+ amount: AmountString | undefined;
forcedDenomSel?: ForcedDenomSel;
restrictAge?: number;
}
@@ -2503,7 +2503,7 @@ export interface TxIdResponse {
export interface WithdrawUriInfoResponse {
operationId: string;
- status: WithdrawalOperationStatus;
+ status: WithdrawalOperationStatusFlag;
confirmTransferUrl?: string;
currency: string;
amount: AmountString | undefined;
diff --git a/packages/taler-wallet-core/src/balance.ts
b/packages/taler-wallet-core/src/balance.ts
index ddc0fb804..b4cb02481 100644
--- a/packages/taler-wallet-core/src/balance.ts
+++ b/packages/taler-wallet-core/src/balance.ts
@@ -80,6 +80,7 @@ import {
RefreshOperationStatus,
WalletDbReadOnlyTransaction,
WithdrawalGroupStatus,
+ WithdrawalRecordType,
} from "./db.js";
import {
getExchangeScopeInfo,
@@ -400,15 +401,28 @@ export async function getBalancesInsideTransaction(
break;
}
case WithdrawalGroupStatus.PendingWaitConfirmBank: {
- checkDbInvariant(
- wg.denomsSel !== undefined,
- "wg in confirmed state should have been initialized",
- );
checkDbInvariant(
wg.exchangeBaseUrl !== undefined,
- "wg in kyc state should have been initialized",
+ "withdrawal group in PendingWaitConfirmBank state should have been
initialized",
);
- const currency = Amounts.currencyOf(wg.denomsSel.totalCoinValue);
+
+ // FIXME: Consider just having the currency as a fixed field in the
DB
+ // instead of having it in many locations.
+ let currency: string;
+
+ if (wg.denomsSel) {
+ currency = Amounts.currencyOf(wg.denomsSel.totalCoinValue);
+ } else if (
+ wg.wgInfo.withdrawalType === WithdrawalRecordType.BankIntegrated &&
+ wg.wgInfo.bankInfo.currency
+ ) {
+ currency = wg.wgInfo.bankInfo.currency;
+ } else {
+ logger.warn(
+ "could not determine currency for confirmed withdrawal group",
+ );
+ break;
+ }
await balanceStore.setFlagIncomingConfirmation(
currency,
wg.exchangeBaseUrl,
diff --git a/packages/taler-wallet-core/src/withdraw.ts
b/packages/taler-wallet-core/src/withdraw.ts
index 952de45e4..c2b4ae805 100644
--- a/packages/taler-wallet-core/src/withdraw.ts
+++ b/packages/taler-wallet-core/src/withdraw.ts
@@ -95,7 +95,6 @@ import {
codecForExchangeWithdrawBatchResponse,
codecForLegitimizationNeededResponse,
codecForReserveStatus,
- codecForWithdrawOperationStatusResponse,
encodeCrock,
getErrorDetailFromException,
getRandomBytes,
@@ -430,38 +429,26 @@ export class WithdrawTransactionContext implements
TransactionContext {
}
if (
- !wgRecord.instructedAmount ||
- !wgRecord.denomsSel ||
+ // !wgRecord.instructedAmount ||
+ // !wgRecord.denomsSel ||
!wgRecord.exchangeBaseUrl
) {
// withdrawal group is in preparation, nothing to update
return;
}
- if (
- wgRecord.wgInfo.withdrawalType === WithdrawalRecordType.BankIntegrated
- ) {
- } else if (
- wgRecord.wgInfo.withdrawalType === WithdrawalRecordType.BankManual
- ) {
- checkDbInvariant(
- wgRecord.instructedAmount !== undefined,
- "manual withdrawal without amount can't be created",
- );
- checkDbInvariant(
- wgRecord.denomsSel !== undefined,
- "manual withdrawal without denoms can't be created",
- );
- } else {
- // FIXME: If this is an orphaned withdrawal for a p2p transaction, we
- // still might want to report the withdrawal.
+ let currency: string | undefined;
+ if (wgRecord.rawWithdrawalAmount) {
+ currency = Amounts.currencyOf(wgRecord.rawWithdrawalAmount);
+ }
+ if (!currency) {
return;
}
await tx.transactionsMeta.put({
transactionId: ctx.transactionId,
status: wgRecord.status,
timestamp: wgRecord.timestampStart,
- currency: Amounts.currencyOf(wgRecord.instructedAmount),
+ currency,
exchanges: [wgRecord.exchangeBaseUrl],
});
@@ -1177,7 +1164,7 @@ export async function getBankWithdrawalInfo(
let editableAmount = false;
if (status.amount !== undefined) {
amount = Amounts.parseOrThrow(status.amount);
- } else {
+ } else if (!status.no_amount_to_wallet) {
amount =
status.suggested_amount === undefined
? undefined
@@ -2912,10 +2899,10 @@ async function processBankRegisterReserve(
const status = await readSuccessResponseJsonOrThrow(
statusResp,
- codecForWithdrawOperationStatusResponse(),
+ codecForBankWithdrawalOperationStatus(),
);
- if (status.aborted) {
+ if (status.status === "aborted") {
return transitionBankAborted(ctx);
}
@@ -2973,21 +2960,40 @@ async function processReserveBankStatus(
const status = await readSuccessResponseJsonOrThrow(
statusResp,
- codecForWithdrawOperationStatusResponse(),
+ codecForBankWithdrawalOperationStatus(),
);
if (logger.shouldLogTrace()) {
logger.trace(`response body: ${j2s(status)}`);
}
- if (status.aborted) {
+ if (status.status === "aborted") {
return transitionBankAborted(ctx);
}
- if (!status.transfer_done) {
+ if (status.status != "confirmed") {
return TaskRunResult.longpollReturnedPending();
}
+ let denomSel: undefined | DenomSelectionState = undefined;
+
+ if (withdrawalGroup.denomsSel == null) {
+ const exchangeBaseUrl = withdrawalGroup.exchangeBaseUrl;
+ if (!exchangeBaseUrl) {
+ throw Error("invalid state");
+ }
+ if (!status.amount) {
+ throw Error("bank did not provide amount");
+ }
+ const instructedAmount = Amounts.parseOrThrow(status.amount);
+ denomSel = await getInitialDenomsSelection(
+ wex,
+ exchangeBaseUrl,
+ instructedAmount,
+ undefined,
+ );
+ }
+
const transitionInfo = await ctx.transition({}, async (r) => {
if (!r) {
return TransitionResult.stay();
@@ -3002,11 +3008,17 @@ async function processReserveBankStatus(
if (r.wgInfo.withdrawalType !== WithdrawalRecordType.BankIntegrated) {
throw Error("invariant failed");
}
- if (status.transfer_done) {
+ if (status.status == "confirmed") {
logger.info("withdrawal: transfer confirmed by bank.");
const now = AbsoluteTime.toPreciseTimestamp(AbsoluteTime.now());
r.wgInfo.bankInfo.timestampBankConfirmed = timestampPreciseToDb(now);
r.status = WithdrawalGroupStatus.PendingQueryingStatus;
+ if (denomSel != null) {
+ r.denomsSel = denomSel;
+ r.rawWithdrawalAmount = denomSel.totalWithdrawCost;
+ r.effectiveWithdrawalAmount = denomSel.totalCoinValue;
+ r.instructedAmount = denomSel.totalWithdrawCost;
+ }
return TransitionResult.transition(r);
} else {
return TransitionResult.stay();
@@ -3389,7 +3401,8 @@ export async function confirmWithdrawal(
): Promise<AcceptWithdrawalResponse> {
const parsedTx = parseTransactionIdentifier(req.transactionId);
const selectedExchange = req.exchangeBaseUrl;
- const instructedAmount = Amounts.parseOrThrow(req.amount);
+ const instructedAmount =
+ req.amount == null ? undefined : Amounts.parseOrThrow(req.amount);
if (parsedTx?.tag !== TransactionType.Withdrawal) {
throw Error("invalid withdrawal transaction ID");
@@ -3412,10 +3425,20 @@ export async function confirmWithdrawal(
throw Error("not a bank integrated withdrawal");
}
+ let instructedCurrency: string;
+ if (instructedAmount) {
+ instructedCurrency = instructedAmount.currency;
+ } else {
+ if (!withdrawalGroup.wgInfo.bankInfo.currency) {
+ throw Error("currency must be provided by bank");
+ }
+ instructedCurrency = withdrawalGroup.wgInfo.bankInfo.currency;
+ }
+
const exchange = await fetchFreshExchange(wex, selectedExchange);
requireExchangeTosAcceptedOrThrow(exchange);
- if (checkWithdrawalHardLimitExceeded(exchange, req.amount)) {
+ if (req.amount && checkWithdrawalHardLimitExceeded(exchange, req.amount)) {
throw Error("withdrawal would exceed hard KYC limit");
}
@@ -3452,14 +3475,17 @@ export async function confirmWithdrawal(
bankWireTypes,
);
- const withdrawalAccountList = await fetchWithdrawalAccountInfo(
- wex,
- {
- exchange,
- instructedAmount,
- },
- wex.cancellationToken,
- );
+ let withdrawalAccountList: WithdrawalExchangeAccountDetails[] = [];
+ if (instructedAmount) {
+ withdrawalAccountList = await fetchWithdrawalAccountInfo(
+ wex,
+ {
+ exchange,
+ instructedAmount,
+ },
+ wex.cancellationToken,
+ );
+ }
const senderWire = withdrawalGroup.wgInfo.bankInfo.senderWire;
@@ -3498,9 +3524,9 @@ export async function confirmWithdrawal(
await tx.bankAccountsV2.indexes.byPaytoUri.get(senderWire);
if (existingAccount) {
// Add currency for existing known bank account if necessary
- if (existingAccount.currencies?.includes(instructedAmount.currency))
{
+ if (existingAccount.currencies?.includes(instructedCurrency)) {
existingAccount.currencies = [
- instructedAmount.currency,
+ instructedCurrency,
...(existingAccount.currencies ?? []),
];
existingAccount.currencies.sort();
@@ -3511,7 +3537,7 @@ export async function confirmWithdrawal(
const myId = `acct:${encodeCrock(getRandomBytes(32))}`;
await tx.bankAccountsV2.put({
- currencies: [instructedAmount.currency],
+ currencies: [instructedCurrency],
kycCompleted: false,
paytoUri: senderWire,
bankAccountId: myId,
@@ -3533,12 +3559,17 @@ export async function confirmWithdrawal(
wex,
withdrawalGroup.withdrawalGroupId,
);
- const initalDenoms = await getInitialDenomsSelection(
- wex,
- exchange.exchangeBaseUrl,
- instructedAmount,
- req.forcedDenomSel,
- );
+
+ let initialDenoms: DenomSelectionState | undefined;
+
+ if (instructedAmount != null) {
+ initialDenoms = await getInitialDenomsSelection(
+ wex,
+ exchange.exchangeBaseUrl,
+ instructedAmount,
+ req.forcedDenomSel,
+ );
+ }
let pending = false;
await ctx.transition({}, async (rec) => {
@@ -3560,9 +3591,19 @@ export async function confirmWithdrawal(
rec.exchangeBaseUrl = exchange.exchangeBaseUrl;
rec.instructedAmount = req.amount;
rec.restrictAge = req.restrictAge;
- rec.denomsSel = initalDenoms;
- rec.rawWithdrawalAmount = initalDenoms.totalWithdrawCost;
- rec.effectiveWithdrawalAmount = initalDenoms.totalCoinValue;
+ if (initialDenoms != null) {
+ rec.denomsSel = initialDenoms;
+ rec.rawWithdrawalAmount = initialDenoms.totalWithdrawCost;
+ rec.effectiveWithdrawalAmount = initialDenoms.totalCoinValue;
+ } else {
+ rec.denomsSel = undefined;
+ rec.rawWithdrawalAmount = Amounts.stringify(
+ Amounts.zeroOfCurrency(instructedCurrency),
+ );
+ rec.effectiveWithdrawalAmount = Amounts.stringify(
+ Amounts.zeroOfCurrency(instructedCurrency),
+ );
+ }
checkDbInvariant(
rec.wgInfo.withdrawalType === WithdrawalRecordType.BankIntegrated,
"withdrawal type mismatch",
@@ -3583,6 +3624,7 @@ export async function confirmWithdrawal(
await wex.taskScheduler.resetTaskRetries(ctx.taskId);
+ // FIXME: Merge with transaction above!
const res = await wex.db.runReadWriteTx(
{
storeNames: ["exchanges"],
@@ -3660,14 +3702,20 @@ export async function acceptBankIntegratedWithdrawal(
contents: "prepared acceptBankIntegratedWithdrawal",
});
- let amount: AmountString;
+ let amount: AmountString | undefined;
if (p.info.amount == null) {
if (req.amount == null) {
- throw Error(
- "amount required, as withdrawal operation has flexible amount",
- );
+ if (p.info.editableAmount) {
+ throw Error(
+ "amount required, as withdrawal operation has flexible amount",
+ );
+ }
+ // Amount will be determined by the bank only after withdrawal has
+ // been confirmed by the wallet.
+ amount = undefined;
+ } else {
+ amount = Amounts.stringify(req.amount);
}
- amount = Amounts.stringify(req.amount);
} else {
if (req.amount == null) {
amount = p.info.amount;
@@ -3686,7 +3734,7 @@ export async function acceptBankIntegratedWithdrawal(
logger.info(`confirming withdrawal with tx ${p.transactionId}`);
await confirmWithdrawal(wex, {
- amount: Amounts.stringify(amount),
+ amount: amount == null ? undefined : Amounts.stringify(amount),
exchangeBaseUrl: selectedExchange,
transactionId: p.transactionId,
restrictAge: req.restrictAge,
@@ -3783,7 +3831,7 @@ async function fetchAccount(
cancellationToken: CancellationToken,
): Promise<WithdrawalExchangeAccountDetails> {
let paytoUri: string;
- let transferAmount: AmountString | undefined = undefined;
+ let transferAmount: AmountString | undefined;
let currencySpecification: CurrencySpecification | undefined = undefined;
if (acct.conversion_url != null) {
const reqUrl = new URL("cashin-rate", acct.conversion_url);
@@ -3860,9 +3908,7 @@ async function fetchAccount(
currencySpecification,
creditRestrictions: acct.credit_restrictions,
};
- if (transferAmount != null) {
- acctInfo.transferAmount = transferAmount;
- }
+ acctInfo.transferAmount = transferAmount;
return acctInfo;
}
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-wallet-core] branch master updated: wallet-core,harness: support cash acceptor withdrawals, test,
gnunet <=