第一步:准备工作
1、只有在微信开放平台认证过开发者资质的才能调用微信支付接口,所以首先就是要认证一下,很简单,只不过微信会收取300元的审核费用
2、设置支付目录
登录微信支付商户平台(pay.weixin.qq.com)-->产品中心-->开发配置,进行设置,设置后一般5分钟内生效。
这是微信官方的文档
3、设置授权域名
其实这一步如果之前做过微信登陆的就不用在做了,因为之前已经设置过了
之所以要设置授权域名是为了拿到openid,统一下单接口中要求必传用户openid,只有设置过授权域名的才能获取有效的openid。(如果用户登陆的时候拿到的是开放平台的unionid,那么你进行下单的时候,把unionid的属性名换成openid,一样可以成功支付)
第二步:正式开始
1、第一步
用户点击支付按钮,发出请求
function bay(pid,uid,money,name){ $.ajax({ url:ajaxurl+'/Car/payh5', dataType: 'json', data:{ pid:pid,//商品id uid:uid,//用户id money:money,//价格 name: name,//商品名称 }, type: 'post', success: function (data) { var state=data.state; if(state==0){ var RETURN_MSG =data.RETURN_MSG; alert(RETURN_MSG); }else{ callpay(data); } } }); }
2、第二步
根据前端传的参数,查询用户和商品的详细信息,生成支付订单
public function pay() { //获取微信支付所需信息 /** * @param [sting] $appid [小程序APPID] * @param [sting] $openid [用户openID] * @param [sting] $mch_id [商户ID](微信商户平台的id) * @param [sting] $key [商户key](微信商户平台的key) * @param [sting] $money [支付金额] * @param [sting] $body [商品描述] * @param [sting] $notify_url [回调地址]回调地址在后面写 * @return [sting] $data [响应数据] */ $merchpay = new \WeiXinPay($appid,$openid,$mch_id,$key,$money,$body,$notify_status,$o_number); $data = $merchpay->Pay(); $data['appId']=$appid; echo $this->ajaxReturn($data); }
我在这里把添加订单到自己数据表的步骤省略了,用的时候别忘了
之后,就是把参数转化为xml,调用统一下单接口
class WeiXinPay{ private $appid; private $openid; private $mch_id; private $key; private $money; private $body; private $notify_status; private $order_id; public function __construct($appid,$openid,$mch_id,$key,$money,$body,$notify_status,$order_id) { $this->appid = $appid; $this->openid = $openid; $this->mch_id = $mch_id; $this->key = $key; $this->money = $money; $this->body = $body; $this->notify_status = $notify_status; $this->order_id = $order_id; } public function Pay() { $fee = $this->money;//举例充值0.01 $appid = $this->appid;//支付APPID $body = $this->body; $mch_id = $this->mch_id; $nonce_str = $this->nonce_str();//随机字符串 // $notify_url = $this->$notify_url; if($this->notify_status==1){ $notify_url ='http://'.$_SERVER['HTTP_HOST'].U('Car/notify'); }else{ $notify_url ='http://'.$_SERVER['HTTP_HOST'].U('Car/qnotify'); } // p($notify_url); $openid = $this->openid; $order_id = $this->order_id; $getServerIP= $this->getServerIP(); // $out_trade_no = $this->order_number($openid);//商户订单号 $out_trade_no = $order_id;//商户订单号 $spbill_create_ip = $getServerIP;//ip $total_fee = $fee*100;//因为充值金额最小是1 而且单位为分 如果是充值1元所以这里需要*100 $trade_type = 'JSAPI';//交易类型 默认 //这里是按照顺序的 因为下面的签名是按照顺序 排序错误 肯定出错 $post['appid'] = $appid; $post['body'] = $body; $post['mch_id'] = $mch_id; $post['nonce_str'] = $nonce_str;//随机字符串 $post['notify_url'] = $notify_url; $post['openid'] = $openid; $post['out_trade_no'] = $out_trade_no; $post['spbill_create_ip'] = $spbill_create_ip;//终端的ip $post['total_fee'] = $total_fee;//总金额 最低为一块钱 必须是整数 $post['trade_type'] = $trade_type; $sign = $this->sign($post);//签名 $post_xml = ''.$appid.' '.$body.' '.$mch_id.' '.$nonce_str.' '.$notify_url.' '.$openid.' '.$out_trade_no.' '.$spbill_create_ip.' '.$total_fee.' '.$trade_type.' '.$sign.' '; //统一接口prepay_id $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; $xml = $this->http_request($url,$post_xml); $array = $this->xml($xml);//全要大写 // var_dump($array);exit; if($array['RETURN_CODE'] == 'SUCCESS' && $array['RESULT_CODE'] == 'SUCCESS'){ $time = time(); $tmp='';//临时数组用于签名 $tmp['appId'] = $appid; $tmp['nonceStr'] = $nonce_str; $tmp['package'] = 'prepay_id='.$array['PREPAY_ID']; $tmp['signType'] = 'MD5'; $tmp['timeStamp'] = "$time"; $data['state'] = 1; $data['timeStamp'] = "$time";//时间戳 $data['nonceStr'] = $nonce_str;//随机字符串 $data['signType'] = 'MD5';//签名算法,暂支持 MD5 $data['package'] = 'prepay_id='.$array['PREPAY_ID'];//统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=* $data['paySign'] = $this->sign($tmp);//签名,具体签名方案参见微信公众号支付帮助文档; $data['out_trade_no'] = $out_trade_no; }else{ $data['state'] = 0; $data['text'] = "错误"; $data['RETURN_CODE'] = $array['RETURN_CODE']; $data['RETURN_MSG'] = $array['RETURN_MSG']; } // var_dump($data);exit; $data = json_encode($data); return json_decode($data,true); }
在这个pay()方法中有调用了几个方法
32位随机字符串
private function nonce_str() { $result = ''; $str = 'QWERTYUIOPASDFGHJKLZXVBNMqwertyuioplkjhgfdsamnbvcxz'; for ($i=0;$i<32;$i++){ $result .= $str[rand(0,48)]; } return $result; }
签名 $data要先排好顺序
private function sign($data) { $stringA = ''; foreach ($data as $key=>$value){ if(!$value) continue; if($stringA) $stringA .= '&'.$key."=".$value; else $stringA = $key."=".$value; } $wx_key = $this->key;//申请支付后有给予一个商户账号和密码,登陆后自己设置key $stringSignTemp = $stringA.'&key='.$wx_key; return strtoupper(md5($stringSignTemp)); }
获取xml
private function xml($xml) { $p = xml_parser_create(); xml_parse_into_struct($p, $xml, $vals, $index); xml_parser_free($p); $data = ""; foreach ($index as $key=>$value) { if($key == 'xml' || $key == 'XML') continue; $tag = $vals[$value[0]]['tag']; $value = $vals[$value[0]]['value']; $data[$tag] = $value; } return $data; }
curl请求
private function http_request($url,$data = null,$headers=array()) { $curl = curl_init(); if( count($headers) >= 1 ){ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); } curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); if (!empty($data)){ curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $data); } curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($curl); curl_close($curl); return $output; }
之后就是回调方法notify(),用来接收处理微信返回的参数
//微信支付回调 public function notify() { $post =$GLOBALS['HTTP_RAW_POST_DATA'];//接受POST数据XML个数 $post_data = $this->xmlToArray($post);//微信支付成功,返回回调地址url的数据:XML转数组Array $postSign = $post_data['sign']; file_put_contents('log1.txt',$postSign); unset($post_data['sign']); // $post_data=unset($post_data['sign']); /* 微信官方提醒: * 商户系统对于支付结果通知的内容一定要做【签名验证】, * 并校验返回的【订单金额是否与商户侧的订单金额】一致, * 防止数据泄漏导致出现“假通知”,造成资金损失。 */ $str = $this->sign($post_data);//对数组数据拼接成key=value字符串 //判断签名 file_put_contents('log2.txt',$postSign); file_put_contents('log3.txt',$str); if($postSign!=$str){ echo '微信支付失败';exit; } $where['o_number'] = $post_data['out_trade_no']; $order_status = M('new_order')->where($where)->find(); if($post_data['return_code']=='SUCCESS'&&$postSign){ /* * 首先判断,订单是否已经更新为ok,因为微信会总共发送8次回调确认 * 其次,订单已经为ok的,直接返回SUCCESS * 最后,订单没有为ok的,更新状态为ok,返回SUCCESS */ $updata['status'] = '2'; // $updata['time']=date('Y-m-d H:i:s',time()); if(M('new_order')->where($where)->save($updata)!=false){ $new_order=M('new_order')->where($where)->find(); M('new_order')->where(array('uid'=>$new_order['uid'],'status'=>1))->delete(); $this->return_success(); } echo exit(''); }else{ echo '微信支付失败'; } }
通知成功
public function return_success() { $return['return_code'] = 'SUCCESS'; $return['return_msg'] = 'OK'; $xml_post = ''.$return['return_code'].' '.$return['return_msg'].' '; echo $xml_post;exit; }
到这里基本上已经算结束了,之后就是根据微信返回的结果进行逻辑处理
这是微信官方的开发文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_4
要是最后没有收到支付通知,无法确定支付状态,那就可以调用微信查询订单接口主动查询订单状态,完成最后一步
查询订单接口:https://api.mch.weixin.qq.com/pay/orderquery
所需要的参数:
<xml> <appid>appidappid> <mch_id>商户号mch_id> <nonce_str>随机字符nonce_str> <transaction_id>微信订单号transaction_id>/<out_trade_no>商户订单号out_trade_no>(二选一) <sign>签名sign> xml>
返回的参数:
当然了,这些返回的数据看着多,其实你只需要找到你要用的就可以了
比如你只要判断是否支付成功,那么只需要用的trade_state参数就可以了
trade_state的值所代表的解释:
SUCCESS—支付成功
REFUND—转入退款
NOTPAY—未支付
CLOSED—已关闭
REVOKED—已撤销(付款码支付)
USERPAYING--用户支付中(付款码支付)
PAYERROR--支付失败(其他原因,如银行返回失败)