Skip to content

Commit 4f3624d

Browse files
committed
feat: ebay
1 parent 295226e commit 4f3624d

3 files changed

Lines changed: 78 additions & 15 deletions

File tree

api/EBAY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,5 @@ Sans emplacement ni politiques, la publication est ignorée avec un message expl
6767
## 8. Dépannage rapide
6868

6969
- **Erreur à l’échange de code** : `redirect_uri` différent de celui enregistré chez eBay, ou `code` déjà consommé / expiré (les codes sont à usage unique et très courts).
70+
- **« User is not eligible for Business Policy »** (logs : erreur 20403 sur `fulfillment_policy`) : le compte doit être inscrit au programme **politiques métier**. L’API appelle automatiquement [`optInToProgram`](https://developer.ebay.com/api-docs/sell/account/resources/program/methods/optInToProgram) avec `SELLING_POLICY_MANAGEMENT` avant de charger les politiques ; eBay peut mettre **jusqu’à ~24 h** à activer — réessayez plus tard ou vérifiez avec `getOptedInPrograms`.
7071
- **Erreur à la publication** : catégorie incorrecte, politiques incompatibles avec le marketplace, ou **descripteurs d’état** obligatoires pour certaines catégories cartes — consultez les messages d’erreur dans les logs API (`ebay_body`).

api/routes/ebay_route.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
fetch_inventory_locations,
2727
fetch_payment_policies,
2828
fetch_return_policies,
29+
opt_in_selling_policy_management,
2930
)
3031
from services.user_settings_service import ebay_listing_config_complete, get_or_create_user_settings
3132

