From 125bed3ad76f432ef1bce32796c28ae637061660 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Fri, 3 Apr 2026 03:58:09 +0000 Subject: [PATCH] fix: try gateway for Transak token refresh before legacy API Production gateway rejects tokens from api.transak.com. Try getting the access token from the gateway endpoint first (which staging confirms works), then fall back to the legacy API endpoint. Co-Authored-By: Claude Opus 4.6 (1M context) --- shared/transak.ts | 63 ++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/shared/transak.ts b/shared/transak.ts index 164db3e..4187f56 100644 --- a/shared/transak.ts +++ b/shared/transak.ts @@ -13,16 +13,18 @@ export type TransakEnv = 'STAGING' | 'PRODUCTION'; -const API_URLS = { - STAGING: 'https://api-stg.transak.com', - PRODUCTION: 'https://api.transak.com', -} as const; - +// Both token refresh and session creation go through the gateway const GATEWAY_URLS = { STAGING: 'https://api-gateway-stg.transak.com', PRODUCTION: 'https://api-gateway.transak.com', } as const; +// Fallback: legacy partner API (if gateway token refresh fails) +const API_URLS = { + STAGING: 'https://api-stg.transak.com', + PRODUCTION: 'https://api.transak.com', +} as const; + // Cached access token (valid for 7 days) let _cachedToken: { token: string; expiresAt: number; env: TransakEnv } | null = null; @@ -76,30 +78,39 @@ async function getAccessToken(): Promise { const apiSecret = getTransakApiSecret(); if (!apiKey || !apiSecret) throw new Error('Transak API key or secret not configured'); - const apiUrl = API_URLS[env]; - const res = await fetch(`${apiUrl}/partners/api/v2/refresh-token`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'api-secret': apiSecret, - }, - body: JSON.stringify({ apiKey }), - }); + const body = JSON.stringify({ apiKey }); + const headers = { 'Content-Type': 'application/json', 'api-secret': apiSecret }; - if (!res.ok) { - const err = await res.text(); - throw new Error(`Transak refresh-token failed: ${res.status} ${err}`); + // Try gateway first (required for production session tokens), then legacy API + const urls = [ + `${GATEWAY_URLS[env]}/partners/api/v2/refresh-token`, + `${API_URLS[env]}/partners/api/v2/refresh-token`, + ]; + + let lastError = ''; + for (const url of urls) { + try { + const res = await fetch(url, { method: 'POST', headers, body }); + if (!res.ok) { + lastError = `${url}: ${res.status} ${await res.text()}`; + console.warn(`[transak] Token refresh failed at ${url}: ${res.status}`); + continue; + } + const data = await res.json() as { data: { accessToken: string; expiresAt: number } }; + _cachedToken = { + token: data.data.accessToken, + expiresAt: data.data.expiresAt, + env, + }; + console.log(`[transak] Access token refreshed from ${url} (env=${env}, expires=${new Date(data.data.expiresAt * 1000).toISOString()})`); + return _cachedToken.token; + } catch (err) { + lastError = `${url}: ${err}`; + console.warn(`[transak] Token refresh error at ${url}:`, err); + } } - const data = await res.json() as { data: { accessToken: string; expiresAt: number } }; - _cachedToken = { - token: data.data.accessToken, - expiresAt: data.data.expiresAt, - env, - }; - - console.log(`[transak] Access token refreshed (env=${env}, expires=${new Date(data.data.expiresAt * 1000).toISOString()})`); - return _cachedToken.token; + throw new Error(`Transak refresh-token failed on all endpoints: ${lastError}`); } /**