220 lines
7.0 KiB
PHP
220 lines
7.0 KiB
PHP
<?php
|
|
|
|
class paypal_plugin
|
|
{
|
|
static public $info = [
|
|
'name' => 'paypal', //支付插件英文名称,需和目录名称一致,不能有重复
|
|
'showname' => 'PayPal', //支付插件显示名称
|
|
'author' => 'PayPal', //支付插件作者
|
|
'link' => 'https://www.paypal.com/', //支付插件作者链接
|
|
'types' => ['paypal'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank
|
|
'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid
|
|
'appid' => [
|
|
'name' => 'ClientId',
|
|
'type' => 'input',
|
|
'note' => '',
|
|
],
|
|
'appkey' => [
|
|
'name' => 'ClientSecret',
|
|
'type' => 'input',
|
|
'note' => '',
|
|
],
|
|
'appswitch' => [
|
|
'name' => '模式选择',
|
|
'type' => 'select',
|
|
'options' => [0=>'线上模式',1=>'沙盒模式'],
|
|
],
|
|
'currency_code' => [
|
|
'name' => '结算货币',
|
|
'type' => 'select',
|
|
'options' => [
|
|
'USD' => '美元 (USD)',
|
|
'AUD' => '澳元 (AUD)',
|
|
'BRL' => '巴西雷亚尔 (BRL)',
|
|
'CAD' => '加拿大元 (CAD)',
|
|
'CNY' => '人民币 (CNY)',
|
|
'CZK' => '克朗 (CZK)',
|
|
'DKK' => '丹麦克朗(DKK)',
|
|
'EUR' => '欧元 (EUR)',
|
|
'HKD' => '港币 (HKD)',
|
|
'HUF' => '匈牙利福林 (HUF)',
|
|
'INR' => '印度卢比 (INR)',
|
|
'ILS' => '以色列新谢克尔 (ILS)',
|
|
'JPY' => '日元 (JPY)',
|
|
'MYR' => '马来西亚林吉特 (MYR)',
|
|
'MXN' => '墨西哥比索 (MXN)',
|
|
'TWD' => '新台币 (TWD)',
|
|
'NZD' => '新西兰元 (NZD)',
|
|
'NOK' => '挪威克朗 (NOK)',
|
|
'PHP' => '菲律宾比索 (PHP)',
|
|
'PLN' => '波兰兹罗提 (PLN)',
|
|
'GBP' => '英镑 (GBP)',
|
|
'RUB' => '俄罗斯卢布 (RUB)',
|
|
'SGD' => '新加坡元 (SGD)',
|
|
'SEK' => '瑞典克朗 (SEK)',
|
|
'CHF' => '瑞士法郎 (CHF)',
|
|
'THB' => '泰铢 (THB)',
|
|
],
|
|
],
|
|
'currency_rate' => [
|
|
'name' => '货币汇率',
|
|
'type' => 'input',
|
|
'note' => '例如1元人民币兑换0.137美元(USD),则此处填0.137',
|
|
],
|
|
],
|
|
'select' => null,
|
|
'note' => '', //支付密钥填写说明
|
|
'bindwxmp' => false, //是否支持绑定微信公众号
|
|
'bindwxa' => false, //是否支持绑定微信小程序
|
|
];
|
|
|
|
static public function submit(){
|
|
global $siteurl, $channel, $order, $ordername, $sitename, $conf, $DB;
|
|
|
|
require_once(PAY_ROOT."inc/PayPalClient.php");
|
|
|
|
if(!$channel['currency_rate']) $channel['currency_rate'] = 1;
|
|
$money = round($order['realmoney'] * $channel['currency_rate'], 2);
|
|
|
|
$parameter = [
|
|
'intent' => 'CAPTURE',
|
|
'purchase_units' => [
|
|
[
|
|
'amount' => [
|
|
'currency_code' => $channel['currency_code'],
|
|
'value' => $money,
|
|
],
|
|
'description' => $order['name'],
|
|
'custom_id' => TRADE_NO,
|
|
'invoice_id' => TRADE_NO,
|
|
],
|
|
],
|
|
'application_context'=> [
|
|
'cancel_url' => $siteurl.'pay/cancel/'.TRADE_NO.'/',
|
|
'return_url' => $siteurl.'pay/return/'.TRADE_NO.'/',
|
|
],
|
|
];
|
|
|
|
try {
|
|
$approvalUrl = \lib\Payment::lockPayData(TRADE_NO, function() use($channel, $parameter) {
|
|
$client = new PayPalClient($channel['appid'], $channel['appkey'], $channel['appswitch']);
|
|
$result = $client->createOrder($parameter);
|
|
|
|
$approvalUrl = null;
|
|
foreach($result['links'] as $link){
|
|
if($link['rel'] == 'approve'){
|
|
$approvalUrl = $link['href'];
|
|
}
|
|
}
|
|
if(empty($approvalUrl)){
|
|
throw new Exception('获取支付链接失败');
|
|
}
|
|
return $approvalUrl;
|
|
});
|
|
|
|
return ['type'=>'jump','url'=>$approvalUrl];
|
|
}
|
|
catch (Exception $ex) {
|
|
sysmsg('PayPal下单失败:'.$ex->getMessage());
|
|
}
|
|
}
|
|
|
|
//同步回调
|
|
static public function return(){
|
|
global $channel, $order;
|
|
|
|
require_once(PAY_ROOT."inc/PayPalClient.php");
|
|
|
|
if (isset($_GET['token']) && isset($_GET['PayerID'])) {
|
|
|
|
$token = $_GET['token'];
|
|
try {
|
|
$client = new PayPalClient($channel['appid'], $channel['appkey'], $channel['appswitch']);
|
|
$result = $client->captureOrder($token);
|
|
} catch (Exception $ex) {
|
|
return ['type'=>'error','msg'=>'支付订单失败 '.$ex->getMessage()];
|
|
}
|
|
|
|
$captures = $result['purchase_units'][0]['payments']['captures'][0];
|
|
$amount = $captures['seller_receivable_breakdown']['gross_amount']['value'];
|
|
$trade_no = $captures['id'];
|
|
$out_trade_no = $captures['invoice_id'];
|
|
$buyer = $result['payer']['email_address'];
|
|
|
|
if($out_trade_no == TRADE_NO){
|
|
processReturn($order, $trade_no, $buyer);
|
|
}else{
|
|
return ['type'=>'error','msg'=>'订单信息校验失败'];
|
|
}
|
|
} else {
|
|
return ['type'=>'error','msg'=>'PayPal返回参数错误'];
|
|
}
|
|
}
|
|
|
|
static public function cancel(){
|
|
return ['type'=>'page','page'=>'error'];
|
|
}
|
|
|
|
static public function webhook(){
|
|
global $channel, $order;
|
|
$json = file_get_contents('php://input');
|
|
$arr = json_decode($json, true);
|
|
if(!$arr || empty($arr['event_type'])){
|
|
exit('事件类型为空');
|
|
}
|
|
if(!in_array($arr['event_type'], ['PAYMENT.CAPTURE.COMPLETED'])){
|
|
exit('其他事件('.$arr['event_type'].':'.$arr['summary'].')');
|
|
}
|
|
if(empty($channel['appsecret'])){
|
|
exit('未配置webhookid');
|
|
}
|
|
|
|
$crc32 = crc32($json);
|
|
if (empty($_SERVER['HTTP_PAYPAL_TRANSMISSION_ID']) || empty($_SERVER['HTTP_PAYPAL_TRANSMISSION_TIME']) || empty($crc32)) {
|
|
exit('签名数据为空');
|
|
}
|
|
$sign_string = $_SERVER['HTTP_PAYPAL_TRANSMISSION_ID'].'|'.$_SERVER['HTTP_PAYPAL_TRANSMISSION_TIME'].'|'.$channel['appsecret'].'|'.$crc32;
|
|
|
|
// 通过PAYPAL-CERT-URL头信息去拿公钥
|
|
$public_key = openssl_pkey_get_public(get_curl($_SERVER['HTTP_PAYPAL_CERT_URL']));
|
|
$details = openssl_pkey_get_details($public_key);
|
|
$verify = openssl_verify($sign_string, base64_decode($_SERVER['HTTP_PAYPAL_TRANSMISSION_SIG']), $details['key'], 'SHA256');
|
|
if($verify != 1)
|
|
{
|
|
exit('签名验证失败');
|
|
}
|
|
|
|
$resource = $arr['resource'];
|
|
$amount = $resource['amount']['value'];
|
|
$trade_no = $resource['id'];
|
|
$out_trade_no = $resource['invoice_id'];
|
|
}
|
|
|
|
//退款
|
|
static public function refund($order){
|
|
global $channel;
|
|
if(empty($order))exit();
|
|
|
|
require_once(PAY_ROOT."inc/PayPalClient.php");
|
|
|
|
if(!$channel['currency_rate']) $channel['currency_rate'] = 1;
|
|
$money = round($order['refundmoney'] * $channel['currency_rate'], 2);
|
|
|
|
$parameter = [
|
|
'amount' => [
|
|
'currency_code' => $channel['currency_code'],
|
|
'value' => $money,
|
|
],
|
|
];
|
|
|
|
try{
|
|
$client = new PayPalClient($channel['appid'], $channel['appkey'], $channel['appswitch']);
|
|
$res = $client->refundPayment($order['api_trade_no'], $parameter);
|
|
$result = ['code'=>0, 'trade_no'=>$res['id'], 'refund_fee'=>$res['amount']['value']];
|
|
}catch(Exception $e){
|
|
$result = ['code'=>-1, 'msg'=>$e->getMessage()];
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
} |