微信小程序支付方法封装
每次搞微信支付都要翻微信开发文档,相当繁锁,有几个文档,还有点不一样,这次理清楚记录一下
微信支付开发文档 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1&index=1
微信支付接口签名校验工具 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=20_1
1 准备材料:
$appid = 'XXX'; //AppID(小程序ID) $AppSecret ='XXX'; //AppSecret(小程序密钥) $mch_id ='XXX'; //商户号 $merkey = 'XXX'; //商户支付密钥
2 微信小程序配置:微信支付,关联好商户号

3 微信商户平台:产品中心-》开发配置-》支付授权目录

支付流程:
1 按要求获取相关参数
https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1&index=1
2 生成签名
3 发送请求,拿到 prepay_id
4 配置参数(appId、nonceStr、package、signType、timeStamp、paySign),package就是第一次签名并请求拿到 prepay_id,paySign二次签名要获得
//创建订单示例
<?php
namespace app\api\controller;
use think\Controller;
class Wxpay extends Controller
{
	
	
	public $appid = 'XXX'; //AppID(小程序ID)
	public $AppSecret ='XXX'; //AppSecret(小程序密钥)
	public $mch_id ='XXX'; //商户号
	public $merkey = 'XXX'; //商户支付密钥
	
	public function index(){
		//echo $this->merkey;
	}
	
	
	//创建订单
	public function unifiedorder()
	{	
	    
		$input = array();
		$input['appid'] = $this->appid; //小程序ID
		$input['mch_id'] = $this->mch_id; //商户号
		$input['nonce_str'] = $this->getNonceStr(); //随机字符串
		$input['body'] = '云打印'; //随机字符串
		
		$input['out_trade_no'] = time(); //商户订单号20150806125358
		$input['total_fee'] = 10; //标价金额
		$input['spbill_create_ip'] = $_SERVER['REMOTE_ADDR']; //终端ip
		$input['notify_url'] = 'https://p.51zuso.com/api/Wxpay/notifyUrl'; //通知地址
		$input['trade_type'] = 'JSAPI'; //交易类型
		$input['openid'] = 'oqDsk4zf6SDAspf2KMKlb5obFhH8'; //openid
		
		
		$input['sign'] = $this->sign($input); //获取签名
		$data = $this->ToXml($input); //提交的xml格式数据
		
		
		$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; //统一下单接口
		$res = $this->postXmlCurl($data,$url); //psot提交
		
		//debug($res);
		$jsApiParameters = $this->GetJsApiParameters($res); //获取jsapi支付的参数
		return $jsApiParameters; 
	}
	
	
	//支付回调,提取xml数据为数组处理罗辑
	public function notifyUrl(){
		
        $xmlData = file_get_contents('php://input');
        libxml_disable_entity_loader(true); // 解析该xml字符串,利用simpleXML
        $xmlToaArr = simplexml_load_string($xmlData, 'SimpleXMLElement', LIBXML_NOCDATA); //禁止xml实体解析,防止xml注入
        $data = json_decode(json_encode($xmlToaArr),true);
		
	}
	
	//签名
	public function sign($value){
		//签名步骤一:按字典序排序参数
		ksort($value);
		//--格式化参数格式化成url参数
		$buff = "";
		foreach ($value as $k => $v)
		{
			if($k != "sign" && $v != "" && !is_array($v)){
				$buff .= $k . "=" . $v . "&";
			}
		}
		$buff = trim($buff, "&");
		$string = $buff;
		 
		//签名步骤二:在string后加入KEY
		$string =  $string . "&key=".$this->merkey;
		 
		//签名步骤三:MD5加密或者HMAC-SHA256
		//$string =hash_hmac("sha256",$string,$key);
		$string = md5($string);
		//签名步骤四:所有字符转为大写
		$result = strtoupper($string);
		 
		return $result;
	}
	
	
	/**
	 *
	 * 产生随机字符串,不长于32位
	 * @param int $length
	 * @return 产生的随机字符串
	 */
	public function getNonceStr($length = 32)
	{
		$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
		$str ="";
		for ( $i = 0; $i < $length; $i++ )  {
			$str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
		}
		return $str;
	}
	
	
	 /**
	 * 输出xml字符
	 * @throws WxPayException
	**/
	public function ToXml($value)
	{
		if(!is_array($value) || count($value) <= 0)
		{
			exit("ToXml-error");
		}
		 
		$xml = "<xml>";
		foreach ($value as $key=>$val)
		{
			if (is_numeric($val)){
				$xml.="<".$key.">".$val."</".$key.">";
			}else{
				$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
			}
		}
		$xml.="</xml>";
		return $xml; 
	}
	
	
	/*
	 * 以post方式提交xml到对应的接口url
	 *
	 * @param string $xml  需要post的xml数据
	 * @param string $url  url 提交的接口地址
	 * @param int $second   url执行超时时间,默认30s
	 * @throws WxPayException
	*/
	public function postXmlCurl($xml, $url, $second = 30)
	{
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_TIMEOUT, $second);//设置超时
		curl_setopt($ch,CURLOPT_URL, $url);  //指定URL
		curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE); //忽略证书
		curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false);//忽略证书
		curl_setopt($ch, CURLOPT_HEADER, FALSE); //忽略header头信息
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); //设定请求后返回结果
		 
		//post提交方式
		curl_setopt($ch, CURLOPT_POST, TRUE);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
		//运行curl
		$data = curl_exec($ch);
		//返回结果
		if($data){
			curl_close($ch);
			return $data;
		} else {
			$error = curl_errno($ch);
			curl_close($ch);
			exit("curl:$error");
		}
	}
	
	
	//获取jsapi支付的参数,将xml数据转为json
	public function GetJsApiParameters($xml){
		$arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
		$data = array();
		$data['appId'] = $arr['appid']; //微信小程序appid
		$data['nonceStr'] = $this->getNonceStr(); //随机字符串,长度为32个字符以下
		$data['package'] = 'prepay_id='.$arr['prepay_id']; //统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=***
		$data['signType'] = 'MD5'; //签名算法,应与后台下单时的值一致
		$data['timeStamp'] =$this->getMillisecond(); //时间戳,从 1970 年 1 月 1 日 00:00:00 至今的秒数,即当前的时间
		$data['paySign'] = $this->sign($data); //二次签名
		return  json_encode($data);
	}
	
	
	/**
	* 获取毫秒级别的时间戳
	*/
	public function getMillisecond()
	{
		//获取毫秒的时间戳
		$time = explode ( " ", microtime () );
		$time = $time[1] . ($time[0] * 1000);
		$time2 = explode( ".", $time );
		$time = $time2[0];
		return $time;
	}
	
}