java 学习第三方对接-----微信支付

1.微信支付申请流程和步骤

1.注册微信支付账号和微信小程序账号
2.获取微信小程序的APPID
3.获取微信商家的商户ID
4.获取微信商家的API私钥
5.配置微信支付回调地址
6.绑定微信小程序和微信支付的关系
7. 搭建Springboot 工程定义后台编写支付接口
8. 发布部署接口服务项目
9. 使用微信小程序或者Uniapp完成微信支付的调用
10.对支付接口的封装以及前端代码的封装
11.配置jwt 和openid的token派发,让接口更加安全可靠
12.原生微信小程序完成支付对接

2.申请注册流程和文档

官方地址:https://pay.weixin.qq.com/index.php
官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/index.shtml
PC端微信小程序Navtive文档:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_7_0.shtml
微信小程序支付对接文档:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml

生成私钥的代码

/**
* 获取一定长度的随机字符串
* @param length 指定字符串长度
* @return 一定的长度的字符串
**/
public static String getRandomStringByLength(int length){
	String base = "abcdefghijklmnopqrstuvwxyz0123456789";
	Random rondom = new Random();
	StringBuffer sb = new StringBuffer();
	for(int i = 0; i < random.length; i++){
		int number = random.nextInt(base.length());
		sb.append(base.charAt(number));
	}
	return sb.toString();
}

3.Springboot对接微信支付

3.1 后端获取openid

进入微信官方文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
java 学习第三方对接-----微信支付_第1张图片
controller :

package com.kuang.weixinpay.controller;

import com.google.gson.Gson;
import com.kuang.weixinpay.anno.IgnoreToken;
import com.kuang.weixinpay.common.exception.KuangShenException;
import com.kuang.weixinpay.common.pay.HttpClientUtils;
import com.kuang.weixinpay.config.properties.WeixinLoginProperties;
import com.kuang.weixinpay.vo.R;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;


@RestController
@Log4j2
public class ApiLoginController extends ApiBaseController {

    /***
     * @Author 
     * @Description openid接口的获取
     * @Date 22:22 2021/4/24
     * @Param [code]
     * @return com.kuangstudy.weixinpay.vo.R
     * https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html
     * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
     *
    **/
    @IgnoreToken
    @GetMapping("/wxlogin")
    public R callback(String code) {
        // 1:判断code是否合法
        if (StringUtils.isEmpty(code)) {
            throw new KuangShenException(22008, "登录失败,尝试刷新重新扫码登录!");
        }

        // 2:通过code获取access_token
        // 完成的连接: https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
        // 参考地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
        String baseAccessTokenUrl = WeixinLoginProperties.WX_OPEN_GATEWAY +
                "?appid=%s" +
                "&secret=%s" +
                "&js_code=%s" +
                "&grant_type=authorization_code";

        log.info("1:appid:{},appscrect:{}", WeixinLoginProperties.WX_OPEN_APP_ID, WeixinLoginProperties.WX_OPEN_APP_SECRET);

        String accessTokenUrl = String.format(baseAccessTokenUrl, WeixinLoginProperties.WX_OPEN_APP_ID, WeixinLoginProperties.WX_OPEN_APP_SECRET, code);
        String result = null;
        try {
            //2:执行请求,获取微信请求返回得数据
            result = new HttpClientUtils().get(accessTokenUrl);
            log.info("2---->微信返回的日志信息是:code:{},result:{}", code, result);
            // 3: 对微信返回得数据进行转换
            Gson gson = new Gson();
            Map<String, Object> resultMap = gson.fromJson(result, HashMap.class);
            log.info("3---->微信返回的日志信息是:code:{},resultMap:{}", code, resultMap);
            if (resultMap.get("errcode") != null) {
                throw new KuangShenException(22006, "微信登录出错!");
            }

            // 4: 解析微信用户得唯一凭证openid
            String openid = (String) resultMap.get("openid");
            if (StringUtils.isEmpty(openid)) {
                throw new KuangShenException(22009, "登录失败,尝试刷新重新扫码登录!!!");
            }

            // 5:封装返回
            return R.ok().data("openid", openid).data("resultMap",resultMap);
        } catch (Exception e) {
            return R.error().code(601).message("微信解析失败");
        }
    }
}

微信登录的实体类

