/*
This file is part of GNU Taler
(C) 2022-2024 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
*/
import {
AccessToken,
FailCasesByMethod,
HttpStatusCode,
LibtoolVersion,
PaginationParams,
ResultByMethod,
TalerMerchantApi,
codecForAbortResponse,
codecForAccountAddResponse,
codecForAccountKycRedirects,
codecForAccountsSummaryResponse,
codecForBankAccountEntry,
codecForClaimResponse,
codecForInstancesResponse,
codecForInventorySummaryResponse,
codecForMerchantConfig,
codecForMerchantOrderPrivateStatusResponse,
codecForMerchantPosProductDetail,
codecForMerchantRefundResponse,
codecForOrderHistory,
codecForOtpDeviceDetails,
codecForOtpDeviceSummaryResponse,
codecForOutOfStockResponse,
codecForPaidRefundStatusResponse,
codecForPaymentResponse,
codecForPostOrderResponse,
codecForProductDetail,
codecForQueryInstancesResponse,
codecForStatusGoto,
codecForStatusPaid,
codecForStatusStatusUnpaid,
codecForTansferList,
codecForTemplateDetails,
codecForTemplateSummaryResponse,
codecForTokenFamiliesList,
codecForTokenFamilyDetails,
codecForWalletRefundResponse,
codecForWalletTemplateDetails,
codecForWebhookDetails,
codecForWebhookSummaryResponse,
opEmptySuccess,
opKnownAlternativeFailure,
opKnownHttpFailure,
} from "@gnu-taler/taler-util";
import {
HttpRequestLibrary,
HttpResponse,
createPlatformHttpLib,
readTalerErrorResponse,
} from "@gnu-taler/taler-util/http";
import { opSuccessFromHttp, opUnknownFailure } from "../operation.js";
import {
CacheEvictor,
addMerchantPaginationParams,
makeBearerTokenAuthHeader,
nullEvictor,
} from "./utils.js";
export type TalerMerchantInstanceResultByMethod<
prop extends keyof TalerMerchantInstanceHttpClient,
> = ResultByMethod;
export type TalerMerchantInstanceErrorsByMethod<
prop extends keyof TalerMerchantInstanceHttpClient,
> = FailCasesByMethod;
export enum TalerMerchantInstanceCacheEviction {
CREATE_ORDER,
UPDATE_ORDER,
DELETE_ORDER,
UPDATE_CURRENT_INSTANCE,
DELETE_CURRENT_INSTANCE,
CREATE_BANK_ACCOUNT,
UPDATE_BANK_ACCOUNT,
DELETE_BANK_ACCOUNT,
CREATE_PRODUCT,
UPDATE_PRODUCT,
DELETE_PRODUCT,
CREATE_TRANSFER,
DELETE_TRANSFER,
CREATE_DEVICE,
UPDATE_DEVICE,
DELETE_DEVICE,
CREATE_TEMPLATE,
UPDATE_TEMPLATE,
DELETE_TEMPLATE,
CREATE_WEBHOOK,
UPDATE_WEBHOOK,
DELETE_WEBHOOK,
CREATE_TOKENFAMILY,
UPDATE_TOKENFAMILY,
DELETE_TOKENFAMILY,
LAST,
}
export enum TalerMerchantManagementCacheEviction {
CREATE_INSTANCE = TalerMerchantInstanceCacheEviction.LAST + 1,
UPDATE_INSTANCE,
DELETE_INSTANCE,
}
/**
* Protocol version spoken with the core bank.
*
* Endpoint must be ordered in the same way that in the docs
* Response code (http and taler) must have the same order that in the docs
* That way is easier to see changes
*
* Uses libtool's current:revision:age versioning.
*/
export class TalerMerchantInstanceHttpClient {
public readonly PROTOCOL_VERSION = "15:0:0";
readonly httpLib: HttpRequestLibrary;
readonly cacheEvictor: CacheEvictor;
constructor(
readonly baseUrl: string,
httpClient?: HttpRequestLibrary,
cacheEvictor?: CacheEvictor,
) {
this.httpLib = httpClient ?? createPlatformHttpLib();
this.cacheEvictor = cacheEvictor ?? nullEvictor;
}
isCompatible(version: string): boolean {
const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version);
return compare?.compatible ?? false;
}
/**
* https://docs.taler.net/core/api-merchant.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, codecForMerchantConfig());
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
//
// Wallet API
//
/**
* https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-orders-$ORDER_ID-claim
*/
async claimOrder(orderId: string, body: TalerMerchantApi.ClaimRequest) {
const url = new URL(`orders/${orderId}/claim`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
});
switch (resp.status) {
case HttpStatusCode.Ok: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_ORDER,
);
return opSuccessFromHttp(resp, codecForClaimResponse());
}
case HttpStatusCode.Conflict:
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-merchant.html#post-[-instances-$INSTANCE]-orders-$ORDER_ID-pay
*/
async makePayment(orderId: string, body: TalerMerchantApi.PayRequest) {
const url = new URL(`orders/${orderId}/pay`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
});
switch (resp.status) {
case HttpStatusCode.Ok: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_ORDER,
);
return opSuccessFromHttp(resp, codecForPaymentResponse());
}
case HttpStatusCode.BadRequest:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.PaymentRequired:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Forbidden:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.RequestTimeout:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Gone:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.PreconditionFailed:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.BadGateway:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.GatewayTimeout:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-orders-$ORDER_ID
*/
async getPaymentStatus(
orderId: string,
params: TalerMerchantApi.PaymentStatusRequestParams = {},
) {
const url = new URL(`orders/${orderId}`, this.baseUrl);
if (params.allowRefundedForRepurchase !== undefined) {
url.searchParams.set(
"allow_refunded_for_repurchase",
params.allowRefundedForRepurchase ? "YES" : "NO",
);
}
if (params.awaitRefundObtained !== undefined) {
url.searchParams.set(
"await_refund_obtained",
params.allowRefundedForRepurchase ? "YES" : "NO",
);
}
if (params.claimToken !== undefined) {
url.searchParams.set("token", params.claimToken);
}
if (params.contractTermHash !== undefined) {
url.searchParams.set("h_contract", params.contractTermHash);
}
if (params.refund !== undefined) {
url.searchParams.set("refund", params.refund);
}
if (params.sessionId !== undefined) {
url.searchParams.set("session_id", params.sessionId);
}
if (params.timeout !== undefined) {
url.searchParams.set("timeout_ms", String(params.timeout));
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
// body,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForStatusPaid());
case HttpStatusCode.Accepted:
return opSuccessFromHttp(resp, codecForStatusGoto());
// case HttpStatusCode.Found: not possible since content is not HTML
case HttpStatusCode.PaymentRequired:
return opSuccessFromHttp(resp, codecForStatusStatusUnpaid());
case HttpStatusCode.Forbidden:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotAcceptable:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#demonstrating-payment
*/
async demostratePayment(orderId: string, body: TalerMerchantApi.PaidRequest) {
const url = new URL(`orders/${orderId}/paid`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
});
switch (resp.status) {
case HttpStatusCode.Ok: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_ORDER,
);
return opSuccessFromHttp(resp, codecForPaidRefundStatusResponse());
}
case HttpStatusCode.BadRequest:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Forbidden:
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-merchant.html#aborting-incomplete-payments
*/
async abortIncompletePayment(
orderId: string,
body: TalerMerchantApi.AbortRequest,
) {
const url = new URL(`orders/${orderId}/abort`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
});
switch (resp.status) {
case HttpStatusCode.Ok: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_ORDER,
);
return opSuccessFromHttp(resp, codecForAbortResponse());
}
case HttpStatusCode.BadRequest:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Forbidden:
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-merchant.html#obtaining-refunds
*/
async obtainRefund(
orderId: string,
body: TalerMerchantApi.WalletRefundRequest,
) {
const url = new URL(`orders/${orderId}/refund`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
});
switch (resp.status) {
case HttpStatusCode.Ok: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_ORDER,
);
return opSuccessFromHttp(resp, codecForWalletRefundResponse());
}
case HttpStatusCode.BadRequest:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Forbidden:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
//
// Management
//
/**
* https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-auth
*/
async updateCurrentInstanceAuthentication(
token: AccessToken | undefined,
body: TalerMerchantApi.InstanceAuthConfigurationMessage,
) {
const url = new URL(`private/auth`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok: // FIXME: missing in docs
return opEmptySuccess(resp);
case HttpStatusCode.NoContent:
return opEmptySuccess(resp);
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#patch-[-instances-$INSTANCE]-private
*/
async updateCurrentInstance(
token: AccessToken | undefined,
body: TalerMerchantApi.InstanceReconfigurationMessage,
) {
const url = new URL(`private`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "PATCH",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_CURRENT_INSTANCE,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#get-[-instances-$INSTANCE]-private
*
*/
async getCurrentInstanceDetails(token: AccessToken) {
const url = new URL(`private`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForQueryInstancesResponse());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private
*/
async deleteCurrentInstance(
token: AccessToken | undefined,
params: { purge?: boolean } = {},
) {
const url = new URL(`private`, this.baseUrl);
if (params.purge !== undefined) {
url.searchParams.set("purge", params.purge ? "YES" : "NO");
}
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "DELETE",
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.DELETE_CURRENT_INSTANCE,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#get--instances-$INSTANCE-private-kyc
*/
async getCurrentIntanceKycStatus(
token: AccessToken | undefined,
params: TalerMerchantApi.GetKycStatusRequestParams = {},
) {
const url = new URL(`private/kyc`, this.baseUrl);
if (params.wireHash) {
url.searchParams.set("h_wire", params.wireHash);
}
if (params.exchangeURL) {
url.searchParams.set("exchange_url", params.exchangeURL);
}
if (params.timeout) {
url.searchParams.set("timeout_ms", String(params.timeout));
}
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Accepted:
return opSuccessFromHttp(resp, codecForAccountKycRedirects());
case HttpStatusCode.NoContent:
return opEmptySuccess(resp);
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.BadGateway:
return opKnownAlternativeFailure(
resp,
resp.status,
codecForAccountKycRedirects(),
);
case HttpStatusCode.ServiceUnavailable:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.GatewayTimeout:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
//
// Bank Accounts
//
/**
* https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-accounts
*/
async addBankAccount(
token: AccessToken | undefined,
body: TalerMerchantApi.AccountAddDetails,
) {
const url = new URL(`private/accounts`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.CREATE_BANK_ACCOUNT,
);
return opSuccessFromHttp(resp, codecForAccountAddResponse());
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private-accounts-$H_WIRE
*/
async updateBankAccount(
token: AccessToken | undefined,
wireAccount: string,
body: TalerMerchantApi.AccountPatchDetails,
) {
const url = new URL(`private/accounts/${wireAccount}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "PATCH",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_BANK_ACCOUNT,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#get-[-instances-$INSTANCE]-private-accounts
*/
async listBankAccounts(token: AccessToken, params?: PaginationParams) {
const url = new URL(`private/accounts`, this.baseUrl);
// addMerchantPaginationParams(url, params);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForAccountsSummaryResponse());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#get-[-instances-$INSTANCE]-private-accounts-$H_WIRE
*/
async getBankAccountDetails(
token: AccessToken | undefined,
wireAccount: string,
) {
const url = new URL(`private/accounts/${wireAccount}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForBankAccountEntry());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#delete-[-instances-$INSTANCE]-private-accounts-$H_WIRE
*/
async deleteBankAccount(token: AccessToken | undefined, wireAccount: string) {
const url = new URL(`private/accounts/${wireAccount}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "DELETE",
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.DELETE_BANK_ACCOUNT,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
//
// Inventory Management
//
/**
* https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-products
*/
async addProduct(
token: AccessToken | undefined,
body: TalerMerchantApi.ProductAddDetail,
) {
const url = new URL(`private/products`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.CREATE_PRODUCT,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private-products-$PRODUCT_ID
*/
async updateProduct(
token: AccessToken | undefined,
productId: string,
body: TalerMerchantApi.ProductPatchDetail,
) {
const url = new URL(`private/products/${productId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "PATCH",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_PRODUCT,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-products
*/
async listProducts(
token: AccessToken | undefined,
params?: PaginationParams,
) {
const url = new URL(`private/products`, this.baseUrl);
addMerchantPaginationParams(url, params);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForInventorySummaryResponse());
case HttpStatusCode.Unauthorized: // FIXME: not in docs
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-merchant.html#get-[-instances-$INSTANCE]-private-pos
*/
async getPointOfSaleInventory(token: AccessToken | undefined) {
const url = new URL(`private/pos`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForMerchantPosProductDetail());
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-products-$PRODUCT_ID
*/
async getProductDetails(token: AccessToken | undefined, productId: string) {
const url = new URL(`private/products/${productId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForProductDetail());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#reserving-inventory
*/
async lockProduct(
token: AccessToken | undefined,
productId: string,
body: TalerMerchantApi.LockRequest,
) {
const url = new URL(`private/products/${productId}/lock`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_PRODUCT,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Gone:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#removing-products-from-inventory
*/
async deleteProduct(token: AccessToken | undefined, productId: string) {
const url = new URL(`private/products/${productId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "DELETE",
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.DELETE_PRODUCT,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
//
// Payment processing
//
/**
* https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-orders
*/
async createOrder(
token: AccessToken | undefined,
body: TalerMerchantApi.PostOrderRequest,
) {
const url = new URL(`private/orders`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
return this.procesOrderCreationResponse(resp);
}
private async procesOrderCreationResponse(resp: HttpResponse) {
switch (resp.status) {
case HttpStatusCode.Ok: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.CREATE_ORDER,
);
return opSuccessFromHttp(resp, codecForPostOrderResponse());
}
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Gone:
return opKnownAlternativeFailure(
resp,
resp.status,
codecForOutOfStockResponse(),
);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#inspecting-orders
*/
async listOrders(
token: AccessToken | undefined,
params: TalerMerchantApi.ListOrdersRequestParams = {},
) {
const url = new URL(`private/orders`, this.baseUrl);
if (params.date) {
url.searchParams.set("date_s", String(params.date));
}
if (params.fulfillmentUrl) {
url.searchParams.set("fulfillment_url", params.fulfillmentUrl);
}
if (params.paid !== undefined) {
url.searchParams.set("paid", params.paid ? "YES" : "NO");
}
if (params.refunded !== undefined) {
url.searchParams.set("refunded", params.refunded ? "YES" : "NO");
}
if (params.sessionId) {
url.searchParams.set("session_id", params.sessionId);
}
if (params.timeout) {
url.searchParams.set("timeout", String(params.timeout));
}
if (params.wired !== undefined) {
url.searchParams.set("wired", params.wired ? "YES" : "NO");
}
addMerchantPaginationParams(url, params);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForOrderHistory());
case HttpStatusCode.NotFound: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-orders-$ORDER_ID
*/
async getOrderDetails(
token: AccessToken | undefined,
orderId: string,
params: TalerMerchantApi.GetOrderRequestParams = {},
) {
const url = new URL(`private/orders/${orderId}`, this.baseUrl);
if (params.allowRefundedForRepurchase !== undefined) {
url.searchParams.set(
"allow_refunded_for_repurchase",
params.allowRefundedForRepurchase ? "YES" : "NO",
);
}
if (params.sessionId) {
url.searchParams.set("session_id", params.sessionId);
}
if (params.timeout) {
url.searchParams.set("timeout_ms", String(params.timeout));
}
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(
resp,
codecForMerchantOrderPrivateStatusResponse(),
);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.BadGateway:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.GatewayTimeout:
return opKnownAlternativeFailure(
resp,
resp.status,
codecForOutOfStockResponse(),
);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#private-order-data-cleanup
*/
async forgetOrder(
token: AccessToken | undefined,
orderId: string,
body: TalerMerchantApi.ForgetRequest,
) {
const url = new URL(`private/orders/${orderId}/forget`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "PATCH",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_ORDER,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.NoContent:
return opEmptySuccess(resp);
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.BadRequest:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-orders-$ORDER_ID
*/
async deleteOrder(token: AccessToken | undefined, orderId: string) {
const url = new URL(`private/orders/${orderId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "DELETE",
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.DELETE_ORDER,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
//
// Refunds
//
/**
* https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-orders-$ORDER_ID-refund
*/
async addRefund(
token: AccessToken | undefined,
orderId: string,
body: TalerMerchantApi.RefundRequest,
) {
const url = new URL(`private/orders/${orderId}/refund`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_ORDER,
);
return opSuccessFromHttp(resp, codecForMerchantRefundResponse());
}
case HttpStatusCode.Forbidden:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Gone:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
//
// Wire Transfer
//
/**
* https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-transfers
*/
async informWireTransfer(
token: AccessToken | undefined,
body: TalerMerchantApi.TransferInformation,
) {
const url = new URL(`private/transfers`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.CREATE_TRANSFER,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-transfers
*/
async listWireTransfers(
token: AccessToken | undefined,
params: TalerMerchantApi.ListWireTransferRequestParams = {},
) {
const url = new URL(`private/transfers`, this.baseUrl);
if (params.after) {
url.searchParams.set("after", String(params.after));
}
if (params.before) {
url.searchParams.set("before", String(params.before));
}
if (params.paytoURI) {
url.searchParams.set("payto_uri", params.paytoURI);
}
if (params.verified !== undefined) {
url.searchParams.set("verified", params.verified ? "YES" : "NO");
}
addMerchantPaginationParams(url, params);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForTansferList());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-transfers-$TID
*/
async deleteWireTransfer(token: AccessToken | undefined, transferId: string) {
const url = new URL(`private/transfers/${transferId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "DELETE",
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.DELETE_TRANSFER,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
//
// OTP Devices
//
/**
* https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-otp-devices
*/
async addOtpDevice(
token: AccessToken | undefined,
body: TalerMerchantApi.OtpDeviceAddDetails,
) {
const url = new URL(`private/otp-devices`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.CREATE_DEVICE,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#patch-[-instances-$INSTANCE]-private-otp-devices-$DEVICE_ID
*/
async updateOtpDevice(
token: AccessToken | undefined,
deviceId: string,
body: TalerMerchantApi.OtpDevicePatchDetails,
) {
const url = new URL(`private/otp-devices/${deviceId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "PATCH",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_DEVICE,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-otp-devices
*/
async listOtpDevices(
token: AccessToken | undefined,
params?: PaginationParams,
) {
const url = new URL(`private/otp-devices`, this.baseUrl);
addMerchantPaginationParams(url, params);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForOtpDeviceSummaryResponse());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#get-[-instances-$INSTANCE]-private-otp-devices-$DEVICE_ID
*/
async getOtpDeviceDetails(
token: AccessToken | undefined,
deviceId: string,
params: TalerMerchantApi.GetOtpDeviceRequestParams = {},
) {
const url = new URL(`private/otp-devices/${deviceId}`, this.baseUrl);
if (params.faketime) {
url.searchParams.set("faketime", String(params.faketime));
}
if (params.price) {
url.searchParams.set("price", params.price);
}
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForOtpDeviceDetails());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#delete-[-instances-$INSTANCE]-private-otp-devices-$DEVICE_ID
*/
async deleteOtpDevice(token: AccessToken | undefined, deviceId: string) {
const url = new URL(`private/otp-devices/${deviceId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "DELETE",
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.DELETE_DEVICE,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
//
// Templates
//
/**
* https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-templates
*/
async addTemplate(
token: AccessToken | undefined,
body: TalerMerchantApi.TemplateAddDetails,
) {
const url = new URL(`private/templates`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.CREATE_TEMPLATE,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#patch-[-instances-$INSTANCE]-private-templates-$TEMPLATE_ID
*/
async updateTemplate(
token: AccessToken | undefined,
templateId: string,
body: TalerMerchantApi.TemplatePatchDetails,
) {
const url = new URL(`private/templates/${templateId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "PATCH",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_TEMPLATE,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#inspecting-template
*/
async listTemplates(
token: AccessToken | undefined,
params?: PaginationParams,
) {
const url = new URL(`private/templates`, this.baseUrl);
addMerchantPaginationParams(url, params);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForTemplateSummaryResponse());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#get-[-instances-$INSTANCE]-private-templates-$TEMPLATE_ID
*/
async getTemplateDetails(token: AccessToken | undefined, templateId: string) {
const url = new URL(`private/templates/${templateId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForTemplateDetails());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#delete-[-instances-$INSTANCE]-private-templates-$TEMPLATE_ID
*/
async deleteTemplate(token: AccessToken | undefined, templateId: string) {
const url = new URL(`private/templates/${templateId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "DELETE",
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.DELETE_TEMPLATE,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#get-[-instances-$INSTANCE]-templates-$TEMPLATE_ID
*/
async useTemplateGetInfo(templateId: string) {
const url = new URL(`templates/${templateId}`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForWalletTemplateDetails());
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCES]-templates-$TEMPLATE_ID
*/
async useTemplateCreateOrder(
templateId: string,
body: TalerMerchantApi.UsingTemplateDetails,
) {
const url = new URL(`templates/${templateId}`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
});
return this.procesOrderCreationResponse(resp);
}
//
// Webhooks
//
/**
* https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCES]-private-webhooks
*/
async addWebhook(
token: AccessToken | undefined,
body: TalerMerchantApi.WebhookAddDetails,
) {
const url = new URL(`private/webhooks`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.CREATE_WEBHOOK,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#patch-[-instances-$INSTANCES]-private-webhooks-$WEBHOOK_ID
*/
async updateWebhook(
token: AccessToken | undefined,
webhookId: string,
body: TalerMerchantApi.WebhookPatchDetails,
) {
const url = new URL(`private/webhooks/${webhookId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "PATCH",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_WEBHOOK,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-webhooks
*/
async listWebhooks(
token: AccessToken | undefined,
params?: PaginationParams,
) {
const url = new URL(`private/webhooks`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForWebhookSummaryResponse());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#get-[-instances-$INSTANCES]-private-webhooks-$WEBHOOK_ID
*/
async getWebhookDetails(token: AccessToken | undefined, webhookId: string) {
const url = new URL(`private/webhooks/${webhookId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent:
return opSuccessFromHttp(resp, codecForWebhookDetails());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#delete-[-instances-$INSTANCES]-private-webhooks-$WEBHOOK_ID
*/
async deleteWebhook(token: AccessToken | undefined, webhookId: string) {
const url = new URL(`private/webhooks/${webhookId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "DELETE",
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.DELETE_WEBHOOK,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
//
// token families
//
/**
* https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCES]-private-tokenfamilies
*/
async createTokenFamily(
token: AccessToken | undefined,
body: TalerMerchantApi.TokenFamilyCreateRequest,
) {
const url = new URL(`private/tokenfamilies`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.CREATE_TOKENFAMILY,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#patch-[-instances-$INSTANCES]-private-tokenfamilies-$TOKEN_FAMILY_SLUG
*/
async updateTokenFamily(
token: AccessToken | undefined,
tokenSlug: string,
body: TalerMerchantApi.TokenFamilyUpdateRequest,
) {
const url = new URL(`private/tokenfamilies/${tokenSlug}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_TOKENFAMILY,
);
return opSuccessFromHttp(resp, codecForTokenFamilyDetails());
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#get-[-instances-$INSTANCES]-private-tokenfamilies
*/
async listTokenFamilies(
token: AccessToken | undefined,
params?: PaginationParams,
) {
const url = new URL(`private/tokenfamilies`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForTokenFamiliesList());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#get-[-instances-$INSTANCES]-private-tokenfamilies-$TOKEN_FAMILY_SLUG
*/
async getTokenFamilyDetails(
token: AccessToken | undefined,
tokenSlug: string,
) {
const url = new URL(`private/tokenfamilies/${tokenSlug}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForTokenFamilyDetails());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#delete-[-instances-$INSTANCES]-private-tokenfamilies-$TOKEN_FAMILY_SLUG
*/
async deleteTokenFamily(token: AccessToken | undefined, tokenSlug: string) {
const url = new URL(`private/tokenfamilies/${tokenSlug}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "DELETE",
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.DELETE_TOKENFAMILY,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* Get the auth api against the current instance
*
* https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-token
* https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-token
*/
getAuthenticationAPI(): URL {
return new URL(`private/`, this.baseUrl);
}
}
export type TalerMerchantManagementResultByMethod<
prop extends keyof TalerMerchantManagementHttpClient,
> = ResultByMethod;
export type TalerMerchantManagementErrorsByMethod<
prop extends keyof TalerMerchantManagementHttpClient,
> = FailCasesByMethod;
export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttpClient {
readonly cacheManagementEvictor: CacheEvictor<
TalerMerchantInstanceCacheEviction | TalerMerchantManagementCacheEviction
>;
constructor(
readonly baseUrl: string,
httpClient?: HttpRequestLibrary,
// cacheManagementEvictor?: CacheEvictor,
cacheEvictor?: CacheEvictor<
TalerMerchantInstanceCacheEviction | TalerMerchantManagementCacheEviction
>,
) {
super(baseUrl, httpClient, cacheEvictor);
this.cacheManagementEvictor = cacheEvictor ?? nullEvictor;
}
getSubInstanceAPI(instanceId: string) {
return new URL(`instances/${instanceId}/`, this.baseUrl);
}
//
// Instance Management
//
/**
* https://docs.taler.net/core/api-merchant.html#post--management-instances
*/
async createInstance(
token: AccessToken | undefined,
body: TalerMerchantApi.InstanceConfigurationMessage,
) {
const url = new URL(`management/instances`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheManagementEvictor.notifySuccess(
TalerMerchantManagementCacheEviction.CREATE_INSTANCE,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#post--management-instances-$INSTANCE-auth
*/
async updateInstanceAuthentication(
token: AccessToken | undefined,
instanceId: string,
body: TalerMerchantApi.InstanceAuthConfigurationMessage,
) {
const url = new URL(
`management/instances/${instanceId}/auth`,
this.baseUrl,
);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent:
return opEmptySuccess(resp);
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#patch--management-instances-$INSTANCE
*/
async updateInstance(
token: AccessToken | undefined,
instanceId: string,
body: TalerMerchantApi.InstanceReconfigurationMessage,
) {
const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "PATCH",
body,
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheManagementEvictor.notifySuccess(
TalerMerchantManagementCacheEviction.UPDATE_INSTANCE,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
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-merchant.html#get--management-instances
*/
async listInstances(
token: AccessToken | undefined,
params?: PaginationParams,
) {
const url = new URL(`management/instances`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForInstancesResponse());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#get--management-instances-$INSTANCE
*
*/
async getInstanceDetails(token: AccessToken | undefined, instanceId: string) {
const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForQueryInstancesResponse());
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#delete--management-instances-$INSTANCE
*/
async deleteInstance(
token: AccessToken | undefined,
instanceId: string,
params: { purge?: boolean } = {},
) {
const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
if (params.purge !== undefined) {
url.searchParams.set("purge", params.purge ? "YES" : "NO");
}
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "DELETE",
headers,
});
switch (resp.status) {
case HttpStatusCode.NoContent: {
this.cacheManagementEvictor.notifySuccess(
TalerMerchantManagementCacheEviction.DELETE_INSTANCE,
);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-merchant.html#get--management-instances-$INSTANCE-kyc
*/
async getIntanceKycStatus(
token: AccessToken | undefined,
instanceId: string,
params: TalerMerchantApi.GetKycStatusRequestParams,
) {
const url = new URL(`management/instances/${instanceId}/kyc`, this.baseUrl);
if (params.wireHash) {
url.searchParams.set("h_wire", params.wireHash);
}
if (params.exchangeURL) {
url.searchParams.set("exchange_url", params.exchangeURL);
}
if (params.timeout) {
url.searchParams.set("timeout_ms", String(params.timeout));
}
const headers: Record = {};
if (token) {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers,
});
switch (resp.status) {
case HttpStatusCode.Accepted:
return opSuccessFromHttp(resp, codecForAccountKycRedirects());
case HttpStatusCode.NoContent:
return opEmptySuccess(resp);
case HttpStatusCode.NotFound:
return opEmptySuccess(resp);
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.BadGateway:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.ServiceUnavailable:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
}