PayPal Checkout with Payment Buttons in PHP
PayPal is globally used electronic payment processor. People can shop online, transfer money online and receive money online using a single PayPal account. Because of its features and global reach PayPal has become a "must be included" payment method for online shopping stores.

Note: This post was initially published using PayPal's Checkout-PHP-SDK Package. Since that package had been archived on Jul 21, 2022 this post has been updated to use REST API requests as recommended by PayPal.
PayPal had provided API integrations for online shopping stores to receive payments for placed orders. Recently PayPal has updated orders API and introduced Payment Buttons for PayPal checkout integration and recommends using this new API integration which uses Javascript SDK in combination with Orders API v2. PayPal is not going to update old checkout API which uses checkout.js for new features and enhancements. So it is recommended to use the new checkout integration using Payment Buttons. Learn in this post how to add PayPal payment button for PayPal checkout integration with working demo and code snippets.
How do Payment Buttons work?
- You place the buttons on checkout page on your website.
- Buyer clicks the buttons to pay for order.
- Buttons launch the checkout experience.
- Buyer makes the payment and Orders API is called to finalize the transaction.
- You show order confirmation to buyer upon successful transaction.
How to setup PayPal Checkout with Payment Buttons?
PayPal has become one of the must be included payment method for online applications that allow to receive payments. In this post we are going to integrate PayPal checkout experience in PHP application. We will create an HTML page with payment buttons. We will create and capture PayPal order with PayPal order API in PHP script on server side. You can refer to PayPal's Call Orders API from Your Server for more details. Steps to add PayPal checkout with payment buttons:
- Create PayPal sandbox account.
- Get OAuth 2.0 client ID and client secret of this sandbox account.
- Create a PHP class to handle PayPal checkout API requests for creating and capturing orders.
- Create an HTML page with cart items and add PayPal checkout buttons.
- Render PayPal payment buttons in Javascript and send requests to server side script.
- Create and save PayPal order by sending requests from Javascript code.
- Show a success page when an order is successfully created.
Before we get started, For demonstration I have created a session to hold cart products and amount statically. I shall not be saving order to database after successful transaction and I am under opinion you already have a cart setup. So lets get started and implement PayPal checkout with payment buttons. We are going to create following files:
- constants.php: Contains PayPal constants.
- paypal-sdk/paypal_client.php: A PHP class containing code to connect to PayPal endpoints and handle requests.
- index.php: HTML page containing the cart items and PayPal checkout button.
- javascript.js: Javascript code to render buttons.
- paypal-request.php: Processes order requests received from our client side code.
- style.css: CSS styles for our page.
Create a PayPal Sandbox App and Get Client Credentials
- Login to your PayPal developer account and navigate to PayPal Developer
- Click on Sandbox Accounts under Testing Tools top navigation menu item.
- Click on Create Account button, We need two accounts one for buyer and other for merchant. You don't need to create these accounts if you already have them created.
- Now click on App & Credentials in top navigation menu.
- Click on Create App and enter App name and assign a merchant account to App.
- After you have created your app you will see a screen like this copy Client ID and Client Secret.
Now that we have our Sandbox Client ID & Secret, we are ready to setup payment buttons.
Add Constants
Add constants to store PayPal sandbox credentials.
constants.php
<?php
define('PAYPAL_ENVIRONMENT', 'sandbox');
define('PAYPAL_CLIENT_ID', 'PAYPAL_CLIENT_ID');
define('PAYPAL_CLIENT_SECRET', 'PAYPAL_CLIENT_SECRET');
Create PayPal Client Class to Send Requests to PayPal API
A custom PHP class to send requests to PayPal endpoints. Class has the following properties in use:
- $base_url: Holds the endpoint URL for sandbox or production.
- $client_id: Client id created in previous steps.
- $client_secret: Client secret created in previous steps.
- $access_token: Access token for PayPal requests authorization.
- $token_type: The type of token i.e "Bearer".
- $expires_in: The expire time of current access token. Used to refresh/recreate the token when expired.
- $created_at: The creation time of current access token. Used to refresh/recreated the token when expired.
- __construct: Constructor of class when and instance of class is created. Sets $client_id, $client_secret $base_url and $access_token properties of the class.
- set_access_token: Sends the request to PayPal's authorization endpoint to retrieve access token for future requests. Sets $access_token, $token_type, $expires_in and $created_at properties of class.
- curl_request: A generic function used to send curl requests to PayPal endpoints.
paypal_client.php
<?php
class paypal_client{
public string $base_url;
private string $client_id;
private string $client_secret;
private ?string $access_token;
private string $token_type;
private int $expires_in;
private int $created_at;
public function __construct($client_id, $client_secret, $sandbox = false)
{
$this->client_id = $client_id;
$this->client_secret = $client_secret;
$this->base_url = $sandbox
? 'https://api-m.sandbox.paypal.com'
: 'https://api-m.paypal.com';
$this->access_token = null;
}
/**
* @throws Exception
*/
public function set_access_token() :stdClass|array
{
$headers = [
'content-type' => 'application/x-www-form-urlencoded'
];
$curl_options = [
CURLOPT_USERPWD => $this->client_id . ':' . $this->client_secret
];
$body = [
'grant_type' => 'client_credentials'
];
$response = $this->curl_request('/v1/oauth2/token', 'POST', $headers, http_build_query($body), $curl_options);
$response = json_decode($response);
$this->access_token = $response->access_token;
$this->token_type = $response->token_type;
$this->expires_in = $response->expires_in;
$this->created_at = time();
return $response;
}
/**
* @param string $path
* @param string $method
* @param array $headers
* @param array|string $body
* @param array $curl_options
* @return bool|string
* @throws Exception
*/
public function curl_request(string $path, string $method, array $headers = [], array|string $body = [], array $curl_options = []): bool|string
{
$curl = curl_init();
array_change_key_case($headers);
$headers = array_merge(['accept' => 'application/json'], $headers);
curl_setopt($curl, CURLOPT_URL, $this->base_url . $path);
curl_setopt($curl, CURLOPT_TIMEOUT, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
if(str_starts_with($this->base_url, 'https://')){
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
}
if(!array_key_exists('authorization', $headers) && !str_ends_with($path, 'v1/oauth2/token')){
if (is_null($this->access_token) || time() > ($this->created_at + $this->expires_in)){
$this->set_access_token();
}
$headers['authorization'] = sprintf('%s %s', $this->token_type, $this->access_token);
}
// If any headers set add them to curl request
if(!empty($headers)){
curl_setopt($curl, CURLOPT_HTTPHEADER, array_map(function($key, $value){
return $key . ': '. $value;
}, array_keys($headers), array_values($headers)));
}
// Set the request type , GET, POST, PUT or DELETE
switch(strtoupper($method)){
case 'POST':
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
break;
case 'PUT':
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
break;
case 'DELETE':
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
break;
default:
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET');
break;
}
// If any data is supposed to be sent along with request add it to curl request
if(!empty($body)){
curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
}
// Any extra curl options to add in curl object
if(!empty($curl_options)){
foreach($curl_options as $option_key => $option_value){
curl_setopt($curl, $option_key, $option_value);
}
}
$response = curl_exec($curl);
$error = curl_error($curl);
$error_code = curl_errno($curl);
$status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if($error_code > 0){
throw new Exception($error, $error_code);
}
if ($status_code < 200 || $status_code >= 300) {
throw new Exception($response, $status_code);
}
curl_close($curl);
return $response;
}
}
Create an HTML Page and PayPal Buttons for Checkout
Create an HTML page to display cart items in tabular format and HTML element container for payment buttons. I have created a static array of products
for demonstration. Next loop through products array to calculate total
tax amount and total items amount (without tax). Then I created a cart
session that can be accessed in our server side script.
index.php
<?php
session_start();
include __DIR__ . '/constants.php';
$products = [];
// Static array of products
$products[] = ['product_title' => 'Javascript Book - PDF', 'product_price' => 10.00, 'product_tax' => 2.00, 'product_quantity' => 3];
$products[] = ['product_title' => 'PHP Book - PDF', 'product_price' => 15.00, 'product_tax' => 3.00, 'product_quantity' => 2];
$items_total = $tax_total = 0.00;
// Loop through products and calculate tax total & items total
foreach($products as $product){
$tax_total += floatval($product['product_tax'] * $product['product_quantity']);
$items_total += floatval($product['product_price'] * $product['product_quantity']);
}
// Function to show amount in price format
function to_price($amount = 0.00, $locale = 'en_US', $currency_code = 'USD'){
return numfmt_format_currency(numfmt_create( $locale, NumberFormatter::CURRENCY ), $amount, $currency_code);
}
// Create a cart session
$_SESSION['cart']['products'] = $products;
$_SESSION['cart']['items_total'] = $items_total;
$_SESSION['cart']['tax_total'] = $tax_total;
$_SESSION['cart']['cart_total'] = $tax_total + $items_total;
?>
<!DOCTYPE html>
<html>
<head>
<title>PayPal Checkout with Payment Buttons - Demo</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<script defer="defer" src="https://www.paypal.com/sdk/js?client-id=<?=PAYPAL_CLIENT_ID?>"></script>
<script defer="defer" src="js/javascript.js"></script>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<div class="container">
<div class="my-4">
<table class="table table-bordered mb-4">
<thead>
<tr>
<th colspan="2">Product</th>
<th style="width: 120px">Price</th>
<th style="width:120px;">Tax</th>
<th style="width:100px;">Quantity</th>
<th style="width: 120px;">Item Total</th>
</tr>
</thead>
<tbody>
<?php foreach($_SESSION['cart']['products'] as $product){
$item_total = $product['product_price'] * $product['product_quantity'];
$item_tax = $product['product_tax'] * $product['product_quantity'];
?>
<tr>
<td width="31">
<img src="images/pdf-book.png" />
</td>
<td>
<?=$product['product_title'];?>
</td>
<td>
<?=to_price($product['product_price']);?>
</td>
<td>
<?=to_price($product['product_tax']);?>
</td>
<td>
<?=$product['product_quantity']?>
</td>
<td>
<?=to_price($item_total + $item_tax);?>
</td>
</tr>
<?php }?>
<tr>
<th colspan="5" class="text-right">
Tax Total
</th>
<th class="text-right">
<?=to_price($_SESSION['cart']['tax_total']);?>
</th>
</tr>
<tr>
<th colspan="5" class="text-right">
Items Total
</th>
<th class="text-right">
<?=to_price($_SESSION['cart']['items_total']);?>
</th>
</tr>
<tr>
<th colspan="5" class="text-right">
Cart Total
</th>
<th class="text-right">
<?=to_price($_SESSION['cart']['cart_total']);?>
</th>
</tr>
</tbody>
</table>
<div id="paypal-buttons"></div>
</div>
</div>
</body>
</html>
Render PayPal Payment Buttons in Javascript
Listen to window load event and render PayPal buttons. Then define endpoints for "createOrder" to create an order in our server side script. Define "onApprove" to capture order and redirect to a thank you/success page after successful
transaction.
javascript.js
window.addEventListener("load", function(){
// Render paypal Buttons
paypal.Buttons({
style:{
layout: "horizontal"
},
// Call your server to create an order
createOrder: function(data, actions) {
return fetch("paypal-request.php", {
mode: "no-cors",
method: "POST",
headers: {
"content-type": "application/json"
},
body: JSON.stringify({
action: "create-order",
})
}).then(function(res) {
return res.json();
}).then(function(data) {
if( !data.success && data.message ){
console.error(data.message);
}
return data.id;
});
},
// Call your server to save the transaction
onApprove: function(data, actions){
return fetch("paypal-request.php", {
mode: "no-cors",
method: "POST",
headers: {
"content-type": "application/json"
},
body: JSON.stringify({
action: "save-order",
id: data.orderID
})
}).then(function(res) {
return res.json();
}).then(function(data){
// Redirect to thank you/success page after saving transaction
if(data.success){
window.location.assign("payment-success.php");
}
});
}
}).render("#paypal-buttons");
});
Create and Capture Order in PayPal
Handle PayPal requests received from our client side Javascript. Use paypal_client class to create and order in PayPal and then capture it after successful payment.
paypal-request.php
<?php
session_start();
include __DIR__ . '/constants.php';
// Import the PayPal SDK client that was created in 'Set up Server-Side SDK'.
include __DIR__ . '/paypal-sdk/paypal_client.php';
// Capture the input received from fetch() request
$json_data = file_get_contents('php://input');
$request = json_decode($json_data);
$paypal_client = new paypal_client(PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PAYPAL_ENVIRONMENT === 'sandbox');
switch($request->action){
case 'create-order':
$headers = [
'prefer' => 'return=representation',
'content-type' => 'application/json'
];
// Assign cart from session to a variable
$cart = $_SESSION['cart'];
// Set currency code used in transaction
$currency_code = 'USD';
$request_body = [];
$application_context = [];
$purchase_units = [];
$pu_amount = [];
// Prepare context of our request
$application_context['locale'] = 'en-US';
$application_context['user_action'] = 'PAY_NOW';
// Prepare amount & break down of amount for order
$pu_amount['currency_code'] = $currency_code;
$pu_amount['value'] = floatval($cart['cart_total']);
// Items total break down without tax
$pu_amount['breakdown']['item_total']['currency_code'] = $currency_code;
$pu_amount['breakdown']['item_total']['value'] = number_format($cart['items_total'], 2);
// Total tax of all products
$pu_amount['breakdown']['tax_total']['currency_code'] = $currency_code;
$pu_amount['breakdown']['tax_total']['value'] = number_format($cart['tax_total'], 2);
$purchase_units['amount'] = $pu_amount;
$items = [];
// Loop through all products in session and prepare items array for order request
if( !empty($cart['products']) ){
foreach($cart['products'] as $product){
$item['name'] = $product['product_title'];
$item['quantity'] = $product['product_quantity'];
$item['category'] = 'DIGITAL_GOODS';
// Unit amount of product (without tax)
$item['unit_amount']['currency_code'] = $currency_code;
$item['unit_amount']['value']= number_format($product['product_price'], 2);
$items[] = $item;
}
}
$purchase_units['items'] = $items;
// Finally create request body array assigning context & purchase units created above
$request_body['intent'] = 'CAPTURE';
$request_body['application_context'] = $application_context;
$request_body['purchase_units'][] = $purchase_units;
try {
$response = $paypal_client->curl_request('/v2/checkout/orders', 'POST', $headers, json_encode($request_body));
die($response);
} catch (Exception $e) {
echo $e->getMessage();
}
break;
case 'save-order':
$headers = [
'prefer' => 'return=minimal',
'content-type' => 'application/json'
];
try {
$response = $paypal_client->curl_request(sprintf('/v2/checkout/orders/%s/capture', $request->id), 'POST', $headers);
// Save order to database here
// unset cart session
unset($_SESSION['cart']);
echo json_encode(['success' => 1], JSON_PRETTY_PRINT);
} catch (Exception $e) {
echo $e->getMessage();
}
break;
}
Add CSS Styles
Add CSS styles for HTML content and payment buttons.
style.css
*{
box-sizing: border-box;
}
html,body{
margin: 0;
padding: 0;
}
body{
background-color: #f6f6f6;
font-family: "Segoe UI", "Roboto", "Helvetica", sans-serif;
font-size: 15px;
font-weight: normal;
font-style: normal;
line-height: 1.5;
}
.container{
width: 100%;
max-width: 1140px;
margin-right: auto;
margin-left: auto;
padding-right: 15px;
padding-left: 15px;
}
.my-4{
margin-top: 1rem;
margin-bottom: 1rem;
}
.text-right{
text-align: right;
}
.table{
width: 100%;
border-collapse: collapse;
background-color: #fff;
margin-bottom: 1rem;
}
.table tr th,
.table tr td{
border: 1px solid #dddddd;
padding: 10px;
}
#paypal-buttons{
max-width: 750px;
margin-right: auto;
margin-left: auto;
}
Login to your sandbox buyer or merchant account and verify transaction was placed. You would see transaction details like in screenshot below:
