summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-04-05 13:01:45 -0300
committerSebastian <sebasjm@gmail.com>2024-04-05 13:01:45 -0300
commit4759ceae7014771a8a23df4800b0fbd016870621 (patch)
tree1e27323313560e15a35b0122630a846e468e7c28 /packages
parent89dde053665d39be8367c25691efc008fc2a5cc7 (diff)
downloadwallet-core-4759ceae7014771a8a23df4800b0fbd016870621.tar.gz
wallet-core-4759ceae7014771a8a23df4800b0fbd016870621.tar.bz2
wallet-core-4759ceae7014771a8a23df4800b0fbd016870621.zip
wip #8276
Diffstat (limited to 'packages')
-rw-r--r--packages/bank-ui/src/pages/account/ShowAccountDetails.tsx9
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx69
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx173
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx62
-rw-r--r--packages/taler-util/src/http-client/bank-revenue.ts71
-rw-r--r--packages/taler-util/src/http-client/types.ts66
6 files changed, 361 insertions, 89 deletions
diff --git a/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx b/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx
index c071a838a..69a186ca1 100644
--- a/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx
+++ b/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx
@@ -19,27 +19,26 @@ import {
TalerCorebankApi,
TalerError,
TalerErrorCode,
- TalerRevenueHttpClient,
TranslatedString,
assertUnreachable,
- parsePaytoUri,
+ parsePaytoUri
} from "@gnu-taler/taler-util";
import {
CopyButton,
Loading,
LocalNotificationBanner,
+ RouteDefinition,
notifyInfo,
+ useBankCoreApiContext,
useLocalNotification,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js";
-import { useBankCoreApiContext } from "@gnu-taler/web-util/browser";
import { useAccountDetails } from "../../hooks/account.js";
-import { useSessionState } from "../../hooks/session.js";
import { useBankState } from "../../hooks/bank-state.js";
-import { RouteDefinition } from "@gnu-taler/web-util/browser";
+import { useSessionState } from "../../hooks/session.js";
import { LoginForm } from "../LoginForm.js";
import { ProfileNavigation } from "../ProfileNavigation.js";
import { AccountForm } from "../admin/AccountForm.js";
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
index dd77d609c..255caa375 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
@@ -44,8 +44,8 @@ const accountAuthType = ["none", "basic"];
function isValidURL(s: string): boolean {
try {
- const u = new URL("/", s)
- return true;
+ const parsed = new URL("/", s);
+ return parsed instanceof URL;
} catch (e) {
return false;
}
@@ -61,43 +61,54 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
credit_facade_credentials: !state.credit_facade_credentials
? undefined
: undefinedIfEmpty({
- username:
- state.credit_facade_credentials.type === "basic" && !state.credit_facade_credentials.username
- ? i18n.str`required`
- : undefined,
- password:
- state.credit_facade_credentials.type === "basic" && !state.credit_facade_credentials.password
- ? i18n.str`required`
- : undefined,
- }),
+ username:
+ state.credit_facade_credentials.type === "basic" &&
+ !state.credit_facade_credentials.username
+ ? i18n.str`required`
+ : undefined,
+ password:
+ state.credit_facade_credentials.type === "basic" &&
+ !state.credit_facade_credentials.password
+ ? i18n.str`required`
+ : undefined,
+ }),
credit_facade_url: !state.credit_facade_url
? undefined
- : !isValidURL(state.credit_facade_url) ? i18n.str`not valid url`
+ : !isValidURL(state.credit_facade_url)
+ ? i18n.str`not valid url`
+ : undefined,
+ repeatPassword: !state.credit_facade_credentials
+ ? undefined
+ : state.credit_facade_credentials.type === "basic" &&
+ (!state.credit_facade_credentials.password ||
+ state.credit_facade_credentials.password !== state.repeatPassword)
+ ? i18n.str`is not the same`
: undefined,
- repeatPassword:
- !state.credit_facade_credentials
- ? undefined
- : state.credit_facade_credentials.type === "basic" && (!state.credit_facade_credentials.password || state.credit_facade_credentials.password !== state.repeatPassword)
- ? i18n.str`is not the same`
- : undefined,
};
const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
+ (k) => (errors as Record<string, unknown>)[k] !== undefined,
);
const submitForm = () => {
if (hasErrors) return Promise.reject();
- const credit_facade_url = !state.credit_facade_url ? undefined : new URL("/", state.credit_facade_url).href
- const credit_facade_credentials: TalerMerchantApi.FacadeCredentials | undefined =
- credit_facade_url == undefined ? undefined :
- state.credit_facade_credentials?.type === "basic" ? {
- type: "basic",
- password: state.credit_facade_credentials.password,
- username: state.credit_facade_credentials.username,
- } : {
- type: "none"
- }
+ const credit_facade_url = !state.credit_facade_url
+ ? undefined
+ : new URL("/", state.credit_facade_url).href;
+ const credit_facade_credentials:
+ | TalerMerchantApi.FacadeCredentials
+ | undefined =
+ credit_facade_url == undefined
+ ? undefined
+ : state.credit_facade_credentials?.type === "basic"
+ ? {
+ type: "basic",
+ password: state.credit_facade_credentials.password,
+ username: state.credit_facade_credentials.username,
+ }
+ : {
+ type: "none",
+ };
return onCreate({
payto_uri: state.payto_uri!,
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
index 3d27b9a1a..96ca8bf5e 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
@@ -19,8 +19,22 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { TalerMerchantApi } from "@gnu-taler/taler-util";
-import { useMerchantApiContext, useTranslationContext } from "@gnu-taler/web-util/browser";
+import {
+ FacadeCredentials,
+ HttpStatusCode,
+ OperationFail,
+ OperationOk,
+ TalerError,
+ TalerMerchantApi,
+ TalerRevenueHttpClient,
+ assertUnreachable,
+ opFixedSuccess,
+} from "@gnu-taler/taler-util";
+import {
+ BrowserFetchHttpLib,
+ useMerchantApiContext,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { NotificationCard } from "../../../../components/menu/index.js";
@@ -45,10 +59,69 @@ export default function CreateValidator({ onConfirm, onBack }: Props): VNode {
<NotificationCard notification={notif} />
<CreatePage
onBack={onBack}
- onCreate={(request: Entity) => {
- return api.instance.addBankAccount(state.token, request)
+ onCreate={async (request: Entity) => {
+ const revenueAPI = !request.credit_facade_url
+ ? undefined
+ : new URL("/", request.credit_facade_url);
+
+ if (revenueAPI) {
+ const resp = await testRevenueAPI(
+ revenueAPI,
+ request.credit_facade_credentials,
+ );
+ if (resp.type === "fail") {
+ switch (resp.case) {
+ case "no-config": {
+ setNotif({
+ message: i18n.str`Could not create account`,
+ type: "ERROR",
+ description: i18n.str`The endpoint doesn't seems to be a Taler Revenue API`,
+ });
+ return;
+ }
+ case "client-bad-request": {
+ setNotif({
+ message: i18n.str`Could not create account`,
+ type: "ERROR",
+ description: i18n.str`Server replied with "bad request".`,
+ });
+ return;
+ }
+ case "unauthorized": {
+ setNotif({
+ message: i18n.str`Could not create account`,
+ type: "ERROR",
+ description: i18n.str`Unauthorized, try with another credentials.`,
+ });
+ return;
+ }
+ case "not-found": {
+ setNotif({
+ message: i18n.str`Could not create account`,
+ type: "ERROR",
+ description: i18n.str`Check facade URL, server replied with "not found".`,
+ });
+ return;
+ }
+ case "error": {
+ setNotif({
+ message: i18n.str`Could not create account`,
+ type: "ERROR",
+ description: resp.detail.hint,
+ });
+ return;
+ }
+ default: {
+ assertUnreachable(resp)
+ }
+ }
+ }
+ }
+
+ return api.instance
+ .addBankAccount(state.token, request)
.then(() => {
- onConfirm()
+ onConfirm();
})
.catch((error) => {
setNotif({
@@ -62,3 +135,93 @@ export default function CreateValidator({ onConfirm, onBack }: Props): VNode {
</>
);
}
+
+export async function testRevenueAPI(
+ revenueAPI: URL,
+ creds: FacadeCredentials | undefined,
+): Promise<
+ | OperationOk<void>
+ | OperationFail<"no-config">
+ | OperationFail<"client-bad-request">
+ | OperationFail<"unauthorized">
+ | OperationFail<"not-found">
+ | OperationFail<"error">
+> {
+ const api = new TalerRevenueHttpClient(
+ revenueAPI.href,
+ new BrowserFetchHttpLib(),
+ );
+ try {
+ const config = await api.getConfig();
+ if (config.type === "fail") {
+ return {
+ type: "fail",
+ case: "no-config",
+ detail: {
+ code: 1,
+ },
+ };
+ }
+ } catch (err) {
+ if (err instanceof TalerError) {
+ return {
+ type: "fail",
+ case: "error",
+ detail: err.errorDetail,
+ };
+ }
+ }
+ if (creds) {
+ const auth =
+ creds.type === "basic"
+ ? {
+ username: creds.username,
+ password: creds.password,
+ }
+ : undefined;
+
+ try {
+ const history = await api.getHistory(auth);
+ if (history.type === "fail") {
+ switch (history.case) {
+ case HttpStatusCode.BadRequest: {
+ return {
+ type: "fail",
+ case: "client-bad-request",
+ detail: {
+ code: 1,
+ },
+ };
+ }
+ case HttpStatusCode.Unauthorized: {
+ return {
+ type: "fail",
+ case: "unauthorized",
+ detail: {
+ code: 1,
+ },
+ };
+ }
+ case HttpStatusCode.NotFound: {
+ return {
+ type: "fail",
+ case: "not-found",
+ detail: {
+ code: 1,
+ },
+ };
+ }
+ }
+ }
+ } catch (err) {
+ if (err instanceof TalerError) {
+ return {
+ type: "fail",
+ case: "error",
+ detail: err.errorDetail,
+ };
+ }
+ }
+ }
+ return opFixedSuccess(undefined);
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
index 6b8af50a9..cf1d5b0c7 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
@@ -35,6 +35,7 @@ import { Notification } from "../../../../utils/types.js";
import { LoginPage } from "../../../login/index.js";
import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
import { UpdatePage } from "./UpdatePage.js";
+import { testRevenueAPI } from "../create/index.js";
export type Entity = TalerMerchantApi.AccountPatchDetails & WithId;
@@ -79,8 +80,65 @@ export default function UpdateValidator({
<UpdatePage
account={{ ...result.body, id: bid }}
onBack={onBack}
- onUpdate={(data) => {
- return api.instance.updateBankAccount(state.token, bid, data)
+ onUpdate={async (request) => {
+ const revenueAPI = !request.credit_facade_url
+ ? undefined
+ : new URL("/", request.credit_facade_url);
+
+ if (revenueAPI) {
+ const resp = await testRevenueAPI(
+ revenueAPI,
+ request.credit_facade_credentials,
+ );
+ if (resp.type === "fail") {
+ switch (resp.case) {
+ case "no-config": {
+ setNotif({
+ message: i18n.str`Could not create account`,
+ type: "ERROR",
+ description: i18n.str`The endpoint doesn't seems to be a Taler Revenue API`,
+ });
+ return;
+ }
+ case "client-bad-request": {
+ setNotif({
+ message: i18n.str`Could not create account`,
+ type: "ERROR",
+ description: i18n.str`Server replied with "bad request".`,
+ });
+ return;
+ }
+ case "unauthorized": {
+ setNotif({
+ message: i18n.str`Could not create account`,
+ type: "ERROR",
+ description: i18n.str`Unauthorized, try with another credentials.`,
+ });
+ return;
+ }
+ case "not-found": {
+ setNotif({
+ message: i18n.str`Could not create account`,
+ type: "ERROR",
+ description: i18n.str`Check facade URL, server replied with "not found".`,
+ });
+ return;
+ }
+ case "error": {
+ setNotif({
+ message: i18n.str`Could not create account`,
+ type: "ERROR",
+ description: resp.detail.hint,
+ });
+ return;
+ }
+ default: {
+ assertUnreachable(resp)
+ }
+ }
+ }
+ }
+ return api.instance.updateBankAccount(state.token, bid, request)
.then(onConfirm)
.catch((error) => {
setNotif({
diff --git a/packages/taler-util/src/http-client/bank-revenue.ts b/packages/taler-util/src/http-client/bank-revenue.ts
index d2f0c7000..b195e8c8f 100644
--- a/packages/taler-util/src/http-client/bank-revenue.ts
+++ b/packages/taler-util/src/http-client/bank-revenue.ts
@@ -14,9 +14,14 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { HttpRequestLibrary, makeBasicAuthHeader, readTalerErrorResponse } from "../http-common.js";
+import {
+ HttpRequestLibrary,
+ makeBasicAuthHeader,
+ readTalerErrorResponse,
+} from "../http-common.js";
import { HttpStatusCode } from "../http-status-codes.js";
import { createPlatformHttpLib } from "../http.js";
+import { LibtoolVersion } from "../libtool-version.js";
import {
FailCasesByMethod,
ResultByMethod,
@@ -27,7 +32,8 @@ import {
import {
LongPollParams,
PaginationParams,
- codecForMerchantIncomingHistory,
+ codecForRevenueConfig,
+ codecForRevenueIncomingHistory,
} from "./types.js";
import { addLongPollingParam, addPaginationParams } from "./utils.js";
@@ -47,50 +53,65 @@ export class TalerRevenueHttpClient {
constructor(
readonly baseUrl: string,
- readonly username: string,
httpClient?: HttpRequestLibrary,
) {
this.httpLib = httpClient ?? createPlatformHttpLib();
}
- // public readonly PROTOCOL_VERSION = "4:0:0";
- // isCompatible(version: string): boolean {
- // const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version)
- // return compare?.compatible ?? false
- // }
- // /**
- // * https://docs.taler.net/core/api-corebank.html#config
- // *
- // */
- // async getConfig() {
- // const url = new URL(`config`, this.baseUrl);
- // const resp = await this.httpLib.fetch(url.href, {
- // method: "GET"
- // });
- // switch (resp.status) {
- // case HttpStatusCode.Ok: return opSuccess(resp, codecForCoreBankConfig())
- // default: return opUnknownFailure(resp, await resp.text())
- // }
- // }
+ public readonly PROTOCOL_VERSION = "0:0:0";
+
+ isCompatible(version: string): boolean {
+ const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version);
+ return compare?.compatible ?? false;
+ }
+
+ /**
+ * https://docs.taler.net/core/api-bank-revenue.html#get--config
+ *
+ */
+ async getConfig() {
+ const url = new URL(`config`, this.baseUrl);
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "GET",
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(resp, codecForRevenueConfig());
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
/**
* https://docs.taler.net/core/api-bank-revenue.html#get--history
*
* @returns
*/
- async getHistory(auth: string, params?: PaginationParams & LongPollParams) {
+ async getHistory(
+ auth:
+ | {
+ username: string;
+ password: string;
+ }
+ | undefined,
+ params?: PaginationParams & LongPollParams,
+ ) {
const url = new URL(`history`, this.baseUrl);
addPaginationParams(url, params);
addLongPollingParam(url, params);
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers: {
- Authorization: makeBasicAuthHeader(this.username, auth),
+ Authorization: auth
+ ? makeBasicAuthHeader(auth.username, auth.password)
+ : undefined,
},
});
switch (resp.status) {
case HttpStatusCode.Ok:
- return opSuccessFromHttp(resp, codecForMerchantIncomingHistory());
+ return opSuccessFromHttp(resp, codecForRevenueIncomingHistory());
case HttpStatusCode.BadRequest:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Unauthorized:
diff --git a/packages/taler-util/src/http-client/types.ts b/packages/taler-util/src/http-client/types.ts
index ea7ba341b..72189cf0a 100644
--- a/packages/taler-util/src/http-client/types.ts
+++ b/packages/taler-util/src/http-client/types.ts
@@ -1218,26 +1218,33 @@ export const codecForBankWithdrawalOperationPostResponse =
.property("confirm_transfer_url", codecOptional(codecForURL()))
.build("TalerBankIntegrationApi.BankWithdrawalOperationPostResponse");
-export const codecForMerchantIncomingHistory =
- (): Codec<TalerRevenueApi.MerchantIncomingHistory> =>
- buildCodecForObject<TalerRevenueApi.MerchantIncomingHistory>()
+export const codecForRevenueConfig = (): Codec<TalerRevenueApi.RevenueConfig> =>
+ buildCodecForObject<TalerRevenueApi.RevenueConfig>()
+ .property("name", codecForConstString("taler-revenue"))
+ .property("version", codecForString())
+ .property("currency", codecForString())
+ .property("implementation", codecOptional(codecForString()))
+ .build("TalerRevenueApi.RevenueConfig");
+
+export const codecForRevenueIncomingHistory =
+ (): Codec<TalerRevenueApi.RevenueIncomingHistory> =>
+ buildCodecForObject<TalerRevenueApi.RevenueIncomingHistory>()
.property("credit_account", codecForPaytoString())
.property(
"incoming_transactions",
- codecForList(codecForMerchantIncomingBankTransaction()),
+ codecForList(codecForRevenueIncomingBankTransaction()),
)
.build("TalerRevenueApi.MerchantIncomingHistory");
-export const codecForMerchantIncomingBankTransaction =
- (): Codec<TalerRevenueApi.MerchantIncomingBankTransaction> =>
- buildCodecForObject<TalerRevenueApi.MerchantIncomingBankTransaction>()
- .property("row_id", codecForNumber())
- .property("date", codecForTimestamp)
+export const codecForRevenueIncomingBankTransaction =
+ (): Codec<TalerRevenueApi.RevenueIncomingBankTransaction> =>
+ buildCodecForObject<TalerRevenueApi.RevenueIncomingBankTransaction>()
.property("amount", codecForAmountString())
+ .property("date", codecForTimestamp)
.property("debit_account", codecForPaytoString())
- .property("exchange_url", codecForURL())
- .property("wtid", codecForString())
- .build("TalerRevenueApi.MerchantIncomingBankTransaction");
+ .property("row_id", codecForNumber())
+ .property("subject", codecForString())
+ .build("TalerRevenueApi.RevenueIncomingBankTransaction");
export const codecForTransferResponse =
(): Codec<TalerWireGatewayApi.TransferResponse> =>
@@ -1699,18 +1706,34 @@ export namespace TalerWireGatewayApi {
}
export namespace TalerRevenueApi {
- export interface MerchantIncomingHistory {
+ export interface RevenueConfig {
+ // Name of the API.
+ name: "taler-revenue";
+
+ // libtool-style representation of the Bank protocol version, see
+ // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+ // The format is "current:revision:age".
+ version: string;
+
+ // Currency used by this gateway.
+ currency: string;
+
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since v0, may become mandatory in the future.
+ implementation?: string;
+ }
+
+ export interface RevenueIncomingHistory {
// Array of incoming transactions.
- incoming_transactions: MerchantIncomingBankTransaction[];
+ incoming_transactions: RevenueIncomingBankTransaction[];
// Payto URI to identify the receiver of funds.
- // This must be one of the merchant's bank accounts.
// Credit account is shared by all incoming transactions
// as per the nature of the request.
- credit_account: PaytoString;
+ credit_account: string;
}
- export interface MerchantIncomingBankTransaction {
+ export interface RevenueIncomingBankTransaction {
// Opaque identifier of the returned record.
row_id: SafeUint64;
@@ -1721,13 +1744,10 @@ export namespace TalerRevenueApi {
amount: AmountString;
// Payto URI to identify the sender of funds.
- debit_account: PaytoString;
+ debit_account: string;
- // Base URL of the exchange where the transfer originated form.
- exchange_url: string;
-
- // The wire transfer identifier.
- wtid: WireTransferIdentifierRawP;
+ // The wire transfer subject.
+ subject: string;
}
}