本编在对接V3支付的时候连连撞头,希望后来人能少走点弯路,如果有bug请海涵啊,希望各位大佬也能给我点意见,话不多说上代码:
service层
package com.tiyaa.mall.pay.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.squareup.okhttp.HttpUrl;
import com.tiyaa.mall.common.enums.CommonEnum;
import com.tiyaa.mall.common.exceptions.BackendException;
import com.tiyaa.mall.pay.config.WeChatConfig;
import com.tiyaa.mall.pay.config.WeChatPayConfig;
import com.tiyaa.mall.pay.constants.WeChatConstant;
import com.tiyaa.mall.pay.dto.BuyCreateDto;
import com.tiyaa.mall.pay.dto.BuyUrlMapBo;
import com.tiyaa.mall.pay.enums.PayTypeEnum;
import com.tiyaa.mall.pay.enums.WeChatApiTypeEnum;
import com.tiyaa.mall.pay.service.WeChatPayService;
import com.tiyaa.mall.pay.utils.PayUtil;
import com.tiyaa.mall.pay.vo.QueryPayVo;
import com.tiyaa.mall.pay.vo.QueryRefundVo;
import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
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.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author xyl
* @date 2022/8/23 10:33
* @explain
*/
@Slf4j
@Service
public class WeChatPayServiceImpl extends WeChatConfig implements WeChatPayService {
@Resource
private CloseableHttpClient wxPayClient;
@Autowired
private ScheduledUpdateCertificatesVerifier verifier;
@Autowired
private WeChatPayConfig weChatPayConfig;
/**
* 微信下单
*
* @param dto
* @return {@link {@link HashMap < String, String>}}
* @author xyl
* @date 2022/7/12 16:22
* @explain
*/
@Override
public HashMap<String, String> buy(BuyCreateDto dto) {
//下单map
BuyUrlMapBo bo = getBuyUrlMapBo(dto);
HashMap<String, Object> buyMap = bo.getBuyMap();
//支付金额封装map
HashMap<String, Object> amountMap = new HashMap<>();
amountMap.put("total", dto.getTotal());
amountMap.put("currency", "CNY");
//加入金额
buyMap.put("amount", amountMap);
try {
//生成签名
Gson gson = new Gson();
String json = gson.toJson(buyMap);
HttpUrl httpurl = HttpUrl.parse(domainUrl.concat(WeChatApiTypeEnum.GET_CERTIFICATES.getType()));
HashMap<String, String> token = getToken(WeChatConstant.GET, httpurl, json);
HttpPost httpPost = new HttpPost(bo.getUrl());
StringEntity entity = new StringEntity(json, "utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
CloseableHttpResponse response = wxPayClient.execute(httpPost);
//响应体
String bodyAsString = EntityUtils.toString(response.getEntity());
JSONObject jsonObject = JSON.parseObject(bodyAsString);
System.out.println("响应体" + bodyAsString);
//响应状态码
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("状态码" + statusCode);
if (statusCode != 200) {
throw new BackendException(bodyAsString);
}
//封装返回参数
return getResMap(dto.getPayType(), buyMap, token, jsonObject);
} catch (Exception e) {
log.warn("微信支付失败,错误信息:{}", e.getMessage());
System.out.println(("微信支付失败,错误信息:{}" + e.getMessage()));
throw new BackendException(CommonEnum.PARAMETER_ERROR);
}
}
private boolean verifiedSign(String serialNo, String signStr, String signature) {
return verifier.verify(serialNo, signStr.getBytes(StandardCharsets.UTF_8), signature);
}
/**
* 微信下单组装返回
*
* @param buyMap 支付map
* @param token 签名
* @param jsonObject 下单返回结果
* @return {@link {@link HashMap< String, String>}}
* @author xyl
* @date 2022/7/12 16:05
* @explain
*/
private HashMap<String, String> getResMap(Integer payType, HashMap<String, Object> buyMap, HashMap<String, String> token, JSONObject jsonObject) throws InvalidKeyException, SignatureException {
HashMap<String, String> resMap = new HashMap<>();
StringBuilder stringBuilder = new StringBuilder();
String appid = buyMap.get("appid").toString();
resMap.put("appId", appid);
stringBuilder.append(appid).append("\n")
.append(token.get("timestamp")).append("\n")
.append(token.get("nonce_str")).append("\n");
if (payType.equals(PayTypeEnum.WX_APP_WECHAT_PAY.getCode()) || payType.equals(PayTypeEnum.WX_H5_WECHAT_PAY.getCode())) {
resMap.put("package", "prepay_id=" + jsonObject.get("prepay_id").toString());
stringBuilder.append(resMap.get("package")).append("\n");
} else {
resMap.put("package", WeChatConstant.PACKAGE);
resMap.put("prepayId", jsonObject.get("prepay_id").toString());
resMap.put("partnerId", token.get("mchid"));
stringBuilder.append(jsonObject.get("prepay_id").toString()).append("\n");
}
resMap.put("nonceStr", token.get("nonce_str"));
resMap.put("timeStamp", token.get("timestamp"));
log.warn("签名字符串:" + stringBuilder);
String sign = sign(stringBuilder.toString().getBytes(StandardCharsets.UTF_8));
log.warn("获取的签名:" + sign);
resMap.put("signType", "RSA");
resMap.put("paySign", sign);
System.out.println(resMap);
return resMap;
}
/**
* 构建下单基础信息
*
* @param dto
* @return {@link {@link HashMap< String, Object>}}
* @author xyl
* @date 2022/7/12 9:18
* @explain
*/
private BuyUrlMapBo getBuyUrlMapBo(BuyCreateDto dto) {
BuyUrlMapBo bo = new BuyUrlMapBo();
HashMap<String, Object> buyMap = new HashMap<>();
//微信小程序
if (dto.getPayType() == PayTypeEnum.WX_APP_WECHAT_PAY.getCode()) {
//小程序appId
buyMap.put(WeChatConstant.APP_ID, smallAppId);
//用户openId
HashMap<String, Object> payerMap = new HashMap<>();
payerMap.put("openid", dto.getOpenId());
buyMap.put("payer", payerMap);
//微信api接口
String url = domainUrl.concat(WeChatApiTypeEnum.JSAPI_PAY.getType());
bo.setUrl(url);
//微信app
} else if (dto.getPayType() == PayTypeEnum.APP_WECHAT_PAY.getCode()) {
buyMap.put(WeChatConstant.APP_ID, appAppId);
//微信api接口
String url = domainUrl.concat(WeChatApiTypeEnum.APP_PAY.getType());
bo.setUrl(url);
//微信H5 jsapi支付,本质上不算H5支付,只能公众号内部调用
} else if (dto.getPayType() == PayTypeEnum.WX_H5_WECHAT_PAY.getCode()) {
buyMap.put(WeChatConstant.APP_ID, h5AppId);
//用户openId
HashMap<String, Object> payerMap = new HashMap<>();
payerMap.put("openid", dto.getOpenId());
buyMap.put("payer", payerMap);
//微信api接口
String url = domainUrl.concat(WeChatApiTypeEnum.JSAPI_PAY.getType());
bo.setUrl(url);
} else {
log.warn("发起支付失败,支付方式错误,请求参数:{}", dto);
throw new BackendException(CommonEnum.PARAMETER_ERROR);
}
buyMap.put(WeChatConstant.MCH_ID, mchId);
buyMap.put("description", "商品购买");
buyMap.put("out_trade_no", dto.getOutTradeNo());
buyMap.put("notify_url", payNotifyUrl);
bo.setBuyMap(buyMap);
return bo;
}
/**
* 获取微信支付平台证书
*
* @param method 提交方式
* @param url 接口
* @param body 主体
* @return {@link {@link String}}
* @author xyl
* @date 2022/7/12 10:45
* @explain
*/
public HashMap<String, String> getToken(String method, HttpUrl url, String body) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
//随机字符串
String nonceStr = PayUtil.createNonceStr();
//当前时间戳
long timestamp = System.currentTimeMillis() / 1000;
String message = buildMessage(method, url, timestamp, nonceStr, body);
String signature = sign(message.getBytes(StandardCharsets.UTF_8));
HashMap<String, String> map = new HashMap<>();
map.put("mchid", mchId);
map.put("nonce_str", nonceStr);
map.put("timestamp", String.valueOf(timestamp));
map.put("serial_no", serialNo);
map.put("signature", signature);
return map;
}
/**
* 构建消息
*
* @param method 方式
* @param url 请求连接
* @param timestamp 时间戳
* @param nonceStr 随机字符串
* @param body 传递的主体
* @return {@link {@link String}}
* @author xyl
* @date 2022/7/12 10:51
* @explain
*/
String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
String canonicalUrl = url.encodedPath();
if (url.encodedQuery() != null) {
canonicalUrl += "?" + url.encodedQuery();
}
return method + "\n"
+ canonicalUrl + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ body + "\n";
}
/**
* 生成签名
*
* @param message
* @return {@link {@link String}}
* @author xyl
* @date 2022/7/12 10:47
* @explain
*/
String sign(byte[] message) throws InvalidKeyException, SignatureException {
Signature sign = null;
try {
PrivateKey merchantPrivateKey = weChatPayConfig.getPrivateKey();
sign = Signature.getInstance("SHA256withRSA");
sign.initSign(merchantPrivateKey);
try {
sign.update(message);
} catch (SignatureException e) {
e.printStackTrace();
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 微信支付回调
*
* @param request 请求体
* @param response 响应体
* @return {@link {@link HashMap< String, String>}}
* @author xyl
* @date 2022/7/12 16:39
* @explain
*/
@Override
public HashMap<String, String> weChatPayNotify(HttpServletRequest request, HttpServletResponse response) {
//获取报文
String body = getRequestBody(request);
log.info("微信支付回调,获得报文:{}", body);
//随机串
String nonceStr = request.getHeader("Wechatpay-Nonce");
//微信传递过来的签名
String signature = request.getHeader("Wechatpay-Signature");
//证书序列号(微信平台)
String serialNo = request.getHeader("Wechatpay-Serial");
//时间戳
String timestamp = request.getHeader("Wechatpay-Timestamp");
//构造签名串
//应答时间戳\n
//应答随机串\n
//应答报文主体\n
String signStr = Stream.of(timestamp, nonceStr, body).collect(Collectors.joining("\n", "", "\n"));
log.warn("微信支付回调,构建签名串:{}", signStr);
HashMap<String, String> resultMap = new HashMap<>(2);
try {
//验证签名是否通过
boolean result = verifiedSign(serialNo, signStr, signature);
log.warn("微信支付回调,验证签名结果:{}", result);
if (result) {
//解密数据
String plainBody = decryptBody(body);
log.warn("微信支付回调,解密后的明文:{}", plainBody);
Map<String, String> map = convertWeChatPayMsgToMap(plainBody);
//获取订单号
String orderNo = map.get(WeChatConstant.OUT_TRADE_NO);
//处理业务逻辑 TODO
if (map.get(WeChatConstant.TRADE_STATE).equals(WeChatConstant.SUCCESS)) {
//处理业务逻辑 TODO
}
//响应微信
map.put("code", "SUCCESS");
map.put("message", "成功");
}
} catch (Exception e) {
log.warn("微信支付回调异常:{}", e.getMessage());
}
return resultMap;
}
/**
* 解密body的密文
* <p>
* "resource": {
* "original_type": "transaction",
* "algorithm": "AEAD_AES_256_GCM",
* "ciphertext": "",
* "associated_data": "",
* "nonce": ""
* }
*
* @param body
* @return {@link {@link String}}
* @author xyl
* @date 2022/7/12 16:36
* @explain
*/
private String decryptBody(String body) throws GeneralSecurityException {
AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
JSONObject object = JSONObject.parseObject(body);
JSONObject resource = object.getJSONObject("resource");
String ciphertext = resource.getString("ciphertext");
String associatedData = resource.getString("associated_data");
String nonce = resource.getString("nonce");
return aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
}
/**
* 转换body为map
*
* @param plainBody
* @return {@link {@link Map< String, String>}}
* @author xyl
* @date 2022/7/12 16:35
* @explain
*/
private Map<String, String> convertWeChatPayMsgToMap(String plainBody) {
Map<String, String> paramsMap = new HashMap<>(2);
JSONObject jsonObject = JSONObject.parseObject(plainBody);
//商户订单号
paramsMap.put(WeChatConstant.OUT_TRADE_NO, jsonObject.getString(WeChatConstant.OUT_TRADE_NO));
//交易状态
paramsMap.put(WeChatConstant.TRADE_STATE, jsonObject.getString(WeChatConstant.TRADE_STATE));
//微信交易单号
paramsMap.put(WeChatConstant.TRANSACTION_ID, jsonObject.getString(WeChatConstant.TRANSACTION_ID));
//交易金额
String amount = jsonObject.get("amount").toString();
JSONObject amountJson = JSONObject.parseObject(amount);
paramsMap.put(WeChatConstant.TOTAL, amountJson.getString(WeChatConstant.TOTAL));
log.warn("微信V3支付回调,封装返回参数:{}", paramsMap);
return paramsMap;
}
/**
* 读取数据流
*
* @param request
* @return {@link {@link String}}
* @author xyl
* @date 2022/7/12 16:33
* @explain
*/
private String getRequestBody(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
try (ServletInputStream inputStream = request.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
log.warn("读取数据流异常:{}", e.getMessage());
}
return sb.toString();
}
/**
* 查询订单支付情况
*
* @param orderNo
* @return {@link {@link QueryPayVo}}
* @author xyl
* @date 2022/8/24 16:07
* @explain
*/
@Override
public QueryPayVo queryPay(String orderNo) throws SignatureException, InvalidKeyException, NoSuchAlgorithmException, IOException {
String url = domainUrl.concat(WeChatApiTypeEnum.QUERY_PAY.getType() + orderNo + "?mchid=" + mchId);
JSONObject json = getHttpResponse(url);
//交易金额
String amount = json.get("amount").toString();
JSONObject amountJson = JSONObject.parseObject(amount);
return new QueryPayVo(json.get("out_trade_no").toString(), json.get("transaction_id").toString(), json.get("trade_type").toString(), json.get("trade_state").toString(), json.get("trade_state_desc").toString(), json.get("success_time").toString(), amountJson.get("total").toString());
}
/**
* 发起Git请求
*
* @param url
* @return {@link {@link HttpResponse}}
* @author xyl
* @date 2022/8/25 9:18
* @explain
*/
private JSONObject getHttpResponse(String url) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, IOException {
HttpClient httpClient = HttpClientBuilder.create().build();
HttpUrl httpurl = HttpUrl.parse(url);
String getSign = getGetSign(httpurl);
HttpGet httpGet = new HttpGet(url);
httpGet.addHeader("Accept", "application/json");
httpGet.addHeader("Authorization", getSign);
HttpResponse execute = httpClient.execute(httpGet);
String s = EntityUtils.toString(execute.getEntity());
JSONObject json = JSONObject.parseObject(s);
int statusCode = execute.getStatusLine().getStatusCode();
if (statusCode != 200) {
throw new BackendException(json.get("message").toString());
}
return json;
}
/**
* 微信支付GET获取签名
*
* @param httpurl 请求URL
* @return {@link {@link String}}
* @author xyl
* @date 2022/8/24 16:07
* @explain
*/
public String getGetSign(HttpUrl httpurl) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
HashMap<String, String> token = getToken(WeChatConstant.GET, httpurl, "");
return "WECHATPAY2-SHA256-RSA2048 mchid=\"" + token.get("mchid") + "\",nonce_str=\"" + token.get("nonce_str") + "\",signature=\"" + token.get("signature") + "\",timestamp=\"" + token.get("timestamp") + "\",serial_no=\"" + token.get("serial_no") + "\"";
}
/**
* 根据退款订单号查询退款情况
*
* @param refundOrderNo
* @return {@link {@link QueryRefundVo}}
* @author xyl
* @date 2022/8/25 10:18
* @explain
*/
@Override
public QueryRefundVo queryRefund(String refundOrderNo) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, IOException {
String url = domainUrl.concat(WeChatApiTypeEnum.QUERY_REFUND.getType() + refundOrderNo);
JSONObject json = getHttpResponse(url);
//交易金额
String amount = json.get("amount").toString();
JSONObject amountJson = JSONObject.parseObject(amount);
String total = amountJson.get("total").toString();
String refund = amountJson.get("refund").toString();
return new QueryRefundVo(json.get("refund_id").toString(), json.get("out_refund_no").toString(), json.get("transaction_id").toString(), json.get("out_trade_no").toString(), json.get("channel").toString(), json.get("user_received_account").toString(), json.get("success_time").toString(), json.get("status").toString(), total, refund);
}
}
相关的配置:
package com.tiyaa.mall.pay.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* @author xyl
* @date 2022/8/23 10:04
* @explain
*/
@Configuration
@Data
public class WeChatConfig {
/**
* 小程序
*/
@Value("${weChat.small.appId}")
public String smallAppId;
@Value("${weChat.small.secret}")
public String smallSecret;
/**
* h5 微信内頁jspapi调用
*/
@Value("${weChat.h5.appId}")
public String h5AppId;
@Value("${weChat.h5.secret}")
public String h5Secret;
/**
* app
*/
@Value("${weChat.app.appId}")
public String appAppId;
@Value("${weChat.app.secret}")
public String appSecret;
/**
* 商户号 秘钥key 商户API证书序列号 v3秘钥
*/
@Value("${weChat.mch.mchId}")
public String mchId;
@Value("${weChat.mch.mchKey}")
public String mchKey;
@Value("${weChat.mch.serialNo}")
public String serialNo;
@Value("${weChat.mch.apiV3Key}")
public String apiV3Key;
/**
* 支付回调 退款回调 微信服务地址
*/
@Value("${weChat.notifyUrl.payNotifyUrl}")
public String payNotifyUrl;
@Value("${weChat.notifyUrl.refundNotifyUrl}")
public String refundNotifyUrl;
@Value("${weChat.notifyUrl.domainUrl}")
public String domainUrl;
/**
* 项目
*/
@Value("${spring.profiles.active}")
public String active;
}
package com.tiyaa.mall.pay.config;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
/**
* @author xyl
* @date 2022/8/23 10:15
* @explain
*/
@Configuration
public class WeChatPayConfig extends WeChatConfig {
/**
* 获取商户的私钥文件
*
* @return
*/
public PrivateKey getPrivateKey() {
InputStream resourceAsStream = this.getClass().getResourceAsStream("/cert/apiclient_key.pem");
return PemUtil.loadPrivateKey(resourceAsStream);
}
/**
* 获取签名验证器
*
* @return
*/
@Bean
public ScheduledUpdateCertificatesVerifier getVerifier() {
//获取商户私钥
PrivateKey privateKey = getPrivateKey();
System.out.println(privateKey);
//私钥签名对象
PrivateKeySigner privateKeySigner = new PrivateKeySigner(serialNo, privateKey);
//身份认证对象
WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);
// 使用定时更新的签名验证器,不需要传入证书
return new ScheduledUpdateCertificatesVerifier(
wechatPay2Credentials,
apiV3Key.getBytes(StandardCharsets.UTF_8));
}
/**
* 获取http请求对象
*
* @param verifier
* @return
*/
@Bean(name = "wxPayClient")
public CloseableHttpClient getWxPayClient(ScheduledUpdateCertificatesVerifier verifier) {
//获取商户私钥
PrivateKey privateKey = getPrivateKey();
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, serialNo, privateKey)
.withValidator(new WechatPay2Validator(verifier));
// ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient
// 通过WeChatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
return builder.build();
}
/**
* 获取HttpClient,无需进行应答签名验证,跳过验签的流程
*/
@Bean(name = "wxPayNoSignClient")
public CloseableHttpClient getWxPayNoSignClient() {
//获取商户私钥
PrivateKey privateKey = getPrivateKey();
//用于构造HttpClient
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
//设置商户信息
.withMerchant(mchId, serialNo, privateKey)
//无需进行签名验证、通过withValidator((response) -> true)实现
.withValidator((response) -> true);
// 通过WeChatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
return builder.build();
}
}
DTO:
package com.tiyaa.mall.pay.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author xyl
* @date 2022/7/11 17:16
* @explain
*/
@Data
@ApiModel("下单dto")
public class BuyCreateDto {
@ApiModelProperty(value = "商户订单号(自己的订单号)")
private String outTradeNo;
@ApiModelProperty(value = "支付的类型字典11")
private Integer payType;
@ApiModelProperty(value = "支付金额")
private Long total;
@ApiModelProperty(value = "用户OpenId")
private String openId;
public BuyCreateDto() {
}
public BuyCreateDto(String outTradeNo, Integer payType, Long total, String openId) {
this.outTradeNo = outTradeNo;
this.payType = payType;
this.total = total;
this.openId = openId;
}
}
package com.tiyaa.mall.pay.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.HashMap;
/**
* @author xyl
* @date 2022/7/12 13:13
* @explain
*/
@Data
@ApiModel("获取URL和构建支付mapBo")
public class BuyUrlMapBo {
@ApiModelProperty(value = "微信支付API接口")
private String url;
@ApiModelProperty(value = "构建的支付信息map")
private HashMap<String, Object> buyMap;
}
枚举:
package com.tiyaa.mall.pay.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author xyl
* @date 2022/7/12 9:05
* @explain
*/
@AllArgsConstructor
@Getter
public enum WeChatApiTypeEnum {
/**
* 获取商户平台证书的URL
*/
GET_CERTIFICATES("/v3/certificates"),
/**
* JSAPI下单
*/
JSAPI_PAY("/v3/pay/transactions/jsapi"),
/**
* APP下单
*/
APP_PAY("/v3/pay/transactions/app"),
/**
* H5下单
*/
H5_PAY("/v3/pay/transactions/h5"),
/**
* 订单查询
*/
QUERY_PAY("/v3/pay/transactions/out-trade-no/"),
/**
* 订单查询
*/
QUERY_REFUND("/v3/refund/domestic/refunds/");
/**
* 类型
*/
private final String type;
}
package com.tiyaa.mall.pay.enums;
/**
* @author chenbinbin
* @date 2022-03-25 9:25
* 支付类型枚举类
*/
public enum PayTypeEnum {
/**
* @author chenbinbin
* @date 2022-03-25 9:25
* 支付类型枚举类
*/
WX_APP_WECHAT_PAY(0, "小程序微信支付"),
WX_H5_WECHAT_PAY(1, "公众号微信支付"),
H5_ALIPAY(2, "H5支付宝支付"),
APP_WECHAT_PAY(3, "APP微信支付"),
APP_ALIPAY(4, "APP支付宝支付"),
USER_BALANCE_PAY(5, "余额支付"),
BROWSER_H5_WECHAT_PAY(6, "外部浏览器H5微信支付"),
;
private int code;
private String msg;
PayTypeEnum() {
}
PayTypeEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
vo:
package com.tiyaa.mall.pay.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author xyl
* @date 2022/8/24 15:50
* @explain
*/
@Data
@ApiModel("支付查询返回Vo")
public class QueryPayVo {
@ApiModelProperty(value = "订单号")
private String orderNo;
@ApiModelProperty(value = "微信订单号")
private String transactionId;
@ApiModelProperty(value = "交易类型,枚举值:\n" +
"JSAPI:公众号支付\n" +
"NATIVE:扫码支付\n" +
"APP:APP支付\n" +
"MICROPAY:付款码支付\n" +
"MWEB:H5支付\n" +
"FACEPAY:刷脸支付")
private String tradeType;
@ApiModelProperty(value = "交易状态,枚举值:\n" +
"SUCCESS:支付成功\n" +
"REFUND:转入退款\n" +
"NOTPAY:未支付\n" +
"CLOSED:已关闭\n" +
"REVOKED:已撤销(仅付款码支付会返回)\n" +
"USERPAYING:用户支付中(仅付款码支付会返回)\n" +
"PAYERROR:支付失败(仅付款码支付会返回)\n" +
"示例值:SUCCESS")
private String tradeState;
@ApiModelProperty(value = "交易状态描述\n" +
"示例值:支付成功")
private String tradeStateDesc;
@ApiModelProperty(value = "支付完成时间,遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE,yyyy-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。\n" +
"示例值:2018-06-08T10:34:56+08:00")
private String successTime;
@ApiModelProperty(value = "\t订单总金额,单位为分。\n" +
"示例值:100")
private String total;
public QueryPayVo(String orderNo, String transactionId, String tradeType, String tradeState, String tradeStateDesc, String successTime, String total) {
this.orderNo = orderNo;
this.transactionId = transactionId;
this.tradeType = tradeType;
this.tradeState = tradeState;
this.tradeStateDesc = tradeStateDesc;
this.successTime = successTime;
this.total = total;
}
}
package com.tiyaa.mall.pay.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author xyl
* @date 2022/8/25 9:26
* @explain
*/
@Data
@ApiModel("微信查询退款Vo")
public class QueryRefundVo {
@ApiModelProperty(value = "微信支付退款单号\n" +
"示例值:50000000382019052709732678859")
private String refundId;
@ApiModelProperty(value = "商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。\n" +
"示例值:1217752501201407033233368018")
private String outRefundNo;
@ApiModelProperty(value = "微信支付交易订单号\n" +
"示例值:1217752501201407033233368018")
private String transactionId;
@ApiModelProperty(value = "原支付交易对应的商户订单号\n" +
"示例值:1217752501201407033233368018")
private String outTradeNo;
@ApiModelProperty(value = "\t枚举值:\n" +
"ORIGINAL:原路退款\n" +
"BALANCE:退回到余额\n" +
"OTHER_BALANCE:原账户异常退到其他余额账户\n" +
"OTHER_BANKCARD:原银行卡异常退到其他银行卡\n" +
"示例值:ORIGINAL")
private String channel;
@ApiModelProperty(value = "取当前退款单的退款入账方,有以下几种情况:\n" +
"1)退回银行卡:{银行名称}{卡类型}{卡尾号}\n" +
"2)退回支付用户零钱:支付用户零钱\n" +
"3)退还商户:商户基本账户商户结算银行账户\n" +
"4)退回支付用户零钱通:支付用户零钱通\n" +
"示例值:招商银行信用卡0403")
private String userReceivedAccount;
@ApiModelProperty(value = "退款成功时间,当退款状态为退款成功时有返回。\n" +
"示例值:2020-12-01T16:18:12+08:00")
private String successTime;
@ApiModelProperty(value = "款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款。\n" +
"枚举值:\n" +
"SUCCESS:退款成功\n" +
"CLOSED:退款关闭\n" +
"PROCESSING:退款处理中\n" +
"ABNORMAL:退款异常\n" +
"示例值:SUCCESS")
private String status;
@ApiModelProperty(value = "订单总金额,单位为分\n" +
"示例值:100")
private String total;
@ApiModelProperty(value = "退款标价金额,单位为分,可以做部分退款\n" +
"示例值:100")
private String refund;
public QueryRefundVo(String refundId, String outRefundNo, String transactionId, String outTradeNo, String channel, String userReceivedAccount, String successTime, String status, String total, String refund) {
this.refundId = refundId;
this.outRefundNo = outRefundNo;
this.transactionId = transactionId;
this.outTradeNo = outTradeNo;
this.channel = channel;
this.userReceivedAccount = userReceivedAccount;
this.successTime = successTime;
this.status = status;
this.total = total;
this.refund = refund;
}
}
因为阿里代码规范,还设置了点常量
package com.tiyaa.mall.pay.constants;
import org.springframework.stereotype.Component;
/**
* 微信支付支付常量
*
* @author xyl
* @date 2022/8/23 14:40
* @explain
*/
public class WeChatConstant {
public static final String PROD = "prod";
public static final String APP_ID = "appid";
public static final String MCH_ID = "mchid";
public static final String GET = "GET";
public static final String PACKAGE = "Sign=WXPay";
public static final String SUCCESS = "SUCCESS";
public static final String TRADE_STATE = "trade_state";
public static final String OUT_TRADE_NO = "out_trade_no";
public static final String ACCOUNT_NO = "account_no";
public static final String TRANSACTION_ID = "transaction_id";
public static final String TOTAL = "total";
}
当前支付 和 退款查询 因为小编被安排了其他事情,对接支付后续在更新