PayPal Standard Checkout Integration with PHP Using JS-SDK

PayPal is one of the most popular payment gateways, and it allows website owners to accept payments online. PayPal Standard Checkout with the JavaScript SDK is one of the simplest and most secure ways to accept online payments in PHP applications. Unlike older PayPal standards, the JS-SDK instantly renders smart buttons, handles client-side approval, and completes the order through server-side API calls — making it PCI-friendly and developer-efficient.

PayPal Standard Checkout uses JavaScript SDK to integrate payment button in the webpage. The buyer can use this button to make payment with their PayPal account or debit/credit card. Integrate PayPal Standard Checkout system to allow buyers to purchase online and make the payment from the website. In this tutorial, we will show you how to integrate PayPal Standard Checkout in PHP. Here we will provide a step-by-step guide for PayPal Standard Checkout Integration with PHP Using JS-SDK.

In this step-by-step guide, we will integrate:
✔ PayPal JS-SDK (Smart Buttons)
✔ Server-side Order Creation (PHP)
✔ Server-side Capture API (PHP)
✔ Saving Transactions to Database (MySQL)
✔ Displaying Payment Success Page
✔ Using PayPal Sandbox & Production Credentials

🚀 Understanding the Workflow

Below is how PayPal Standard Checkout works with PHP:

User Clicks PayPal Button
           ↓
JS-SDK requests `create_order.php`
           ↓
Server contacts PayPal → Creates Order
           ↓
User approves payment on PayPal popup
           ↓
JS-SDK sends OrderID to `capture_order.php`
           ↓
Server captures the payment & stores it in DB
           ↓
Returns success → Redirects user to success.php

📁 Folder Structure

This PayPal Checkout tutorial references the following PHP structure:

paypal_checkout_with_php/
├── index.php
├── config.php
├── paypal_api.php
├── create_order.php
├── capture_order.php
├── success.php
└── css/
    └── style.css

Requirements

Before you begin, ensure you have:

  • PHP 7.4+
  • cURL enabled
  • MySQL database
  • A PayPal Business Sandbox Account
  • Your PayPal Client ID and Secret Key

Step 1: Set Up PayPal Developer Account

To get started, you need to create a PayPal Developer account and set up a sandbox environment (PayPal REST API app) for testing purposes.

  1. Go to the PayPal Developer Website and sign up for a developer account.
  2. Once logged in, navigate to the Dashboard and create a new app under the Apps & Credentials section.
    • Select Type:Merchant to get Client ID and Secret Key for server-side API calls.
  3. Note down your Client ID and Secret Key for sandbox testing.

Sandbox test account:
Navigate to the Testing Tools → Sandbox Accounts section in the PayPal Developer Dashboard and create a personal sandbox account to simulate buyer transactions during testing.

Step 2: Create Database and Table

Create a MySQL database and a table to store transaction details. Use the following SQL query to create the transactions table:

