spring boot整合微信支付

目录

一、官方概述

二、准备工作

三、着手开发


一、官方概述

com.github.wxpay.sdk.WXPay类下提供了对应的方法:

方法名说明
microPay刷卡支付
unifiedOrder统一下单
orderQuery查询订单
reverse撤销订单
closeOrder关闭订单
refund申请退款
refundQuery查询退款
downloadBill下载对账单
report交易保障
shortUrl转换短链接
authCodeToOpenid授权码查询openid
  • 注意:

  • 证书文件不能放在web服务器虚拟目录,应放在有访问权限控制的目录中,防止被他人下载

  • 建议将证书文件名改为复杂且不容易猜测的文件名

  • 商户服务器要做好病毒和木马防护工作,不被非法侵入者窃取证书文件

  • 请妥善保管商户支付密钥、公众帐号SECRET,避免密钥泄露

  • 参数为Map<String, String>对象,返回类型也是Map<String, String>

  • 方法内部会将参数会转换成含有appidmch_idnonce_strsign\_typesign的XML

  • 可选HMAC-SHA256算法和MD5算法签名

  • 通过HTTPS请求得到返回数据后会对其做必要的处理(例如验证签名,签名错误则抛出异常)

  • 对于downloadBill,无论是否成功都返回Map,且都含有return_codereturn_msg,若成功,其中return_codeSUCCESS,另外data对应对账单数据


二、准备工作

1.申请微信支付平台账号(如果是做公众号最好微信公众平台也同时设置完毕)

2.获取appid

路径:微信支付 -- 首页 -- AppId账号

3.获取mchId

路径:微信支付 -- 首页 -- 商户号(mchId)

4.配置JSAPI域名

路径:微信支付 -- 产品中心 -- 开发配置 -- JSAPI支付 -- 支付授权目录 -- 添加

5.获取api密钥(遗忘需要重置)

路径:微信支付商户平台 -- 账户中心 -- API 安全 -- API 密钥 -- 设置密钥

6.公众号设置配置域名

路径: 公众号 -- 公众号设置 -- 功能设置 -- 网页授权域名 -- 设置

在配置域名的时候需要下载 MP_verify_cqlNsdZY4xZktC2l.txt 文件,放在项目中让微信可以获取到

7.获取apiclient_cert.p12文件

路径:微信支付 -- API安全 -- API证书(需放在可访问目录下,在MyConfig文件设置访问地址)


三、着手开发

引入依赖

         <!-- 微信支付 [开始] -->
         <dependency>
             <groupId>com.github.binarywang</groupId>
             <artifactId>weixin-java-pay</artifactId>
             <version>3.7.0</version>
         </dependency>
         <dependency>
             <groupId>com.github.binarywang</groupId>
             <artifactId>weixin-java-mp</artifactId>
             <version>3.7.0</version>
         </dependency>
         <dependency>
             <groupId>com.github.binarywang</groupId>
             <artifactId>weixin-java-mp</artifactId>
             <version>3.1.0</version>
         </dependency>
         <!-- 微信支付 [结束] -->


GetMpController 文件(获取 MP_verify_cqlNsdZY4xZktC2l.txt 文件内容)

由于实际开发时,微信方面会一直自动调用 MP_verify_cqlNsdZY4xZktC2l.txt 所以编写此类,也可以将MP_verify_cqlNsdZY4xZktC2l.txt文件放在服务器项目相同根目录

 package com.zygh.webapi.controller;
 ​
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RestController;
 ​
 /**
  * @program: 微信支付
  * @description: 获取 MP_verify_cqlNsdZY4xZktC2l.txt 文件内容
  * @author: ID-Tang
  * @create: 2020-12-09 17:15
  **/
 @RestController
 public class GetMpController {
     @RequestMapping(value = "/MP_verify_cqlNsdZY4xZktC2l.txt",method = RequestMethod.GET)
     public Object GetMp(){
         return "{文件内容}";
     }
 }
 ​


