I'm having an issue while trying to integrate PayPal payments in my Next.js application. Specifically, I'm receiving the following error:
app-index.js:32 Error processing PayPal payment: Error: Failed to create order at handleApprove (index.tsx:75:15)
I have a backend folder called endpoints that handles the PayPal order creation. Inside the folder I have two specific files that focus on PayPal payment the first one is a create-payment-intent.ts:
// eslint-disable-next-line simple-import-sort/importsimport type { PayloadHandler } from 'payload/config'import axios from 'axios'import type { CartItems } from '../payload-types'const PAYPAL_API_BASE = 'https://sandbox.paypal.com'const getPayPalAccessToken = async (): Promise<string> => { const clientId = process.env.PAYPAL_CLIENT_ID || '' const clientSecret = process.env.PAYPAL_CLIENT_SECRET || '' const auth = Buffer.from(`${clientId}:${clientSecret}`).toString('base64') const response = await axios.post( `${PAYPAL_API_BASE}/v1/oauth2/token`,'grant_type=client_credentials', { headers: { Authorization: `Basic ${auth}`,'Content-Type': 'application/x-www-form-urlencoded', }, }, ) return response.data.access_token}export const createPayPalOrder: PayloadHandler = async (req, res): Promise<void> => { const { user, payload } = req if (!user) { res.status(401).send('Unauthorized') return } const fullUser = await payload.findByID({ collection: 'users', id: user?.id }) if (!fullUser) { res.status(404).json({ error: 'User not found' }) return } try { let total = 0 const hasItems = fullUser?.cart?.items?.length > 0 if (!hasItems) { throw new Error('No items in cart') } await Promise.all( fullUser?.cart?.items?.map(async (item: CartItems[0]): Promise<null> => { const { product, quantity } = item if (!quantity) { return null } if (typeof product === 'string' || !product?.price) { throw new Error('No Product Price') } total += product.price * quantity return null }), ) if (total === 0) { throw new Error('There is nothing to pay for, add some items to your cart and try again.') } const accessToken = await getPayPalAccessToken() const orderResponse = await axios.post( `${PAYPAL_API_BASE}/v2/checkout/orders`, { intent: 'CAPTURE', purchase_units: [ { amount: { currency_code: 'USD', value: (total / 100).toFixed(2), }, }, ], }, { headers: { Authorization: `Bearer ${accessToken}`,'Content-Type': 'application/json', }, }, ) res.send({ orderID: orderResponse.data.id }) } catch (error: unknown) { const message = error instanceof Error ? error.message : 'Unknown error' payload.logger.error(message) res.json({ error: message }) }}
The second file is called capture-paypal-order.ts:
// eslint-disable-next-line simple-import-sort/importsimport type { PayloadHandler } from 'payload/config'import axios from 'axios'const PAYPAL_API_BASE = 'https://sandbox.paypal.com'const getPayPalAccessToken = async (): Promise<string> => { const clientId = process.env.PAYPAL_CLIENT_ID || '' const clientSecret = process.env.PAYPAL_CLIENT_SECRET || '' const auth = Buffer.from(`${clientId}:${clientSecret}`).toString('base64') const response = await axios.post( `${PAYPAL_API_BASE}/v1/oauth2/token`,'grant_type=client_credentials', { headers: { Authorization: `Basic ${auth}`,'Content-Type': 'application/x-www-form-urlencoded', }, }, ) return response.data.access_token}export const capturePayPalOrder: PayloadHandler = async (req, res): Promise<void> => { const { user } = req if (!user) { res.status(401).send('Unauthorized') return } const { orderID } = req.body if (!orderID) { res.status(400).json({ error: 'Order ID is required' }) return } try { const accessToken = await getPayPalAccessToken() const captureResponse = await axios.post( `${PAYPAL_API_BASE}/v2/checkout/orders/${orderID}/capture`, {}, { headers: { Authorization: `Bearer ${accessToken}`,'Content-Type': 'application/json', }, }, ) res.send({ status: captureResponse.data.status, captureID: captureResponse.data.id }) } catch (error: unknown) { const message = error instanceof Error ? error.message : 'Unknown error' res.json({ error: message }) }}
I am trying to test if I can successfully make online purchases using the sandbox accounts. However, when I log in using the PayPal business account, I encounter the error "I must be a seller to make a purchase." I created a seller account, but I still used the API keys from the business account since I cannot create an app(which gives me api keys) with the seller account.I have consoled logged and verified that my order IDs are being captured correctly:
Order Data after Capture: ObjectcaptureID: "7VU536339L123614E"status: "COMPLETED"
Also here is the PayPal button code in my components folder:
'use client'// eslint-disable-next-line simple-import-sort/importsimport React, { useEffect, useState } from 'react'import { PayPalButtons, PayPalScriptProvider } from '@paypal/react-paypal-js'import { useRouter } from 'next/navigation'import { useCart } from '../../_providers/Cart'const PayPalButton: React.FC = () => { const router = useRouter() const { cart, cartTotal, clearCart } = useCart() const [paypalOrderID, setPaypalOrderID] = useState<string | null>(null) const [error, setError] = useState<string | null>(null) useEffect(() => { const createPayPalOrder = async () => { try { const response = await fetch( `${process.env.NEXT_PUBLIC_SERVER_URL}/api/create-payment-intent`, { method: 'POST', headers: {'Content-Type': 'application/json', }, credentials: 'include', body: JSON.stringify({ cartTotal: cartTotal.raw }), }, ) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } const data = await response.json() console.log('PayPal Order Creation Response:', data) setPaypalOrderID(data.orderID) } catch (error) { console.error('Failed to create PayPal order:', error) setError('Failed to create PayPal order. Please try again.') } } createPayPalOrder() }, [cartTotal]) const handleApprove = async (data: { orderID: string }) => { try { console.log('Handling PayPal Approval for Order ID:', data.orderID) const response = await fetch( `${process.env.NEXT_PUBLIC_SERVER_URL}/api/capture-paypal-order`, { method: 'POST', headers: {'Content-Type': 'application/json', }, credentials: 'include', body: JSON.stringify({ orderID: data.orderID, cartItems: cart?.items, cartTotal: cartTotal.raw, }), }, ) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } const orderData = await response.json() console.log('Order Data after Capture:', orderData) if (orderData.id) { clearCart() router.push(`/order-confirmation?order_id=${orderData.id}`) } else { throw new Error('Failed to create order') } } catch (error) { console.error('Error processing PayPal payment:', error) setError('Error processing payment. Please try again.') } } if (error) { return <div>Error: {error}</div> } if (!paypalOrderID) { return <div>Loading PayPal...</div> } return (<PayPalScriptProvider options={{ clientId: process.env.NEXT_PUBLIC_PAYPAL_CLIENT_ID }}><PayPalButtons createOrder={() => Promise.resolve(paypalOrderID)} onApprove={data => handleApprove(data)} onError={err => { console.error('PayPal Error:', err) setError('PayPal encountered an error. Please try again.') }} /></PayPalScriptProvider> )}export default PayPalButton
I have been struggle with these issue for 2 days doing everything looking back and front and don't know what is wrong. I even tried to google search for the solution but nothing so far has helped me. Any help would be greatly appreciated!