Implementing PayPal Payment Processing in PHP

1. SDK Integration with PayPal API v1

To begin, install the PayPal SDK using Composer:

composer require paypal/rest-api-sdk-php:*

Creating a Payment Handler

class PaymentProcessor
{
    private $apiContext;
    private $paymentAmount;
    private $transactionId;

    public function __construct()
    {
        // CLIENT_ID and SECRET_KEY can be obtained from Apps & Credentials -> API Credentials -> REST API apps
        $this->apiContext = new ApiContext(
            new OAuthTokenCredential(
                "CLIENT_ID",
                "SECRET_KEY"
            )
        );

        $this->apiContext->setConfig(
            [
                'mode' => 'sandbox', // 'sandbox' for testing or 'live' for production
                'log.LogEnabled' => true,
                'log.FileName' => LOG_PATH . 'paypal/paypal.log',
                'log.LogLevel' => 'DEBUG'
            ]
        );
    }

    /**
     * Set payment amount
     * @param string $amount
     * @return $this
     */
    public function setAmount(string $amount): PaymentProcessor
    {
        $this->paymentAmount = $amount;
        return $this;
    }

    /**
     * Set transaction ID
     * @param string $transactionId
     * @return $this
     */
    public function setTransactionId(string $transactionId): PaymentProcessor
    {
        $this->transactionId = $transactionId;
        return $this;
    }

    /**
     * Initiate payment process
     * @return string|null
     * @throws Exception
     */
    public function initiatePayment()
    {
        // Create payment object
        $payment = new Payment();
        $payment->setIntent("sale");

        // Set payer information
        $payer = new Payer();
        $payer->setPaymentMethod('paypal');
        $payment->setPayer($payer);

        // Set payment amount
        $amount = new Amount();
        $amount->setTotal($this->paymentAmount);
        $amount->setCurrency('USD');

        // Create transaction
        $transaction = new Transaction();
        $transaction->setAmount($amount);
        $transaction->setCustom($this->transactionId);

        // Add transaction to payment
        $payment->addTransaction($transaction);

        // Set redirect URLs
        $redirectUrls = new RedirectUrls();
        $redirectUrls->setReturnUrl("success_url")
            ->setCancelUrl("cancel_url");
        $payment->setRedirectUrls($redirectUrls);

        // Create payment and get approval URL
        try {
            $payment->create($this->apiContext);
            return $payment->getApprovalLink();
        } catch (\PayPalConnectionException $e) {
            throw new Exception($e->getMessage());
        }
    }
}

Webhook Configuration

From your PayPal Developer Dashboard, navigate to your application and add a Webhook at the bottom of the page. When selecting event types, you may need to select "ALL Events" to receive webhook notifications.

Webhook Verification

public function verifyWebhook()
{
    $requestBody = file_get_contents('php://input');
    $headers = $this->getHttpHeaders();
    $headers = array_change_key_case($headers, CASE_UPPER);
    
    $signatureVerification = new VerifyWebhookSignature();
    $signatureVerification->setAuthAlgo($headers['PAYPAL-AUTH-ALGO']);
    $signatureVerification->setTransmissionId($headers['PAYPAL-TRANSMISSION-ID']);
    $signatureVerification->setCertUrl($headers['PAYPAL-CERT-URL']);
    $signatureVerification->setWebhookId("YOUR_WEBHOOK_ID");
    $signatureVerification->setTransmissionSig($headers['PAYPAL-TRANSMISSION-SIG']);
    $signatureVerification->setTransmissionTime($headers['PAYPAL-TRANSMISSION-TIME']);
    $signatureVerification->setRequestBody($requestBody);
    
    try {
        $output = $signatureVerification->post($this->apiContext);
    } catch (\Exception $ex) {
        log_message('Webhook verification failed: ' . $ex->getMessage(), 'error');
        http_response_code(400);
        exit(1);
    }
    
    $checkArray = _decode($output);
    if ($checkArray['verification_status'] !== "SUCCESS") {
        log_message('Verification failed: ' . $output, 'error');
        http_response_code(400);
        exit(1);
    }
    
    $body = _decode($requestBody, true);
    $eventType = $body['event_type'];
    
    // Verify event type
    if ($eventType != 'PAYMENTS.PAYMENT.CREATED') {
        exit(1);
    }
    
    // Execute payment
    $paymentId = $body['resource']['id'] ?? "";
    $payerId = $body['resource']['payer']['payer_info']['payer_id'] ?? "";
    
    $payment = Payment::get($paymentId, $this->apiContext);
    $execution = new PaymentExecution();
    $execution->setPayerId($payerId);
    $payment->execute($execution, $this->apiContext);
    
    http_response_code(200);
}

/**
 * Helper function to get HTTP headers
 */
private function getHttpHeaders()
{
    if (function_exists('getallheaders')) {
        return getallheaders();
    }
    
    $headers = [];
    foreach ($_SERVER as $name => $value) {
        if (substr($name, 0, 5) == 'HTTP_') {
            $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
        }
    }
    return $headers;
}

