-missing files
This commit is contained in:
parent
324d9f871c
commit
07d71eb297
334
packages/taler-util/src/MerchantApiClient.ts
Normal file
334
packages/taler-util/src/MerchantApiClient.ts
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2023 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import {
|
||||
createPlatformHttpLib,
|
||||
expectSuccessResponseOrThrow,
|
||||
readSuccessResponseJsonOrThrow,
|
||||
} from "./http.js";
|
||||
import { FacadeCredentials } from "./libeufin-api-types.js";
|
||||
import { Logger } from "./logging.js";
|
||||
import {
|
||||
MerchantReserveCreateConfirmation,
|
||||
codecForMerchantReserveCreateConfirmation,
|
||||
TippingReserveStatus,
|
||||
MerchantInstancesResponse,
|
||||
MerchantPostOrderRequest,
|
||||
MerchantPostOrderResponse,
|
||||
codecForMerchantPostOrderResponse,
|
||||
MerchantOrderPrivateStatusResponse,
|
||||
codecForMerchantOrderPrivateStatusResponse,
|
||||
RewardCreateRequest,
|
||||
RewardCreateConfirmation,
|
||||
MerchantTemplateAddDetails,
|
||||
} from "./merchant-api-types.js";
|
||||
import { AmountString } from "./taler-types.js";
|
||||
import { TalerProtocolDuration } from "./time.js";
|
||||
|
||||
const logger = new Logger("MerchantApiClient.ts");
|
||||
|
||||
export interface MerchantAuthConfiguration {
|
||||
method: "external" | "token";
|
||||
token?: string;
|
||||
}
|
||||
|
||||
// FIXME: Why do we need this? Describe / fix!
|
||||
export interface PartialMerchantInstanceConfig {
|
||||
auth?: MerchantAuthConfiguration;
|
||||
id: string;
|
||||
name: string;
|
||||
paytoUris: string[];
|
||||
address?: unknown;
|
||||
jurisdiction?: unknown;
|
||||
defaultWireTransferDelay?: TalerProtocolDuration;
|
||||
defaultPayDelay?: TalerProtocolDuration;
|
||||
}
|
||||
|
||||
export interface CreateMerchantTippingReserveRequest {
|
||||
// Amount that the merchant promises to put into the reserve
|
||||
initial_balance: AmountString;
|
||||
|
||||
// Exchange the merchant intends to use for tipping
|
||||
exchange_url: string;
|
||||
|
||||
// Desired wire method, for example "iban" or "x-taler-bank"
|
||||
wire_method: string;
|
||||
}
|
||||
|
||||
export interface DeleteTippingReserveArgs {
|
||||
reservePub: string;
|
||||
purge?: boolean;
|
||||
}
|
||||
|
||||
export interface MerchantInstanceConfig {
|
||||
accounts: MerchantBankAccount[];
|
||||
auth: MerchantAuthConfiguration;
|
||||
id: string;
|
||||
name: string;
|
||||
address: unknown;
|
||||
jurisdiction: unknown;
|
||||
use_stefan: boolean;
|
||||
default_wire_transfer_delay: TalerProtocolDuration;
|
||||
default_pay_delay: TalerProtocolDuration;
|
||||
}
|
||||
|
||||
interface MerchantBankAccount {
|
||||
// The payto:// URI where the wallet will send coins.
|
||||
payto_uri: string;
|
||||
|
||||
// Optional base URL for a facade where the
|
||||
// merchant backend can see incoming wire
|
||||
// transfers to reconcile its accounting
|
||||
// with that of the exchange. Used by
|
||||
// taler-merchant-wirewatch.
|
||||
credit_facade_url?: string;
|
||||
|
||||
// Credentials for accessing the credit facade.
|
||||
credit_facade_credentials?: FacadeCredentials;
|
||||
}
|
||||
|
||||
export interface MerchantInstanceConfig {
|
||||
accounts: MerchantBankAccount[];
|
||||
auth: MerchantAuthConfiguration;
|
||||
id: string;
|
||||
name: string;
|
||||
address: unknown;
|
||||
jurisdiction: unknown;
|
||||
use_stefan: boolean;
|
||||
default_wire_transfer_delay: TalerProtocolDuration;
|
||||
default_pay_delay: TalerProtocolDuration;
|
||||
}
|
||||
|
||||
export interface PrivateOrderStatusQuery {
|
||||
instance?: string;
|
||||
orderId: string;
|
||||
sessionId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client for the GNU Taler merchant backend.
|
||||
*/
|
||||
export class MerchantApiClient {
|
||||
/**
|
||||
* Base URL for the particular instance that this merchant API client
|
||||
* is for.
|
||||
*/
|
||||
private baseUrl: string;
|
||||
|
||||
readonly auth: MerchantAuthConfiguration;
|
||||
|
||||
constructor(baseUrl: string, auth?: MerchantAuthConfiguration) {
|
||||
this.baseUrl = baseUrl;
|
||||
|
||||
this.auth = auth ?? {
|
||||
method: "external",
|
||||
};
|
||||
}
|
||||
|
||||
httpClient = createPlatformHttpLib({
|
||||
allowHttp: true,
|
||||
enableThrottling: false,
|
||||
});
|
||||
|
||||
async changeAuth(auth: MerchantAuthConfiguration): Promise<void> {
|
||||
const url = new URL("private/auth", this.baseUrl);
|
||||
const res = await this.httpClient.fetch(url.href, {
|
||||
method: "POST",
|
||||
body: auth,
|
||||
headers: this.makeAuthHeader(),
|
||||
});
|
||||
await expectSuccessResponseOrThrow(res);
|
||||
}
|
||||
|
||||
async deleteTippingReserve(req: DeleteTippingReserveArgs): Promise<void> {
|
||||
const url = new URL(`private/reserves/${req.reservePub}`, this.baseUrl);
|
||||
if (req.purge) {
|
||||
url.searchParams.set("purge", "YES");
|
||||
}
|
||||
const resp = await this.httpClient.fetch(url.href, {
|
||||
method: "DELETE",
|
||||
headers: this.makeAuthHeader(),
|
||||
});
|
||||
logger.info(`delete status: ${resp.status}`);
|
||||
return;
|
||||
}
|
||||
|
||||
async createTippingReserve(
|
||||
req: CreateMerchantTippingReserveRequest,
|
||||
): Promise<MerchantReserveCreateConfirmation> {
|
||||
const url = new URL("private/reserves", this.baseUrl);
|
||||
const resp = await this.httpClient.fetch(url.href, {
|
||||
method: "POST",
|
||||
body: req,
|
||||
headers: this.makeAuthHeader(),
|
||||
});
|
||||
const respData = readSuccessResponseJsonOrThrow(
|
||||
resp,
|
||||
codecForMerchantReserveCreateConfirmation(),
|
||||
);
|
||||
return respData;
|
||||
}
|
||||
|
||||
async getPrivateInstanceInfo(): Promise<any> {
|
||||
const url = new URL("private", this.baseUrl);
|
||||
const resp = await this.httpClient.fetch(url.href, {
|
||||
method: "GET",
|
||||
headers: this.makeAuthHeader(),
|
||||
});
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
async getPrivateTipReserves(): Promise<TippingReserveStatus> {
|
||||
const url = new URL("private/reserves", this.baseUrl);
|
||||
const resp = await this.httpClient.fetch(url.href, {
|
||||
method: "GET",
|
||||
headers: this.makeAuthHeader(),
|
||||
});
|
||||
// FIXME: Validate!
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
async deleteInstance(instanceId: string) {
|
||||
const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
|
||||
const resp = await this.httpClient.fetch(url.href, {
|
||||
method: "DELETE",
|
||||
headers: this.makeAuthHeader(),
|
||||
});
|
||||
await expectSuccessResponseOrThrow(resp);
|
||||
}
|
||||
|
||||
async createInstance(req: MerchantInstanceConfig): Promise<void> {
|
||||
const url = new URL("management/instances", this.baseUrl);
|
||||
await this.httpClient.fetch(url.href, {
|
||||
method: "POST",
|
||||
body: req,
|
||||
headers: this.makeAuthHeader(),
|
||||
});
|
||||
}
|
||||
|
||||
async getInstances(): Promise<MerchantInstancesResponse> {
|
||||
const url = new URL("management/instances", this.baseUrl);
|
||||
const resp = await this.httpClient.fetch(url.href, {
|
||||
headers: this.makeAuthHeader(),
|
||||
});
|
||||
return resp.json();
|
||||
}
|
||||
|
||||
async getInstanceFullDetails(instanceId: string): Promise<any> {
|
||||
const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
|
||||
try {
|
||||
const resp = await this.httpClient.fetch(url.href, {
|
||||
headers: this.makeAuthHeader(),
|
||||
});
|
||||
return resp.json();
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async createOrder(
|
||||
req: MerchantPostOrderRequest,
|
||||
): Promise<MerchantPostOrderResponse> {
|
||||
let url = new URL("private/orders", this.baseUrl);
|
||||
const resp = await this.httpClient.fetch(url.href, {
|
||||
method: "POST",
|
||||
body: req,
|
||||
headers: this.makeAuthHeader(),
|
||||
});
|
||||
return readSuccessResponseJsonOrThrow(
|
||||
resp,
|
||||
codecForMerchantPostOrderResponse(),
|
||||
);
|
||||
}
|
||||
|
||||
async queryPrivateOrderStatus(
|
||||
query: PrivateOrderStatusQuery,
|
||||
): Promise<MerchantOrderPrivateStatusResponse> {
|
||||
const reqUrl = new URL(`private/orders/${query.orderId}`, this.baseUrl);
|
||||
if (query.sessionId) {
|
||||
reqUrl.searchParams.set("session_id", query.sessionId);
|
||||
}
|
||||
const resp = await this.httpClient.fetch(reqUrl.href, {
|
||||
headers: this.makeAuthHeader(),
|
||||
});
|
||||
return readSuccessResponseJsonOrThrow(
|
||||
resp,
|
||||
codecForMerchantOrderPrivateStatusResponse(),
|
||||
);
|
||||
}
|
||||
|
||||
async giveTip(req: RewardCreateRequest): Promise<RewardCreateConfirmation> {
|
||||
const reqUrl = new URL(`private/tips`, this.baseUrl);
|
||||
const resp = await this.httpClient.fetch(reqUrl.href, {
|
||||
method: "POST",
|
||||
body: req,
|
||||
});
|
||||
// FIXME: validate
|
||||
return resp.json();
|
||||
}
|
||||
|
||||
async queryTippingReserves(): Promise<TippingReserveStatus> {
|
||||
const reqUrl = new URL(`private/reserves`, this.baseUrl);
|
||||
const resp = await this.httpClient.fetch(reqUrl.href, {
|
||||
headers: this.makeAuthHeader(),
|
||||
});
|
||||
// FIXME: validate
|
||||
return resp.json();
|
||||
}
|
||||
|
||||
async giveRefund(r: {
|
||||
instance: string;
|
||||
orderId: string;
|
||||
amount: string;
|
||||
justification: string;
|
||||
}): Promise<{ talerRefundUri: string }> {
|
||||
const reqUrl = new URL(`private/orders/${r.orderId}/refund`, this.baseUrl);
|
||||
const resp = await this.httpClient.fetch(reqUrl.href, {
|
||||
method: "POST",
|
||||
body: {
|
||||
refund: r.amount,
|
||||
reason: r.justification,
|
||||
},
|
||||
});
|
||||
const respBody = await resp.json();
|
||||
return {
|
||||
talerRefundUri: respBody.taler_refund_uri,
|
||||
};
|
||||
}
|
||||
|
||||
async createTemplate(req: MerchantTemplateAddDetails) {
|
||||
let url = new URL("private/templates", this.baseUrl);
|
||||
const resp = await this.httpClient.fetch(url.href, {
|
||||
method: "POST",
|
||||
body: req,
|
||||
headers: this.makeAuthHeader(),
|
||||
});
|
||||
if (resp.status !== 204) {
|
||||
throw Error("failed to create template");
|
||||
}
|
||||
}
|
||||
|
||||
private makeAuthHeader(): Record<string, string> {
|
||||
switch (this.auth.method) {
|
||||
case "external":
|
||||
return {};
|
||||
case "token":
|
||||
return {
|
||||
Authorization: `Bearer ${this.auth.token}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
31
packages/taler-util/src/libeufin-api-types.ts
Normal file
31
packages/taler-util/src/libeufin-api-types.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2023 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
export type FacadeCredentials =
|
||||
| NoFacadeCredentials
|
||||
| BasicAuthFacadeCredentials;
|
||||
export interface NoFacadeCredentials {
|
||||
type: "none";
|
||||
}
|
||||
export interface BasicAuthFacadeCredentials {
|
||||
type: "basic";
|
||||
|
||||
// Username to use to authenticate
|
||||
username: string;
|
||||
|
||||
// Password to use to authenticate
|
||||
password: string;
|
||||
}
|
Loading…
Reference in New Issue
Block a user