第一步:准备工作
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 = \'<xml> <appid>\'.$appid.\'</appid> <body>\'.$body.\'</body> <mch_id>\'.$mch_id.\'</mch_id> <nonce_str>\'.$nonce_str.\'</nonce_str> <notify_url>\'.$notify_url.\'</notify_url> <openid>\'.$openid.\'</openid> <out_trade_no>\'.$out_trade_no.\'</out_trade_no> <spbill_create_ip>\'.$spbill_create_ip.\'</spbill_create_ip> <total_fee>\'.$total_fee.\'</total_fee> <trade_type>\'.$trade_type.\'</trade_type> <sign>\'.$sign.\'</sign> </xml> \'; //统一接口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(\'<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>\'); }else{ echo \'微信支付失败\'; } }
通知成功
public function return_success() { $return[\'return_code\'] = \'SUCCESS\'; $return[\'return_msg\'] = \'OK\'; $xml_post = \'<xml> <return_code>\'.$return[\'return_code\'].\'</return_code> <return_msg>\'.$return[\'return_msg\'].\'</return_msg> </xml>\'; 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>appid</appid> <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--支付失败(其他原因,如银行返回失败)