CREATE TABLE IF NOT EXISTS `transactions` (
  `id` int NOT NULL AUTO_INCREMENT,
  `order_id` varchar(255) NOT NULL,
  `transaction_id` varchar(255) DEFAULT NULL,
  `payer_id` varchar(255) DEFAULT NULL,
  `payer_email` varchar(255) DEFAULT NULL,
  `payer_name` varchar(255) DEFAULT NULL,
  `amount` decimal(12,2) DEFAULT NULL,
  `currency` varchar(10) DEFAULT NULL,
  `status` varchar(50) DEFAULT NULL,
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
  `raw_response` longtext,
  PRIMARY KEY (`id`),
  UNIQUE KEY `order_id` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Step 3: Configuration (config.php)

Create a config.php file to store your PayPal API credentials, DB connection settings and product details. Also, create a utility function to establish a MySQLi connection.

  • Product Details: Define constants for product name (PRODUCT_NAME), price (PRODUCT_PRICE), and currency (PRODUCT_CURRENCY).
  • Database Settings: Define constants for database host (DB_HOST), user (DB_USER), password (DB_PASS), and name (DB_NAME).
  • PayPal REST API Credentials: Define constants for PayPal Client ID (PAYPAL_CLIENT_ID), Secret (PAYPAL_SECRET), and Sandbox mode (PAYPAL_SANDBOX: Set to FALSE for live mode).
  • Utility Function: Create a function to establish and return a MySQLi connection.
<?php 
// Product details (example)
define('PRODUCT_NAME''Example Product');
define('PRODUCT_PRICE''9.99');
define('PRODUCT_CURRENCY''USD');

// Database settings
define('DB_HOST''localhost');
define('DB_USER''root');
define('DB_PASS''');
define('DB_NAME''paypal_checkout_db');

// PayPal REST API credentials
// For testing use sandbox credentials and set sandbox true
define('PAYPAL_CLIENT_ID''YOUR_PAYPAL_CLIENT_ID');
define('PAYPAL_SECRET''YOUR_PAYPAL_SECRET');
define('PAYPAL_SANDBOX'TRUE); //TRUE=Sandbox | FALSE=Production

// Utility: create mysqli connection
function get_db_connection() {
    
$mysqli = new mysqli(DB_HOSTDB_USERDB_PASSDB_NAME);
    if (
$mysqli->connect_errno) {
        
http_response_code(500);
        echo 
json_encode(['error' => 'Database connection failed: ' $mysqli->connect_error]);
        exit;
    }
    
$mysqli->set_charset('utf8mb4');
    return 
$mysqli;
}
?>

Step 4: PayPal API Helper (paypal_api.php)

Create a paypal_api.php file to handle PayPal API interactions. This file will include functions to get an access token, create an order, and capture payment.

<?php 
// Include config for constants
require_once __DIR__ '/config.php';

// Define PayPal API base URL
if (PAYPAL_SANDBOX) {
    
define('PAYPAL_BASE''https://api-m.sandbox.paypal.com');
} else {
    
define('PAYPAL_BASE''https://api-m.paypal.com');
}

// Get PayPal access token
function paypal_get_access_token() {
    
$url PAYPAL_BASE '/v1/oauth2/token';

    
$ch curl_init($url);
    
curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
    
curl_setopt($chCURLOPT_USERPWDPAYPAL_CLIENT_ID ':' PAYPAL_SECRET);
    
curl_setopt($chCURLOPT_POSTFIELDS'grant_type=client_credentials');
    
curl_setopt($chCURLOPT_HTTPHEADER, [
        
'Accept: application/json',
        
'Accept-Language: en_US',
    ]);

    
$response curl_exec($ch);
    if (
$response === false) {
        
curl_close($ch);
        return 
null;
    }
    
$httpCode curl_getinfo($chCURLINFO_HTTP_CODE);
    
curl_close($ch);

    if (
$httpCode >= 200 && $httpCode 300) {
        
$data json_decode($responsetrue);
        return 
$data['access_token'] ?? null;
    }
    return 
null;
}

// Create PayPal order
function paypal_create_order($amount$currency 'USD') {
    
$token paypal_get_access_token();
    if (!
$token) return null;

    
$url PAYPAL_BASE '/v2/checkout/orders';

    
$body = [
        
'intent' => 'CAPTURE',
        
'purchase_units' => [[
            
'amount' => [
                
'currency_code' => $currency,
                
'value' => $amount,
                
'breakdown' => [
                    
'item_total' => [
                        
'currency_code' => $currency,
                        
'value' => $amount,
                    ],
                ],
            ],
            
'items' => [[
                
'name' => PRODUCT_NAME,
                
'unit_amount' => [
                    
'currency_code' => $currency,
                    
'value' => $amount,
                ],
                
'quantity' => '1',
            ]],
        ]],
    ];

    
$ch curl_init($url);
    
curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
    
curl_setopt($chCURLOPT_HTTPHEADER, [
        
'Content-Type: application/json',
        
'Authorization: Bearer ' $token,
    ]);
    
curl_setopt($chCURLOPT_POSTtrue);
    
curl_setopt($chCURLOPT_POSTFIELDSjson_encode($body));

    
$response curl_exec($ch);
    if (
$response === false) {
        
curl_close($ch);
        return 
null;
    }
    
$httpCode curl_getinfo($chCURLINFO_HTTP_CODE);
    
curl_close($ch);

    if (
$httpCode >= 200 && $httpCode 300) {
        return 
json_decode($responsetrue);
    }
    return 
null;
}

// Capture PayPal order
function paypal_capture_order($orderId) {
    
$token paypal_get_access_token();
    if (!
$token) return null;

    
$url PAYPAL_BASE '/v2/checkout/orders/' urlencode($orderId) . '/capture';

    
$ch curl_init($url);
    
curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
    
curl_setopt($chCURLOPT_HTTPHEADER, [
        
'Content-Type: application/json',
        
'Authorization: Bearer ' $token,
    ]);
    
curl_setopt($chCURLOPT_POSTtrue);

    
$response curl_exec($ch);
    if (
$response === false) {
        
curl_close($ch);
        return 
null;
    }
    
$httpCode curl_getinfo($chCURLINFO_HTTP_CODE);
    
curl_close($ch);

    if (
$httpCode >= 200 && $httpCode 300) {
        return 
json_decode($responsetrue);
    }
    return 
null;
}
?>

Step 5: Client-Side Checkout Page (index.php)

In this step, we create a simple client-side checkout page using PHP. This page will include a button that, when clicked, will initiate the payment process by calling the server-side script.

  • This code sets up a PayPal button on your checkout page. When the button is clicked, it creates an order on the server and, upon approval, captures the order. It also includes basic status message handling to inform the user of the payment process status.

Include the configuration file (config.php) at the beginning of your PHP script to access the product details defined earlier.

<?php 
require_once __DIR__ '/config.php';
?>

Next, create the HTML structure for the checkout page. Use PHP to dynamically display the product name and price using the constants defined in config.php.

<div class="panel">
    <div class="panel-heading">
        <h1><?php echo htmlspecialchars(PRODUCT_NAME); ?></h1>
        <p>Price: <strong><?php echo htmlspecialchars(PRODUCT_CURRENCY ' ' PRODUCT_PRICE); ?></strong></p>
    </div>
    <div class="panel-body">
        <!-- Display status message -->
        <div id="status-message"></div>
        
        <!-- Set up a container element for the PayPal button -->
        <div id="paypal-button-container"></div>
    </div>
</div>

Next, include the PayPal JavaScript SDK by adding the following script tag. Make sure to replace the placeholders with your actual PayPal client ID and currency code using PHP.

<script src="https://www.paypal.com/sdk/js?client-id=<?php echo urlencode(PAYPAL_CLIENT_ID); ?>&currency=<?php echo urlencode(PRODUCT_CURRENCY); ?>"></script>

Now, add the JavaScript code to handle the PayPal button rendering and payment process. This code will create and capture the order by communicating with the server-side scripts we created earlier.

<script>
// Create order on the server
function createOrderOnServer() {
    hideStatus();
    return fetch('create_order.php', {
        method: 'post',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            amount: '<?php echo PRODUCT_PRICE; ?>',
            currency: '<?php echo PRODUCT_CURRENCY; ?>'
        })
    }).then(function(res){
        return res.json();
    }).catch(function(err){
        showStatus('Could not create order. Please try again.', 'error');
        console.error(err);
        return Promise.reject(err);
    });
}