package com.kuang.weixinpay.config.properties;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
@PropertySource("classpath:application.yml")
public class WeixinLoginProperties implements InitializingBean {
	@Value("${weixin.login.info.gateway}")
	private String gateway;
	@Value("${weixin.login.info.appid}")
	private String appid;
	@Value("${weixin.login.info.appsecret}")
	private String appsecret;
	@Value("${weixin.login.info.redirectUrl}")
	private String redirectUrl;
	public static String WX_OPEN_GATEWAY;
	public static String WX_OPEN_APP_ID;
	public static String WX_OPEN_APP_SECRET;
	public static String WX_OPEN_REDIRECT_URL;
	@Override
	public void afterPropertiesSet() throws Exception {
		WX_OPEN_APP_ID = appid;
		WX_OPEN_GATEWAY = gateway;
		WX_OPEN_APP_SECRET = appsecret;
		WX_OPEN_REDIRECT_URL = redirectUrl;
	}
}

application.yml 进行配置

# 微信登录相关
weixin:
  login:
    info:
      # 微信登录网关
      gateway: https://api.weixin.qq.com/sns/jscode2session
      # 微信小程序APPID
      appid: xxxxx
      # 微信小程序API私钥
      appsecret: xxxxx
      # 微信小程序登录成功回调地址
      redirectUrl: xxxxx

3.2 定义微信支付接口

微信小程序支付

package com.kuang.weixinpay.config.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "weixin.pay.info")
@Data
public class WeixinPayProperties {
    // 定义微信解密获取手机号码的接口地址,固定的
    private String gateway;
    //小程序APPID
    private String appid;
    //小程序APPID
    private String type;
    //小程序APPSceret
    private String appsecret;
    //商户ID
    private String mchid;
    //回调地址
    private String notifyPath;
}
# 配置的xml 文件
pay:
    info:
      # 微信支付网关
      gateway: https://api.mch.weixin.qq.com/pay/unifiedorder
      # 微信支付API秘钥
      appsecret: xxxxx
      # 微信商户id
      mchid: xxxxx
      # 签约产品的类
      type: JSAPI
      # 微信小程序APPID
      appid: xxxxx
      # 支付成功回调地址,如果是微信小程序可以不配置
      notifyPath: xxxxx

微信支付成功信息配置

package com.kuang.weixinpay.common.pay.weixin.request;


import com.kuang.weixinpay.common.pay.weixin.ReportReqData;
import com.kuang.weixinpay.common.pay.weixin.config.WeiXinConstants;
import com.kuang.weixinpay.common.pay.weixin.sign.Signature;
import com.kuang.weixinpay.common.pay.weixin.util.WeiXinRequest;
import com.kuang.weixinpay.common.pay.weixin.util.XMLParser;
import com.kuang.weixinpay.config.properties.WeixinPayProperties;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;


/**
 * 数据提交并且生成二维码
 * WeixinSubmit
* 创建人:小威
* 时间:2015年10月16日-下午2:14:47
* * @version 1.0.0 */
@Log4j2 @Component public class QrCodeRequest { @Autowired private WeixinPayProperties weixinPayProperties; /** * 微信支付成功信息配置 * 方法名:submitWeixinMessage
* 创建人:mofeng
* 时间:2018年11月28日-下午2:06:06
* 手机:1564545646464
* * @param data * @return Map
* @throws
* @since 1.0.0
*/
public Map<String, String> submitWeixinMessage(ReportReqData data) { try { data.setTrade_type(data.getTrade_type());//扫一扫支付方式 NATIVE data.setSign(Signature.getSign(data.toMap(), weixinPayProperties.getAppsecret()));//签名字符 // 发起支付请求 String returnData = WeiXinRequest.submitData(weixinPayProperties.getGateway(), data.toMap()); // returnData返回的是xml格式 System.out.println("======================>" + returnData); if (StringUtils.isNotEmpty(returnData)) { log.info("当前支付成功返回的数据: ============>{}",returnData); //解析返回的字符串 并且组成map集合 Map<String, String> map = XMLParser.getMapFromXML(returnData); System.out.println("==============================>" + map); if (null != map && !map.isEmpty()) { String resultCode = (String) map.get(WeiXinConstants.RESULT); if ("SUCCESS".equals(resultCode)) {//链接生成成功 HashMap<String, String> nmap = new HashMap<>(); String params = data.getAttach().replace("'", "\""); nmap.put("appId", data.getAppid()); nmap.put("timeStamp", System.currentTimeMillis() + ""); nmap.put("signType", "MD5"); nmap.put("nonceStr", data.getNonce_str()); nmap.put("package", "prepay_id=" + map.get("prepay_id")); nmap.put("paySign", Signature.getSign(nmap, weixinPayProperties.getAppsecret())); nmap.put("attach",params); nmap.put("orderNumber", data.getOut_trade_no() + ""); return nmap; } } } return null; } catch (Exception e) { e.printStackTrace(); return null; } } }

微信小程序支付接口

package com.kuang.weixinpay.controller;

import com.alibaba.fastjson.JSONObject;
import com.kuang.weixinpay.common.pay.weixin.ReportReqData;
import com.kuang.weixinpay.common.pay.weixin.request.QrCodeRequest;
import com.kuang.weixinpay.common.pay.weixin.util.RandomStringGenerator;
import com.kuang.weixinpay.config.properties.WeixinPayProperties;
import com.kuang.weixinpay.entity.Product;
import com.kuang.weixinpay.service.ProductService;
import com.kuang.weixinpay.utils.snow.SnowflakeIdWorker;
import com.kuang.weixinpay.vo.R;

import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * @author 
 * @Title:
 * @Package
 * @Description:
 * @date 2021/4/2212:05
 */
@RestController
@Log4j2
public class ApiPayController extends  ApiBaseController {

    @Autowired
    private WeixinPayProperties weixinProperties;
    @Autowired
    private ProductService productService;
    @Autowired
    private QrCodeRequest qrCodeRequest;

    /**
     * @Author 
     * @Description 微信小程序支付的接口
     * @Date 10:39 2021/4/25
     * @Param [request]
     * @return com.kuangstudy.weixinpay.vo.R
    **/
    @GetMapping("/weixinpay")
    public R payVip(HttpServletRequest request) {
        //开始支付,创建数据体
        JSONObject json = new JSONObject();

        //业务数据
        String productId = request.getParameter("pid");
        if(StringUtils.isEmpty(productId))return R.error().code(601).message("产品没找到!");

        // 1:根据产品id查询对应产品信息
        Product product = productService.getById(productId);
        if(product==null)return R.error().code(601).message("产品没找到!");

        // 2:生成订单号
        String orderNo = new SnowflakeIdWorker(1, 2).nextId() + "";
        String ip = "127.0.0.1";
        // 3:获取微信小程序的openid
        String openid = request.getParameter("openid");
        if(StringUtils.isEmpty(openid))return R.error().code(601).message("openid没找到!");
        //4:分账接收方用户Id session或者redis
        Integer userId = 1;

        // 组装微信支付数据
        ReportReqData data = new ReportReqData();
        // openid
        data.setOpenid(openid);
        // 签约的类型JSAPI
        data.setTrade_type(weixinProperties.getType());
        // 微信小程序的appid
        data.setAppid(weixinProperties.getAppid());
        // 商户id
        data.setMch_id(weixinProperties.getMchid());
        // 回调地址 如果是微信小程序不用配置也可以,最好配置
        data.setNotify_url(weixinProperties.getNotifyPath());
        // 业务数据
        data.setBody(product.getTitle());//套餐名称
        data.setOut_trade_no(orderNo);//订单号
        data.setProduct_id(productId+"");//商品ID
        data.setSpbill_create_ip(ip);//ip地址
        data.setTotal_fee(getMoney(product.getPrice()));//金额
        data.setNonce_str(RandomStringGenerator.getRandomStringByLength(32));

        json.put("userId", userId);
        json.put("type", "productpay");
        json.put("ip", ip);
        //创建订单
        String params = json.toString().replace("\"", "'");
        log.info("执行支付传递的参数: {}", params);
        data.setAttach(params);
        log.info("操作完毕,开始发起支付");

        //微信支付返回的结果
        Map<String, String> weixinMap = qrCodeRequest.submitWeixinMessage(data);

        return R.ok().data("weixinMap",weixinMap).data("xxx","xxxx");
    }

    /**
     * 元转换成分
     *
     * @param amount
     * @return
     */
    public static String getMoney(String amount) {
        if (amount == null) {
            return "";
        }
        // 金额转化为分为单位
        // 处理包含, ¥ 或者$的金额
        String currency = amount.replaceAll("\\$|\\¥|\\,", "");
        int index = currency.indexOf(".");
        int length = currency.length();
        Long amLong = 0L;
        if (index == -1) {
            amLong = Long.valueOf(currency + "00");
        } else if (length - index >= 3) {
            amLong = Long.valueOf((currency.substring(0, index + 3)).replace(".", ""));
        } else if (length - index == 2) {
            amLong = Long.valueOf((currency.substring(0, index + 2)).replace(".", "") + 0);
        } else {
            amLong = Long.valueOf((currency.substring(0, index + 1)).replace(".", "") + "00");
        }
        return amLong.toString();
    }
}

你可能感兴趣的:(第三方微信支付,SpringBoot,java)