Просмотр исходного кода

fix: add fallback for ShopRenter ENV names and include all params in HMAC #96

- Support both SHOPRENTER_APP_CLIENT_ID/SECRET and SHOPRENTER_CLIENT_ID/SECRET
- Include all query parameters (except hmac) in HMAC calculation
- Add logging to show which ENV variables are being used
Claude 5 месяцев назад
Родитель
Сommit
3ccb803a18
1 измененных файлов с 21 добавлено и 17 удалено
  1. 21 17
      supabase/functions/oauth-shoprenter-callback/index.ts

+ 21 - 17
supabase/functions/oauth-shoprenter-callback/index.ts

@@ -9,7 +9,7 @@ const corsHeaders = {
 }
 
 // Validate HMAC signature from ShopRenter
-// Per ShopRenter documentation, HMAC is calculated from code, shopname, and timestamp only
+// Per ShopRenter documentation, HMAC is calculated from all query params except 'hmac' itself
 function validateHMAC(params: URLSearchParams, clientSecret: string): boolean {
   if (!clientSecret) {
     console.error('[ShopRenter] Client secret is empty or undefined')
@@ -22,20 +22,20 @@ function validateHMAC(params: URLSearchParams, clientSecret: string): boolean {
     return false
   }
 
-  // Get the required parameters for HMAC validation
-  // Per ShopRenter docs, only code, shopname, and timestamp are included
-  const code = params.get('code')
-  const shopname = params.get('shopname')
-  const timestamp = params.get('timestamp')
-
-  if (!code || !shopname || !timestamp) {
-    console.error('[ShopRenter] Missing required parameters for HMAC validation')
-    return false
+  // Get all parameters except 'hmac' and sort them alphabetically
+  // Per ShopRenter docs pseudocode: const { hmac, ...dataToValidate } = params;
+  const dataToValidate: Record<string, string> = {}
+  for (const [key, value] of params.entries()) {
+    if (key !== 'hmac') {
+      dataToValidate[key] = value
+    }
   }
 
-  // Create query string with parameters in alphabetical order
-  // Per ShopRenter docs: code, shopname, timestamp (alphabetically sorted)
-  const sortedParams = `code=${code}&shopname=${shopname}&timestamp=${timestamp}`
+  // Sort parameters alphabetically by key and create query string
+  const sortedParams = Object.keys(dataToValidate)
+    .sort()
+    .map(key => `${key}=${dataToValidate[key]}`)
+    .join('&')
 
   console.log(`[ShopRenter] HMAC validation - sorted params: ${sortedParams}`)
   console.log(`[ShopRenter] HMAC validation - client secret length: ${clientSecret.length}`)
@@ -144,15 +144,19 @@ serve(wrapHandler('oauth-shoprenter-callback', async (req) => {
       )
     }
 
-    // Get environment variables
-    const shoprenterClientId = Deno.env.get('SHOPRENTER_APP_CLIENT_ID')
-    const shoprenterClientSecret = Deno.env.get('SHOPRENTER_APP_CLIENT_SECRET')
+    // Get environment variables (support both naming conventions with fallback)
+    const shoprenterClientId = Deno.env.get('SHOPRENTER_APP_CLIENT_ID') || Deno.env.get('SHOPRENTER_CLIENT_ID')
+    const shoprenterClientSecret = Deno.env.get('SHOPRENTER_APP_CLIENT_SECRET') || Deno.env.get('SHOPRENTER_CLIENT_SECRET')
     const frontendUrl = Deno.env.get('FRONTEND_URL') || 'https://shopcall.ai'
     const supabaseUrl = Deno.env.get('SUPABASE_URL')!
     const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
 
+    // Log which environment variables are being used
+    console.log(`[ShopRenter] Using client ID from: ${Deno.env.get('SHOPRENTER_APP_CLIENT_ID') ? 'SHOPRENTER_APP_CLIENT_ID' : 'SHOPRENTER_CLIENT_ID'}`)
+    console.log(`[ShopRenter] Using client secret from: ${Deno.env.get('SHOPRENTER_APP_CLIENT_SECRET') ? 'SHOPRENTER_APP_CLIENT_SECRET' : 'SHOPRENTER_CLIENT_SECRET'}`)
+
     if (!shoprenterClientId || !shoprenterClientSecret) {
-      console.error('SHOPRENTER_APP_CLIENT_ID or SHOPRENTER_APP_CLIENT_SECRET not configured')
+      console.error('ShopRenter client credentials not configured. Set either SHOPRENTER_APP_CLIENT_ID/SECRET or SHOPRENTER_CLIENT_ID/SECRET')
       return new Response(
         JSON.stringify({ error: 'ShopRenter integration not configured' }),
         { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }