wallet-core/packages/taler-util/src/taleruri.ts

783 lines
20 KiB
TypeScript
Raw Normal View History

2019-08-27 02:49:38 +02:00
/*
This file is part of GNU Taler
2020-07-27 13:39:52 +02:00
(C) 2019-2020 Taler Systems S.A.
2019-08-27 02:49:38 +02:00
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 { canonicalizeBaseUrl } from "./helpers.js";
2022-08-09 15:00:45 +02:00
import { URLSearchParams, URL } from "./url.js";
2023-04-03 17:12:35 +02:00
export type TalerUri =
| PayUriResult
| PayTemplateUriResult
| DevExperimentUri
| PayPullUriResult
| PayPushUriResult
| BackupRestoreUri
| RefundUriResult
| TipUriResult
| WithdrawUriResult
| ExchangeUri
| AuditorUri;
2019-08-27 02:49:38 +02:00
export interface PayUriResult {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.Pay;
2019-12-06 12:47:28 +01:00
merchantBaseUrl: string;
orderId: string;
2020-07-27 13:39:52 +02:00
sessionId: string;
2023-04-04 20:11:08 +02:00
claimToken?: string;
noncePriv?: string;
2019-08-27 02:49:38 +02:00
}
export interface PayTemplateUriResult {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.PayTemplate;
merchantBaseUrl: string;
templateId: string;
templateParams: Record<string, string>;
}
2019-08-28 02:49:27 +02:00
export interface WithdrawUriResult {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.Withdraw;
2020-07-27 13:39:52 +02:00
bankIntegrationApiBaseUrl: string;
withdrawalOperationId: string;
2019-08-28 02:49:27 +02:00
}
2019-08-31 11:49:36 +02:00
export interface RefundUriResult {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.Refund;
2019-12-05 19:38:19 +01:00
merchantBaseUrl: string;
orderId: string;
2019-08-31 11:49:36 +02:00
}
2019-08-30 17:27:59 +02:00
export interface TipUriResult {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.Tip;
merchantBaseUrl: string;
2023-04-03 17:12:35 +02:00
merchantTipId: string;
}
export interface ExchangeUri {
type: TalerUriAction.Exchange;
exchangeBaseUrl: string;
exchangePub: string;
}
export interface AuditorUri {
type: TalerUriAction.Auditor;
auditorBaseUrl: string;
auditorPub: string;
2019-08-30 17:27:59 +02:00
}
2022-08-09 15:00:45 +02:00
export interface PayPushUriResult {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.PayPush;
2022-08-09 15:00:45 +02:00
exchangeBaseUrl: string;
contractPriv: string;
}
export interface PayPullUriResult {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.PayPull;
exchangeBaseUrl: string;
contractPriv: string;
}
export interface DevExperimentUri {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.DevExperiment;
devExperimentId: string;
}
2023-04-03 17:12:35 +02:00
export interface BackupRestoreUri {
type: TalerUriAction.Restore;
walletRootPriv: string;
2023-04-04 20:11:08 +02:00
providers: Array<string>;
2023-04-03 17:12:35 +02:00
}
2020-07-27 13:39:52 +02:00
/**
* Parse a taler[+http]://withdraw URI.
* Return undefined if not passed a valid URI.
*/
2019-08-28 02:49:27 +02:00
export function parseWithdrawUri(s: string): WithdrawUriResult | undefined {
2020-07-27 19:57:32 +02:00
const pi = parseProtoInfo(s, "withdraw");
if (!pi) {
2019-08-28 02:49:27 +02:00
return undefined;
}
2020-07-27 19:57:32 +02:00
const parts = pi.rest.split("/");
2020-07-27 13:39:52 +02:00
if (parts.length < 2) {
2019-12-06 12:47:28 +01:00
return undefined;
}
2020-07-27 13:39:52 +02:00
const host = parts[0].toLowerCase();
const pathSegments = parts.slice(1, parts.length - 1);
/**
* The statement below does not tolerate a slash-ended URI.
* This results in (1) the withdrawalId being passed as the
* empty string, and (2) the bankIntegrationApi ending with the
* actual withdrawal operation ID. That can be fixed by
* trimming the parts-list. FIXME
*/
2020-07-27 13:39:52 +02:00
const withdrawId = parts[parts.length - 1];
const p = [host, ...pathSegments].join("/");
2019-08-28 02:49:27 +02:00
return {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.Withdraw,
2020-11-16 14:12:37 +01:00
bankIntegrationApiBaseUrl: canonicalizeBaseUrl(`${pi.innerProto}://${p}/`),
2020-07-27 13:39:52 +02:00
withdrawalOperationId: withdrawId,
2019-08-28 02:49:27 +02:00
};
}
2023-04-03 17:12:35 +02:00
/**
* @deprecated use TalerUriAction
*/
export enum TalerUriType {
2019-12-06 12:47:28 +01:00
TalerPay = "taler-pay",
TalerTemplate = "taler-template",
2023-03-10 05:22:07 +01:00
TalerPayTemplate = "taler-pay-template",
2019-12-06 12:47:28 +01:00
TalerWithdraw = "taler-withdraw",
TalerTip = "taler-tip",
TalerRefund = "taler-refund",
TalerPayPush = "taler-pay-push",
TalerPayPull = "taler-pay-pull",
2022-10-20 19:53:29 +02:00
TalerRecovery = "taler-recovery",
TalerDevExperiment = "taler-dev-experiment",
2019-12-06 12:47:28 +01:00
Unknown = "unknown",
}
const talerActionPayPull = "pay-pull";
const talerActionPayPush = "pay-push";
const talerActionPayTemplate = "pay-template";
2023-04-03 17:12:35 +02:00
export enum TalerUriAction {
Pay = "pay",
Withdraw = "withdraw",
Refund = "refund",
Tip = "tip",
PayPull = "pay-pull",
PayPush = "pay-push",
PayTemplate = "pay-template",
Exchange = "exchange",
Auditor = "auditor",
Restore = "restore",
DevExperiment = "dev-experiment",
}
2020-07-27 13:39:52 +02:00
/**
* Classify a taler:// URI.
2023-04-03 17:12:35 +02:00
* @deprecated use parseTalerUri
2020-07-27 13:39:52 +02:00
*/
2019-12-06 12:47:28 +01:00
export function classifyTalerUri(s: string): TalerUriType {
const sl = s.toLowerCase();
2023-04-03 17:12:35 +02:00
if (sl.startsWith("taler://restore/")) {
2022-10-20 19:53:29 +02:00
return TalerUriType.TalerRecovery;
}
2023-04-03 17:12:35 +02:00
if (sl.startsWith("taler+http://restore/")) {
2022-10-20 19:53:29 +02:00
return TalerUriType.TalerRecovery;
}
2019-12-06 12:47:28 +01:00
if (sl.startsWith("taler://pay/")) {
return TalerUriType.TalerPay;
2019-08-27 02:49:38 +02:00
}
2020-07-27 13:39:52 +02:00
if (sl.startsWith("taler+http://pay/")) {
return TalerUriType.TalerPay;
}
if (sl.startsWith("taler://pay-template/")) {
2023-03-10 05:22:07 +01:00
return TalerUriType.TalerPayTemplate;
}
if (sl.startsWith("taler+http://pay-template/")) {
2023-03-10 05:22:07 +01:00
return TalerUriType.TalerPayTemplate;
}
2019-12-06 12:47:28 +01:00
if (sl.startsWith("taler://tip/")) {
return TalerUriType.TalerTip;
}
2020-07-27 13:39:52 +02:00
if (sl.startsWith("taler+http://tip/")) {
return TalerUriType.TalerTip;
}
2019-12-06 12:47:28 +01:00
if (sl.startsWith("taler://refund/")) {
return TalerUriType.TalerRefund;
}
2020-07-27 13:39:52 +02:00
if (sl.startsWith("taler+http://refund/")) {
return TalerUriType.TalerRefund;
}
2019-12-06 12:47:28 +01:00
if (sl.startsWith("taler://withdraw/")) {
return TalerUriType.TalerWithdraw;
}
2020-10-02 17:16:19 +02:00
if (sl.startsWith("taler+http://withdraw/")) {
return TalerUriType.TalerWithdraw;
}
if (sl.startsWith(`taler://${talerActionPayPush}/`)) {
2022-08-09 15:00:45 +02:00
return TalerUriType.TalerPayPush;
}
if (sl.startsWith(`taler+http://${talerActionPayPush}/`)) {
2022-08-09 15:00:45 +02:00
return TalerUriType.TalerPayPush;
}
if (sl.startsWith(`taler://${talerActionPayPull}/`)) {
return TalerUriType.TalerPayPull;
}
if (sl.startsWith(`taler+http://${talerActionPayPull}/`)) {
return TalerUriType.TalerPayPull;
}
if (sl.startsWith("taler://dev-experiment/")) {
return TalerUriType.TalerDevExperiment;
}
2019-12-06 12:47:28 +01:00
return TalerUriType.Unknown;
}
2020-07-27 13:39:52 +02:00
interface TalerUriProtoInfo {
innerProto: "http" | "https";
2020-07-27 13:39:52 +02:00
rest: string;
}
function parseProtoInfo(
s: string,
action: string,
): TalerUriProtoInfo | undefined {
2020-07-27 13:39:52 +02:00
const pfxPlain = `taler://${action}/`;
const pfxHttp = `taler+http://${action}/`;
if (s.toLowerCase().startsWith(pfxPlain)) {
return {
innerProto: "https",
rest: s.substring(pfxPlain.length),
};
2020-07-27 13:39:52 +02:00
} else if (s.toLowerCase().startsWith(pfxHttp)) {
return {
innerProto: "http",
rest: s.substring(pfxHttp.length),
};
2020-07-27 13:39:52 +02:00
} else {
2019-08-27 02:49:38 +02:00
return undefined;
}
2020-07-27 13:39:52 +02:00
}
2019-08-27 02:49:38 +02:00
2023-04-03 17:12:35 +02:00
type Parser = (s: string) => TalerUri | undefined;
const parsers: { [A in TalerUriAction]: Parser } = {
[TalerUriAction.Pay]: parsePayUri,
[TalerUriAction.PayPull]: parsePayPullUri,
[TalerUriAction.PayPush]: parsePayPushUri,
[TalerUriAction.PayTemplate]: parsePayTemplateUri,
[TalerUriAction.Restore]: parseRestoreUri,
[TalerUriAction.Refund]: parseRefundUri,
[TalerUriAction.Tip]: parseTipUri,
[TalerUriAction.Withdraw]: parseWithdrawUri,
[TalerUriAction.DevExperiment]: parseDevExperimentUri,
[TalerUriAction.Exchange]: parseExchangeUri,
[TalerUriAction.Auditor]: parseAuditorUri,
};
export function parseTalerUri(string: string): TalerUri | undefined {
const https = string.startsWith("taler://");
const http = string.startsWith("taler+http://");
if (!https && !http) return undefined;
const actionStart = https ? 8 : 13;
const actionEnd = string.indexOf("/", actionStart + 1);
const action = string.substring(actionStart, actionEnd);
const found = Object.values(TalerUriAction).find((x) => x === action);
if (!found) return undefined;
return parsers[found](string);
}
export function stringifyTalerUri(uri: TalerUri): string {
switch (uri.type) {
case TalerUriAction.DevExperiment: {
return stringifyDevExperimentUri(uri);
}
case TalerUriAction.Pay: {
return stringifyPayUri(uri);
}
case TalerUriAction.PayPull: {
return stringifyPayPullUri(uri);
}
case TalerUriAction.PayPush: {
return stringifyPayPushUri(uri);
}
case TalerUriAction.PayTemplate: {
return stringifyPayTemplateUri(uri);
}
case TalerUriAction.Restore: {
return stringifyRestoreUri(uri);
}
case TalerUriAction.Refund: {
return stringifyRefundUri(uri);
}
case TalerUriAction.Tip: {
return stringifyTipUri(uri);
}
case TalerUriAction.Withdraw: {
return stringifyWithdrawUri(uri);
}
case TalerUriAction.Exchange: {
return stringifyExchangeUri(uri);
}
case TalerUriAction.Auditor: {
return stringifyAuditorUri(uri);
}
}
}
2020-07-27 13:39:52 +02:00
/**
* Parse a taler[+http]://pay URI.
* Return undefined if not passed a valid URI.
*/
export function parsePayUri(s: string): PayUriResult | undefined {
const pi = parseProtoInfo(s, "pay");
if (!pi) {
2019-08-27 02:49:38 +02:00
return undefined;
}
2020-07-27 13:39:52 +02:00
const c = pi?.rest.split("?");
2020-07-30 13:58:09 +02:00
const q = new URLSearchParams(c[1] ?? "");
const claimToken = q.get("c") ?? undefined;
2021-09-17 20:48:33 +02:00
const noncePriv = q.get("n") ?? undefined;
2020-07-27 13:39:52 +02:00
const parts = c[0].split("/");
if (parts.length < 3) {
2019-08-27 02:49:38 +02:00
return undefined;
}
2020-07-27 13:39:52 +02:00
const host = parts[0].toLowerCase();
const sessionId = parts[parts.length - 1];
const orderId = parts[parts.length - 2];
const pathSegments = parts.slice(1, parts.length - 2);
const p = [host, ...pathSegments].join("/");
2020-11-16 14:12:37 +01:00
const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`);
2019-08-27 02:49:38 +02:00
return {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.Pay,
2019-12-06 12:47:28 +01:00
merchantBaseUrl,
orderId,
2023-04-03 17:12:35 +02:00
sessionId,
2020-07-30 13:58:09 +02:00
claimToken,
2021-09-17 20:48:33 +02:00
noncePriv,
2019-08-28 02:49:27 +02:00
};
2019-08-27 02:49:38 +02:00
}
2019-08-30 17:27:59 +02:00
export function parsePayTemplateUri(
2023-04-03 17:12:35 +02:00
uriString: string,
): PayTemplateUriResult | undefined {
2023-04-03 17:12:35 +02:00
const pi = parseProtoInfo(uriString, talerActionPayTemplate);
if (!pi) {
return undefined;
}
2023-04-03 17:12:35 +02:00
const c = pi.rest.split("?");
const parts = c[0].split("/");
if (parts.length < 2) {
return undefined;
}
2023-04-03 17:12:35 +02:00
const q = new URLSearchParams(c[1] ?? "");
const params: Record<string, string> = {};
q.forEach((v, k) => {
params[k] = v;
});
2023-04-03 17:12:35 +02:00
const host = parts[0].toLowerCase();
const templateId = parts[parts.length - 1];
const pathSegments = parts.slice(1, parts.length - 1);
const hostAndSegments = [host, ...pathSegments].join("/");
const merchantBaseUrl = canonicalizeBaseUrl(
`${pi.innerProto}://${hostAndSegments}/`,
);
return {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.PayTemplate,
merchantBaseUrl,
templateId,
templateParams: params,
};
}
2022-08-09 15:00:45 +02:00
export function parsePayPushUri(s: string): PayPushUriResult | undefined {
const pi = parseProtoInfo(s, talerActionPayPush);
if (!pi) {
return undefined;
}
const c = pi?.rest.split("?");
const parts = c[0].split("/");
if (parts.length < 2) {
return undefined;
}
const host = parts[0].toLowerCase();
const contractPriv = parts[parts.length - 1];
const pathSegments = parts.slice(1, parts.length - 1);
2023-04-03 17:12:35 +02:00
const hostAndSegments = [host, ...pathSegments].join("/");
const exchangeBaseUrl = canonicalizeBaseUrl(
`${pi.innerProto}://${hostAndSegments}/`,
);
return {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.PayPush,
exchangeBaseUrl,
contractPriv,
};
}
export function parsePayPullUri(s: string): PayPullUriResult | undefined {
const pi = parseProtoInfo(s, talerActionPayPull);
2022-08-09 15:00:45 +02:00
if (!pi) {
return undefined;
}
const c = pi?.rest.split("?");
const parts = c[0].split("/");
if (parts.length < 2) {
return undefined;
}
const host = parts[0].toLowerCase();
const contractPriv = parts[parts.length - 1];
const pathSegments = parts.slice(1, parts.length - 1);
2023-04-03 17:12:35 +02:00
const hostAndSegments = [host, ...pathSegments].join("/");
const exchangeBaseUrl = canonicalizeBaseUrl(
`${pi.innerProto}://${hostAndSegments}/`,
);
2022-08-09 15:00:45 +02:00
return {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.PayPull,
2022-08-09 15:00:45 +02:00
exchangeBaseUrl,
contractPriv,
};
}
2020-07-27 13:39:52 +02:00
/**
* Parse a taler[+http]://tip URI.
* Return undefined if not passed a valid URI.
*/
2019-08-30 17:27:59 +02:00
export function parseTipUri(s: string): TipUriResult | undefined {
2020-07-27 13:39:52 +02:00
const pi = parseProtoInfo(s, "tip");
if (!pi) {
2019-08-30 17:27:59 +02:00
return undefined;
}
2020-07-27 13:39:52 +02:00
const c = pi?.rest.split("?");
const parts = c[0].split("/");
if (parts.length < 2) {
2019-08-30 17:27:59 +02:00
return undefined;
}
2020-07-27 13:39:52 +02:00
const host = parts[0].toLowerCase();
const tipId = parts[parts.length - 1];
const pathSegments = parts.slice(1, parts.length - 1);
2023-04-03 17:12:35 +02:00
const hostAndSegments = [host, ...pathSegments].join("/");
const merchantBaseUrl = canonicalizeBaseUrl(
`${pi.innerProto}://${hostAndSegments}/`,
);
2019-08-30 17:27:59 +02:00
return {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.Tip,
merchantBaseUrl,
2020-07-27 13:39:52 +02:00
merchantTipId: tipId,
2019-08-30 17:27:59 +02:00
};
}
2019-08-31 11:49:36 +02:00
2023-04-03 17:12:35 +02:00
export function parseExchangeUri(s: string): ExchangeUri | undefined {
const pi = parseProtoInfo(s, "exchange");
if (!pi) {
return undefined;
}
const c = pi?.rest.split("?");
const parts = c[0].split("/");
if (parts.length < 2) {
return undefined;
}
const host = parts[0].toLowerCase();
const exchangePub = parts[parts.length - 1];
const pathSegments = parts.slice(1, parts.length - 1);
const hostAndSegments = [host, ...pathSegments].join("/");
const exchangeBaseUrl = canonicalizeBaseUrl(
`${pi.innerProto}://${hostAndSegments}/`,
);
return {
type: TalerUriAction.Exchange,
exchangeBaseUrl,
exchangePub,
};
}
export function parseAuditorUri(s: string): AuditorUri | undefined {
const pi = parseProtoInfo(s, "auditor");
if (!pi) {
return undefined;
}
const c = pi?.rest.split("?");
const parts = c[0].split("/");
if (parts.length < 2) {
return undefined;
}
const host = parts[0].toLowerCase();
const auditorPub = parts[parts.length - 1];
const pathSegments = parts.slice(1, parts.length - 1);
const hostAndSegments = [host, ...pathSegments].join("/");
const auditorBaseUrl = canonicalizeBaseUrl(
`${pi.innerProto}://${hostAndSegments}/`,
);
return {
type: TalerUriAction.Auditor,
auditorBaseUrl,
auditorPub,
};
}
2020-07-27 13:39:52 +02:00
/**
* Parse a taler[+http]://refund URI.
* Return undefined if not passed a valid URI.
*/
2019-08-31 11:49:36 +02:00
export function parseRefundUri(s: string): RefundUriResult | undefined {
2020-07-27 13:39:52 +02:00
const pi = parseProtoInfo(s, "refund");
if (!pi) {
2019-08-31 11:49:36 +02:00
return undefined;
}
2020-07-27 13:39:52 +02:00
const c = pi?.rest.split("?");
const parts = c[0].split("/");
if (parts.length < 3) {
2019-08-31 11:49:36 +02:00
return undefined;
}
2020-07-27 13:39:52 +02:00
const host = parts[0].toLowerCase();
2023-04-04 20:11:08 +02:00
const sessionId = parts[parts.length - 1];
const orderId = parts[parts.length - 2];
const pathSegments = parts.slice(1, parts.length - 2);
2023-04-03 17:12:35 +02:00
const hostAndSegments = [host, ...pathSegments].join("/");
const merchantBaseUrl = canonicalizeBaseUrl(
`${pi.innerProto}://${hostAndSegments}/`,
);
2019-08-31 11:49:36 +02:00
return {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.Refund,
2019-12-05 19:38:19 +01:00
merchantBaseUrl,
orderId,
2019-08-31 11:49:36 +02:00
};
2019-10-01 20:45:36 +02:00
}
2022-08-09 15:00:45 +02:00
export function parseDevExperimentUri(s: string): DevExperimentUri | undefined {
const pi = parseProtoInfo(s, "dev-experiment");
const c = pi?.rest.split("?");
if (!c) {
return undefined;
}
const parts = c[0].split("/");
return {
2023-04-03 17:12:35 +02:00
type: TalerUriAction.DevExperiment,
devExperimentId: parts[0],
};
}
2023-04-03 17:12:35 +02:00
export function parseRestoreUri(uri: string): BackupRestoreUri | undefined {
const pi = parseProtoInfo(uri, "restore");
if (!pi) {
return undefined;
2022-08-09 15:00:45 +02:00
}
2023-04-03 17:12:35 +02:00
const c = pi.rest.split("?");
const parts = c[0].split("/");
if (parts.length < 2) {
return undefined;
2022-08-09 15:00:45 +02:00
}
2023-04-03 17:12:35 +02:00
const walletRootPriv = parts[0];
if (!walletRootPriv) return undefined;
2023-04-04 20:11:08 +02:00
const providers = new Array<string>();
2023-04-03 17:12:35 +02:00
parts[1].split(",").map((name) => {
2023-04-04 20:11:08 +02:00
const url = canonicalizeBaseUrl(
`${pi.innerProto}://${decodeURIComponent(name)}/`,
);
providers.push(url);
2023-04-03 17:12:35 +02:00
});
return {
type: TalerUriAction.Restore,
walletRootPriv,
providers,
};
}
// ================================================
// To string functions
// ================================================
/**
* @deprecated use stringifyRecoveryUri
*/
export function constructRecoveryUri(args: {
walletRootPriv: string;
2023-04-04 20:11:08 +02:00
providers: string[];
2023-04-03 17:12:35 +02:00
}): string {
return stringifyRestoreUri(args);
2022-08-09 15:00:45 +02:00
}
2023-04-03 17:12:35 +02:00
/**
* @deprecated stringifyPayPullUri
*/
export function constructPayPullUri(args: {
exchangeBaseUrl: string;
contractPriv: string;
}): string {
2023-04-03 17:12:35 +02:00
return stringifyPayPullUri(args);
}
/**
* @deprecated use stringifyPayPushUri
*/
export function constructPayPushUri(args: {
exchangeBaseUrl: string;
contractPriv: string;
}): string {
return stringifyPayPushUri(args);
}
/**
*
* @deprecated use stringifyPayUri
*/
export function constructPayUri(
merchantBaseUrl: string,
orderId: string,
sessionId: string,
claimToken?: string,
noncePriv?: string,
): string {
return stringifyPayUri({
merchantBaseUrl,
orderId,
sessionId,
claimToken,
noncePriv,
});
}
export function stringifyPayUri({
merchantBaseUrl,
orderId,
sessionId,
claimToken,
noncePriv,
}: Omit<PayUriResult, "type">): string {
const { proto, path, query } = getUrlInfo(merchantBaseUrl, {
c: claimToken,
n: noncePriv,
});
return `${proto}://pay/${path}${orderId}/${sessionId}${query}`;
}
export function stringifyPayPullUri({
contractPriv,
exchangeBaseUrl,
}: Omit<PayPullUriResult, "type">): string {
const { proto, path } = getUrlInfo(exchangeBaseUrl);
return `${proto}://pay-pull/${path}${contractPriv}`;
}
export function stringifyPayPushUri({
contractPriv,
exchangeBaseUrl,
}: Omit<PayPushUriResult, "type">): string {
const { proto, path } = getUrlInfo(exchangeBaseUrl);
return `${proto}://pay-push/${path}${contractPriv}`;
}
export function stringifyRestoreUri({
providers,
walletRootPriv,
}: Omit<BackupRestoreUri, "type">): string {
2023-04-04 20:11:08 +02:00
const list = providers
.map((url) => `${encodeURIComponent(new URL(url).href)}`)
.join(",");
2023-04-03 17:12:35 +02:00
return `taler://restore/${walletRootPriv}/${list}`;
}
export function stringifyDevExperimentUri({
devExperimentId,
}: Omit<DevExperimentUri, "type">): string {
return `taler://dev-experiment/${devExperimentId}`;
}
export function stringifyPayTemplateUri({
merchantBaseUrl,
templateId,
templateParams,
}: Omit<PayTemplateUriResult, "type">): string {
const { proto, path, query } = getUrlInfo(merchantBaseUrl, templateParams);
return `${proto}://pay-template/${path}${templateId}${query}`;
}
export function stringifyRefundUri({
merchantBaseUrl,
orderId,
}: Omit<RefundUriResult, "type">): string {
const { proto, path } = getUrlInfo(merchantBaseUrl);
return `${proto}://refund/${path}${orderId}`;
}
export function stringifyTipUri({
merchantBaseUrl,
merchantTipId,
}: Omit<TipUriResult, "type">): string {
const { proto, path } = getUrlInfo(merchantBaseUrl);
return `${proto}://tip/${path}${merchantTipId}`;
}
export function stringifyExchangeUri({
exchangeBaseUrl,
exchangePub,
}: Omit<ExchangeUri, "type">): string {
const { proto, path } = getUrlInfo(exchangeBaseUrl);
return `${proto}://exchange/${path}${exchangePub}`;
}
export function stringifyAuditorUri({
auditorBaseUrl,
auditorPub,
}: Omit<AuditorUri, "type">): string {
const { proto, path } = getUrlInfo(auditorBaseUrl);
return `${proto}://auditor/${path}${auditorPub}`;
}
export function stringifyWithdrawUri({
bankIntegrationApiBaseUrl,
withdrawalOperationId,
}: Omit<WithdrawUriResult, "type">): string {
const { proto, path } = getUrlInfo(bankIntegrationApiBaseUrl);
return `${proto}://withdraw/${path}${withdrawalOperationId}`;
}
/**
* Use baseUrl to defined http or https
* create path using host+port+pathname
* use params to create a query parameter string or empty
*
* @param baseUrl
* @param params
* @returns
*/
function getUrlInfo(
baseUrl: string,
params: Record<string, string | undefined> = {},
): { proto: string; path: string; query: string } {
const url = new URL(baseUrl);
let proto: string;
if (url.protocol === "https:") {
proto = "taler";
} else if (url.protocol === "http:") {
proto = "taler+http";
} else {
2023-04-03 17:12:35 +02:00
throw Error(`Unsupported URL protocol in ${baseUrl}`);
}
2023-04-03 17:12:35 +02:00
let path = url.hostname;
if (url.port) {
path = path + ":" + url.port;
}
2023-04-03 17:12:35 +02:00
if (url.pathname) {
path = path + url.pathname;
2022-10-20 19:53:29 +02:00
}
2023-04-03 17:12:35 +02:00
if (!path.endsWith("/")) {
path = path + "/";
2022-10-20 19:53:29 +02:00
}
2023-04-03 17:12:35 +02:00
const qp = new URLSearchParams();
let withParams = false;
Object.entries(params).forEach(([name, value]) => {
if (value) {
withParams = true;
qp.append(name, value);
}
});
const query = withParams ? "?" + qp.toString() : "";
return { proto, path, query };
2022-10-20 19:53:29 +02:00
}