import { string } from 'alga-js' import refreshTokenHelper from "../../../utils/refreshTokenHelper" import fetchHelper from "../../../utils/fetchHelper" import getTokenHelper from "../../../utils/getTokenHelper" const LWA_TOKEN_URL = 'https://api.amazon.com/auth/o2/token' const SP_API_BASE_URL = 'https://sellingpartnerapi-eu.amazon.com' export default defineEventHandler(async (event) => { const config = useRuntimeConfig() const query = getQuery(event) const code = query.spapi_oauth_code as string | undefined const state = query.state as string | undefined // UID of the order source const sellingPartnerId = query.selling_partner_id as string | undefined // If the merchant declined or an error occurred on the Amazon side if (!code || !state) { const errorMsg = encodeURIComponent(query.error_description as string || 'Amazon authorization was declined or failed') return sendRedirect(event, `/settings/order-sources?oauth_error=${errorMsg}`, 302) } if (!config.api.amazonLwaClientId || !config.api.amazonLwaClientSecret || !config.api.amazonOauthRedirectUri) { const errorMsg = encodeURIComponent('Amazon OAuth is not configured on the server') return sendRedirect(event, `/settings/order-sources?oauth_error=${errorMsg}`, 302) } // Look up the order source by UID (validates the state param) let orderSource: any = null let token: string | null = null try { token = await getTokenHelper(event) const res: any = await fetchHelper( event, `models/c_ordersource?$filter=${string.urlEncode(`C_OrderSource_UU eq '${state}'`)}`, 'GET', token, null ) orderSource = res?.records?.[0] } catch (err: any) { try { token = await refreshTokenHelper(event) const res: any = await fetchHelper( event, `models/c_ordersource?$filter=${string.urlEncode(`C_OrderSource_UU eq '${state}'`)}`, 'GET', token, null ) orderSource = res?.records?.[0] } catch (error: any) { const errorMsg = encodeURIComponent('Failed to fetch order source during OAuth callback') return sendRedirect(event, `/settings/order-sources?oauth_error=${errorMsg}`, 302) } } if (!orderSource) { const errorMsg = encodeURIComponent('Order source not found') return sendRedirect(event, `/settings/order-sources?oauth_error=${errorMsg}`, 302) } const orderSourceId = orderSource.id // Exchange the LWA authorization code for a refresh token (our public app's credentials) let tokenResponse: any = null try { tokenResponse = await $fetch(LWA_TOKEN_URL, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', code: code, redirect_uri: String(config.api.amazonOauthRedirectUri), client_id: String(config.api.amazonLwaClientId), client_secret: String(config.api.amazonLwaClientSecret), }).toString(), }) } catch (err: any) { console.error('Amazon LWA token exchange error:', err?.data || err?.message || err) const errorMsg = encodeURIComponent(err?.data?.error_description || 'Failed to exchange authorization code for tokens') return sendRedirect(event, `/settings/order-sources/${orderSourceId}/edit?oauth_error=${errorMsg}`, 302) } if (!tokenResponse?.refresh_token) { const errorMsg = encodeURIComponent('Amazon did not return a refresh token') return sendRedirect(event, `/settings/order-sources/${orderSourceId}/edit?oauth_error=${errorMsg}`, 302) } // Persist onto the order source: our app's LWA credentials + the merchant's refresh // token (plain string — what every Amazon SP-API consumer expects in marketplace_token). // SHOPWARE_KEY/SECRET are written too so the legacy column pair stays in sync with // what the edit form reads/writes. const updatePayload: any = { marketplace_key: String(config.api.amazonLwaClientId), SHOPWARE_KEY: String(config.api.amazonLwaClientId), marketplace_secret: String(config.api.amazonLwaClientSecret), SHOPWARE_SECRET: String(config.api.amazonLwaClientSecret), marketplace_token: String(tokenResponse.refresh_token), tableName: 'c_ordersource', } if (sellingPartnerId) { updatePayload.amazon_merchant_id = sellingPartnerId } // Ensure the SP-API base URL is set — Laravel's env fallback is the SANDBOX endpoint. const currentUrl = String(orderSource.marketplace_url || '') if (!currentUrl.startsWith('https://sellingpartnerapi')) { updatePayload.marketplace_url = SP_API_BASE_URL } try { await fetchHelper(event, `models/c_ordersource/${orderSourceId}`, 'PUT', token, updatePayload) } catch (err: any) { try { token = await refreshTokenHelper(event) await fetchHelper(event, `models/c_ordersource/${orderSourceId}`, 'PUT', token, updatePayload) } catch (error: any) { console.error('Failed to save Amazon tokens:', error?.data || error?.message || error) const errorMsg = encodeURIComponent('Tokens obtained but failed to save to order source') return sendRedirect(event, `/settings/order-sources/${orderSourceId}/edit?oauth_error=${errorMsg}`, 302) } } return sendRedirect(event, `/settings/order-sources/${orderSourceId}/edit?oauth_success=amazon`, 302) })