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,