2. Direct API Integration with PayPal v2

For a more lightweight approach, you can integrate directly with PayPal's v2 API without using the SDK.

Authentication

Access Token Management

class PayPalAPI
{
    const API_TOKEN_CACHE_KEY = 'paypal_api_token';
    protected $apiEndpoint;
    protected $clientId;
    protected $clientSecret;
    
    public function __construct()
    {
        // API endpoints
        // Production: https://api-m.paypal.com
        // Sandbox: https://api-m.sandbox.paypal.com
        $this->apiEndpoint = "https://api-m.sandbox.paypal.com";
        $this->clientId = 'YOUR_CLIENT_ID';
        $this->clientSecret = 'YOUR_CLIENT_SECRET';
    }
    
    /**
     * Get access token (cached when available)
     * @return array
     */
    public function getAccessToken()
    {
        try {
            // Check cache first
            $cache = Cache::store('redis');
            $tokenData = $cache->get(self::API_TOKEN_CACHE_KEY);
            
            if ($tokenData) {
                $tokenArray = _decode($tokenData, true);
                return ['success' => true, 'token' => $tokenArray['access_token']];
            }
            
            // Get new token
            $credentials = base64_encode($this->clientId . ':' . $this->clientSecret);
            $url = $this->apiEndpoint . '/v1/oauth2/token';
            
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, 'grant_type=client_credentials');
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'Content-Type: application/x-www-form-urlencoded',
                'Authorization: Basic ' . $credentials
            ]);
            
            $result = curl_exec($ch);
            
            if (curl_errno($ch)) {
                return ['success' => false, 'error' => curl_error($ch)];
            }
            
            curl_close($ch);
            
            // Cache the token
            $tokenArray = _decode($result, true);
            $cache->set(self::API_TOKEN_CACHE_KEY, $result, $tokenArray['expires_in']);
            
            return ['success' => true, 'token' => $tokenArray['access_token']];
        } catch (\Exception $e) {
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }
}

CURL Request Handler

/**
 * Execute HTTP request to PayPal API
 * @param string $url
 * @param array $data
 * @param string $method
 * @return array
 */
public function executeRequest(string $url, array $data = [], string $method = 'GET'): array
{
    try {
        $payload = empty($data) ? '' : _encode($data);
        
        // Get access token
        $tokenResult = $this->getAccessToken();
        if (!$tokenResult['success']) {
            return ['success' => false, 'error' => $tokenResult['error']];
        }
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/',
            'Authorization: Bearer ' . $tokenResult['token']
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        
        if ($method === 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
        } elseif ($method === 'GET') {
            curl_setopt($ch, CURLOPT_HTTPGET, true);
        }
        
        $result = curl_exec($ch);
        
        if (curl_errno($ch)) {
            return ['success' => false, 'error' => curl_error($ch)];
        }
        
        $data = _decode($result, true);
        $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        
        if ($httpStatusCode == 422) {
            $message = $data['message'] ?? "";
            return ['success' => false, 'error' => 'Validation Error: ' . $message];
        } elseif ($httpStatusCode == 504) {
            return ['success' => false, 'error' => 'Gateway Timeout'];
        }
        
        curl_close($ch);
        
        return ['success' => true, 'data' => $data];
    } catch (\Exception $e) {
        return ['success' => false, 'error' => $e->getMessage()];
    }
}

Order Creation

public function createOrder()
{
    $orderId = "YOUR_ORDER_ID";
    $successUrl = "http://yourdomain.com/success?order_id=" . $orderId;
    $cancelUrl = "http://yourdomain.com/cancel?order_id=" . $orderId;
    
    $url = $this->apiEndpoint . "/v2/checkout/orders";
    
    $data = [
        'intent' => 'CAPTURE',
        'purchase_units' => [
            [
                'reference_id' => $orderId,
                'description' => "Order description",
                'amount' => [
                    'currency_code' => 'USD',
                    'value' => 0.01
                ]
            ]
        ],
        'application_context' => [
            'shipping_preference' => 'NO_SHIPPING',
            'return_url' => $successUrl,
            'cancel_url' => $cancelUrl,
        ]
    ];
    
    $result = $this->executeRequest($url, $data, 'POST');
    
    if ($result['success']) {
        // Redirect to payment page
        $paymentUrl = $result['data']['links'][1]['href'];
        header("Location: {$paymentUrl}");
        exit(0);
    }
    
    return $result;
}

Payment Capture

public function capturePayment()
{
    $token = $_GET['token'] ?? '';
    $url = $this->apiEndpoint . "/v2/checkout/orders/{$token}/capture";
    
    $result = $this->executeRequest($url, [], 'POST');
    
    if ($result['success']) {
        // Process successful payment
        // Update order status, fulfill order, etc.
    }
    
    return $result;
}

Tags: PHP PayPal Payment Integration API sdk

Posted on Wed, 04 Nov 2026 16:37:05 +0000 by fasmy98