First Commit
This commit is contained in:
1 parent
5eb9dd037d
commit
2e62c0e60b
11877 files changed
+2035312
No files matched your search
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
/**
|
||||
* PayPal Checkout API Helper
|
||||
*
|
||||
* Handles communication with PayPal REST API v2
|
||||
* Uses client ID and secret for OAuth authentication
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/paypal_checkout_config.php';
|
||||
|
||||
class PayPalCheckoutAPI {
|
||||
|
||||
private $clientId;
|
||||
private $clientSecret;
|
||||
private $baseUrl;
|
||||
private $accessToken;
|
||||
|
||||
public function __construct() {
|
||||
$this->clientId = getPayPalClientId();
|
||||
$this->clientSecret = getPayPalClientSecret();
|
||||
$this->baseUrl = getPayPalBaseUrl();
|
||||
}
|
||||
|
||||
private function httpRequest($method, $url, $headers = [], $body = null, $basicAuth = null) {
|
||||
$method = strtoupper($method);
|
||||
|
||||
$verifySsl = defined('PAYPAL_VERIFY_SSL') ? (bool)PAYPAL_VERIFY_SSL : true;
|
||||
|
||||
// Prefer cURL when available
|
||||
if (function_exists('curl_init')) {
|
||||
$ch = curl_init();
|
||||
|
||||
$opts = [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CUSTOMREQUEST => $method,
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
CURLOPT_SSL_VERIFYPEER => $verifySsl,
|
||||
CURLOPT_SSL_VERIFYHOST => $verifySsl ? 2 : 0,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
];
|
||||
|
||||
if ($basicAuth) {
|
||||
$opts[CURLOPT_USERPWD] = $basicAuth;
|
||||
}
|
||||
if ($body !== null) {
|
||||
$opts[CURLOPT_POSTFIELDS] = $body;
|
||||
}
|
||||
|
||||
curl_setopt_array($ch, $opts);
|
||||
|
||||
$responseBody = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($error) {
|
||||
throw new Exception("cURL Error: $error");
|
||||
}
|
||||
|
||||
return [$httpCode, $responseBody];
|
||||
}
|
||||
|
||||
// Fallback to file_get_contents if cURL isn't enabled
|
||||
$headerLines = $headers;
|
||||
if ($basicAuth) {
|
||||
$headerLines[] = 'Authorization: Basic ' . base64_encode($basicAuth);
|
||||
}
|
||||
|
||||
$context = stream_context_create([
|
||||
'http' => [
|
||||
'method' => $method,
|
||||
'header' => implode("\r\n", $headerLines),
|
||||
'content' => $body ?? '',
|
||||
'ignore_errors' => true,
|
||||
'timeout' => 30,
|
||||
],
|
||||
'ssl' => [
|
||||
'verify_peer' => $verifySsl,
|
||||
'verify_peer_name' => $verifySsl,
|
||||
],
|
||||
]);
|
||||
|
||||
$responseBody = @file_get_contents($url, false, $context);
|
||||
$httpCode = 0;
|
||||
|
||||
if (isset($http_response_header) && is_array($http_response_header)) {
|
||||
foreach ($http_response_header as $h) {
|
||||
if (preg_match('#^HTTP/\S+\s+(\d{3})#', $h, $m)) {
|
||||
$httpCode = intval($m[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($responseBody === false) {
|
||||
$lastError = error_get_last();
|
||||
$msg = $lastError['message'] ?? 'HTTP request failed';
|
||||
throw new Exception($msg);
|
||||
}
|
||||
|
||||
return [$httpCode, $responseBody];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OAuth 2.0 access token from PayPal
|
||||
*/
|
||||
public function getAccessToken() {
|
||||
if ($this->accessToken) {
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
[$httpCode, $response] = $this->httpRequest(
|
||||
'POST',
|
||||
$this->baseUrl . '/v1/oauth2/token',
|
||||
[
|
||||
'Accept: application/json',
|
||||
'Accept-Language: en_US',
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
],
|
||||
'grant_type=client_credentials',
|
||||
$this->clientId . ':' . $this->clientSecret
|
||||
);
|
||||
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if ($httpCode !== 200 || !isset($data['access_token'])) {
|
||||
$errorMsg = isset($data['error_description']) ? $data['error_description'] : 'Failed to get access token';
|
||||
throw new Exception("PayPal Auth Error: $errorMsg");
|
||||
}
|
||||
|
||||
$this->accessToken = $data['access_token'];
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a PayPal order
|
||||
*
|
||||
* @param float $amount The order amount
|
||||
* @param string $currency Currency code (e.g., 'USD', 'CAD')
|
||||
* @param string $description Order description
|
||||
* @param string $customId Your internal reference ID (e.g., TRXnum)
|
||||
* @return array Order data including order ID
|
||||
*/
|
||||
public function createOrder($amount, $currency, $description = '', $customId = '') {
|
||||
$token = $this->getAccessToken();
|
||||
|
||||
$orderData = [
|
||||
'intent' => 'CAPTURE',
|
||||
'purchase_units' => [
|
||||
[
|
||||
'amount' => [
|
||||
'currency_code' => $currency,
|
||||
'value' => number_format($amount, 2, '.', '')
|
||||
],
|
||||
'description' => $description,
|
||||
'custom_id' => $customId // This is like the 'custom' field in old PayPal
|
||||
]
|
||||
],
|
||||
'application_context' => [
|
||||
'brand_name' => 'Hot Lunches Online',
|
||||
'landing_page' => 'NO_PREFERENCE',
|
||||
'user_action' => 'PAY_NOW',
|
||||
'return_url' => $this->getCurrentUrl() . $this->appendParam('paypal=success'),
|
||||
'cancel_url' => $this->getCurrentUrl() . $this->appendParam('paypal=cancel')
|
||||
]
|
||||
];
|
||||
|
||||
[$httpCode, $response] = $this->httpRequest(
|
||||
'POST',
|
||||
$this->baseUrl . '/v2/checkout/orders',
|
||||
[
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Bearer ' . $token,
|
||||
'PayPal-Request-Id: ' . uniqid('order_', true),
|
||||
],
|
||||
json_encode($orderData)
|
||||
);
|
||||
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if ($httpCode !== 201 || !isset($data['id'])) {
|
||||
$errorMsg = isset($data['message']) ? $data['message'] : 'Failed to create order';
|
||||
if (isset($data['details'])) {
|
||||
$errorMsg .= ': ' . json_encode($data['details']);
|
||||
}
|
||||
throw new Exception("PayPal Create Order Error: $errorMsg");
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture a PayPal order (complete the payment)
|
||||
*
|
||||
* @param string $orderId PayPal order ID
|
||||
* @return array Capture result including transaction details
|
||||
*/
|
||||
public function captureOrder($orderId) {
|
||||
$token = $this->getAccessToken();
|
||||
|
||||
[$httpCode, $response] = $this->httpRequest(
|
||||
'POST',
|
||||
$this->baseUrl . '/v2/checkout/orders/' . $orderId . '/capture',
|
||||
[
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Bearer ' . $token,
|
||||
'PayPal-Request-Id: ' . uniqid('capture_', true),
|
||||
],
|
||||
'{}'
|
||||
);
|
||||
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if ($httpCode !== 201 || !isset($data['status'])) {
|
||||
$errorMsg = isset($data['message']) ? $data['message'] : 'Failed to capture order';
|
||||
if (isset($data['details'])) {
|
||||
$errorMsg .= ': ' . json_encode($data['details']);
|
||||
}
|
||||
throw new Exception("PayPal Capture Error: $errorMsg");
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order details
|
||||
*
|
||||
* @param string $orderId PayPal order ID
|
||||
* @return array Order details
|
||||
*/
|
||||
public function getOrder($orderId) {
|
||||
$token = $this->getAccessToken();
|
||||
|
||||
[$httpCode, $response] = $this->httpRequest(
|
||||
'GET',
|
||||
$this->baseUrl . '/v2/checkout/orders/' . $orderId,
|
||||
[
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Bearer ' . $token,
|
||||
]
|
||||
);
|
||||
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current page URL (for return/cancel URLs) — includes the path and
|
||||
* query string, not just the host, so PayPal redirects the buyer back
|
||||
* to the same page/view they paid from (the one that can capture the
|
||||
* order, e.g. ?action=account-deposit), not the site root.
|
||||
*/
|
||||
private function getCurrentUrl() {
|
||||
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
|
||||
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
|
||||
$uri = $_SERVER['REQUEST_URI'] ?? '/';
|
||||
return $protocol . '://' . $host . $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a query param to a URL that may already have a query string.
|
||||
*/
|
||||
private function appendParam($param) {
|
||||
$uri = $_SERVER['REQUEST_URI'] ?? '/';
|
||||
return (strpos($uri, '?') !== false ? '&' : '?') . $param;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user