From 07cdfb2e4ec761021477271776b81f33af0e731d Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 17 Mar 2021 17:56:37 +0100 Subject: towards wallet-core / util split --- packages/taler-util/src/taleruri.ts | 220 ++++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 packages/taler-util/src/taleruri.ts (limited to 'packages/taler-util/src/taleruri.ts') diff --git a/packages/taler-util/src/taleruri.ts b/packages/taler-util/src/taleruri.ts new file mode 100644 index 000000000..d8366fd0f --- /dev/null +++ b/packages/taler-util/src/taleruri.ts @@ -0,0 +1,220 @@ +/* + This file is part of GNU Taler + (C) 2019-2020 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 { canonicalizeBaseUrl } from "./helpers"; +import { URLSearchParams } from "./url"; + +export interface PayUriResult { + merchantBaseUrl: string; + orderId: string; + sessionId: string; + claimToken: string | undefined; +} + +export interface WithdrawUriResult { + bankIntegrationApiBaseUrl: string; + withdrawalOperationId: string; +} + +export interface RefundUriResult { + merchantBaseUrl: string; + orderId: string; +} + +export interface TipUriResult { + merchantTipId: string; + merchantBaseUrl: 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; + } + const parts = pi.rest.split("/"); + + if (parts.length < 2) { + return undefined; + } + + const host = parts[0].toLowerCase(); + const pathSegments = parts.slice(1, parts.length - 1); + const withdrawId = parts[parts.length - 1]; + const p = [host, ...pathSegments].join("/"); + + return { + bankIntegrationApiBaseUrl: canonicalizeBaseUrl(`${pi.innerProto}://${p}/`), + withdrawalOperationId: withdrawId, + }; +} + +export enum TalerUriType { + TalerPay = "taler-pay", + TalerWithdraw = "taler-withdraw", + TalerTip = "taler-tip", + TalerRefund = "taler-refund", + TalerNotifyReserve = "taler-notify-reserve", + Unknown = "unknown", +} + +/** + * Classify a taler:// URI. + */ +export function classifyTalerUri(s: string): TalerUriType { + const sl = s.toLowerCase(); + if (sl.startsWith("taler://pay/")) { + return TalerUriType.TalerPay; + } + if (sl.startsWith("taler+http://pay/")) { + return TalerUriType.TalerPay; + } + if (sl.startsWith("taler://tip/")) { + return TalerUriType.TalerTip; + } + if (sl.startsWith("taler+http://tip/")) { + return TalerUriType.TalerTip; + } + if (sl.startsWith("taler://refund/")) { + return TalerUriType.TalerRefund; + } + if (sl.startsWith("taler+http://refund/")) { + return TalerUriType.TalerRefund; + } + if (sl.startsWith("taler://withdraw/")) { + return TalerUriType.TalerWithdraw; + } + if (sl.startsWith("taler+http://withdraw/")) { + return TalerUriType.TalerWithdraw; + } + if (sl.startsWith("taler://notify-reserve/")) { + return TalerUriType.TalerNotifyReserve; + } + return TalerUriType.Unknown; +} + +interface TalerUriProtoInfo { + innerProto: "http" | "https"; + rest: string; +} + +function parseProtoInfo( + s: string, + action: string, +): TalerUriProtoInfo | undefined { + const pfxPlain = `taler://${action}/`; + const pfxHttp = `taler+http://${action}/`; + if (s.toLowerCase().startsWith(pfxPlain)) { + return { + innerProto: "https", + rest: s.substring(pfxPlain.length), + }; + } else if (s.toLowerCase().startsWith(pfxHttp)) { + return { + innerProto: "http", + rest: s.substring(pfxHttp.length), + }; + } else { + return undefined; + } +} + +/** + * 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) { + return undefined; + } + const c = pi?.rest.split("?"); + const q = new URLSearchParams(c[1] ?? ""); + const claimToken = q.get("c") ?? undefined; + const parts = c[0].split("/"); + if (parts.length < 3) { + return undefined; + } + 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("/"); + const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`); + + return { + merchantBaseUrl, + orderId, + sessionId: sessionId, + claimToken, + }; +} + +/** + * Parse a taler[+http]://tip URI. + * Return undefined if not passed a valid URI. + */ +export function parseTipUri(s: string): TipUriResult | undefined { + const pi = parseProtoInfo(s, "tip"); + 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 tipId = parts[parts.length - 1]; + const pathSegments = parts.slice(1, parts.length - 1); + const p = [host, ...pathSegments].join("/"); + const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`); + + return { + merchantBaseUrl, + merchantTipId: tipId, + }; +} + +/** + * Parse a taler[+http]://refund URI. + * Return undefined if not passed a valid URI. + */ +export function parseRefundUri(s: string): RefundUriResult | undefined { + const pi = parseProtoInfo(s, "refund"); + if (!pi) { + return undefined; + } + const c = pi?.rest.split("?"); + const parts = c[0].split("/"); + if (parts.length < 3) { + return undefined; + } + 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("/"); + const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`); + + return { + merchantBaseUrl, + orderId, + }; +} -- cgit v1.2.3