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,