分享SpringBoot整合微信公众号支付项目,对接微信JSAPI支付类型遇到的问题和过程封装的工具类,目前已正常使用,有问题大家评论区互动哈,有需要源码的可以私信我。
1、创建SpringBoot项目
自行创建简单SpringBootDemo
2、映入微信支付maven依赖
<!-- Mybatis-plus 依赖配置 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!-- 微信支付api v3 -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.9</version>
</dependency>
<!-- 引入hutool工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.2.4</version>
</dependency>
3、在bootstrap.yam映入配置信息
# 微信支付相关
wxPay:
appId: 微信支付APPID
## jsApi支付相关
jsApi:
## 商户账号
mchId: 商户账号
## 商户API证书的证书序列号
mchSerialNo: 商户API证书的证书序列号
## 商户API私钥
apiV3Key: 商户API私钥
## 回调地址
notifyUrl: 回调地址
4、封装JSAPI需要的工具类(我这边集成:jsapi下单、jsapi查单逻辑)
WxPayJsApiHttpUtil:jsapi接口工具类
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.ycg.pay.common.constant.WxPayJsApiConstant;
import com.ycg.pay.common.constant.WxPayJsApiHeaderConstant;
import com.ycg.pay.common.constant.WxPayJsApiOtherConstant;
import com.ycg.pay.model.bo.WxPayJsApiAmountBO;
import com.ycg.pay.model.bo.WxPayJsApiOrderBO;
import com.ycg.pay.model.bo.WxPayPayerBO;
import com.ycg.pay.model.vo.WxPayJsApiPreVO;
import lombok.RequiredArgsConstructor;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.*;
/**
* @ClassName WxPayJsApiCreateSignUtil
* @Description JSAPI工具类
* @Author 俞春旺
* @Company RBGT
* @Date 2023年4月21日 0021 上午 08:58:36
* @Version 1.0
*/
@Component
@RefreshScope
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class WxPayJsApiHttpUtil {
private static Logger logger = LoggerFactory.getLogger(WxPayJsApiHttpUtil.class);
private final WxPayJsApiSignUtil wxPayJsApiSignUtil;
/**
* appId
**/
@Value("${wxPay.appId}")
String appId;
/**
* mchId
**/
@Value("${wxPay.jsApi.mchId}")
String mchId;
/**
* mchSerialNo
**/
@Value("${wxPay.jsApi.mchSerialNo}")
String mchSerialNo;
/**
* apiV3Key
**/
@Value("${wxPay.jsApi.apiV3Key}")
String apiV3Key;
/**
* notifyUrl
**/
@Value("${wxPay.jsApi.notifyUrl}")
String notifyUrl;
/**
* 操作 - 微信JS_API下单
*
* @return com.ycg.pay.model.vo.WxPayJsApiPreVO
* @Author 俞春旺
* @Date 下午 04:38:03 2023年4月22日 0022
**/
public WxPayJsApiPreVO jsApiV3() throws Exception {
//请求URL
HttpPost httpPost = new HttpPost(WxPayJsApiConstant.JS_API_URL);
String out_trade_no = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN);
// 请求body参数
WxPayJsApiAmountBO amount = new WxPayJsApiAmountBO();
amount.setTotal(1);
amount.setCurrency("CNY");
WxPayPayerBO payer = new WxPayPayerBO();
payer.setOpenid("opneid");
WxPayJsApiOrderBO request = new WxPayJsApiOrderBO();
request.setMchid(mchId);
request.setOut_trade_no(out_trade_no);
request.setAttach("order_no_20230423");
request.setAppid(appId);
request.setDescription("练视频推送套餐");
request.setNotify_url(notifyUrl);
request.setAmount(amount);
request.setPayer(payer);
StringEntity entity = new StringEntity(JSONUtil.toJsonStr(request), "utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader(WxPayJsApiHeaderConstant.HTTP_HEADER_ACCEPT, WxPayJsApiHeaderConstant.HTTP_HEADER_ACCEPT_VAL);
// 构建 - 请求信息
CloseableHttpClient closeableHttpClient = initCloseableHttpClientV3();
// 操作 - 完成请求
CloseableHttpResponse response = closeableHttpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
System.out.println(bodyAsString);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) {
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
closeableHttpClient.close();
}
// 构造 - 响应信息
WxPayJsApiPreVO result = convertWxPayJsApiPreInfo((String) JSONUtil.parseObj(bodyAsString).get(WxPayJsApiOtherConstant.HTTP_OTHER_PREPAY_ID));
System.out.println("result -> " + JSONUtil.toJsonStr(result));
return result;
}
/**
* 获取 - jsApi加密串及前端换起JSAPI支付界面
*
* @param prepayId
* @return com.ycg.pay.model.vo.WxPayJsApiPreVO
* @Author 俞春旺
* @Date 下午 01:42:42 2023年4月21日 0021
**/
protected WxPayJsApiPreVO convertWxPayJsApiPreInfo(String prepayId) throws Exception {
logger.info("[开始]预支付信息-prepayId : {}", prepayId);
// 构建 - 相关参数
String time = String.valueOf(System.currentTimeMillis() / 1000);
String nonceStr = UUID.randomUUID().toString().replace("-", "");
String packageStr = StrUtil.format(WxPayJsApiConstant.SIGN_PACKAGE_STR, prepayId);
// 构建 - 签名信息
ArrayList<String> list = new ArrayList<>();
list.add(appId);
list.add(time);
list.add(nonceStr);
list.add(packageStr);
// 操作 - 签名
String packageSign = wxPayJsApiSignUtil.signV3(WxPayJsApiSignUtil.buildSignMessage(list).getBytes());
// 构建 - 响应信息
WxPayJsApiPreVO result = new WxPayJsApiPreVO();
result.setWxAppId(appId);
result.setWxTimeStamp(time);
result.setWxNonceStr(nonceStr);
result.setWxPackage(packageStr);
result.setWxSignType(WxPayJsApiConstant.SIGN_TYPE);
result.setWxPaySign(packageSign);
logger.info("[结束]预支付信息-WxPayJsApiPreVO : {}", JSONUtil.toJsonStr(result));
return result;
}
/**
* 查看 - JSAPI微信支付订单号查询
*
* @param transaction_id 微信支付订单号
* @return com.alibaba.fastjson.JSONObject
* @Author 俞春旺
* @Date 下午 04:24:44 2023年4月22日 0022
**/
public JSONObject queryJsApiOrderInfoV3(String transaction_id) throws HttpCodeException, GeneralSecurityException, NotFoundException, IOException, URISyntaxException {
String url = StrUtil.format(WxPayJsApiConstant.TRANSACTIONS_ORDER_QUERY_URL, transaction_id, mchId);
URIBuilder uriBuilder = new URIBuilder(url);
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader(WxPayJsApiHeaderConstant.HTTP_HEADER_ACCEPT, WxPayJsApiHeaderConstant.HTTP_HEADER_ACCEPT_VAL);
CloseableHttpClient httpClient = initCloseableHttpClientV3();
CloseableHttpResponse response = httpClient.execute(httpGet);
String bodyAsString = EntityUtils.toString(response.getEntity());
System.out.println("bodyAsString:" + bodyAsString);
return JSONUtil.parseObj(bodyAsString);
}
/**
* 查询 - 实时获取resources实时新增的文件目录
*
* @param
* @return java.lang.String
* @Author 俞春旺
* @Date 下午 07:29:28 2023年3月30日 0030
**/
protected static String getResourcesPath() {
// 这里需要注意的是ApplicationHome是属于SpringBoot的类
// 获取项目下resources/static/img路径
ApplicationHome applicationHome = new ApplicationHome(WxPayJsApiHttpUtil.class);
// 保存目录位置根据项目需求可随意更改 - win和linux目录不一样
String path = applicationHome.getDir().getParentFile().getParentFile().getAbsolutePath() + "/src/main/resources/pay/";
System.out.println("path:" + path);
return path;
}
/**
* 构造 - 通用CloseableHttpClient(定时更新平台证书功能)
*
* @return org.apache.http.impl.client.CloseableHttpClient
* @Author 俞春旺
* @Date 下午 04:31:47 2023年4月22日 0022
**/
protected CloseableHttpClient initCloseableHttpClientV3() throws IOException, HttpCodeException, GeneralSecurityException, NotFoundException {
// 获取 - 秘钥证书信息
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(WxPayJsApiHttpUtil.getResourcesPath() + "apiclient_key.pem"));
// 获取 - 证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
// 操作 - 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
// ... 若有多个商户号,可继续调用putMerchant添加商户信息
// 操作 - 从证书管理器中获取verifier
Verifier verifier = certificatesManager.getVerifier(mchId);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier));
// 构造通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
return builder.build();
}
}
WxPayJsApiNotifyUtil:支付完成回调工具类
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.ycg.pay.common.constant.WxPayJsApiNotifyConstant;
import com.ycg.pay.common.constant.WxPayJsApiResponseConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @ClassName WxPayJsApiNotifyUtil
* @Description 回调通知
* @Author 俞春旺
* @Company RBGT
* @Date 2023年4月21日 0021 上午 08:58:36
* @Version 1.0
*/
@Component
@RefreshScope
public class WxPayJsApiNotifyUtil {
private static Logger logger = LoggerFactory.getLogger(WxPayJsApiNotifyUtil.class);
/**
* 商户API私钥
**/
@Value("${wxPay.jsApi.apiV3Key}")
String apiV3Key;
/**
* 操作 - 微信支付回调状态处理逻辑
* @param request 请求入参
* @param response 请求响应
* @return java.lang.String
* @Author 俞春旺
* @Date 下午 05:44:08 2023年4月22日 0022
**/
public String notifyV3(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 构建 - 微信支付回调通知body数据
String body = getVerifyNotifyBody(request);
// 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
String plainText = verifyNotify(body, apiV3Key);
// 构建 - 微信支付回调通知map数据
Map<String, String> map = new LinkedHashMap<>();
if (StrUtil.isNotEmpty(plainText)) {
response.setStatus(WxPayJsApiResponseConstant.RESPONSE_SUCCESS);
map.put(WxPayJsApiResponseConstant.RESPONSE_CODE, WxPayJsApiResponseConstant.RESPONSE_SUCCESS_MESSAGE);
map.put(WxPayJsApiResponseConstant.RESPONSE_MESSAGE, WxPayJsApiResponseConstant.RESPONSE_SUCCESS_MESSAGE);
logger.info("微信支付回调成功,处理成功 " + map);
} else {
response.setStatus(WxPayJsApiResponseConstant.RESPONSE_ERROR);
map.put(WxPayJsApiResponseConstant.RESPONSE_CODE, WxPayJsApiResponseConstant.RESPONSE_ERROR_MESSAGE);
map.put(WxPayJsApiResponseConstant.RESPONSE_MESSAGE, "签名错误");
logger.info("微信支付回调成功,处理失败 " + map);
}
// 构建 - 响应信息
response.setHeader("Content-type", ContentType.JSON.toString());
response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
response.flushBuffer();
String out_trade_no = (String) JSONUtil.parseObj(plainText).get("out_trade_no");
return out_trade_no;
}
/**
* 获取 - 微信支付回调通知请求头body内容
* @param request HttpServletRequest
* @return java.lang.String
* @Author 俞春旺
* @Date 上午 10:11:18 2023年4月23日 0023
**/
public static String getVerifyNotifyBody(HttpServletRequest request) {
BufferedReader br = null;
try {
StringBuilder result = new StringBuilder();
br = request.getReader();
for (String line; (line = br.readLine()) != null; ) {
if (result.length() > 0) {
result.append("\n");
}
result.append(line);
}
return result.toString();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* v3 支付异步通知验证签名
*
* @param body 异步通知密文
* @param key api 密钥
* @return 异步通知明文
* @throws Exception 异常信息
*/
public static String verifyNotify(String body, String key) throws Exception {
// 获取 - 请求body相关加密信息
JSONObject resultObject = JSONUtil.parseObj(body);
JSONObject resource = resultObject.getJSONObject(WxPayJsApiNotifyConstant.HTTP_NOTIFY_RESOURCE);
String cipherText = resource.getStr(WxPayJsApiNotifyConstant.HTTP_NOTIFY_CIPHER_TEXT);
String nonceStr = resource.getStr(WxPayJsApiNotifyConstant.HTTP_NOTIFY_NONCE);
String associatedData = resource.getStr(WxPayJsApiNotifyConstant.HTTP_NOTIFY_ASSOCIATED_DATA);
// 构造 - 解密工具类
AesUtil aesUtil = new AesUtil(key.getBytes(StandardCharsets.UTF_8));
// 操作 - 密文解密
String result = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonceStr.getBytes(StandardCharsets.UTF_8), cipherText);
logger.info("微信支付成功,解密回调信息 - result : {}",result);
return result;
}
}
WxPayJsApiSignUtil:支付签名工具类
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Signer;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.ycg.pay.common.constant.WxPayJsApiConstant;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Base64;
/**
* @ClassName WxPayJsApiSignUtil
* @Description TODO
* @Author 俞春旺
* @Company RBGT
* @Date 2023年4月22日 0022 下午 04:39:33
* @Version 1.0
*/
@Component
@RefreshScope
public class WxPayJsApiSignUtil {
/**
* mchSerialNo
**/
@Value("${wxPay.jsApi.mchSerialNo}")
String mchSerialNo;
/**
* 操作 - 数据加密
*
* @param message 消息信息
* @return java.lang.String
* @Author 俞春旺
* @Date 下午 01:24:56 2023年4月21日 0021
**/
public String signV3(byte[] message) throws IOException {
// 构建 - 签名工具类
PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, getPrivateKey(WxPayJsApiHttpUtil.getResourcesPath() + WxPayJsApiConstant.SIGN_API_CLIENT_KEY));
return privateKeySigner.sign(message).getSign();
}
/**
* 获取私钥。
* 这是个静态方法,可以直接用类名调用
*
* @param filename 私钥文件路径 (required)
* @return 私钥对象
* <p>
* 完全不需要修改,注意此方法也是去掉了头部和尾部,注意文件路径名
*/
public static PrivateKey getPrivateKey(String filename) throws IOException {
String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
return PemUtil.loadPrivateKey(content);
}
/**
* 转换 - 加密数据
*
* @param signMessage
* @return java.lang.String
* @Author 俞春旺
* @Date 下午 01:19:45 2023年4月21日 0021
**/
public static String buildSignMessage(ArrayList<String> signMessage) {
if (signMessage == null || signMessage.size() <= 0) {
return null;
}
StringBuilder sbf = new StringBuilder();
for (String str : signMessage) {
sbf.append(str).append("\n");
}
return sbf.toString();
}
}
common.constant:常量代码信息
/**
* @ClassName WxPayJsApiConstant
* @Description TODO
* @Author 俞春旺
* @Company RBGT
* @Date 2023年4月22日 0022 下午 05:01:15
* @Version 1.0
*/
public interface WxPayJsApiConstant {
// 请求地址
/**
* 微信JS_API下单地址
*/
String JS_API_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
/**
* 微信支付订单号查询
*/
String TRANSACTIONS_ORDER_QUERY_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/id/{}?mchid={}";
// 签名相关
/**
* PACKAGE_STR
*/
String SIGN_PACKAGE_STR = "prepay_id={}";
/**
* SIGN_TYPE
*/
String SIGN_TYPE = "RSA";
/**
* SIGN_API_CLIENT_KEY
*/
String SIGN_API_CLIENT_KEY = "apiclient_key.pem";
}
/**
* @ClassName WxPayJsApiConstant
* @Description TODO
* @Author 俞春旺
* @Company RBGT
* @Date 2023年4月22日 0022 下午 05:01:15
* @Version 1.0
*/
public interface WxPayJsApiHeaderConstant {
/**
* 请求相关_头部_ACCEPT
*/
String HTTP_HEADER_ACCEPT = "Accept";
/**
* 请求相关_头部_ACCEPT_VAL
*/
String HTTP_HEADER_ACCEPT_VAL = "application/json";
/**
* 请求相关_头部_ACCEPT
*/
String HTTP_OTHER_PREPAY_ID = "prepay_id";
}
/**
* @ClassName WxPayJsApiConstant
* @Description TODO
* @Author 俞春旺
* @Company RBGT
* @Date 2023年4月22日 0022 下午 05:01:15
* @Version 1.0
*/
public interface WxPayJsApiNotifyConstant {
/**
* 回调通知常量-resource
*/
String HTTP_NOTIFY_RESOURCE = "resource";
/**
* 回调通知常量-cipher-text
*/
String HTTP_NOTIFY_CIPHER_TEXT = "ciphertext";
/**
* 回调通知常量-nonce
*/
String HTTP_NOTIFY_NONCE = "nonce";
/**
* 回调通知常量-associated_data
*/
String HTTP_NOTIFY_ASSOCIATED_DATA = "associated_data";
}
/**
* @ClassName WxPayJsApiConstant
* @Description TODO
* @Author 俞春旺
* @Company RBGT
* @Date 2023年4月22日 0022 下午 05:01:15
* @Version 1.0
*/
public interface WxPayJsApiOtherConstant {
/**
* 请求相关_头部_ACCEPT
*/
String HTTP_OTHER_PREPAY_ID = "prepay_id";
}
/**
* @ClassName WxPayJsApiResponseConstant
* @Description 响应常量信息
* @Author 俞春旺
* @Company RBGT
* @Date 2023年4月22日 0022 下午 05:01:15
* @Version 1.0
*/
public interface WxPayJsApiResponseConstant {
/**
* 响应code
*/
String RESPONSE_CODE = "code";
/**
* 响应成功-200
*/
Integer RESPONSE_SUCCESS = 200;
/**
* 响应成功消息
*/
String RESPONSE_SUCCESS_MESSAGE = "SUCCESS";
/**
* 响应失败-200
*/
Integer RESPONSE_ERROR = 500;
/**
* 响应失败消息
*/
String RESPONSE_ERROR_MESSAGE = "ERROR";
/**
* 响应message
*/
String RESPONSE_MESSAGE = "message";
}
model包底下的类
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
* @ClassName WxPayAmountBO
* @Description TODO
* @Author 俞春旺
* @Company RBGT
* @Date 2023年4月21日 0021 上午 08:56:49
* @Version 1.0
*/
@Data
public class WxPayJsApiAmountBO {
@ApiModelProperty("总金额:订单总金额,单位为分。")
private Integer total;
@ApiModelProperty("货币类型:CNY:人民币,境内商户号仅支持人民币。[长度:1~16位]")
private String currency;
}
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @ClassName OperationJsApiOrderBO
* @Description JS_API下单
* @Author 俞春旺
* @Company RBGT
* @Date 2023年4月22日 0022 下午 05:46:17
* @Version 1.0
*/
@Data
public class WxPayJsApiOrderBO {
@ApiModelProperty("[必填:是]直连商户号:直连商户的商户号,由微信支付生成并下发[长度:1~32位]")
private String mchid;
@ApiModelProperty("[必填:是]商户订单号:商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯(1217752501201407033233368018)[长度:6~32位]")
private String out_trade_no;
@ApiModelProperty("[必填:是]应用ID:由微信生成的应用ID,全局唯一。请求基础下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的服务号APPID[长度:1~32位]")
private String appid;
@ApiModelProperty("[必填:是]商品描述:Image形象店-深圳腾大-QQ公仔[长度:1~127位]")
private String description;
@ApiModelProperty("[必填:否]附加数据:附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用,实际情况下只有支付完成状态才会返回该字段")
private String attach;
@ApiModelProperty("[必填:是]通知地址:异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 公网域名必须为https,如果是走专线接入,使用专线NAT IP或者私有回调域名可使用http[长度:1~256位]")
private String notify_url;
@ApiModelProperty("[必填:是]订单金额:订单金额信息")
private WxPayJsApiAmountBO amount;
@ApiModelProperty("[必填:是]支付者:支付者信息")
private WxPayPayerBO payer;
}
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @ClassName WxPayPayerBO
* @Description TODO
* @Author 俞春旺
* @Company RBGT
* @Date 2023年4月22日 0022 下午 05:48:37
* @Version 1.0
*/
@Data
public class WxPayPayerBO {
@ApiModelProperty("微信openId[长度:1~128位]")
private String openid;
}
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @ClassName WxPayJsApiPreVO
* @Description JsApi与支付响应信息,唤起前端支付信息
* @Author 俞春旺
* @Company RBGT
* @Date 2023年4月21日 0021 下午 01:33:56
* @Version 1.0
*/
@Data
public class WxPayJsApiPreVO {
@ApiModelProperty("应用ID:商户申请的公众号对应的appId")
private String wxAppId;
@ApiModelProperty("时间戳")
private String wxTimeStamp;
@ApiModelProperty("随机字符串")
private String wxNonceStr;
@ApiModelProperty("订单详情扩展字符串")
private String wxPackage;
@ApiModelProperty("签名方式")
private String wxSignType;
@ApiModelProperty("签名")
private String wxPaySign;
}
5、controller测试代码
import cn.hutool.json.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.ycg.common.core.domain.R;
import com.ycg.pay.utils.jsapi.WxPayJsApiHttpUtil;
import com.ycg.pay.model.vo.WxPayJsApiPreVO;
import com.ycg.pay.utils.jsapi.WxPayJsApiNotifyUtil;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
/**
* @ClassName JsApiDemoTestController
* @Description TODO
* @Author 俞春旺
* @Company RBGT
* @Date 2023年4月21日 0021 下午 02:08:42
* @Version 1.0
*/
@Api(tags = "WEB端 ->访客通行记录相关接口")
@RestController
@RequestMapping("/jsapi")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@CrossOrigin(origins = "*")
public class JsApiDemoTestController {
private final WxPayJsApiHttpUtil wxPayJsApiHttpUtil;
private final WxPayJsApiNotifyUtil wxPayJsApiNotifyUtil;
@GetMapping("/test")
public R<WxPayJsApiPreVO> test() throws Exception {
return R.ok(wxPayJsApiHttpUtil.jsApiV3());
}
@GetMapping("/queryJsApiOrderInfo")
public R<JSONObject> queryJsApiOrderInfo(@RequestParam("transactionId") String transactionId) throws URISyntaxException, GeneralSecurityException, IOException, NotFoundException, HttpCodeException {
return R.ok(wxPayJsApiHttpUtil.queryJsApiOrderInfoV3(transactionId));
}
@PostMapping("/notify")
public void wxPayResult(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("微信公众号支付异步消息");
String out_trade_no = wxPayJsApiNotifyUtil.notifyV3(request, response);
System.out.println("out_trade_no:" + out_trade_no);
}
}
中途遇到的问题和大家分享一下:
(1)在对接JSAPI时应认真阅读官方文档,要多看几遍,然后在开始整理自己SDK的DEMO,确保在调试过程中,正常发起支付,且微信官方将支付完成信息正常回调通知到你的平台;
(2)唤起支付的页面域名,必须要在商户应用中心做好配置(必须是http/https,不允许本地,可以结合花生壳内网穿透调试,实在不行就打包线上调试),不然会提示:该域名尚未注册;