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;
}