From 70926f609c766d47235a1fc47f189482bc9052bd Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 13 Jun 2024 15:22:43 -0300 Subject: fix encoding payto parms --- packages/taler-util/src/payto.test.ts | 9 ++++- packages/taler-util/src/payto.ts | 64 +++++++++++++++++++++++------------ 2 files changed, 51 insertions(+), 22 deletions(-) (limited to 'packages/taler-util') diff --git a/packages/taler-util/src/payto.test.ts b/packages/taler-util/src/payto.test.ts index 66a05b3a2..0802e1cd4 100644 --- a/packages/taler-util/src/payto.test.ts +++ b/packages/taler-util/src/payto.test.ts @@ -16,7 +16,7 @@ import test from "ava"; -import { parsePaytoUri } from "./payto.js"; +import { PaytoString, parsePaytoUri, stringifyPaytoUri } from "./payto.js"; test("basic payto parsing", (t) => { const r1 = parsePaytoUri("https://example.com/"); @@ -29,3 +29,10 @@ test("basic payto parsing", (t) => { t.is(r3?.targetType, "x-taler-bank"); t.is(r3?.targetPath, "123"); }); + +test("parsing payto and stringify again", (t) => { + const payto1 = + "payto://iban/DE1231231231?reciever-name=John%20Doe" as PaytoString; + + t.is(stringifyPaytoUri(parsePaytoUri(payto1)!), payto1); +}); diff --git a/packages/taler-util/src/payto.ts b/packages/taler-util/src/payto.ts index ac21fc398..ff33519c5 100644 --- a/packages/taler-util/src/payto.ts +++ b/packages/taler-util/src/payto.ts @@ -15,7 +15,14 @@ */ import { generateFakeSegwitAddress } from "./bitcoin.js"; -import { Codec, Context, DecodingError, buildCodecForObject, codecForStringURL, renderContext } from "./codec.js"; +import { + Codec, + Context, + DecodingError, + buildCodecForObject, + codecForStringURL, + renderContext, +} from "./codec.js"; import { AccessToken, codecForAccessToken, codecOptional } from "./index.js"; import { URLSearchParams } from "./url.js"; @@ -152,15 +159,33 @@ export function addPaytoQueryParams( params: { [name: string]: string }, ): string { const [acct, search] = s.slice(paytoPfx.length).split("?"); - const searchParams = new URLSearchParams(search || ""); - const keys = Object.keys(params); - if (keys.length === 0) { + const paramList = !params ? [] : Object.entries(params); + if (paramList.length === 0) { return paytoPfx + acct; } - for (const k of keys) { - searchParams.set(k, params[k]); - } - return paytoPfx + acct + "?" + searchParams.toString(); + return paytoPfx + acct + "?" + createSearchParams(paramList); + +} + +/** + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#encoding_for_rfc3986 + */ +function encodeRFC3986URIComponent(str: string): string { + return encodeURIComponent(str).replace( + /[!'()*]/g, + (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`, + ); +} +const rfc3986 = encodeRFC3986URIComponent; + +/** + * + * https://www.rfc-editor.org/rfc/rfc3986 + */ +function createSearchParams(paramList: [string, string][]): string { + return paramList + .map(([key, value]) => `${rfc3986(key)}=${rfc3986(value)}`) + .join("&"); } /** @@ -172,9 +197,7 @@ export function addPaytoQueryParams( export function stringifyPaytoUri(p: PaytoUri): PaytoString { const url = new URL(`${paytoPfx}${p.targetType}/${p.targetPath}`); const paramList = !p.params ? [] : Object.entries(p.params); - paramList.forEach(([key, value]) => { - url.searchParams.set(key, value); - }); + url.search = createSearchParams(paramList) return url.href as PaytoString; } @@ -206,7 +229,7 @@ export function parsePaytoUri(s: string): PaytoUri | undefined { const searchParams = new URLSearchParams(search || ""); searchParams.forEach((v, k) => { - params[k] = v; + params[k] = decodeURIComponent(v); }); if (targetType === "x-taler-bank") { @@ -295,9 +318,9 @@ export function talerPaytoFromExchangeReserve( /** * The account letter is all the information - * the merchant backend requires from the + * the merchant backend requires from the * bank account to check transfer. - * + * */ export type AccountLetter = { accountURI: PaytoString; @@ -305,10 +328,9 @@ export type AccountLetter = { accountToken?: AccessToken; }; -export const codecForAccountLetter = - (): Codec => - buildCodecForObject() - .property("infoURL", codecForStringURL(true)) - .property("accountURI", codecForPaytoString()) - .property("accountToken", codecOptional(codecForAccessToken())) - .build("AccountLetter"); +export const codecForAccountLetter = (): Codec => + buildCodecForObject() + .property("infoURL", codecForStringURL(true)) + .property("accountURI", codecForPaytoString()) + .property("accountToken", codecOptional(codecForAccessToken())) + .build("AccountLetter"); -- cgit v1.2.3