基于Salesforce Commerce Cloud SDK的单例客户端令牌刷新失效问题
问题:SFCC访问令牌过期后未触发自动刷新逻辑,请求失败
我在Next.js服务端使用Salesforce commerce-sdk实现了单例PublicSFCCProductClient类,用于调用Salesforce Commerce Cloud API。该类设计为在access_token过期时自动刷新令牌并重试请求,但实际access_token过期后未触发刷新逻辑,请求失败。
PublicSFCCProductClient类实现代码
export class PublicSFCCProductClient { private static instance: PublicSFCCProductClient; private client: Product.ShopperProducts | null = null; private refreshToken: TokenResponse["refresh_token"] | null = null; private constructor() {} private static async getParameters(): Promise<ClientConfig> { const parameters = sfccParametersSchema.parse({ clientId: process.env.SFDC_CLIENT_ID, organizationId: process.env.SFDC_ORGANIZATIONID, shortCode: process.env.SFDC_SHORTCODE, siteId: process.env.SFDC_SITEID }); return { parameters }; } private static setAuthorization( config: ClientConfig, token: string ): ClientConfig { return { ...config, headers: { ...config.headers, authorization: `Bearer ${token}` } }; } private static async getGuestTokenResponse( config: ClientConfig ): Promise<TokenResponse> { try { const clientSecret = z.string().parse(process.env.SFDC_SECRET); const slasClient = new Customer.ShopperLogin(config); const guestTokenResponse = await slasHelpers.loginGuestUserPrivate( slasClient, { clientSecret } ); return guestTokenResponse; } catch (e: any) { console.error("Error logging guest customer"); console.error(await e.response.json()); throw e; } } private static async refreshAccessToken( config: ClientConfig, refreshToken: string ): Promise<TokenResponse> { const clientSecret = z.string().parse(process.env.SFDC_SECRET); const slasClient = new Customer.ShopperLogin(config); try { const refreshTokenResponse = await slasHelpers.refreshAccessTokenPrivate( slasClient, { clientSecret }, { refreshToken } ); return refreshTokenResponse; } catch (e: any) { console.error("Error refreshing token"); console.error(await e.response.json()); throw e; } } private async createSFCCProductClient(): Promise<Product.ShopperProducts> { const parameters = await PublicSFCCProductClient.getParameters(); const tokenResponse = await PublicSFCCProductClient.getGuestTokenResponse( parameters ); this.refreshToken = tokenResponse.refresh_token; const configWithToken = PublicSFCCProductClient.setAuthorization( parameters, tokenResponse.access_token ); const client = new Product.ShopperProducts(configWithToken); return client; } public async apiCallWithTokenRefresh(apiCall: () => Promise<any>) { try { return await apiCall(); } catch (e: any) { if (e?.response?.status === 401 && this.refreshToken) { // Access token expired, refresh it and retry the request console.error("access token expired, try to refresh", e); const parameters = await PublicSFCCProductClient.getParameters(); const refreshedTokenResponse = await PublicSFCCProductClient.refreshAccessToken( parameters, this.refreshToken ); this.refreshToken = refreshedTokenResponse.refresh_token; const newConfigWithToken = PublicSFCCProductClient.setAuthorization( parameters, refreshedTokenResponse.access_token ); this.client = new Product.ShopperProducts(newConfigWithToken); console.log( "new accessToken issued", refreshedTokenResponse.access_token ); // Retry the request with the new access token return apiCall(); } throw e; } } }
单例实例使用代码
const publicSFCCProductClientInstance = PublicSFCCProductClient.getInstance(); const publicProductsList = await getPublicSFCCProductsList({ clientInstance: publicSFCCProductClientInstance, productIds: productsIds, currency: cookies.currency, locale: cookies.locale });
getPublicSFCCProductsList函数实现代码
export async function getPublicSFCCProductsList({ clientInstance, productIds, currency, locale, expand }: { clientInstance: PublicSFCCProductClient; productIds: string; currency: string; locale: string; expand?: string[]; }): Promise<Product.ShopperProducts.ProductResult | undefined> { console.log(`Getting products: [${productIds}]`); const publicSFCCProductClient = await clientInstance.getClient(); const products = await clientInstance.apiCallWithTokenRefresh(async () => { try { return await publicSFCCProductClient.getProducts({ parameters: { ids: productIds, currency, locale, expand: !!expand ? expand : ["images", "prices", "availability", "variations"] } }); } catch (error) { console.error("error fetching products", error); } }); return products; }
产品可正常获取,但access_token过期后未触发刷新逻辑,请求失败,无法定位问题原因。
内容的提问来源于stack exchange,提问作者ufollettu
相关产品推荐
相关产品推荐

