From 1c906c5f6cc69163fc02a1d66b9eea80b11461b1 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 16 Apr 2026 19:56:10 +0100 Subject: [PATCH] feat: auto opt-in to Business Policies and create default shipping/payment/returns policies on first post --- .../Services/EbayListingService.cs | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/EbayListingTool/Services/EbayListingService.cs b/EbayListingTool/Services/EbayListingService.cs index 0ca60ab..2ddfe55 100644 --- a/EbayListingTool/Services/EbayListingService.cs +++ b/EbayListingTool/Services/EbayListingService.cs @@ -203,6 +203,111 @@ public class EbayListingService return $"Could not fetch {policyType} from eBay. Please check your account settings."; } + /// + /// Called automatically on first 20403 error. Opts the account in to SELLING_POLICY_MANAGEMENT + /// then creates minimal default policies so posting can proceed without manual eBay setup. + /// + private async Task SetupDefaultBusinessPoliciesAsync(string token) + { + // Step 1: opt in to the Business Policies programme + try + { + var optInBody = JsonConvert.SerializeObject(new { programType = "SELLING_POLICY_MANAGEMENT" }); + using var req = MakeRequest(HttpMethod.Post, + $"{_auth.BaseUrl}/sell/account/v1/program/opt_in", token); + req.Content = new StringContent(optInBody, Encoding.UTF8, "application/json"); + await _http.SendAsync(req); // 204 = success; ignore other responses + } + catch { /* opt-in failure is non-fatal — account may already be opted in */ } + + // Step 2: create one policy of each required type (ignore errors — they may already exist) + await TryCreateFulfillmentPolicyAsync(token); + await TryCreatePaymentPolicyAsync(token); + await TryCreateReturnPolicyAsync(token); + } + + private async Task TryCreateFulfillmentPolicyAsync(string token) + { + try + { + var body = new + { + name = "Standard UK Shipping", + marketplaceId = "EBAY_GB", + categoryTypes = new[] { new { name = "ALL_EXCLUDING_MOTORS_VEHICLES" } }, + handlingTime = new { value = 1, unit = "DAY" }, + shippingOptions = new[] + { + new + { + optionType = "DOMESTIC", + costType = "FLAT_RATE", + shippingServices = new[] + { + new + { + sortOrder = 1, + shippingCarrierCode = "ROYALMAIL", + shippingServiceCode = "UK_RoyalMailSecondClass", + shippingCost = new { value = "2.85", currency = "GBP" }, + additionalShippingCost = new { value = "0.00", currency = "GBP" } + } + } + } + } + }; + using var req = MakeRequest(HttpMethod.Post, + $"{_auth.BaseUrl}/sell/account/v1/fulfillment_policy", token); + req.Content = new StringContent( + JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"); + await _http.SendAsync(req); + } + catch { } + } + + private async Task TryCreatePaymentPolicyAsync(string token) + { + try + { + var body = new + { + name = "Managed Payments", + marketplaceId = "EBAY_GB", + categoryTypes = new[] { new { name = "ALL_EXCLUDING_MOTORS_VEHICLES" } }, + paymentMethods = Array.Empty() + }; + using var req = MakeRequest(HttpMethod.Post, + $"{_auth.BaseUrl}/sell/account/v1/payment_policy", token); + req.Content = new StringContent( + JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"); + await _http.SendAsync(req); + } + catch { } + } + + private async Task TryCreateReturnPolicyAsync(string token) + { + try + { + var body = new + { + name = "Standard Returns", + marketplaceId = "EBAY_GB", + categoryTypes = new[] { new { name = "ALL_EXCLUDING_MOTORS_VEHICLES" } }, + returnsAccepted = true, + returnPeriod = new { value = 30, unit = "DAY" }, + refundMethod = "MONEY_BACK", + returnShippingCostPayer = "BUYER" + }; + using var req = MakeRequest(HttpMethod.Post, + $"{_auth.BaseUrl}/sell/account/v1/return_policy", token); + req.Content = new StringContent( + JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"); + await _http.SendAsync(req); + } + catch { } + } + private async Task CreateMerchantLocationAsync(string token, string postcode) { if (string.IsNullOrWhiteSpace(postcode))