微信小程序支付方法封装
每次搞微信支付都要翻微信开发文档,相当繁锁,有几个文档,还有点不一样,这次理清楚记录一下
微信支付开发文档 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; } }