From 412531f04ed88e4189b2f67dade1675f2ec46eaf Mon Sep 17 00:00:00 2001 From: David Lau Date: Fri, 29 May 2026 19:32:54 +0800 Subject: [PATCH] fix(signer): percent-encode query params in buildMessage to match server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Server's CanonicalQueryString uses url.QueryEscape on all keys and values, but buildMessage was using raw string interpolation. Cursor values (base64) contain +, =, / which diverge under encoding — causing 401 on all paginated signed requests (e.g. portfolio holdings page 2+). Co-Authored-By: Claude Sonnet 4.6 --- src/client/signer.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/client/signer.ts b/src/client/signer.ts index 619a658..a1a67d4 100644 --- a/src/client/signer.ts +++ b/src/client/signer.ts @@ -44,11 +44,12 @@ export function buildMessage( const sortedQs = Object.keys(queryParams) .sort() .flatMap((k) => { + const ek = encodeURIComponent(k); const v = queryParams[k]; if (Array.isArray(v)) { - return [...v].sort().map((item) => `${k}=${item}`); + return [...v].sort().map((item) => `${ek}=${encodeURIComponent(item)}`); } - return [`${k}=${v}`]; + return [`${ek}=${encodeURIComponent(String(v))}`]; }) .join("&"); return `${subPath}:${sortedQs}:${body}:${timestamp}`;