// Capture order on the server
function captureOrderOnServer(orderID) {
    hideStatus();
    return fetch('capture_order.php', {
        method: 'post',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ orderID: orderID })
    }).then(function(res){
        return res.json();
    }).catch(function(err){
        showStatus('Payment processing failed. Please try again or contact support.', 'error');
        console.error(err);
        return Promise.reject(err);
    });
}

paypal.Buttons({
    createOrder: function(data, actions) {
        return createOrderOnServer().then(function(data) {
            if (data && data.id) return data.id;
            return actions.reject();
        });
    },
    onApprove: function(data, actions) {
        return captureOrderOnServer(data.orderID).then(function(res){
            if (res && res.success && res.db_id) {
                // redirect to success page with DB id
                showStatus('Payment successful — redirecting...', 'success');
                // small delay to show message then redirect
                setTimeout(function(){
                    window.location.href = 'success.php?id=' + encodeURIComponent(res.db_id);
                }, 700);
            } else {
                showStatus('Capture failed. See console for details.', 'error');
                console.log(res);
            }
        }).catch(function(err){
            // errors already handled in captureOrderOnServer; ensure visible
            showStatus('Capture failed. See console for details.', 'error');
            console.error(err);
        });
    },
    onError: function(err) {
        console.error(err);
        showStatus('An error occurred during payment. Please try again.', 'error');
    }
}).render('#paypal-button-container');

// Helper UI functions: show and hide status messages
function showStatus(message, type) {
    var el = document.getElementById('status-message');
    if (!el) return;
    el.textContent = message;
    el.style.display = 'block';
    // basic styling for types
    if (type === 'error') {
        el.style.background = '#ffe6e6';
        el.style.color = '#a00';
        el.style.border = '1px solid #f5c2c2';
    } else if (type === 'success') {
        el.style.background = '#e6ffea';
        el.style.color = '#047a12';
        el.style.border = '1px solid #bfe6c8';
    } else {
        el.style.background = '#eef3ff';
        el.style.color = '#0b3d91';
        el.style.border = '1px solid #c9d9ff';
    }
}

