import { NextFetchEvent, NextMiddleware, NextResponse } from 'next/server'; import Settings from 'settings'; import logger from '../utils/log'; import { getUrlPathWithLocale } from '../utils/localization'; import { PzNextRequest } from '.'; import { getCheckoutPath } from '../utils'; const withMasterpassRestCallback = (middleware: NextMiddleware) => async (req: PzNextRequest, event: NextFetchEvent) => { const url = req.nextUrl.clone(); const ip = req.headers.get('x-forwarded-for') ?? ''; const sessionId = req.cookies.get('osessionid'); if (!url.pathname.includes('/orders/masterpass-rest-callback')) { return middleware(req, event); } if (req.method !== 'POST') { logger.warn('Invalid request method for masterpass REST callback', { middleware: 'masterpass-rest-callback', method: req.method, ip }); return NextResponse.redirect( `${url.origin}${getUrlPathWithLocale( '/orders/checkout/', req.cookies.get('pz-locale')?.value )}`, 303 ); } const responseCode = url.searchParams.get('responseCode'); const token = url.searchParams.get('token'); if (!responseCode || !token) { logger.warn('Missing required parameters for masterpass REST callback', { middleware: 'masterpass-rest-callback', responseCode, token, ip }); return NextResponse.redirect( `${url.origin}${getUrlPathWithLocale( '/orders/checkout/', req.cookies.get('pz-locale')?.value )}`, 303 ); } try { const formData = await req.formData(); const body: Record = {}; Array.from(formData.entries()).forEach(([key, value]) => { body[key] = value.toString(); }); if (!sessionId) { logger.warn( 'Make sure that the SESSION_COOKIE_SAMESITE environment variable is set to None in Commerce.', { middleware: 'masterpass-rest-callback', ip } ); return NextResponse.redirect( `${url.origin}${getUrlPathWithLocale( '/orders/checkout/', req.cookies.get('pz-locale')?.value )}`, 303 ); } const isPostCheckout = req.cookies.get('pz-post-checkout-flow')?.value === 'true'; const requestUrl = new URL(getCheckoutPath(isPostCheckout), Settings.commerceUrl); requestUrl.searchParams.set('page', 'MasterpassRestCompletePage'); requestUrl.searchParams.set('responseCode', responseCode); requestUrl.searchParams.set('token', token); requestUrl.searchParams.set( 'three_d_secure', body.transactionType?.includes('3D') ? 'true' : 'false' ); requestUrl.searchParams.set( 'transactionType', body.transactionType || '' ); const requestHeaders = { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Requested-With': 'XMLHttpRequest', Cookie: req.headers.get('cookie') ?? '', 'x-currency': req.cookies.get('pz-currency')?.value ?? '', 'x-forwarded-for': ip, 'User-Agent': req.headers.get('user-agent') ?? '' }; const request = await fetch(requestUrl.toString(), { method: 'POST', headers: requestHeaders, body: new URLSearchParams(body) }); logger.info('Masterpass REST callback request', { requestUrl: requestUrl.toString(), status: request.status, requestHeaders, ip }); const response = await request.json(); const { context_list: contextList, errors } = response; let redirectUrl = response.redirect_url; if (!redirectUrl && contextList && contextList.length > 0) { for (const context of contextList) { if (context.page_context && context.page_context.redirect_url) { redirectUrl = context.page_context.redirect_url; break; } } } if (errors && Object.keys(errors).length) { logger.error('Error while processing masterpass REST callback', { middleware: 'masterpass-rest-callback', errors, requestHeaders, ip }); const errorResponse = NextResponse.redirect( `${url.origin}${getUrlPathWithLocale( '/orders/checkout/', req.cookies.get('pz-locale')?.value )}`, { status: 303, headers: { 'Set-Cookie': `pz-pos-error=${encodeURIComponent(JSON.stringify(errors))}; path=/;` } } ); // Add error cookie errorResponse.cookies.set('pz-pos-error', JSON.stringify(errors), { path: '/' }); return errorResponse; } logger.info('Masterpass REST callback response', { middleware: 'masterpass-rest-callback', contextList, redirectUrl, ip }); if (!redirectUrl) { logger.warn( 'No redirection url found in response. Redirecting to checkout page.', { middleware: 'masterpass-rest-callback', requestHeaders, response: JSON.stringify(response), ip } ); const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale( '/orders/checkout/', req.cookies.get('pz-locale')?.value )}`; return NextResponse.redirect(redirectUrlWithLocale, 303); } const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale( redirectUrl, req.cookies.get('pz-locale')?.value )}`; logger.info('Redirecting after masterpass REST callback', { middleware: 'masterpass-rest-callback', redirectUrl: redirectUrlWithLocale, ip }); const nextResponse = NextResponse.redirect(redirectUrlWithLocale, 303); // Forward set-cookie headers from the upstream response const setCookieHeader = request.headers.get('set-cookie'); if (setCookieHeader) { setCookieHeader.split(',').forEach((cookie) => { nextResponse.headers.append('Set-Cookie', cookie.trim()); }); } return nextResponse; } catch (error) { logger.error('Error while processing masterpass REST callback', { middleware: 'masterpass-rest-callback', error, requestHeaders: { Cookie: req.headers.get('cookie') ?? '', 'x-currency': req.cookies.get('pz-currency')?.value ?? '' }, ip }); return NextResponse.redirect( `${url.origin}${getUrlPathWithLocale( '/orders/checkout/', req.cookies.get('pz-locale')?.value )}`, 303 ); } }; export default withMasterpassRestCallback;