封裝 PayPal Login(OAuth2 Authorization Code Flow + OpenID Connect UserInfo)完整流程:構建授權 URL、換取 Access Token、獲取用戶資料。支持沙箱/正式環境切換、自定義 Scope/State/Nonce、CSRF 防護。
<?php
class paypallogin {
// 沙箱/正式環境端點
private $_sanbox_oauth2_auth_uri = 'https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize';
private $_live_oauth2_auth_uri = 'https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize';
private $_acquire_user_profile_sandbox_url = 'https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/userinfo?schema=openid&access_token=';
private $_acquire_user_profile_live_url = 'https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/userinfo?schema=openid&access_token=';
private $_token_service_sandbox_url = 'https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/tokenservice';
private $_token_service_live_url = 'https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/tokenservice';
private $_sanbox_flag = true;
private $_client_id = null;
private $_client_secret = null;
private $_redirect_uri = null;
private $_state = '';
private $_scope = 'openid email phone profile address https://uri.paypal.com/services/paypalattributes';
public $token = null;
public $protocol = "http";
public function __construct($redirect_uri, $client_id, $client_secret, $scope, $state, $flag = true) {
$this->_sanbox_flag = $flag;
$this->_redirect_uri = $redirect_uri;
$this->_client_id = $client_id;
$this->_client_secret= $client_secret;
$this->_scope = $scope;
$this->_state = $state;
}
/** 生成授權頁面 URL */
public function create_request_url() {
$auth_uri = $this->_sanbox_flag ? $this->_sanbox_oauth2_auth_uri : $this->_live_oauth2_auth_uri;
$params = array(
'client_id' => $this->_client_id,
'response_type' => 'code',
'scope' => $this->_scope,
'redirect_uri' => urlencode($this->_redirect_uri),
'nonce' => time() . rand(),
'state' => $this->_state,
);
return $auth_uri . '?' . http_build_query($params);
}
/** 用授權碼換取 Access Token */
public function acquire_access_token($code) {
$postvals = sprintf(
"client_id=%s&client_secret=%s&grant_type=authorization_code&code=%s",
$this->_client_id, $this->_client_secret, $code
);
$url = $this->_sanbox_flag ? $this->_token_service_sandbox_url : $this->_token_service_live_url;
$ch = curl_init($url);
$options = array(
CURLOPT_POST => 1,
CURLOPT_VERBOSE => 1,
CURLOPT_POSTFIELDS => $postvals,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_SSL_VERIFYPEER => false,
);
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
curl_close($ch);
if (!$response) throw new Exception("Error retrieving access token: " . curl_error($ch));
$json = json_decode($response);
if (isset($json->access_token)) return $json->access_token;
throw new Exception("No access_token in response");
}
/** 獲取用戶資料 */
public function acquire_paypal_user_profile($accessToken) {
$url = ($this->_sanbox_flag ? $this->_acquire_user_profile_sandbox_url : $this->_acquire_user_profile_live_url) . $accessToken;
$ch = curl_init($url);
$options = array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_SSL_VERIFYPEER => false,
);
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
curl_close($ch);
if (!$response) return false;
return json_decode($response);
}
}
$pp = new paypallogin($redirect_uri, $client_id, $client_secret, $scope, $state, $sandbox);header('Location: ' . $pp->create_request_url());$redirect_uri?code=xxx&state=yyy。state 防 CSRF,調用 $token = $pp->acquire_access_token($_GET['code']);。$profile = $pp->acquire_paypal_user_profile($token);,解析 $profile->user_id、$profile->email 等。| 參數 | 說明 |
|---|---|
response_type=code | 授權碼模式,安全性高,Token 不暴露於前端 |
scope | 權限範圍,openid 必填,其他按需:email、phone、profile、address、paypalattributes |
nonce | 防重放攻擊隨機值,建議 timestamp + base64(random) |
state | CSRF 防護,回調時驗證與發起時一致 |
redirect_uri 必須 HTTPS,PayPal 審核要求。CURLOPT_SSL_VERIFYPEER => false 僅測試用,**生產必須開啟驗證**並配置 CA 憑證。client_secret,不得前端暴露、不入代碼倉庫(用環境變量/密鑰管理)。此類為 OAuth2 Authorization Code + OpenID Connect 標準實現,可作為 PayPal 登錄集成的參考基礎,按項目架構調整異步處理、錯誤重試、日誌審計等工程化細節。