1.注册微信支付账号和微信小程序账号
2.获取微信小程序的APPID
3.获取微信商家的商户ID
4.获取微信商家的API私钥
5.配置微信支付回调地址
6.绑定微信小程序和微信支付的关系
7. 搭建Springboot 工程定义后台编写支付接口
8. 发布部署接口服务项目
9. 使用微信小程序或者Uniapp完成微信支付的调用
10.对支付接口的封装以及前端代码的封装
11.配置jwt 和openid的token派发,让接口更加安全可靠
12.原生微信小程序完成支付对接
官方地址: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();
}
进入微信官方文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
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
微信小程序支付
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();
}
}