uniapp 开发App使用微信H5支付解决方案
我们在开发app常常需要接入支付功能,但是有时候出于包体积或审核的因素,并不想接入支付相关的sdk,这个时候,就可以考虑使用h5支付完成购买服务,只需要访问后端返回的h5支付链接即可,便捷而简单。
话不多说,进入正题!
1、开通h5支付
前往微信商户平台 - 产品中心 - 我的产品 - 支付产品 - h5支付,申请开通h5支付,要审核1-7天,没问题的话一般两三天就能完成审核。
2、前端部分
2.1、请求h5支付接口
注意两个点:
1、先下单再进行支付,下单逻辑不同的项目业务逻辑也不同,故这里不多做阐述;
2、微信h5支付返回的mweb_url,前端直接访问,会报错:商家参数格式有误,请联系商家解决。解决方案是需要设置正确的Refere,具体看下方代码
// 发起支付请求, order_no为订单号requestPay(order_no){
let that = this
// #ifdef APP-PLUS// 我们的项目封装了统一管理API的管理类,请求接口封装每个项目不同,换成自己的方法即可
this.$api.apiH5Pay({'order_no': order_no
}).then(res =>{if(res.data.code ==200){
let data = res.data.data
let mweb_url = data.url
let msg = data.msg
let confirmText = data.confirm
uni.showModal({
title:'提示',content: msg,confirmText: confirmText,showCancel:false,success:function(res){if(res.confirm){
console.log('用户点击已完成支付');// 此处自行编写相应的业务逻辑代码,如订单状态查询或刷新用户信息等}}});// app创建webview访问服务端返回的h5支付链接constplatform= uni.getSystemInfoSync().platform
// 申请h5支付的域名,换成自己的constdomain='www.xxx.com'// 创建一个webviewconstwebview= plus.webview.create('','https://'+ domain);// 通过webview打开链接,后面加referer表示该链接是从哪里打开的,请填写申请h5支付的域名,比如:http://www.baidu.com,需要已备案switch(platform){case'android':
webview.loadURL(mweb_url,{'Referer':'https://'+ domain});break;case'ios':
webview.loadURL(mweb_url,{'Referer': domain +'://'});break;default:break;}}}).catch(err=>{//})// #endif},
2.2、 可能遇到的问题
可能会遇到iOS跳转微信完成支付后无法自动返回app,是因为UrlSchemes没有配置好。
解决方案:
点击项目的manifest.json文件, App常用其它设置-UrlSchemes,将申请h5支付的域名输入进去作为返回app的scheme
3、后端部分
3.1 支付接口
// app微信h5支付接口publicfunctionh5Pay(){// 订单号$order_no=request()->param("order_no");// 我设计接口时,做了统一控制,需要登录的接口会自动识别token是否有效,有效则会提前token中的userId。各位可以用各自的方案获取下单的用户信息$userModel=User::get($this->userId);$orderModel=Order::get(['order_no'=>$order_no]);if(empty($userModel)||empty($orderModel)){returnRApi::error("没有找到订单");}// 值得一提:如果校验工具通过,却提示签名错误,前往商户平台重新设置一下商户支付密钥就可以了,即使设置一模一样的商户支付密钥也可以,微信的bug$timeStamp=time();$nonceStr=Self::createNoncestr();$signType='MD5';$outTradeNo=$order_no;$body=$orderModel->body;//描述$total_fee=$orderModel->total_fee*100;$openid=$userModel->app_openid;//统一下单接口$result=$this->unifiedorder($openid,$outTradeNo,$nonceStr,$total_fee,$body,'MWEB');// 前端主要用到mweb_url进行支付, app访问5支付链接时,会自动跳转微信app进行支付操作,这个时候比较友好的做法是,在我们的app购买商品页面,弹出一个提示,这里的示例,msg是提示的内容,confirm是提示框按钮文本,后端控制文案,会更灵活,当你遇到变脸很快的产品和老板时,他们突然要改个提示,总不能重新发一个提审包吧returnRApi::success(['url'=>$result['mweb_url'],'msg'=>'请前往微信完成付款','confirm'=>'我已完成支付']);}
3.2 微信订单支付状态查询
publicfunctionorderquery(){$order_no=request()->param("order_no");$userModel=User::get($this->userId);$orderModel=Order::get(['order_no'=>$order_no]);if(empty($orderModel)){returnRApi::error("没有找到订单:".$order_no);}$appid='';// 微信开放平台创建的移动应用appid$mch_id='';// 商户号$transaction_id=$orderModel->transaction_id;//微信订单号if(empty($transaction_id)){returnRApi::error('微信订单号为空',201,[]);}// 当前时间$time=time();// 生成随机字符串$nonce_str=md5($time.mt_rand(00000,99999));//API参数$params=['appid'=>$appid,//应用ID'mch_id'=>$mch_id,//商户号'transaction_id'=>$transaction_id,//商户订单号'nonce_str'=>$nonce_str,// 随机字符串];//请求API$url='https://api.mch.weixin.qq.com/pay/orderquery';//生成签名$params['sign']=$this->getSign($params);$xmlData=$this->arrayToXml($params);$result=$this->xmlToArray($this->postXmlCurl($xmlData,$url,60));// 请求失败if($result['return_code']==='FAIL'){returnRApi::error('查询失败',201,$result);}// 微信返回的订单状态是已完成支付if($result['return_code']==='SUCCESS'||$result['result_code']==='SUCCESS'||$result['trade_state']==='SUCCESS'){// 查询到订单状态的时候,如果已经完成付款,数据库表订单状态却没改变,则进行商品发放并标记已支付if($orderModel->state!=1){// 假如数据库状态为未支付,此处添加业务代码完成支付标记操作}returnRApi::success(1,'支付已完成');}// 查询到了,但是订单状态为未完成支付returnRApi::success(0,'等待支付');}
3.3 其余部分相关代码块
// 统一下单接口privatefunctionunifiedorder($openid,$out_trade_no,$noncestr,$total_fee,$body='',$trade_type='JSAPI'){$appid='';// 微信开放平台创建的移动应用appid$mch_id='';// 商户号$notify_url='https://www.xxx.com/pay/wxNotify';// 改成自己的回调地址$url='https://api.mch.weixin.qq.com/pay/unifiedorder';$parameters=array('appid'=>$appid,// 应用id'mch_id'=>$mch_id,// 商户号'body'=>$body,// 商品描述'nonce_str'=>$noncestr,// 随机字符串'notify_url'=>$notify_url,// 通知地址 确保外网能正常访问'openid'=>$openid,// 用户微信openid'out_trade_no'=>$out_trade_no,// 商户订单号'spbill_create_ip'=>$this->get_client_ip(),// 终端IP'total_fee'=>$total_fee,// 单位 分'trade_type'=>$trade_type// 交易类型);//统一下单签名$parameters['sign']=$this->getSign($parameters);$xmlData=$this->arrayToXml($parameters);$result=$this->xmlToArray($this->postXmlCurl($xmlData,$url,60));//$return = $this->postXmlCurl($xmlData, $url, 60);if(!isset($result['prepay_id'])||empty($result['prepay_id'])){// 统一下单失败returnfalse}return$result;}// 生成签名privatefunctiongetSign($Obj){foreach($Objas$k=>$v){$Parameters[$k]=$v;}//签名步骤一:按字典序排序参数ksort($Parameters);$String=$this->formatBizQueryParaMap($Parameters,false);//签名步骤二:在string后加入KEY$String=$String."&key=".$this->mch_key;//签名步骤三:MD5加密$String=md5($String);//签名步骤四:所有字符转为大写$result_=strtoupper($String);return$result_;}// 格式化参数privatefunctionformatBizQueryParaMap($paraMap,$urlencode){$buff="";ksort($paraMap);foreach($paraMapas$k=>$v){if($urlencode){$v=urlencode($v);}$buff.=$k."=".$v."&";}$reqPar='';if(strlen($buff)>0){$reqPar=substr($buff,0,strlen($buff)-1);}return$reqPar;}// 数组转xmlprivatefunctionarrayToXml($arr){$xml="<xml>";foreach($arras$key=>$val){if(is_numeric($val)){$xml.="<".$key.">".$val."</".$key.">";}else{$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";}}$xml.="</xml>";return$xml;}// xml转数组privatefunctionxmlToArray($xml){//禁止引用外部 xml 实体libxml_disable_entity_loader(true);$xmlstring=simplexml_load_string($xml,'SimpleXMLElement',LIBXML_NOCDATA);$val=json_decode(json_encode($xmlstring),true);return$val;}// xml提交post请求privatestaticfunctionpostXmlCurl($xml,$url,$second=30){$ch=curl_init();//设置超时curl_setopt($ch,CURLOPT_TIMEOUT,$second);curl_setopt($ch,CURLOPT_URL,$url);curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);//严格校验//设置 headercurl_setopt($ch,CURLOPT_HEADER,FALSE);//要求结果为字符串且输出到屏幕上curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);//post 提交方式curl_setopt($ch,CURLOPT_POST,TRUE);curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,20);curl_setopt($ch,CURLOPT_TIMEOUT,40);set_time_limit(0);//运行 curl$data=curl_exec($ch);//返回结果if($data){curl_close($ch);return$data;}else{$error=curl_errno($ch);curl_close($ch);thrownewWxPayException("curl 出错,错误码:$error");}}// 生成随机字符串publicstaticfunctioncreateNoncestr($length=32){$chars="abcdefghijklmnopqrstuvwxyz0123456789";$str="";for($i=0;$i<$length;$i++){$str.=substr($chars,mt_rand(0,strlen($chars)-1),1);}return$str;}
版权归原作者 it老欧 所有, 如有侵权,请联系我们删除。