33from __future__ import annotations
44
55import base64
6+ import json
67import logging
78from typing import Any
89from urllib .parse import urlencode
2021]
2122
2223
24+ class EbayOAuthError (RuntimeError ):
25+ """Erreur OAuth eBay exploitable côté UI (code + message lisibles)."""
26+
27+ def __init__ (self , code : str , message : str , * , status : int | None = None ) -> None :
28+ super ().__init__ (code )
29+ self .code = code
30+ self .message = message
31+ self .status = status
32+
33+ def __str__ (self ) -> str :
34+ return self .message or self .code
35+
36+
37+ def _describe_ebay_oauth_error (status : int , body : str ) -> EbayOAuthError :
38+ """Transforme un body 4xx eBay (`{"error": "...", "error_description": "..."}`)
39+ en exception `RuntimeError` lisible, avec un code applicatif stable pour l'UI."""
40+ payload : dict [str , Any ] = {}
41+ try :
42+ parsed = json .loads (body ) if body else {}
43+ if isinstance (parsed , dict ):
44+ payload = parsed
45+ except ValueError :
46+ payload = {}
47+ err = str (payload .get ("error" ) or "" ).strip ()
48+ desc = str (payload .get ("error_description" ) or "" ).strip ()
49+
50+ if err == "invalid_scope" :
51+ return EbayOAuthError (
52+ "ebay_scope_mismatch" ,
53+ "Votre consentement eBay n'inclut pas tous les scopes demandés. "
54+ "Reconnectez votre compte eBay dans les Paramètres pour accorder les nouveaux accès." ,
55+ status = status ,
56+ )
57+ if err in {"invalid_grant" , "invalid_token" }:
58+ return EbayOAuthError (
59+ "ebay_refresh_token_invalid" ,
60+ "Le refresh token eBay a expiré ou a été révoqué. Reconnectez votre compte eBay." ,
61+ status = status ,
62+ )
63+ if err == "invalid_client" :
64+ return EbayOAuthError (
65+ "ebay_client_invalid" ,
66+ "Identifiants application eBay invalides (client_id / client_secret). "
67+ "Vérifiez la configuration serveur." ,
68+ status = status ,
69+ )
70+
71+ message = desc or err or f"Erreur eBay ({ status } )."
72+ return EbayOAuthError (err or "ebay_token_http_error" , message , status = status )
73+
74+
2375def _auth_base_url (app : AppSettings ) -> str :
2476 return "https://auth.sandbox.ebay.com" if app .ebay_use_sandbox else "https://auth.ebay.com"
2577
@@ -78,19 +130,28 @@ async def exchange_authorization_code(code: str, *, app: AppSettings | None = No
78130 resp = await client .post (url , data = data , headers = headers )
79131 if resp .status_code >= 400 :
80132 logger .warning ("eBay token exchange failed: %s %s" , resp .status_code , resp .text [:500 ])
81- resp .raise_for_status ( )
133+ raise _describe_ebay_oauth_error ( resp .status_code , resp . text )
82134 return resp .json ()
83135
84136
85137async def refresh_user_access_token (refresh_token : str , * , app : AppSettings | None = None ) -> dict [str , Any ]:
138+ """Rafraîchit l'access token utilisateur.
139+
140+ On ne force **volontairement pas** le paramètre ``scope`` : eBay renvoie alors
141+ un token portant les scopes tels qu'ils ont été consentis au moment de la
142+ connexion. C'est indispensable quand ``EBAY_SCOPES`` évolue (ajout de
143+ ``sell.fulfillment`` par exemple) : les utilisateurs déjà connectés avant
144+ l'ajout continuent de publier (inventory + account) sans reconsentement ;
145+ les fonctionnalités nécessitant le nouveau scope détecteront son absence et
146+ proposeront la reconnexion.
147+ """
86148 s = app or get_settings ()
87149 if not ebay_oauth_configured (s ):
88150 raise RuntimeError ("ebay_oauth_not_configured" )
89151 url = f"{ _api_base_url (s )} /identity/v1/oauth2/token"
90152 data = {
91153 "grant_type" : "refresh_token" ,
92154 "refresh_token" : refresh_token .strip (),
93- "scope" : " " .join (EBAY_SCOPES ),
94155 }
95156 headers = {
96157 "Content-Type" : "application/x-www-form-urlencoded" ,
@@ -100,5 +161,5 @@ async def refresh_user_access_token(refresh_token: str, *, app: AppSettings | No
100161 resp = await client .post (url , data = data , headers = headers )
101162 if resp .status_code >= 400 :
102163 logger .warning ("eBay refresh failed: %s %s" , resp .status_code , resp .text [:500 ])
103- resp .raise_for_status ( )
164+ raise _describe_ebay_oauth_error ( resp .status_code , resp . text )
104165 return resp .json ()
0 commit comments