@@ -158,6 +159,7 @@ async def ebay_seller_setup(
158159
raise HTTPException(status_code=400, detail=str(exc)) from exc
159160
mp = (marketplace_id or ms.ebay_marketplace_id or "EBAY_FR").strip()
160161
try:
162+
await opt_in_selling_policy_management(token)
161163
locations = await fetch_inventory_locations(token)
162164
fulfillment = await fetch_fulfillment_policies(token, mp)
163165
payment = await fetch_payment_policies(token, mp)

api/services/ebay_seller_metadata_service.py

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,83 @@
1212

1313
logger = logging.getLogger(__name__)
1414

15+
# Locale per marketplace (eBay REST request components) for Account API policy calls.
16+
_MARKETPLACE_LOCALE: dict[str, str] = {
17+
"EBAY_US": "en-US",
18+
"EBAY_MOTORS_US": "en-US",
19+
"EBAY_AT": "de-AT",
20+
"EBAY_AU": "en-AU",
21+
"EBAY_BE": "fr-BE",
22+
"EBAY_CA": "fr-CA",
23+
"EBAY_CH": "de-CH",
24+
"EBAY_DE": "de-DE",
25+
"EBAY_ES": "es-ES",
26+
"EBAY_FR": "fr-FR",
27+
"EBAY_GB": "en-GB",
28+
"EBAY_HK": "zh-HK",
29+
"EBAY_IE": "en-IE",
30+
"EBAY_IT": "it-IT",
31+
"EBAY_MY": "en-US",
32+
"EBAY_NL": "nl-NL",
33+
"EBAY_PH": "en-PH",
34+
"EBAY_PL": "pl-PL",
35+
"EBAY_SG": "en-US",
36+
"EBAY_TW": "zh-TW",
37+
}
38+
39+
40+
def _marketplace_locale(marketplace_id: str) -> str:
41+
return _MARKETPLACE_LOCALE.get(marketplace_id.strip().upper(), "en-US")
42+
1543

1644
def _marketplace_header(marketplace_id: str) -> dict[str, str]:
1745
return {"X-EBAY-C-MARKETPLACE-ID": marketplace_id.strip()}
1846

1947

48+
def _account_api_headers(access_token: str, marketplace_id: str) -> dict[str, str]:
49+
loc = _marketplace_locale(marketplace_id)
50+
return {
51+
"Authorization": f"Bearer {access_token}",
52+
"Content-Type": "application/json",
53+
"Accept": "application/json",
54+
"Content-Language": loc,
55+
"Accept-Language": loc,
56+
**_marketplace_header(marketplace_id),
57+
}
58+
59+
60+
async def opt_in_selling_policy_management(access_token: str, *, app: AppSettings | None = None) -> None:
61+
"""
62+
Opt seller into Business Policies (SELLING_POLICY_MANAGEMENT).
63+
Fixes "User is not eligible for Business Policy" on getFulfillmentPolicies; eBay may take up to ~24h
64+
to process (see getOptedInPrograms).
65+
"""
66+
s = app or get_settings()
67+
root = _api_base_url(s)
68+
url = f"{root}/sell/account/v1/program/opt_in"
69+
headers = {
70+
"Authorization": f"Bearer {access_token}",
71+
"Content-Type": "application/json",
72+
}
73+
async with httpx.AsyncClient(timeout=60.0) as client:
74+
resp = await client.post(
75+
url,
76+
json={"programType": "SELLING_POLICY_MANAGEMENT"},
77+
headers=headers,
78+
)
79+
if resp.status_code == 200:
80+
logger.info("eBay opt_in SELLING_POLICY_MANAGEMENT: OK")
81+
return
82+
if resp.status_code in (409, 400):
83+
logger.info(
84+
"eBay opt_in SELLING_POLICY_MANAGEMENT: %s %s (often already opted in or pending)",
85+
resp.status_code,
86+
resp.text[:400],
87+
)
88+
return
89+
logger.warning("eBay opt_in SELLING_POLICY_MANAGEMENT: %s %s", resp.status_code, resp.text[:500])
90+
91+
2092
async def fetch_inventory_locations(access_token: str, *, app: AppSettings | None = None) -> list[dict[str, Any]]:
2193
s = app or get_settings()
2294
root = _api_base_url(s)
@@ -57,11 +129,7 @@ async def fetch_fulfillment_policies(
57129
s = app or get_settings()
58130
root = _api_base_url(s)
59131
url = f"{root}/sell/account/v1/fulfillment_policy"
60-
headers = {
61-
"Authorization": f"Bearer {access_token}",
62-
"Content-Type": "application/json",
63-
**_marketplace_header(marketplace_id),
64-
}
132+
headers = _account_api_headers(access_token, marketplace_id)
65133
async with httpx.AsyncClient(timeout=60.0) as client:
66134
resp = await client.get(url, params={"marketplace_id": marketplace_id}, headers=headers)
67135
if resp.status_code >= 400:
@@ -85,11 +153,7 @@ async def fetch_payment_policies(
85153
s = app or get_settings()
86154
root = _api_base_url(s)
87155
url = f"{root}/sell/account/v1/payment_policy"
88-
headers = {
89-
"Authorization": f"Bearer {access_token}",
90-
"Content-Type": "application/json",
91-
**_marketplace_header(marketplace_id),
92-
}
156+
headers = _account_api_headers(access_token, marketplace_id)
93157
async with httpx.AsyncClient(timeout=60.0) as client:
94158
resp = await client.get(url, params={"marketplace_id": marketplace_id}, headers=headers)
95159
if resp.status_code >= 400:
@@ -113,11 +177,7 @@ async def fetch_return_policies(
113177
s = app or get_settings()
114178
root = _api_base_url(s)
115179
url = f"{root}/sell/account/v1/return_policy"
116-
headers = {
117-
"Authorization": f"Bearer {access_token}",
118-
"Content-Type": "application/json",
119-
**_marketplace_header(marketplace_id),
120-
}
180+
headers = _account_api_headers(access_token, marketplace_id)
121181
async with httpx.AsyncClient(timeout=60.0) as client:
122182
resp = await client.get(url, params={"marketplace_id": marketplace_id}, headers=headers)
123183
if resp.status_code >= 400:

0 commit comments

Comments
 (0)