前言
微信支付-微信H5外部浏览器支付「本文」
微信H5内部浏览器支付「待写」
PC端扫码支付「待写」
一直计划着写一写微信支付相关的文章,希望能加深一下自己的印象,拖了一天又一天…
最近终于空出时间来填坑了,我将文章分为微信H5外部浏览器支付、微信H5内部浏览器支付、PC端扫码支付三篇来写。
本篇是微信H5外部浏览器支付:支付时会唤起微信APP进行支付。
扫盲补充:关于微信H5支付,分为内部浏览器支付+外部浏览器支付,两者还是稍微有点点区别的,内部浏览器即在微信内打开网页,进行支付,支付调用由前端发起「JSSDK」;而外部浏览器「比如QQ浏览器等」则通过后台返回的mweb_url交由前端唤起微信APP发起支付操作,微信官方提供了个测试网页 /mch/pay/h5.v2.php,可以在手机浏览器打开体验一番。
本文开发环境: Java + SpringBoot + IDEA + WxJava(开源SDK)
再多啰嗦几句,最开始并没有选择WxJava开源SDK,因为没有仔细阅读官方文档,反正各种报错,比如:支付验证签名失败等等~,最后妥协不重复造轮子了,如下为正文。
1、 引入依赖包
pom.xml文件中引入WxJava依赖「本文使用的是3.3.0版本」
<groupId>com.github.binarywang</groupId> <artifactId>weixin-java-pay</artifactId> <version>3.3.0</version> </dependency><dependency>
2、基础配置
WxJava提供了微信支付的Demo,可以参考 /binarywang/weixin-java-pay-demo
2.1、增加支付配置信息
下面提供application.yml、application.properties两种格式,具体如下:
wx.pay.mchId=#微信支付商户号 wx.pay.mchKey=#微信支付商户密钥 wx.pay.notifyUrl=#支付成功回调URL wx.pay.keyPath=#p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头) #----------------------------------------- wx: pay: appId:#微信公众号或者小程序等的appid mchId:#微信支付商户号 mchKey:#微信支付商户密钥 notifyUrl:#支付成功回调URL keyPath:#p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)wx.pay.appId=#微信公众号或者小程序等的appid
补充:keyPath 用来指定证书路径,关于证书的用途:发红包/企业付款/退款等操作,本文不涉及,留空。
2.2、代码中的配置
一个是用来读取配置信息的实体类,一个是用来初始化支付SDK的Configuration
读取配置类:WxProperties.java
@Configuration @ConfigurationProperties(prefix="wx.pay") publicclassWxProperties{ /** *设置微信公众号或者小程序等的appid */ privateStringappId; /** *微信支付商户号 */ privateStringmchId; /** *微信支付商户密钥 */ privateStringmchKey; /** *apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定 */ privateStringkeyPath; /** *微信回调接口地址 */ privateStringnotifyUrl; }@Data
初始化支付SDK类:WxConfig.java
publicclassWxConfig{ @Autowired privateWxPropertiesproperties; @Bean @ConditionalOnMissingBean publicWxPayServicewxService(){ WxPayConfigpayConfig=newWxPayConfig(); payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId())); payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId())); payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey())); payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath())); payConfig.setNotifyUrl(StringUtils.trimToNull(this.properties.getNotifyUrl())); //可以指定是否使用沙箱环境 payConfig.setUseSandboxEnv(false); WxPayServicewxPayService=newWxPayServiceImpl(); wxPayService.setConfig(payConfig); returnwxPayService; } }@Configuration
2.3、微信支付接口
微信非内部浏览器支付,比如在手机QQ浏览器中发起支付,会唤起微信APP进行支付操作,此时调用微信接口返回的是一个URL,返回结果如下:
weixin://wxpay/bizpayurl?pr=IzX8nS
下方是获取支付URL的后端接口方法:
publicResult<Object>createQRCode(UserModeluser,HttpServletResponseresponse,@RequestBodyWechatOrderRequestobj){ Ordersorders=null; if(StringUtils.isNotBlank(obj.getOrderId())){ orders=ordersService.searchOrder(user,obj.getOrderId()); }else{ orders=ordersService.createOrder(user,obj); } WechatOrderResponsewechatOrderResponse=newWechatOrderResponse(); wechatOrderResponse.setCodeUrl(wechatService.createOrderInfo(orders,user.getPayType())); wechatOrderResponse.setOrderId(orders.getOrderId()); returnResultUtil.success(wechatOrderResponse); }@RequestMapping(value="createOrder",method={RequestMethod.POST})
该方法仅供参考,上方方法中对订单id进行了一个判空操作,因为我这边有可能是用户未支付订单,继续支付的操作,代码主要是wechatService.createOrderInfo方法,实现如下:
WxPayMwebOrderResultresult=null; try{ WxPayUnifiedOrderRequestorderRequest=newWxPayUnifiedOrderRequest(); orderRequest.setOutTradeNo(orders.getOrderId()); orderRequest.setBody("我是商品描述"); orderRequest.setTotalFee(orders.getAmount().multiply(newBigDecimal("100")).intValue());//金额需要扩大100倍:1代表支付时是0.01 orderRequest.setSpbillCreateIp(DispatchParams.getInstance().getWechatSpbillCreateIp()); orderRequest.setProductId(orders.getOrderId()); orderRequest.setTradeType(WxPayConstants.TradeType.MWEB);//h5网页支付 result=wxPayService.createOrder(orderRequest); returnresult.getMwebUrl(); }catch(WxPayExceptione){ logger.error("[微信支付异常]异常",e); //抛出一个自定义全局异常「自己定义」 thrownewCommonException(微信支付异常提示信息,状态码); } }publicStringcreateOrderInfo(Ordersorders,StringpayType){
具体参数就不啰嗦了,详细请看官方支付文档。
综上,当前端调用createOrder方法,将 weixin://wxpay/bizpayurl?pr=IzX8nS 返回给前端,那么前端怎么调用呢?
下面是我的一个测试例子,其中res.codeUrl为后端返回的URL:
window.open(res.codeUrl,'_blank’);
是的,就是这么简单,新窗口打开就可以了,看一下运行调起微信的截图「我手机装了两个微信」:
支付成功后会回调后端接口,具体由后端参数配置的return_url控制。
2.4、微信回调接口
当支付完成后,微信会自动回调该接口,我们可以根据返回的信息修改订单状态,看一下方法,代码仅供参考:
@ResponseBody publicStringnotify(Stringbody)throwsException{ WxPayOrderNotifyResultresult=null; try{ result=wxPayService.parseOrderNotifyResult(body); }catch(WxPayExceptione){ logger.error("[微信解析回调请求]异常",e); returnWxPayNotifyResponse.fail(e.getMessage()); } logger.info("处理微信支付平台的订单支付"); logger.info(JSONObject.toJSONString(result)); Stringappid=result.getAppid();//应用ID Stringattach=result.getAttach();//商家数据包 Stringbank_type=result.getBankType();//付款银行 Integercash_fee=result.getCashFee();//现金支付金额 Stringfee_type=result.getFeeType();//货币种类 Stringis_subscribe=result.getIsSubscribe();//是否关注公众账号 Stringmch_id=result.getMchId();//商户号 Stringnonce_str=result.getNonceStr();//随机字符串 Stringopenid=result.getOpenid();//用户标识 Stringout_trade_no=result.getOutTradeNo();//获取商户订单号 Stringresult_code=result.getResultCode();//业务结果 Stringreturn_code=result.getReturnCode();//SUCCESS/FAIL Stringsign=result.getSign();//获取签名 Stringtime_end=result.getTimeEnd();//支付完成时间 Integertotal_fee=result.getTotalFee();//获取订单金额 Stringtrade_type=result.getTradeType();//交易类型 Stringtransaction_id=result.getTransactionId();//微信支付订单号 //如果成功写入数据库 if("SUCCESS".equals(return_code)){//如果微信返回的结果是success,则修改订单状态 Ordersorders=ordersDao.selectByOrderId(out_trade_no); //验证签名 if(orders!=null){ if(!"1".equals(orders.getOrderStatus())){//判断是否订单已经完成了 //判断金额是否跟数据库订单金额一致,放置人为修改 if(orders.getAmount().multiply(newBigDecimal("100")).compareTo(newBigDecimal(total_fee))==0){ //更新订单状态 业务逻辑处理部分... returnWxPayNotifyResponse.success("订单已经处理成功!"); }else{ logger.error("微信:金额不一致!"); returnWxPayNotifyResponse.fail("订单金额不一致"); } }else{ returnWxPayNotifyResponse.success("订单已经处理成功!"); } }else{ returnWxPayNotifyResponse.fail("商户订单号不匹配"); } } System.out.println("回调成功"); System.out.println("----返回给微信的xml:"+result); returnWxPayNotifyResponse.success("支付成功!"); }@RequestMapping(value="/notify")
如上代码,微信返回的是XML,经过wxPayService.parseOrderNotifyResult()方法转换后得到WxPayOrderNotifyResult实体,具体参数我上边罗列出来了「尽管没用到」,然后就是修改数据库订单状态等操作。
最后
博客地址:/niceyoo
如果觉得这篇文章有丶东西,不放关注一下我,关注是对我最大的鼓励~
专科毕业后,期间一度迷茫,最近我创建了一个公众号用来记录自己的成长。