function hideStatus() {
    var el = document.getElementById('status-message');
    if (!el) return;
    el.style.display = 'none';
}
</script>

Step 6: Server-Side Order Creation (create_order.php)

In this step, we create a server-side PHP script to handle the creation of PayPal orders. This script will receive the order details from the client-side, call the PayPal API to create the order, and return the order ID to the client.

  • This code processes a request to create a PayPal order. It reads the amount and currency from the request, calls the PayPal API to create the order, and returns the order ID in JSON format.
<?php 
// Include PayPal API functions
require_once __DIR__ '/paypal_api.php';
// Include configuration file
require_once __DIR__ '/config.php';

// Set response content type to JSON
header('Content-Type: application/json');

// Get amount and currency from request, or use defaults
$data json_decode(file_get_contents('php://input'), true);
$amount = isset($data['amount']) ? $data['amount'] : PRODUCT_PRICE;
$currency = isset($data['currency']) ? $data['currency'] : PRODUCT_CURRENCY;

// Create PayPal order
$order paypal_create_order($amount$currency);
if (!
$order) {
    
http_response_code(500);
    echo 
json_encode(['error' => 'Could not create order']);
    exit;
}

echo 
json_encode($order);
?>

Step 7: Server-Side Order Capture (capture_order.php)

Finally, we create another server-side PHP script to handle the capture of PayPal orders. This script will receive the order ID from the client-side, call the PayPal API to capture the payment, store the transaction details in the database, and return a success response to the client.

  • This code processes a request to capture a PayPal order. It reads the order ID from the request, calls the PayPal API to capture the order, stores the transaction details in a database, and returns a success response in JSON format.
<?php 
// Include PayPal API functions
require_once __DIR__ '/paypal_api.php';
// Include configuration file
require_once __DIR__ '/config.php';

// Set response content type to JSON
header('Content-Type: application/json');

// Get orderID from request
$input json_decode(file_get_contents('php://input'), true);
if (!isset(
$input['orderID'])) {
    
http_response_code(400);
    echo 
json_encode(['error' => 'orderID required']);
    exit;
}
$orderID $input['orderID'];

// Capture PayPal order
$capture paypal_capture_order($orderID);
if (!
$capture) {
    
http_response_code(500);
    echo 
json_encode(['error' => 'Capture failed']);
    exit;
}

// Extract payer and transaction info
$status $capture['status'] ?? null;
$payer $capture['payer'] ?? [];
$payer_id $payer['payer_id'] ?? ($payer['payer_id'] ?? null);
$payer_email $payer['email_address'] ?? null;
$payer_name null;
if (isset(
$payer['name'])) {
    
$payer_name trim(($payer['name']['given_name'] ?? '') . ' ' . ($payer['name']['surname'] ?? ''));
}

$purchase_unit $capture['purchase_units'][0] ?? null;
$payments $purchase_unit['payments'] ?? null;
$captures null;
if (
$payments && isset($payments['captures'][0])) {
    
$captures $payments['captures'][0];
}

$transaction_id $captures['id'] ?? null;
$amount $captures['amount']['value'] ?? ($purchase_unit['amount']['value'] ?? null);
$currency $captures['amount']['currency_code'] ?? ($purchase_unit['amount']['currency_code'] ?? null);

// Store to database using MySQLi prepared statements
$mysqli get_db_connection();
$stmt $mysqli->prepare("INSERT INTO transactions
    (order_id, transaction_id, payer_id, payer_email, payer_name, amount, currency, status, raw_response)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
);
if (!
$stmt) {
    
http_response_code(500);
    echo 
json_encode(['error' => 'DB prepare failed: ' $mysqli->error]);
    exit;
}
$raw_response json_encode($capture);
$stmt->bind_param('sssssssss'$orderID$transaction_id$payer_id$payer_email$payer_name$amount$currency$status$raw_response);
$exec $stmt->execute();
if (!
$exec) {
    
http_response_code(500);
    echo 
json_encode(['error' => 'DB insert failed: ' $stmt->error]);
    
$stmt->close();
    exit;
}
$db_id $stmt->insert_id;
$stmt->close();
$mysqli->close();

// Return success response with encoded DB id
echo json_encode(['success' => true'db_id' => base64_encode($db_id), 'capture' => $capture]);
?>

Step 8: Display Transaction Details (success.php)

After capturing the order and storing the transaction details, we can create a simple PHP page to display the transaction information to the user. This page will retrieve the transaction ID from the query parameters, fetch the details from the database, and display them in a readable format.

  • This code retrieves transaction details from a database based on an ID provided in the query parameters and displays the information in an HTML format.
