2023年使用SDK实现小程序加Java后端完成V3接口支付_微信小程序支付sdk-CSDN博客
代码出自↑,借鉴前人栽树,后人乘凉的帖子,成功完成微信支付操作。
代码简洁的原因是调用了github的IJPAY的聚合支付
附上gitee地址:https://gitee.com/javen205/IJPay
感谢大佬们栽培的大树,让没接过支付的我也能成功上手
下面是代码层。
配置类(需要提前在yml中配置好所需参数
package com.ruoyi.config;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/**
* 小程序获取配置类
*
* @author pzy
* @version 0.1.0
* @description TODO
*/
@Slf4j
@Data
@Component
@ConfigurationProperties(prefix = "wechat.mini-app")
public class WechatConfigProperties {
private String appId;
private String appSecret;
//# 证书序列号
private String mchSerialNo;
//#平台证书地址,平台证书需要手动下载或者使用v3接口进行下载
private String platformCertPath;
//客户证书地址,也就是apiclient_cert.pem的路径
private String apiClientCertPath;
//#证书密钥地址,也就是apiclient_key.pem的路径
private String apiClientKeyPath;
//商户号
private String mchId;
//apiv3秘钥w
private String apiKey;
//微信服务器地址
private String domain;
//回调,自己的回调地址,暂时内网穿透
private String notifyDomain;
service类
package com.ruoyi.service;
import com.ijpay.wxpay.WxPayApiConfig;
import com.ruoyi.vo.IWxPayParamVO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
public interface WechatPayService {
/**
* 微信统一下单接口
*
* @return Map
* @throws Exception 异常
*/
Map<String, String> doUnifiedOrder(IWxPayParamVO iWxPayParamVO) throws Exception;
/**
* 获取v3的证书
*
* @return String类型
* @throws IOException IO异常
*/
String createPlatformCert() throws IOException;
/**
* 微信支付回调接口
*
* @param request 请求
* @param response 详情
*/
void callBack(HttpServletRequest request, HttpServletResponse response);
/**
* 查询账单
*
* @param outTradeNo
* @return
*/
String query(String outTradeNo);
serviceimpl类
package com.ruoyi.service.impl;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.AuthTypeEnum;
import com.ijpay.core.enums.RequestMethodEnum;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.AesUtil;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.core.utils.DateTimeZoneUtil;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxDomainEnum;
import com.ijpay.wxpay.enums.v3.BasePayApiEnum;
import com.ijpay.wxpay.enums.v3.CertAlgorithmTypeEnum;
import com.ijpay.wxpay.model.v3.Amount;
import com.ijpay.wxpay.model.v3.Payer;
import com.ijpay.wxpay.model.v3.UnifiedOrderModel;
import com.ruoyi.config.WechatConfigProperties;
import com.ruoyi.domain.XcCustomer;
import com.ruoyi.mapper.XcCustomerMapper;
import com.ruoyi.service.WechatPayService;
import com.ruoyi.vo.IWxPayParamVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.ijpay.wxpay.WxPayApiConfig;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.RefundModel;
import static com.ijpay.wxpay.WxPayApiConfigKit.getWxPayApiConfig;
@Service
@Slf4j
public class WechatPayServiceImpl implements WechatPayService {
@Autowired
private XcCustomerMapper xcCustomerMapper ;
private String notifyUrl;
private String refundNotifyUrl;
private final WechatConfigProperties wechatConfigProperties;
public WechatPayServiceImpl(WechatConfigProperties wechatConfigProperties) {
this.wechatConfigProperties = wechatConfigProperties;
}
/**
* 微信统一下单接口
*
* @return Map
* @throws Exception 异常
*/
@Override
public Map<String, String> doUnifiedOrder(IWxPayParamVO iWxPayParamVO) throws Exception {
//根据用户手机号查openid
String openId = "";
XcCustomer xcCustomer = new XcCustomer();
xcCustomer.setNumber(iWxPayParamVO.getAccount());
List<XcCustomer> xcCustomers = xcCustomerMapper.selectXcCustomerList(xcCustomer);
for (XcCustomer xcCustomer1:xcCustomers){
openId = xcCustomer1.getOpenId();
}
//先写死1, 1就是1分钱,100=1元
String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(wechatConfigProperties.getAppId())
.setMchid(wechatConfigProperties.getMchId())
.setDescription("支付说明")
//这是随机数
.setOut_trade_no(PayKit.generateStr())
.setTime_expire(timeExpire)
.setAttach("附加说明")
//回调地址
.setNotify_url(wechatConfigProperties.getNotifyDomain())
.setAmount(new Amount().setTotal(iWxPayParamVO.getAmount()))
.setPayer(new Payer().setOpenid(openId));
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
IJPayHttpResponse response = WxPayApi.v3(
RequestMethodEnum.POST,
WxDomainEnum.CHINA.toString(),
BasePayApiEnum.JS_API_PAY.toString(),
wechatConfigProperties.getMchId(),
getSerialNumber(),
null,
wechatConfigProperties.getApiClientKeyPath(),
JSONUtil.toJsonStr(unifiedOrderModel)
);
log.info("统一下单响应 {}", response);
Map<String, String> map = new HashMap<>(16);
//这个是证书文件,先写死,后续调整成读取证书文件的服务器存放地址,根据证书序列号查询对应的证书来验证签名结果
boolean verifySignatures = WxPayKit.verifySignature(response, wechatConfigProperties.getApiClientCertPath());
log.info("verifySignature: {}", verifySignatures);
if (response.getStatus() == HttpServletResponse.SC_OK) {
// 根据证书序列号查询对应的证书来验证签名结果
String body = response.getBody();
JSONObject jsonObject = JSONUtil.parseObj(body);
String prepayId = jsonObject.getStr("prepay_id");
// 私钥
map = WxPayKit.jsApiCreateSign(wechatConfigProperties.getAppId(), prepayId, wechatConfigProperties.getApiClientKeyPath());
log.info("唤起支付参数:{}", map);
}
// todo 微信预支付的订单新入库,为了业务查询记录
return map;
}
/**
* 微信支付回调接口
*
* @param request 请求
* @param response 详情
*/
@Override
public void callBack(HttpServletRequest request, HttpServletResponse response) {
log.info("收到微信支付回调");
Map<String, String> map = new HashMap<>(12);
try {
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String serialNo = request.getHeader("Wechatpay-Serial");
String signature = request.getHeader("Wechatpay-Signature");
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
String result = HttpKit.readData(request);
log.info("支付通知密文 {}", result);
// 根据证书序列号查询对应的证书来验证签名结果
String platformCertPath = this.wechatConfigProperties.getPlatformCertPath();
//这个商户号对应的那个V3秘钥
String mckKey = "";
//需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp, mckKey, platformCertPath);
log.info("支付通知明文 {}", plainText);
//这个就是具体的业务情况
savePayPlainText(plainText);
//回复微信
if (StrUtil.isNotEmpty(plainText)) {
response.setStatus(200);
map.put("code", "SUCCESS");
map.put("message", "SUCCESS");
} else {
response.setStatus(500);
map.put("code", "ERROR");
map.put("message", "签名错误");
}
response.setHeader("Content-type", ContentType.JSON.toString());
response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
response.flushBuffer();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 查询账单
*
* @param outTradeNo
* @return
*/
@Override
public String query(String outTradeNo) {
try {
Map<String, String> params = new HashMap<>(16);
params.put("mchid", wechatConfigProperties.getMchId());
log.info("统一下单参数 {}", JSONUtil.toJsonStr(params));
IJPayHttpResponse response = WxPayApi.v3(
RequestMethodEnum.GET,
WxDomainEnum.CHINA.toString(),
String.format(BasePayApiEnum.ORDER_QUERY_BY_OUT_TRADE_NO.toString(), outTradeNo),
wechatConfigProperties.getMchId(),
getSerialNumber(),
null,
wechatConfigProperties.getApiClientKeyPath(),
params
);
log.info("查询响应 {}", response);
if (response.getStatus() == HttpServletResponse.SC_OK) {
// 根据证书序列号查询对应的证书来验证签名结果
boolean verifySignature = WxPayKit.verifySignature(response,wechatConfigProperties.getPlatformCertPath());
log.info("verifySignature: {}", verifySignature);
return response.getBody();
}
return JSONUtil.toJsonStr(response);
} catch (Exception e) {
log.error("系统异常", e);
return e.getMessage();
}
}
/**
* 获取v3的证书
*
* @return String类型
*/
@Override
public String createPlatformCert() {
//这个商户号对应的那个V3秘钥
String mckKey = "";
// 获取平台证书列表
try {
IJPayHttpResponse response = WxPayApi.v3(
RequestMethodEnum.GET,
WxDomainEnum.CHINA.toString(),
CertAlgorithmTypeEnum.getCertSuffixUrl(CertAlgorithmTypeEnum.RSA.getCode()),
wechatConfigProperties.getMchId(),
getSerialNumber(),
null,
wechatConfigProperties.getApiClientKeyPath(),
"",
AuthTypeEnum.RSA.getCode()
);
String serialNumber = response.getHeader("Wechatpay-Serial");
String body = response.getBody();
int status = response.getStatus();
log.info("serialNumber: {}", serialNumber);
log.info("status: {}", status);
// log.info("body: {}", body);
if (status == HttpServletResponse.SC_OK) {
JSONObject jsonObject = JSONUtil.parseObj(body);
JSONArray dataArray = jsonObject.getJSONArray("data");
// 默认认为只有一个平台证书
JSONObject encryptObject = dataArray.getJSONObject(0);
JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
String associatedData = encryptCertificate.getStr("associated_data");
String cipherText = encryptCertificate.getStr("ciphertext");
String nonce = encryptCertificate.getStr("nonce");
String serialNo = encryptObject.getStr("serial_no");
//生成第四个证书文件
final String platSerialNo = savePlatformCert(associatedData, mckKey, nonce, cipherText, wechatConfigProperties.getPlatformCertPath());
log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo);
}
// 根据证书序列号查询对应的证书来验证签名结果
boolean verifySignature = WxPayKit.verifySignature(response, wechatConfigProperties.getPlatformCertPath());
if (verifySignature) {
return body;
} else {
return "平台证书不正确";
}
} catch (Exception e) {
e.printStackTrace();
return "不能获取平台证书";
}
}
private String savePlatformCert(String associatedData, String apiKey3, String nonce, String cipherText, String certPath) {
try {
AesUtil aesUtil = new AesUtil(apiKey3.getBytes(StandardCharsets.UTF_8));
// 平台证书密文解密
// encrypt_certificate 中的 associated_data nonce ciphertext
String publicKey = aesUtil.decryptToString(
associatedData.getBytes(StandardCharsets.UTF_8),
nonce.getBytes(StandardCharsets.UTF_8),
cipherText
);
// log.info("获取证书key:{},保存路径platformCert:{}", publicKey, certPath);
log.info("保存路径platformCert:{}", certPath);
//将生成的证书写入指定路径,文件名为:cert.pem
FileOutputStream fos = new FileOutputStream(certPath);
fos.write(publicKey.getBytes());
fos.close();
// 获取平台证书序列号
X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
return certificate.getSerialNumber().toString(16).toUpperCase();
} catch (Exception e) {
log.error("写入证书错误:{}", e);
return e.getMessage();
}
}
/**
* 保存订单的支付通知明文
*
* @param plainText 纯文本
*/
private void savePayPlainText(String plainText) {
JSONObject jsonObject = JSONUtil.parseObj(plainText);
//这个就是发起订单时的那个订单号
String outTradeNo = jsonObject.getStr("out_trade_no");
//todo 把微信支付回调的明文消息存进数据库,方便后续校验查看
log.info("业务订单号,outTradeNo:{}", outTradeNo);
//todo 把微信支付后需要处理的具体业务处理了
}
/**
* 获取证书序列号
*
* @return String
*/
private String getSerialNumber() {
log.info("验证证书路径:{}", wechatConfigProperties.getApiClientCertPath());
// 获取证书序列号
X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wechatConfigProperties.getApiClientCertPath()));
String serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
log.info("获取证书序列号:{},", serialNo);
return serialNo;
}
controller接口
package com.ruoyi.controller;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.service.WechatPayService;
import com.ruoyi.vo.IWxPayParamVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
@Api(tags ="微信下单支付")
@RequiredArgsConstructor
@RestController
@RequestMapping("/payment")
public class WeChatPayController extends BaseController {
private final WechatPayService wxPayNewService;
/**
* 微信统一下单接口
*
* @param iWxPayParamVO 业务需要的参数
* @return
*/
@ApiOperation("微信统一下单接口")
@PostMapping("/doUnifiedOrder")
public Map<String, String> doUnifiedOrder(IWxPayParamVO iWxPayParamVO) throws Exception {
return wxPayNewService.doUnifiedOrder(iWxPayParamVO);
}
/**
* 微信支付的回调接收接口
*
* @param request
* @param response
*/
@ApiOperation("微信支付的回调接收接口")
@RequestMapping(value = "/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
public void callBack(HttpServletRequest request, HttpServletResponse response) {
wxPayNewService.callBack(request, response);
}
/**
* 生成v3证书
*/
@ApiOperation("生成v3证书")
@RequestMapping("/createPlatformCert")
@ResponseBody
public String createPlatformCert() throws IOException {
return wxPayNewService.createPlatformCert();
}
/**
* 通过订单号查询支付情况
*
* @param outTradeNo 订单号
* @return String
*/
@ApiOperation("通过订单号查询支付情况")
@RequestMapping("/query")
@ResponseBody
public String query(@RequestParam String outTradeNo) {
return wxPayNewService.query(outTradeNo);
}
没了,就这些就够了,因为大部分的代码都被开源IJPAY的大佬们帮忙封装好了
成功的记录