PHP实现支付宝当面付-扫码支付接口[纯手写非SDK]
该接口为卢雨高手写完成,不借助其他任何类或现成的SDK,
该支付方式不需要支付宝商户后台做授权,只需要拿到app_id、privatekey、publickey三个参数即可,拿到这三个参数任意一个网站都可以发起支付和通知
类代码
<?php
//支付宝当面付,整合扫码支付
//2020年12月3日
//By:sevstudio
namespace SevStudio;
class Alipayf2fpay{
private $AppID = null;
private $PrivateKey = null;
private $PublicKey = null;
function __construct($config=[]){
if(!empty($config['app_id'])) $this->AppID = $config['app_id'];
if(!empty($config['privatekey'])) $this->PrivateKey = $config['privatekey'];
if(!empty($config['publicKey'])) $this->PublicKey = $config['publicKey'];
}
//传入配置
public function init($config){
if(!is_array($config)) throw new \Exception('配置格式错误');
if(empty($config['app_id'])) throw new \Exception('请传入商户APPID');
if(empty($config['privatekey'])) throw new \Exception('请传入商户私钥');
if(empty($config['publicKey'])) throw new \Exception('请传入商户公钥');
$this->AppID = $config['app_id'];
$this->PrivateKey = $config['privatekey'];
$this->PublicKey = $config['publicKey'];
}
//检测配置
private function checkInit(){
if(empty($this->AppID)) throw new \Exception('请传入商户APPID');
if(empty($this->PrivateKey)) throw new \Exception('请传入商户私钥');
if(empty($this->PublicKey)) throw new \Exception('请传入商户公钥');
}
//发起支付
public function pay($param){
//订单基本信息
$this->checkInit();
if(empty($param['orderno'])) throw new \Exception('请提供订单号orderno');
if(empty($param['money_pay'])) throw new \Exception('请提供支付金额(元)money_pay');
if(empty($param['subject'])) throw new \Exception('请提供商品名称subject');
$order = [
'out_trade_no' => $param['orderno'],
'total_amount' => floatval($param['money_pay']), //支付金额,单位:元
'subject' => $param['subject']
];
//公共参数
$base = [
'app_id' => $this->AppID,
'method' => 'alipay.trade.precreate',
'charset' => 'utf-8',
'sign_type' => 'RSA2',
'timestamp' => date('Y-m-d H:i:s'),
'version' => '1.0',
'notify_url' => APP_URL . '/bin/payclass/Alipayf2fpay/notify.php', //异步通知页面,可换成自己的
'biz_content' => json_encode($order),
];
//生成签名
ksort($base);
$query = [];
foreach($base as $k=>$v){
$query[] = $k . '=' . $v;
}
$query = implode('&',$query);
$sign = $this->getSignWithSHA256($query,$this->PrivateKey);
$base['sign'] = $sign;
//创建支付宝订单
$url = 'https://openapi.alipay.com/gateway.do';
$result = $this->curl_post($url,$base);
$json = json_decode($result,true);
if($json && isset($json['alipay_trade_precreate_response']) &&
isset($json['alipay_trade_precreate_response']['code']) &&
isset($json['alipay_trade_precreate_response']['qr_code']) &&
$json['alipay_trade_precreate_response']['code'] == '10000'
){
//下单成功,返回二维码链接
return $json['alipay_trade_precreate_response']['qr_code'];
}
return '';
}
//获取支付宝发送过来的数据
public function getRequestData(){
$response = file_get_contents("php://input");
parse_str($response,$json_gbk); //$json_gbk
if(empty($json_gbk)) return [null,null];
//编码转utf-8
$json_utf8 = $this->array_iconv($json_gbk);
if(empty($json_utf8)){
$this->_log('转换失败','notify');
}
return [$json_gbk,$json_utf8];
}
//异步通知
public function notify(&$result_data){
list($json,$json_utf8) = $this->getRequestData();
if(empty($json) || empty($json_utf8)){
return false;
}
$this->checkInit();
//验证签名
$param = [];
foreach($json as $k=>$v){
if($k == 'sign' || $k == 'sign_type')
continue;
$param[$k] = $v;
}
ksort($param);
$arr = [];
foreach($param as $k=>$v){
$arr[] = $k . '=' .$v;
}
$query = implode('&',$arr);
$ok = $this->verify($query,$json_utf8['sign'],$this->PublicKey);
if($ok !== 1){
$this->_log('签名验证失败','notify');
return false;
}
//验证是否支付成功
if(isset($json_utf8['trade_status']) && $json_utf8['trade_status'] == 'TRADE_SUCCESS'){
//支付成功了
$result_data = [
'orderno' => $json_utf8['out_trade_no'], //我方订单号
'liushui' => $json_utf8['trade_no'], //支付宝的交易流水号
'money' => $json_utf8['total_amount'],//订单金额
'time_pay' => $json_utf8['gmt_payment'],//付款时间 格式:2020-12-02 22:40:26
];
}
}
//返回通知成功
public function notify_success(){
exit('success');
}
public function _log($msg,$title='基础'){
$dir = __DIR__ . '/log/' . date('Ym');
if(!is_dir($dir) && !mkdir($dir,0777,true))
return false;
$file = $dir . '/' . date('d') . '.txt';
$content = is_string($msg) ? $msg : json_encode($msg , JSON_UNESCAPED_UNICODE);
return file_put_contents($file, '['.date('Y-m-d H:i:s') . '][' . $title .']' . PHP_EOL . $content . PHP_EOL . PHP_EOL , FILE_APPEND) > 0;
}
//签名方法
//生成 sha256WithRSA 签名
private function getSignWithSHA256($content, $privateKey){
$privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" .
wordwrap($privateKey, 64, "\n", true) .
"\n-----END RSA PRIVATE KEY-----";
$key = openssl_get_privatekey($privateKey);
openssl_sign($content, $signature, $key, "SHA256");
openssl_free_key($key);
return base64_encode($signature);
}
//验证 sha256WithRSA 签名
private function verify($content, $sign, $publicKey){
$publicKey = "-----BEGIN PUBLIC KEY-----\n" .
wordwrap($publicKey, 64, "\n", true) .
"\n-----END PUBLIC KEY-----";
$key = openssl_get_publickey($publicKey);
$ok = openssl_verify($content,base64_decode($sign), $key, 'SHA256');
openssl_free_key($key);
//如果签名正确返回int 1, 签名错误返回 0, 内部发生错误则返回-1.
return $ok;
}
private function curl_post($postUrl,$data,$header = ''){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$postUrl);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
if(substr($postUrl,0,5) == "https"){
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
}
if(is_array($header) && count($header)>0){
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
}
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
//数组编码转换
private function array_iconv($arr, $in_charset='gbk', $out_charset='utf-8'){
$ret = eval('return ' . iconv($in_charset,$out_charset,var_export($arr,true).';'));
return $ret;
}
}
date_default_timezone_set('Asia/Shanghai');
使用方法
实例化时传三个参数或者通过init()方法传递配置参数,然后$url=$object->pay('订单信息');即可获取到二维码图片内容,然后直接通过phpqrcode工具QRcode::png($url);就能显示支付二维码
同样异步通知的时候$object->notify($data); 支付成功会把相关信息存入$data,