WXinConfig

 package com.zygh.webapi.config;
 ​
 import me.chanjar.weixin.mp.api.WxMpConfigStorage;
 import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
 import me.chanjar.weixin.mp.api.WxMpService;
 import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
 import org.springframework.context.annotation.Bean;
 import org.springframework.stereotype.Component;
 ​
 @Component
 public class WXinConfig {
 ​
     @Bean
     public WxMpService wxmpservice() {
 ​
         WxMpServiceImpl wxMpService = new WxMpServiceImpl();
 ​
         wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
         return wxMpService;
     }
 ​
     @Bean
     public WxMpConfigStorage wxMpConfigStorage() {
         MyConfig config = new MyConfig();
 ​
         WxMpInMemoryConfigStorage wxMpConfigStorage = new WxMpInMemoryConfigStorage();
         wxMpConfigStorage.setSecret(config.getAppSecret());
         wxMpConfigStorage.setAppId(config.getAppID());
         return wxMpConfigStorage;
     }
 }


MyConfig文件

 package com.zygh.webapi.config;
 ​
 import com.github.wxpay.sdk.WXPayConfig;
 ​
 import java.io.*;
 ​
 /**
  * @program: 微信支付
  * @description: 微信支付基本配置
  * @author: ID-Tang
  * @create: 2020-12-11 11:29
  **/
 public class MyConfig implements WXPayConfig {
 ​
     private byte[] certData;
 ​
     /**
      * 获取微信证书内容
      */
     public MyConfig(){
         //证书存放位置
         String certPath = "/home/developer/apiclient_cert.p12";
         File file = new File(certPath);
         InputStream certStream = null;
         try {
             certStream = new FileInputStream(file);
             this.certData = new byte[(int) file.length()];
             certStream.read(this.certData);
             certStream.close();
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
 ​
     @Override
     public String getAppID() {
         return "{appId}";
     }
 ​
     @Override
     public String getMchID() {
         return "{mchId}";
     }
 ​
     @Override
     public String getKey() {
         return "{key}";
     }
 ​
     @Override
     public InputStream getCertStream() {
         ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
         return certBis;
     }
 ​
 ​
     @Override
     public int getHttpConnectTimeoutMs() {
         return 8000;
     }
 ​
     @Override
     public int getHttpReadTimeoutMs() {
         return 10000;
     }
 ​
     /**
      * 通知地址(通知URL必须为直接可访问的URL,不允许携带查询串)
      *
      * @return notifyUrl
      */
     public String getHttpNotifyUrl() {
         return "{通知URL}";
     }
 ​
     /**
      * 商户密钥
      * @return appSecret
      */
     public String getAppSecret(){
         return "{appSecret}";
     }
 ​
     /**
      * redirect_uri设置
      * 微信授权之后回调地址,回调时可获取所需code
      *
      * @return 域名(例 : http://243300e5s6.wicp.vip) + /WxPay/getCode(此处固定)
      */
 //    public String getRedirectUri() {
 //        return "{redirect_uri}";
 //    }
 ​
     /**
      * 微信授权方式,可选 隐式授权 或 显式授权
      * snsapi_base 隐式授权,静默授权并自动跳转到回调页
      * snsapi_userinfo 显式授权,需要用户手动同意
      *
      * @return 授权方式
      */
 //    public String getScope() {
 //        return "snsapi_userinfo";
 //    }
 ​
 }
 ​


WxPayEntity文件

 package com.zygh.db.entity;
 ​
 import io.swagger.annotations.ApiModelProperty;
 ​
 /**
  * @program: 微信支付
  * @description: 微信支付需要的参数
  * @author: ID-Tang
  * @create: 2020-12-12 13:42
  **/
 public class WxPayEntity {
     /**
      * 商品描述
      */
     @ApiModelProperty("商品描述")
     private String body;
     /**
      * 商户订单号
      */
     @ApiModelProperty("商户订单号")
     private String outTradeNo;
     /**
      * 订单总金额,单位为分
      */
     @ApiModelProperty("订单总金额,单位为分")
     private String totalFee;
     /**
      * 用户标识(用户在直连商户appId下的唯一标识)
      */
     @ApiModelProperty("用户标识(用户在直连商户appId下的唯一标识)")
     private String openid;
 ​
     public String getBody() {
         return body;
     }
 ​
     public void setBody(String body) {
         this.body = body;
     }
 ​
     public String getOutTradeNo() {
         return outTradeNo;
     }
 ​
     public void setOutTradeNo(String outTradeNo) {
         this.outTradeNo = outTradeNo;
     }
 ​
     public String getTotalFee() {
         return totalFee;
     }
 ​
     public void setTotalFee(String totalFee) {
         this.totalFee = totalFee;
     }
 ​
     public String getOpenid() {
         return openid;
     }
 ​
     public void setOpenid(String openid) {
         this.openid = openid;
     }
 ​
     public WxPayEntity() {
     }
 ​
     public WxPayEntity(String body, String outTradeNo, String totalFee, String openid) {
         this.body = body;
         this.outTradeNo = outTradeNo;
         this.totalFee = totalFee;
         this.openid = openid;
     }
 ​
     @Override
     public String toString() {
         return "WxPayEntity{" +
                 "body='" + body + '\'' +
                 ", outTradeNo='" + outTradeNo + '\'' +
                 ", totalFee='" + totalFee + '\'' +
                 ", openid='" + openid + '\'' +
                 '}';
     }
 }


WxPayController文件

 package com.zygh.webapi.controller;
 ​
 import com.alibaba.fastjson.JSON;
 import com.github.wxpay.sdk.WXPay;
 import com.github.wxpay.sdk.WXPayUtil;
 import com.zygh.common.component.CommonResult;
 import com.zygh.db.entity.WxPayEntity;
 import com.zygh.webapi.config.MyConfig;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.mp.api.WxMpService;
 import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.*;
 ​
 import javax.annotation.Resource;
 import java.util.HashMap;
 import java.util.Map;
 ​
 /**
  * 该类不可使用@RestController
  * 如需使用json,请手动在方法上面加入@ResponseBody注解
  *
  * @program: 微信支付
  * @description: 微信支付Controller
  * @author: ID-Tang
  * @create: 2020-12-09 13:50
  **/
 @Controller
 @Api(tags = "微信支付")
 @RequestMapping(value = "/WxPay")
 public class WxPayController {
 ​
     @Resource
     WxMpService wxmpservice;
 ​
     /**
      * 引入自定义config类
      */
     private static final MyConfig config = new MyConfig();
     /**
      * 声明微信支付对象
      */
     private static final WXPay wxpay = new WXPay(config);
 ​
     //    @ApiOperation("微信授权")
 //    @RequestMapping(value = "/getWxCode", method = RequestMethod.GET)
 //    public Object getWxCode() throws UnsupportedEncodingException {
 //        MyConfig config = new MyConfig();
 //
 //        // 1、redirect_uri设置
 //        String redict = URLEncoder.encode(config.getRedirectUri(), "UTF-8");
 //
 //
 //        // 2、获取code的url
 //        String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" +
 //                "appid=" + config.getAppID() + "&redirect_uri=" + redict +
 //                "&response_type=code&scope=" + config.getScope() + "#wechat_redirect";
 //
 //        // 3、重定向去微信获取code
 //        return "redirect:" + url;
 //    }
 ​
 //    @ResponseBody
 //    @RequestMapping(value = "/getCode", method = RequestMethod.GET)
 //    public Object getWxOpenId(HttpServletRequest request) throws WxErrorException {
 //        // 1、获取code
 //        String code = request.getParameter("code");
 //        //传入code生成Token
 //        WxMpOAuth2AccessToken wxMpOAuth2AccessToken = wxmpservice.oauth2getAccessToken(code);
 //
 //        //获取openId
 //        String openId = wxMpOAuth2AccessToken.getOpenId();
 //
 //        System.out.println(openId);
 //
 //        return openId;
 //    }
 ​
     @ResponseBody
     @RequestMapping(value = "/getCode", method = RequestMethod.GET)
     @ApiOperation("获取用户openid(此接口无需手动调用,如需获取openid请调用微信授权(/getWxCode)接口)")
     public Object getWxOpenId(String code) throws WxErrorException {
         //传入code生成Token
         WxMpOAuth2AccessToken wxMpOAuth2AccessToken = wxmpservice.oauth2getAccessToken(code);
 ​
         //获取openId
         String openId = wxMpOAuth2AccessToken.getOpenId();
 ​
         return openId;
     }
 ​
     @ResponseBody
     @ApiOperation("微信JSAPI支付统一下单")
     @RequestMapping(value = "/toWxPay", method = RequestMethod.POST)
     public Object toWxPay(@RequestBody WxPayEntity wxPayEntity) {
 ​
         // 声明一个map集合用于提交
         Map<String, String> data = new HashMap<>(16);
         // 商品描述
         data.put("body", wxPayEntity.getBody());
         // 商户订单号
         data.put("out_trade_no", wxPayEntity.getOutTradeNo());
         // 订单总金额,单位为分。
         data.put("total_fee", wxPayEntity.getTotalFee());
         // 通知地址(通知URL必须为直接可访问的URL,不允许携带查询串)
         data.put("notify_url", config.getHttpNotifyUrl());
         // 此处指定为JSAPI支付(注:此方法为公众号支付方法,故不建议更改为其它方式)
         data.put("trade_type", "JSAPI");
         // 用户标识(用户在直连商户appid下的唯一标识)
         data.put("openid", wxPayEntity.getOpenid());
 ​
         try {
             // 统一下单返回的map集合,用于返回给前端
             Map<String, String> resp = wxpay.unifiedOrder(data);
 ​
             // 获取时间戳
             long l = System.currentTimeMillis() / 1000;
             // 将时间戳加入返回值
             resp.put("timeStamp", l + "");
 ​
             // 声明一个新的map集合用于二次签名
             // 固定以下五个字段,除加密方式外别的不用修改
             // 签名需要五个字段:appId、package、timeStamp、nonceStr、signType
             Map<String, String> signMap = new HashMap<>(16);
             //用户标识(注:虽然一次授权时参数名为appid但是二次授权时必须为appId)
             signMap.put("appId", resp.get("appid"));
             signMap.put("package", "prepay_id=" + resp.get("prepay_id"));
             signMap.put("timeStamp", resp.get("timeStamp"));
             signMap.put("nonceStr", resp.get("nonce_str"));
             // 签名类型,此处使用MD5(注:使用新版需使用MD5)
             signMap.put("signType", "MD5");
 ​
             // 二次签名
             String signature = WXPayUtil.generateSignature(signMap, config.getKey());
             // 前端返回集合中加入package字段,内容为统一下单所返回的预支付标识
             // 注:数据格式为"prepay_id=wx121126358624152875fa2a35b144c50000"
             resp.put("package", signMap.get("package"));
 ​
             // 返回前端集合中加入签名
             // 注:这里需要的是二次签名后的签名
             resp.put("paySign", signature);
 ​
             //将返回前端的map集合转换为json格式的字符串
             String jsonString = JSON.toJSONString(resp);
 ​
             return jsonString;
         } catch (Exception e) {
             e.printStackTrace();
         }
 ​
         return null;
     }
 ​
     @ResponseBody
     @ApiOperation(value = "订单查询")
     @RequestMapping(value = "/orderInquiry", method = RequestMethod.GET)
     public Object orderInquiry(@RequestParam String orderId) {
 ​
         Map<String, String> data = new HashMap<>(16);
         data.put("out_trade_no", orderId);
 ​
         try {
             Map<String, String> resp = wxpay.orderQuery(data);
             System.out.println(resp);
             return new CommonResult().success(resp);
         } catch (Exception e) {
             e.printStackTrace();
         }
         return new CommonResult().validateFailed("查询失败");
     }
 ​
     @ResponseBody
     @ApiOperation(value = "申请退款")
     @RequestMapping(value = "/orderRefund", method = RequestMethod.POST)
     public Object orderRefund(@RequestBody WxPayEntity wxPayEntity) {
         //声明一个集合存放退款所需参数
         Map<String, String> data = new HashMap<>(16);
         data.put("appid", config.getAppID());
         data.put("mch_id", config.getMchID());
         data.put("nonce_str", WXPayUtil.generateNonceStr());
         data.put("out_trade_no", wxPayEntity.getOutTradeNo());
         data.put("out_refund_no", wxPayEntity.getOutTradeNo());
         data.put("total_fee", wxPayEntity.getTotalFee());
         data.put("refund_fee", wxPayEntity.getTotalFee());
         try {
             // 签名
             String signature = WXPayUtil.generateSignature(data, config.getKey());
             data.put("sign",signature);
             Map<String, String> map = wxpay.refund(data);
             return new CommonResult().success(map);
         } catch (Exception e) {
             e.printStackTrace();
         }
         return new CommonResult().validateFailed("申请退款失败!");
     }
 ​
 }
 ​


前端

获取code

 window.location.href ='https://open.weixin.qq.com/connect/oauth2/authorize?appid={appId}&redirect_uri={redirect_uri}&response_type=code&scope=snsapi_userinfo&state=1&connect_redirect=1#wechat_redirect';


获取openId

  this.$axiox({
               method: 'GET',
               url: this.$AXIOSURL + "/WxPay/getCode",
               params: {
                     code: this.getQueryVariable("code")
                        }
               }).then((response) => {
                     // this.openId = response;
                     sessionStorage.setItem('openId', response);
                     this.$router.replace('/myCenter');
                     // console.log(response, 418);
               }).catch((response) => {
                    console.log(response)
                    Toast.fail('网络异常');
   })


调起微信支付

 wechatPay() {
                 // Toast.loading;
                 this.unpaid = this.unpaid.toString();
                 var dataobj = {
                     "body": this.form.name,
                     "outTradeNo": this.outTradeNo,
                     "totalFee": "1",
                     // "totalFee": this.unpaid,
                     "openid": this.openId,
                 }
                 this.$axiox({
                     method: 'POST',
                     url: this.$AXIOSURL + "/WxPay/toWxPay",
                     data: dataobj
                 }).then((res) => {
                     // console.log(res)
                     this.payData = res;
                     console.log(this.unpaid)
                     if (typeof WeixinJSBridge == 'undefined') {
                         if (document.addEventListener) {
                             document.addEventListener(
                                 'WeixinJSBridgeReady',
                                 this.onBridgeReady,
                                 false
                             );
                         } else if (document.attachEvent) {
                             document.attachEvent(
                                 'WeixinJSBridgeReady',
                                 this.onBridgeReady
                             );
                             document.attachEvent(
                                 'onWeixinJSBridgeReady',
                                 this.onBridgeReady
                             );
                         }
                     } else {
                         this.onBridgeReady();
                     }
                 })
             },
             onBridgeReady() {
                 console.log(this.payData)
                 let that = this;
                 // let data = getLocalStorage('prepay_data');
                 var dataobj = {
                     "appId": that.payData.appid, //公众号名称,由商户传入
                     "timeStamp": that.payData.timeStamp, //时间戳,自1970年以来的秒数
                     "nonceStr": that.payData.nonce_str, //随机串
                     "package": that.payData.package,
                     "signType": "MD5", //微信签名方式:
                     "paySign": that.payData.paySign, //微信签名
                 }
                 WeixinJSBridge.invoke('getBrandWCPayRequest', dataobj,
                     // {
                     //         "appId": that.payData.appid, //公众号名称,由商户传入
                     //         "timeStamp": that.payData.timeStamp, //时间戳,自1970年以来的秒数
                     //         "nonceStr": that.payData.nonce_str, //随机串
                     //         "package": "prepay_id=" + that.payData.prepay_id,
                     //         "signType": "MD5", //微信签名方式:
                     //         "paySign": that.payData.paySign, //微信签名
                     //     },
                     function(res) {
                         if (res.err_msg == 'get_brand_wcpay_request:ok') {
                             that.gotosuccess();
                             // window.location.href = 'http://pay.yisuide.com/#/paysuccess?id=this.outTradeNo'
                         } else if (res.err_msg == 'get_brand_wcpay_request:cancel') {
                             //支付取消
                             alert('支付取消');
                             that.$router.replace({
                                 path: "/myorder",
                             })
                             // window.location.href = 'http://www.shiyuanlive.com/shiyuanmall/#/order'
                         } else if (res.err_msg == "get_brand_wcpay_request:fail") {
                             //支付失败
                             alert('支付失败');
                             // WeixinJSBridge.call('closeWindow');
                         } //使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
 ​
                     }
 ​
                 );
             },

*注:所有{}内的内容为需要替换的内容,所有前后端代码已脱敏处理,前端接口替换未使用{}进行标注,需自行查找并修改

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ID-Tang

梦想支持度+1

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值