<?php 
// Include configuration file
require_once __DIR__ '/config.php';

// Get transaction ID from query parameter
$id = isset($_GET['id']) ? intval(base64_decode($_GET['id'])) : 0;
if (
$id <= 0) {
    echo 
'Invalid ID';
    exit;
}

// Fetch transaction details from database
$mysqli get_db_connection();
$stmt $mysqli->prepare('SELECT id, order_id, transaction_id, payer_id, payer_email, payer_name, amount, currency, status, created_at, raw_response FROM transactions WHERE id = ?');
$stmt->bind_param('i'$id);
$stmt->execute();
$res $stmt->get_result();
$row $res->fetch_assoc();
$stmt->close();
$mysqli->close();

if (!
$row) {
    echo 
'Transaction not found';
    exit;
}
?> <h1>Payment Success</h1> <p><strong>Order ID:</strong> <?php echo htmlspecialchars($row['order_id']); ?></p> <p><strong>Transaction ID:</strong> <?php echo htmlspecialchars($row['transaction_id']); ?></p> <p><strong>Payer ID:</strong> <?php echo htmlspecialchars($row['payer_id']); ?></p> <p><strong>Payer:</strong> <?php echo htmlspecialchars($row['payer_name'] . ' (' $row['payer_email'] . ')'); ?></p> <p><strong>Amount:</strong> <?php echo htmlspecialchars($row['currency'] . ' ' $row['amount']); ?></p> <p><strong>Status:</strong> <?php echo htmlspecialchars($row['status']); ?></p> <p><strong>Created:</strong> <?php echo htmlspecialchars($row['created_at']); ?></p>

Make PayPal Standard Checkout Payment Gateway Live

Once the integration is completed and the payment process works as expected in the sandbox environment, you can proceed to make the PayPal Standard Checkout payment gateway live for real transactions.

To make the PayPal Standard Checkout payment gateway live, you need to switch from the PayPal sandbox environment to the live environment. This involves updating your API credentials in your configuration file.

  1. Log in to your PayPal Developer account and navigate to the “Apps & Credentials” section.
    • Switch to the Live environment using the toggle at the top left corner.
    • Locate your live REST API app or create a new one if you haven’t done so already.
    • Copy the Client ID and Secret for the live app.
  2. Update your configuration file (e.g., config.php) with the live API credentials:
    define('PAYPAL_CLIENT_ID''YOUR_PAYPAL_LIVE_CLIENT_ID'); 
    define('PAYPAL_SECRET''YOUR_PAYPAL_LIVE_SECRET');
    define('PAYPAL_SANDBOX'FALSE);

Advanced Settings: Disable Shipping Address for Debit/Credit Card Payment Method

If you are using PayPal checkout for the DIGITAL_GOODS and want to disable the shipping address requirement for debit/credit card payments through PayPal Checkout, you can do so by modifying the order creation request to include the appropriate parameters.

  • When defining request body for creating an order (API: checkout/orders), you can set the application_context to NO_SHIPPING indicate that no shipping address is needed.
$body = [ 
    ...
    
'shipping_preference' => 'NO_SHIPPING'
];

🛡️ Security and Best Practices

  • Never expose PayPal secret key on client side
  • Always create & capture orders server-side
  • Validate currency and amount on server
  • Log raw PayPal responses
  • Use HTTPS on production
  • Sanitize all database insertions

🎉 Conclusion

With the PayPal JS-SDK and PHP REST API integration, you can seamlessly accept payments on any website with minimal setup. This approach ensures:

  • Fully secure server-side operations
  • PCI-compliant checkout
  • Simple user experience
  • Easy switch between sandbox and live

Whether you’re building an eCommerce system, selling digital goods, or implementing subscription billing, this PayPal Standard Checkout workflow is reliable, scalable, and production-ready. Happy coding! 🚀

If you want to explore more advanced features like subscriptions, vaulting, or PayPal advanced card payments, check out the other articles in this series.

Looking for expert assistance to implement or extend this script’s functionality? Submit a Service Request

9 Comments

  1. Ian Said...
  2. Alberto Said...
  3. Andreas Said...
  4. Ulrich Said...
    • CodexWorld Said...
  5. Bon Said...
  6. Yianni Said...
  7. Yianni Said...
  8. Nico Said...

Leave a reply

construction Need this implemented in your project? Request Implementation Help → keyboard_double_arrow_up