贵阳网站推广,陕西煤化建设集团网站,软件开发培训视频,网站导航设计应注意哪些问题看了好多关于讲解微信H5支付开发的文章#xff0c;大多数都是通过微信内部浏览器来调用支付接口(其实就是公众号支付)#xff0c;可能是因为H5支付接口刚开放不久吧。微信官方体验链接#xff1a;http://wxpay.wxutil.com/mch/pay/h5.v2.php#xff0c;请在微信外浏览器打开…看了好多关于讲解微信H5支付开发的文章大多数都是通过微信内部浏览器来调用支付接口(其实就是公众号支付)可能是因为H5支付接口刚开放不久吧。微信官方体验链接http://wxpay.wxutil.com/mch/pay/h5.v2.php请在微信外浏览器打开。看了上面的体验链接如果感兴趣可以接着往下看希望对你有所帮助。一、Android端Android端代码相对来说比较简单一些我这边直接调用系统浏览器打开H5支付页面Intent intent new Intent();intent.setAction(android.intent.action.VIEW);Uri content_url Uri.parse(url); //url里面包含了后端需要用到的参数例如金额用户IDintent.setData(content_url);startActivity(intent);刚开始考虑过使用webview来加载支付页面但是调试接口的时候发现一直报如下错误微信官方报错示例图根据微信官方的报错示例可以看出应该是webview中没有设置referer导致的。于是我按照说明加上了referer然鹅并没有什么卵用依然报错。我只好放弃使用webview改用系统浏览器。使用系统浏览器的弊端还是挺明显的客户端和后台传值不好处理(正在看文章的你如果用webview调用成功了还请赐教)二、PHP端1.获取从Android端传过来的参数目前我只想到一种方法就是通过url携带参数给服务端传值OK获取方式如下//当前页面的URL地址为http://www.XXXXX.com/wechatpay/h5Pay.php?money100uid337828932$string $_SERVER[QUERY_STRING];//通过$_SERVER[QUERY_STRING]函数获取url地址?后面的值$androidData convertUrlQuery($string);//将字符串$string转化为数组$money $androidData[money ];//获取money值 100$uid $androidData[uid];//获取用户ID 337828932上面用到的convertUrlQuery函数代码//将字符串参数变为数组function convertUrlQuery($query){$queryParts explode(, $query);$params array(); foreach ($queryParts as $param) {$item explode(, $param);$params[$item[0]] $item[1];} return $params;}2.调用微信统一下单API2.1 具体参数名称以及各参数的用途请查看微信官方文档 统一下单API//配置需要传递给微信的参数$input new WxPayUnifiedOrder();$input-SetBody(xxxx-商品购买);$input-SetAttach(xxxx);$input-SetDevice_info(WEB);$input-SetOut_trade_no(WxPayConfig::MCHID . date(YmdHis).$uid);//把UID加在末尾主要用于识别用户给对应的用户充值余额$input-SetTotal_fee($money);$input-SetTime_start(date(YmdHis));$input-SetTime_expire(date(YmdHis, time() 600));$input-SetGoods_tag(t);$input-SetNotify_url(http://www.xxxxx.com/wechatpay/notify.php);//用于接收微信下发的支付结果通知$input-SetTrade_type(MWEB);$input-SetOpenid($openId);$input-SetScene_info({\h5_info\: \h5_info\{\type\: \Wap\,\wap_url\: \http://www.xxxxx.com/shop\,\wap_name\: \xxx\}});2.2 接下来通过$order WxPayApi::unifiedOrder($input);获取微信返回的参数unifiedOrder函数具体实现如下/**** 统一下单WxPayUnifiedOrder中out_trade_no、body、total_fee、trade_type必填* appid、mchid、spbill_create_ip、nonce_str不需要填入* param WxPayUnifiedOrder $inputObj* param int $timeOut* throws WxPayException* return 成功时返回其他抛异常*/public static function unifiedOrder($inputObj, $timeOut 6){$url https://api.mch.weixin.qq.com/pay/unifiedorder; //检测必填参数if(!$inputObj-IsOut_trade_noSet()) { throw new WxPayException(缺少统一支付接口必填参数out_trade_no);}else if(!$inputObj-IsBodySet()){ throw new WxPayException(缺少统一支付接口必填参数body);}else if(!$inputObj-IsTotal_feeSet()) { throw new WxPayException(缺少统一支付接口必填参数total_fee);}else if(!$inputObj-IsTrade_typeSet()) { throw new WxPayException(缺少统一支付接口必填参数trade_type);}//异步通知url未设置则使用配置文件中的urlif(!$inputObj-IsNotify_urlSet()){$inputObj-SetNotify_url(WxPayConfig::NOTIFY_URL);//异步通知url}$inputObj-SetAppid(WxPayConfig::APPID);//公众账号ID$inputObj-SetMch_id(WxPayConfig::MCHID);//商户号$inputObj-SetSpbill_create_ip(self::getIp());//终端ip//$inputObj-SetSpbill_create_ip(1.1.1.1);$inputObj-SetNonce_str(self::getNonceStr());//随机字符串//签名$inputObj-SetSign();$xml $inputObj-ToXml();$startTimeStamp self::getMillisecond();//请求开始时间$response self::postXmlCurl($xml, $url, false, $timeOut);$result WxPayResults::Init($response); self::reportCostTime($url, $startTimeStamp, $result);//上报请求花费时间return $result;}2.3 上面代码中需要注意的有以下3点获取客服端的真实IP地址使用REMOTE_ADDR只能获取访问者本地连接中设置的IP。经本人粗略测试使用UC浏览的时候将无法直接获取真实IP这是如果把这个IP传过去微信将会返回 网络环境未能通过安全验证请稍后再试 的错误这时可以使用以下方式获取用户真实IP//获取用户真实IPpublic static function getIp(){$ip ; if(isset($_SERVER[HTTP_X_FORWARDED_FOR])){$ip $_SERVER[HTTP_X_FORWARDED_FOR];}elseif(isset($_SERVER[HTTP_CLIENT_IP])){$ip $_SERVER[HTTP_CLIENT_IP];}else{$ip $_SERVER[REMOTE_ADDR];}$ip_arr explode(,, $ip); return $ip_arr[0];}把参数转换成XML格式在这JSON横行的年代还使用XML格式传递数据也许这是微信的高端操作也许是他们以前的框架就是那样这我们就不管了。直接上代码/*** 输出xml字符* throws WxPayException**/public function ToXml(){ if(!is_array($this-values)|| count($this-values) 0){ throw new WxPayException(数组数据异常);}$xml ; foreach ($this-values as $key$val){ if (is_numeric($val)){$xml..$val..$key.;}else{$xml..$key.;}}$xml.; return $xml;}签名方法签名生成的通用步骤如下第一步设所有发送或者接收到的数据为集合M将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序)使用URL键值对的格式(即key1value1key2value2…)拼接成字符串stringA。特别注意以下重要规则参数名ASCII码从小到大排序(字典序)如果参数的值为空不参与签名参数名区分大小写验证调用返回或微信主动通知签名时传送的sign参数不参与签名将生成的签名与该sign值作校验。微信接口可能增加字段验证签名时必须支持增加的扩展字段第二步在stringA最后拼接上key得到stringSignTemp字符串并对stringSignTemp进行MD5运算再将得到的字符串所有字符转换为大写得到sign值signValue。key设置路径微信商户平台(pay.weixin.qq.com)--账户设置--API安全--密钥设置/*** 生成签名* return 签名*/public function MakeSign(){ //签名步骤一按字典序排序参数ksort($this-values);$string $this-ToUrlParams(); //签名步骤二在string后加入KEY$string $string . key.WxPayConfig::KEY; //签名步骤三MD5加密$string md5($string); //签名步骤四所有字符转为大写$result strtoupper($string); return $result;} /*** 格式化参数 --格式化成url参数*/public function ToUrlParams(){$buff ; foreach ($this-values as $k $v){ if($k ! sign $v ! !is_array($v)){$buff . $k . . $v . ;}}$buff trim($buff, ); return $buff;}2.4 通过返回的mweb_url参数调起微信APPif ($order[mweb_url]) {$orderId $input-GetOut_trade_no();$payUrl $order[mweb_url].redirect_urlhttp%3A%2F%2Fwww.xxxx.com%2Fwechatpay%2Forderquery.php%3Ftransaction_id%3D.$orderId;Log::DEBUG($payUrl); //通过JS打开链接调起微信APP支付echo ;}这里详细解释一下payUrl中redirect_url参数的含义正常流程用户支付完成后会返回至发起支付的页面如需返回至指定页面则可以在MWEB_URL后拼接上redirect_url参数来指定回调页面。如您希望用户支付完成后跳转至https://www.wechatpay.com.cn则可以做如下处理假设您通过统一下单接口获到的MWEB_URL https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_idwx20161110163838f231619da20804912345package1037687096则拼接后的地址为MWEB_URL https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_idwx20161110163838f231619da20804912345package1037687096redirect_urlhttps%3A%2F%2Fwww.wechatpay.com.cn注意1.需对redirect_url进行urlencode处理2.由于设置redirect_url后,回跳指定页面的操作可能发生在1,微信支付中间页调起微信收银台后超过5秒 2,用户点击“取消支付“或支付完成后点“完成”按钮。因此无法保证页面回跳时支付流程已结束所以商户设置的redirect_url地址不能自动执行查单操作应让用户去点击按钮触发查单操作,而我后面拼接的$orderId就是用于查单操作的参数。回跳页面展示效果可参考下图回调页面示例图.png3.处理微信支付结果通知支付完成后微信会把相关支付结果和用户信息发送给商户商户需要接收处理并返回应答。对后台通知交互时如果微信收到商户的应答不是成功或超时微信认为通知失败微信会通过一定的策略定期重新发起通知尽可能提高通知的成功率但微信不保证通知最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600单位秒)注意同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。推荐的做法是当收到通知进行处理时首先检查对应业务数据的状态判断该通知是否已经处理过如果没有处理过再进行处理如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前要采用数据锁进行并发控制以避免函数重入造成的数据混乱。特别提醒商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致防止数据泄漏导致出现“假通知”造成资金损失。支付回调基础类/**** 回调基础类* author widyhu**/class WxPayNotify extends WxPayNotifyReply{ /**** 回调入口* param bool $needSign 是否需要签名输出*/final public function Handle($needSign true){$msg OK; //当返回false的时候表示notify中调用NotifyCallBack回调失败获取签名校验失败此时直接回复失败$result WxpayApi::notify(array($this, NotifyCallBack), $msg); if($result false){ $this-SetReturn_code(FAIL); $this-SetReturn_msg($msg); $this-ReplyNotify(false); return;} else { //该分支在成功回调到NotifyCallBack方法处理完成之后流程$this-SetReturn_code(SUCCESS); $this-SetReturn_msg(OK);} $this-ReplyNotify($needSign);}/**** 回调方法入口子类可重写该方法* 注意* 1、微信回调超时时间为2s建议用户使用异步处理流程确认成功之后立刻回复微信服务器* 2、微信服务器在调用失败或者接到回包为非确认包的时候会发起重试需确保你的回调是可以重入* param array $data 回调解释出的参数* param string $msg 如果回调处理失败可以将错误信息输出到该方法* return true回调出来完成不需要继续回调false回调处理未完成需要继续回调*/public function NotifyProcess($data, $msg){ //TODO 用户基础该类之后需要重写该方法成功的时候返回true失败返回falsereturn true;}/**** notify回调方法该方法中需要赋值需要输出的参数,不可重写* param array $data* return true回调出来完成不需要继续回调false回调处理未完成需要继续回调*/final public function NotifyCallBack($data){$msg OK;$result $this-NotifyProcess($data, $msg);if($result true){ $this-SetReturn_code(SUCCESS); $this-SetReturn_msg(OK);} else { $this-SetReturn_code(FAIL); $this-SetReturn_msg($msg);} return $result;}/**** 回复通知* param bool $needSign 是否需要签名输出*/final private function ReplyNotify($needSign true){ //如果需要签名if($needSign true $this-GetReturn_code($return_code) SUCCESS){ $this-SetSign();}WxpayApi::replyNotify($this-ToXml());}}需要注意的是如果用户只是打开付款界面而没有执行付款操作的话是不会触发通知的。当我们接收到的参数中同时含有return_coderesult_codetrade_state并且每个返回值都为SUCCESS的时候代表用户付款成功if(array_key_exists(return_code, $result) array_key_exists(result_code, $result)array_key_exists(trade_state, $resultData) $resultData[trade_state] SUCCESS $result[return_code] SUCCESS $result[result_code] SUCCESS){ //这里执行给用户充值余额的操作}4.redirect_url回调页面处理如果在第2.4步骤中MWEB_URL后拼接上了redirect_url参数用户支付完成后将返回到这个页面。当用户点击界面的已完成按钮将触发查单操作微信查单操作API查单操作相关代码如下//查询订单public function Queryorder($transaction_id){$input new WxPayOrderQuery();$input-SetTransaction_id($transaction_id);$result WxPayApi::orderQuery($input);Log::DEBUG(query: . json_encode($result)); if(array_key_exists(return_code, $result) array_key_exists(result_code, $result) $result[return_code] SUCCESS $result[result_code] SUCCESS){ return $result;} return false;}界面布局以及相关的逻辑处理if(isset($_REQUEST[out_trade_no]) $_REQUEST[out_trade_no] ! ){$out_trade_no $_REQUEST[out_trade_no];$input new WxPayOrderQuery();$input-SetOut_trade_no($out_trade_no);Log::DEBUG(out_trade_no.$out_trade_no);// printf_info(WxPayApi::orderQuery($input));$resultWxPayApi::orderQuery($input); if(array_key_exists(return_code, $result) array_key_exists(result_code, $result) array_key_exists(trade_state, $result) $result[trade_state] SUCCESS $result[return_code] SUCCESS $result[result_code] SUCCESS){$successUrlsuccess.html; echo ;}else{$failUrlfail.html; echo ;} exit();}?window.function () { var Requestnew UrlSearch(); //实例化document.getElementById(out_trade_no).valueRequest.transaction_id;document.getElementById(transaction_id).style.height 0;document.getElementById(out_trade_no).style.height 0;document.getElementById(btn).style.height 0;$.DialogByZ.Confirm({Title: , Content: 请确认微信支付是否已完成,FunL:confirmL,FunR:Immediate})} ///function confirmL(){$.DialogByZ.Close();document.getElementById(btn).click();} function Immediate(){ //location.hrefhttp://sc.chinaz.com/jiaoben/window.location.hrefnonglegou://data/?statefail;} function UrlSearch(){ var name,value; var strlocation.href; //取得整个地址栏var numstr.indexOf(?)strstr.substr(num1); //取得所有参数 stringvar.substr(start [, length ]var arrstr.split(); //各个参数放到数组里for(var i0;i arr.length;i){numarr[i].indexOf(); if(num0){namearr[i].substring(0,num);valuearr[i].substr(num1);this[name]value;}}}三、总结微信H5支付的整个流程大概就是这样子以下是流程图接口流程图用户在商户侧完成下单使用微信支付进行支付由商户后台向微信支付发起下单请求(调用统一下单接口)注交易类型trade_typeMWEB统一下单接口返回支付相关参数给商户后台如支付跳转url(参数名“mweb_url”)商户通过mweb_url调起微信支付中间页中间页进行H5权限的校验安全性检查(此处常见错误请见下文)如支付成功商户后台会接收到微信侧的异步通知用户在微信支付收银台完成支付或取消支付,返回商户页面(默认为返回支付发起页面)商户在展示页面引导用户主动发起支付结果的查询商户后台判断是否接到收微信侧的支付结果通知如没有后台调用我们的订单查询接口确认订单状态展示最终的订单支付结果给用户