summaryrefslogtreecommitdiff
path: root/packages/taler-util/src/taleruri.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-util/src/taleruri.ts')
-rw-r--r--packages/taler-util/src/taleruri.ts146
1 files changed, 127 insertions, 19 deletions
diff --git a/packages/taler-util/src/taleruri.ts b/packages/taler-util/src/taleruri.ts
index 97b82c061..54b7525e3 100644
--- a/packages/taler-util/src/taleruri.ts
+++ b/packages/taler-util/src/taleruri.ts
@@ -25,9 +25,10 @@
*/
import { Codec, Context, DecodingError, renderContext } from "./codec.js";
import { canonicalizeBaseUrl } from "./helpers.js";
+import { opFixedSuccess, opKnownTalerFailure } from "./operation.js";
+import { TalerErrorCode } from "./taler-error-codes.js";
import { AmountString } from "./taler-types.js";
import { URL, URLSearchParams } from "./url.js";
-
/**
* A parsed taler URI.
*/
@@ -40,7 +41,8 @@ export type TalerUri =
| BackupRestoreUri
| RefundUriResult
| WithdrawUriResult
- | WithdrawExchangeUri;
+ | WithdrawExchangeUri
+ | AddExchangeUri;
declare const __action_str: unique symbol;
export type TalerUriString = string & { [__action_str]: true };
@@ -81,7 +83,6 @@ export interface PayTemplateUriResult {
type: TalerUriAction.PayTemplate;
merchantBaseUrl: string;
templateId: string;
- templateParams: TemplateParams;
}
export interface WithdrawUriResult {
@@ -122,23 +123,29 @@ export interface BackupRestoreUri {
export interface WithdrawExchangeUri {
type: TalerUriAction.WithdrawExchange;
exchangeBaseUrl: string;
- exchangePub?: string;
amount?: AmountString;
}
+export interface AddExchangeUri {
+ type: TalerUriAction.AddExchange;
+ exchangeBaseUrl: string;
+}
+
/**
* Parse a taler[+http]://withdraw URI.
* Return undefined if not passed a valid URI.
*/
-export function parseWithdrawUri(s: string): WithdrawUriResult | undefined {
- const pi = parseProtoInfo(s, "withdraw");
- if (!pi) {
- return undefined;
+export function parseWithdrawUriWithError(s: string) {
+ const pi = parseProtoInfoWithError(s, "withdraw");
+ if (pi.type === "fail") {
+ return pi;
}
- const parts = pi.rest.split("/");
+ const parts = pi.body.rest.split("/");
if (parts.length < 2) {
- return undefined;
+ return opKnownTalerFailure(TalerErrorCode.WALLET_TALER_URI_MALFORMED, {
+ code: TalerErrorCode.WALLET_TALER_URI_MALFORMED,
+ });
}
const host = parts[0].toLowerCase();
@@ -153,11 +160,69 @@ export function parseWithdrawUri(s: string): WithdrawUriResult | undefined {
const withdrawId = parts[parts.length - 1];
const p = [host, ...pathSegments].join("/");
- return {
+ const result: WithdrawUriResult = {
type: TalerUriAction.Withdraw,
- bankIntegrationApiBaseUrl: canonicalizeBaseUrl(`${pi.innerProto}://${p}/`),
+ bankIntegrationApiBaseUrl: canonicalizeBaseUrl(
+ `${pi.body.innerProto}://${p}/`,
+ ),
withdrawalOperationId: withdrawId,
};
+ return opFixedSuccess(result);
+}
+
+/**
+ *
+ * @deprecated use parseWithdrawUriWithError
+ */
+export function parseWithdrawUri(s: string): WithdrawUriResult | undefined {
+ const r = parseWithdrawUriWithError(s);
+ if (r.type === "fail") return undefined;
+ return r.body;
+}
+
+/**
+ * Parse a taler[+http]://withdraw URI.
+ * Return undefined if not passed a valid URI.
+ */
+export function parseAddExchangeUriWithError(s: string) {
+ const pi = parseProtoInfoWithError(s, "add-exchange");
+ if (pi.type === "fail") {
+ return pi;
+ }
+ const parts = pi.body.rest.split("/");
+
+ if (parts.length < 2) {
+ return opKnownTalerFailure(TalerErrorCode.WALLET_TALER_URI_MALFORMED, {
+ code: TalerErrorCode.WALLET_TALER_URI_MALFORMED,
+ });
+ }
+
+ 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
+ */
+ const p = [host, ...pathSegments].join("/");
+
+ const result: AddExchangeUri = {
+ type: TalerUriAction.AddExchange,
+ exchangeBaseUrl: canonicalizeBaseUrl(`${pi.body.innerProto}://${p}/`),
+ };
+ return opFixedSuccess(result);
+}
+
+/**
+ *
+ * @deprecated use parseWithdrawUriWithError
+ */
+export function parseAddExchangeUri(s: string): AddExchangeUri | undefined {
+ const r = parseAddExchangeUriWithError(s);
+ if (r.type === "fail") return undefined;
+ return r.body;
}
/**
@@ -187,6 +252,7 @@ export enum TalerUriAction {
Restore = "restore",
DevExperiment = "dev-experiment",
WithdrawExchange = "withdraw-exchange",
+ AddExchange = "add-exchange",
}
interface TalerUriProtoInfo {
@@ -215,6 +281,34 @@ function parseProtoInfo(
}
}
+function parseProtoInfoWithError(s: string, action: string) {
+ if (
+ !s.toLowerCase().startsWith("taler://") &&
+ !s.toLowerCase().startsWith("taler+http://")
+ ) {
+ return opKnownTalerFailure(TalerErrorCode.WALLET_TALER_URI_MALFORMED, {
+ code: TalerErrorCode.WALLET_TALER_URI_MALFORMED,
+ });
+ }
+ const pfxPlain = `taler://${action}/`;
+ const pfxHttp = `taler+http://${action}/`;
+ if (s.toLowerCase().startsWith(pfxPlain)) {
+ return opFixedSuccess({
+ innerProto: "https",
+ rest: s.substring(pfxPlain.length),
+ });
+ } else if (s.toLowerCase().startsWith(pfxHttp)) {
+ return opFixedSuccess({
+ innerProto: "http",
+ rest: s.substring(pfxHttp.length),
+ });
+ } else {
+ return opKnownTalerFailure(TalerErrorCode.WALLET_TALER_URI_MALFORMED, {
+ code: TalerErrorCode.WALLET_TALER_URI_MALFORMED,
+ });
+ }
+}
+
type Parser = (s: string) => TalerUri | undefined;
const parsers: { [A in TalerUriAction]: Parser } = {
[TalerUriAction.Pay]: parsePayUri,
@@ -226,6 +320,7 @@ const parsers: { [A in TalerUriAction]: Parser } = {
[TalerUriAction.Withdraw]: parseWithdrawUri,
[TalerUriAction.DevExperiment]: parseDevExperimentUri,
[TalerUriAction.WithdrawExchange]: parseWithdrawExchangeUri,
+ [TalerUriAction.AddExchange]: parseAddExchangeUri,
};
export function parseTalerUri(string: string): TalerUri | undefined {
@@ -269,6 +364,9 @@ export function stringifyTalerUri(uri: TalerUri): string {
case TalerUriAction.WithdrawExchange: {
return stringifyWithdrawExchange(uri);
}
+ case TalerUriAction.AddExchange: {
+ return stringifyAddExchange(uri);
+ }
}
}
@@ -338,7 +436,6 @@ export function parsePayTemplateUri(
type: TalerUriAction.PayTemplate,
merchantBaseUrl,
templateId,
- templateParams: params,
};
}
@@ -405,7 +502,14 @@ export function parseWithdrawExchangeUri(
return undefined;
}
const host = parts[0].toLowerCase();
- const exchangePub = parts.length > 1 ? parts[parts.length - 1] : undefined;
+ // Used to be the reserve public key, now it's empty!
+ const lastPathComponent =
+ parts.length > 1 ? parts[parts.length - 1] : undefined;
+
+ if (lastPathComponent) {
+ // invalid taler://withdraw-exchange URI, must end with a slash
+ return undefined;
+ }
const pathSegments = parts.slice(1, parts.length - 1);
const hostAndSegments = [host, ...pathSegments].join("/");
const exchangeBaseUrl = canonicalizeBaseUrl(
@@ -417,7 +521,6 @@ export function parseWithdrawExchangeUri(
return {
type: TalerUriAction.WithdrawExchange,
exchangeBaseUrl,
- exchangePub: exchangePub != "" ? exchangePub : undefined,
amount,
};
}
@@ -539,13 +642,19 @@ export function stringifyRestoreUri({
export function stringifyWithdrawExchange({
exchangeBaseUrl,
- exchangePub,
amount,
}: Omit<WithdrawExchangeUri, "type">): string {
const { proto, path, query } = getUrlInfo(exchangeBaseUrl, {
a: amount,
});
- return `${proto}://withdraw-exchange/${path}${exchangePub ?? ""}${query}`;
+ return `${proto}://withdraw-exchange/${path}${query}`;
+}
+
+export function stringifyAddExchange({
+ exchangeBaseUrl,
+}: Omit<AddExchangeUri, "type">): string {
+ const { proto, path } = getUrlInfo(exchangeBaseUrl);
+ return `${proto}://add-exchange/${path}`;
}
export function stringifyDevExperimentUri({
@@ -557,9 +666,8 @@ export function stringifyDevExperimentUri({
export function stringifyPayTemplateUri({
merchantBaseUrl,
templateId,
- templateParams,
}: Omit<PayTemplateUriResult, "type">): string {
- const { proto, path, query } = getUrlInfo(merchantBaseUrl, templateParams);
+ const { proto, path, query } = getUrlInfo(merchantBaseUrl);
return `${proto}://pay-template/${path}${templateId}${query}`;
}