How to integrate PayPal in React
Integrating PayPal in React is straightforward with the official @paypal/react-paypal-js package, which renders PayPal’s smart payment buttons and handles the payment flow without requiring you to build custom UI.
As the creator of CoreUI with 25 years of web development experience, I’ve implemented PayPal payments in several e-commerce applications alongside Stripe to maximize payment method coverage.
The correct approach creates the PayPal order on your server to keep credentials secure, then confirms the capture server-side as well.
Never process order creation or capture entirely on the frontend — always validate and confirm on your server.
Install the PayPal React SDK.
npm install @paypal/react-paypal-js
Create the PayPal order on your server (Node.js example):
// server: POST /api/paypal/create-order
import fetch from 'node-fetch'
const PAYPAL_API = process.env.NODE_ENV === 'production'
? 'https://api.paypal.com'
: 'https://api.sandbox.paypal.com'
async function getAccessToken() {
const credentials = Buffer.from(
`${process.env.PAYPAL_CLIENT_ID}:${process.env.PAYPAL_CLIENT_SECRET}`
).toString('base64')
const res = await fetch(`${PAYPAL_API}/v1/oauth2/token`, {
method: 'POST',
headers: {
Authorization: `Basic ${credentials}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'grant_type=client_credentials'
})
const { access_token } = await res.json()
return access_token
}
app.post('/api/paypal/create-order', async (req, res) => {
const { amount } = req.body
const token = await getAccessToken()
const response = await fetch(`${PAYPAL_API}/v2/checkout/orders`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
intent: 'CAPTURE',
purchase_units: [{
amount: { currency_code: 'USD', value: amount.toFixed(2) }
}]
})
})
const order = await response.json()
res.json({ id: order.id })
})
The server fetches an access token using your Client ID and Secret. The order is created server-side so credentials never reach the browser. The response returns only the order id to the client.
The PayPal Button Component
Render PayPal’s hosted button UI.
// PayPalCheckout.jsx
import { PayPalScriptProvider, PayPalButtons } from '@paypal/react-paypal-js'
export function PayPalCheckout({ amount, onSuccess }) {
return (
<PayPalScriptProvider options={{
'client-id': import.meta.env.VITE_PAYPAL_CLIENT_ID,
currency: 'USD'
}}>
<PayPalButtons
style={{ layout: 'vertical', color: 'gold' }}
createOrder={async () => {
const res = await fetch('/api/paypal/create-order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount })
})
const { id } = await res.json()
return id
}}
onApprove={async (data) => {
const res = await fetch(`/api/paypal/capture-order/${data.orderID}`, {
method: 'POST'
})
const details = await res.json()
if (details.status === 'COMPLETED') {
onSuccess(details)
}
}}
onError={(err) => console.error('PayPal error:', err)}
/>
</PayPalScriptProvider>
)
}
createOrder calls your server to create the PayPal order and returns the order ID. onApprove is called after the user approves payment in PayPal’s popup — it calls your server to capture the payment. PayPal’s UI (the smart buttons, login, and review screens) is entirely hosted by PayPal.
Capturing the Payment Server-Side
// server: POST /api/paypal/capture-order/:orderId
app.post('/api/paypal/capture-order/:orderId', async (req, res) => {
const token = await getAccessToken()
const { orderId } = req.params
const response = await fetch(
`${PAYPAL_API}/v2/checkout/orders/${orderId}/capture`,
{
method: 'POST',
headers: { Authorization: `Bearer ${token}` }
}
)
const details = await res.json()
// Save order to your database here
res.json(details)
})
Always capture on the server. Capturing client-side would mean trusting the client’s claim that payment was approved, which is a security vulnerability.
Best Practice Note
This is the same PayPal integration pattern referenced in CoreUI e-commerce templates. Use PayPal’s sandbox environment for testing — set PAYPAL_API to https://api.sandbox.paypal.com and use sandbox credentials. For side-by-side Stripe and PayPal options, render both payment methods and let users choose. See how to integrate Stripe in React for the Stripe alternative.



