框架:springDateJPA
对接微信支付首先需要需要在微信商户平台注册微信商户号
微信商户平台入口
微信公众平台入口
这里主要是微信支付的方式,一共有七种,我这里主要对接了3种,App支付,JSAPI支付,H5支付,其实都大同小异,会对接一种就会对接其他的了,只不过是参数的不同.
开通的流程,这里就不描述了,主要是提交一些资料,
APP支付: 需要一个打包到应用商店时获得的应用号.
JSAPI支付: 需要公司的服务号,还需要配置JSAP支付路径,需要获取用户的OpenID
H5支付:没有特别的参数
最后需要配置回调地址,回调地址需要外网可以直接访问到的接口全路径,逻辑其实很简单
例如: http://test.m.xxxxxx.com/jewelrybespoke/weixin/pay/callback/wxorder
开发之前首先要搞清楚,自己公司开通的是普通商户版,还是服务商版这很重要,因为对接的接口是不同的,我就被坑了
APIV3接口文档入口
服务商模式与普通商户的模式的区别就是服务商多一个子商户号参数
服务商类似与加盟商,一个用这个的,做聚合支付的公司用的比较多,
普通商户类似与个体户,其实一个公司用普通商户模式完全满足了,因为子商户会有一些限制
对接流程都是一样的,只不过是多一个子商户号的参数
我对接的就是服务商模式,如果是对接普通商户模式自己去掉子商户的参数就可以了
这里是微信支付涉及到的所有必要参数
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.2.1</version>
</dependency>
<!-- OKHttp3依赖 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.8.1</version>
</dependency>
Entity
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//统一下单参数
@Setter
@Getter
@Component
public class WXPayConfigEntity {
public enum SignType {
MD5, HMACSHA256
}
//应用ID
@Value(value = "${wx.appid}")
private String apppid;
//商户ID
@Value(value = "${wx.mchid}")
private String mch_id;
//二级应用ID
private String sub_apppid = "暂时没有";
//二级商户ID
@Value(value = "${wx.sub_mchid}")
private String sub_mchid;
//签名
private String sign;
//终端IP
@Value(value = "${wx.payer_client_ip}")
private String payer_client_ip;
//API密钥key
@Value(value = "${wx.apikey}")
private String apiKey ;
//APIV3密钥key
@Value(value = "${wx.apiv3key}")
private String apiV3Key;
//支付通知回调地址
@Value(value = "${wx.notify.url}")
private String notify_url;
//支付公钥pash
@Value(value = "${v3.certPath}")
private String certPath;
//支付支付私钥pash
@Value(value = "${v3.keyPath}")
private String keyPath;
//支付证书pash
@Value(value = "${v3.p12}")
private String p12;
//平台证书pash
@Value(value = "${v3.platformcertificate}")
private String platformcertificate;
//支付证书序列号
@Value(value = "${v3.serialNo}")
private String serialNo;
//平台证书获取接口路径
@Value(value = "${v3.certificates}")
private String certificates;
//终端IP
@Value(value = "${wx.appserect}")
private String appserect;
//退款通知回调地址
@Value(value = "${wx.refundnotify.url}")
private String refundnotify_url;
//交易类型
private final String tradetype = "APP";
String authType ="WECHATPAY2-SHA256-RSA2048";
//APP下单支付路径
private final String payurl ="https://api.mch.weixin.qq.com/v3/pay/partner/transactions/app";
//H5下单支付路径
private final String payH5url ="https://api.mch.weixin.qq.com/v3/pay/partner/transactions/h5";
//Native下单支付路径
private final String payNativeurl ="https://api.mch.weixin.qq.com/v3/pay/partner/transactions/native";
//jspapi下单支付路径
private final String payJSAPIurl ="https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi";
//查询订单
private final String paySelectUrl= "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/id/{transaction_id}";
//申请退款
private final String payRefundurl ="https://api.mch.weixin.qq.com/secapi/pay/refund";
//查询退款
private final String RefundSelecturl="https://api.mch.weixin.qq.com/pay/refundquery";
}
Controller
微信支付接口,我是根据微信文档自己写的.
V3接口提供自动签名和验签自动获取最新平台证书的方法,算是目前最新的方法,
回调验签有参考这位老哥的微信支付回调参考博文
V2接口参考了我强哥的强哥的微信支付博文
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mc.common.constant.CommonConstant;
import com.mc.common.exception.BusinessException;
import com.mc.common.utils.ResultUtils;
import com.mc.common.vo.Result;
import com.mc.jewelrybespoke.app.service.AppSkuService;
import com.mc.jewelrybespoke.app.wxutil.*;
import com.mc.jewelrybespoke.app.po.WeiXinPayPo;
import com.mc.jewelrybespoke.app.utils.LocalDateTimeUtil;
import com.mc.jewelrybespoke.app.vo.AppSalesOrderHeaderVo;
import com.mc.jewelrybespoke.entity.SalesOrderDetail;
import com.mc.jewelrybespoke.entity.SalesOrderHeader;
import com.mc.jewelrybespoke.entity.Sku;
import com.mc.jewelrybespoke.query.SalesOrderDetailQuery;
import com.mc.jewelrybespoke.service.ISalesOrderDetailService;
import com.mc.jewelrybespoke.service.ISalesOrderHeaderService;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
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.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.*;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
@Api(tags = "5.0 微信支付对接")
@RestController
@RequestMapping("/weixin/pay")
public class WeiXinPayController {
@Autowired
private WXPayConfigEntity wxPayConfigEntity; //支付参数
@Autowired
WXPayUtil wxPayUtil;
@Autowired
private ISalesOrderHeaderService salesOrderHeaderService;
@Autowired
private ISalesOrderDetailService salesOrderDetailService;
@Autowired
private AppSkuService appSkuService;
@Autowired
private ClientCustomSSL clientCustomSSL; //证书
private long timestamp = LocalDateTimeUtil.getLocalDateTimeSec();
private String nonceStr = RandomStringUtils.randomAlphanumeric(32);
private static Lock lock = new ReentrantLock();
public static final String POST = "POST";
public static final String GET = "GET";
@ApiOperation(value = "微信App下单,返回微信预付款订单号", notes = "传入付款金额,订单号 ,商品信息")
@PostMapping("/create/wxorder")
public Object createWXOrder(@RequestBody WeiXinPayPo weiXinPayPo) {
String out_trade_no = weiXinPayPo.getOut_trade_no();//商品订单号
SalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(out_trade_no);
if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId())) {
return new ResultUtils<AppSalesOrderHeaderVo>().setErrorMsg("订单不存在!");
}
if (!CommonConstant.STATUS_DRAFT.equals(salesOrderHeader.getStatus())) {
return new ResultUtils<AppSalesOrderHeaderVo>().setErrorMsg("订单状态异常,请确认订单状态!");
}
SortedMap<String, Object> sortedMap = new TreeMap<String, Object>();
sortedMap.put("sp_appid", wxPayConfigEntity.getApppid()); //应用ID
sortedMap.put("sp_mchid", wxPayConfigEntity.getMch_id()); //商户号
sortedMap.put("sub_mchid", wxPayConfigEntity.getSub_mchid());//子商户号
sortedMap.put("description", "珠宝纯定制-" + weiXinPayPo.getBody()); //商品描述
sortedMap.put("out_trade_no", out_trade_no); //商户订单号
sortedMap.put("notify_url", wxPayConfigEntity.getNotify_url()); //通知回调地址
//总金额
int totalFee = weiXinPayPo.getTotal_fee().multiply(new BigDecimal(100)).intValue();//总金额 需要乘以100 ,微信支付默认单位分
Map<String, Object> map1 = new HashMap<>();
map1.put("total", totalFee);
map1.put("currency", "CNY");
//订单金额
sortedMap.put("amount", map1);
JSONObject json = new JSONObject(sortedMap);
String reqdata = json.toJSONString();
log.info(reqdata);
//校验支付路径
// HttpUrl httpurl = HttpUrl.parse(wxPayConfigEntity.getPayurl());
//String Sign = null;
// Sign = wxPayUtil.getToken(nonceStr,timestamp,POST,httpurl,reqdata);
//======设置请求头========
//不需要传入微信支付证书,将会自动更新
// 建议从Verifier中获得微信支付平台证书,或使用预先下载到本地的平台证书文件中
// X509Certificate wechatpayCertificate = verifier.getValidCertificate();
AutoUpdateCertificatesVerifier verifier = wxPayUtil.getAutoUpdateCertificatesVerifier();
//通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
CloseableHttpClient httpClient = wxPayUtil.getCloseableHttpClient();
HttpPost httpPost = new HttpPost(wxPayConfigEntity.getPayurl());
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();
httpPost.setConfig(requestConfig);
httpPost.addHeader("Content-Type", "application/json");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36");
String authType = wxPayConfigEntity.getAuthType();
//String authorization = wxPayUtil.getAuthorization(Sign, authType);
//httpPost.addHeader("Authorization",authorization);
StringEntity entity = new StringEntity(reqdata, "UTF-8");
httpPost.setEntity(entity);
//======发起请求========
CloseableHttpResponse response = null;
String result = null;
try {
response = httpClient.execute(httpPost);
result = EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
Header firstHeader = response.getFirstHeader("Request-ID");
String s = firstHeader.toString();
log.info("Request-ID:" + s);
int statusCode = response.getStatusLine().getStatusCode();
Map<String, Object> map = new HashMap<>();
if (statusCode == 200) {
Map<String, Object> resultMap = JSONObject.parseObject(result, Map.class);
String prepayId = (String) resultMap.get("prepay_id");//微信下单号
salesOrderHeader.setPayAmount(weiXinPayPo.getTotal_fee());
salesOrderHeader.setRefNo(prepayId);
salesOrderHeader.setPaid(OrdersOperateEnum.SUBMIT.code);
salesOrderHeader.setIsChannel(weiXinPayPo.getIsChannel());
salesOrderHeader.setPayType(weiXinPayPo.getPayType());
salesOrderHeaderService.update(salesOrderHeader); //根据付款单号修改订单表保存微信需要付款总金额,支付微信下单号
String sign2 = null;
try {
sign2 = wxPayUtil.getToken2(nonceStr, timestamp, prepayId, wxPayConfigEntity.getSub_apppid());
} catch (IOException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
map.put("return_code", "操作成功");
map.put("appid", wxPayConfigEntity.getSub_apppid());
map.put("partnerid", wxPayConfigEntity.getSub_mchid());
map.put("prepayid", prepayId);
map.put("package", "Sign=WXPay");
map.put("noncestr", nonceStr);
map.put("timestamp", timestamp);
map.put("paySign", sign2);
return map;
}
map.put("return_code", result);
return map;
}
/**
* 微信支付回调地址
*/
@ApiOperation(value = "微信支付回调地址")
@PostMapping("/callback/wxorder")
public Map<String, String> callBack(HttpServletRequest request) {
log.info("这里是微信支付回调!");
Map<String, String> map = new HashMap<>(2);
try {
PrivateKey privateKey = wxPayUtil.getPrivateKey(wxPayConfigEntity.getKeyPath());
//微信返回的请求体
String body = wxPayUtil.getRequestBody(request);
//如果验证签名序列号通过
if (wxPayUtil.verifiedSign(request, body, privateKey)) {
//微信支付通知实体类
WXPayNotifyVO wxPayNotifyVO = JSONObject.parseObject(body, WXPayNotifyVO.class);
//如果支付成功
if ("TRANSACTION.SUCCESS".equals(wxPayNotifyVO.getEvent_type())) {
//通知资源数据
WXPayNotifyVO.Resource resource = wxPayNotifyVO.getResource();
//解密后资源数据
String notifyResourceStr = PayResponseUtils.decryptResponseBody(wxPayConfigEntity.getApiV3Key(), resource.getAssociated_data(), resource.getNonce(), resource.getCiphertext());
//通知资源数据对象
NotifyResourceVO notifyResourceVO = JSONObject.parseObject(notifyResourceStr, NotifyResourceVO.class);
//查询订单 可以优化把订单放入redis
SalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(notifyResourceVO.getOut_trade_no());//根据付款单号查询订单表保存微信需要付款总金额
//支付完成时间
String success_time = notifyResourceVO.getSuccess_time();
LocalDateTime localDateTime = LocalDateTimeUtil.string3LocalDateTime(success_time);
//微信支付订单号
String transactionId = notifyResourceVO.getTransaction_id();
if (salesOrderHeader != null) {
//如果订单状态是提交状态
if (OrdersOperateEnum.SUBMIT.code == salesOrderHeader.getPaid() || OrdersOperateEnum.USERPAYING.code == salesOrderHeader.getPaid()) {
//如果付款成功
if ("SUCCESS".equals(notifyResourceVO.getTrade_state())) {
Integer total = notifyResourceVO.getAmount().getTotal();
Integer payer_total = notifyResourceVO.getAmount().getPayer_total();
int totalAmount = salesOrderHeader.getTotalAmount().multiply(new BigDecimal(100)).intValue();
if (totalAmount == total && payer_total == total) {
salesOrderHeader.setPayTime(localDateTime);
salesOrderHeader.setRefNo(transactionId);
salesOrderHeader.setPaid(OrdersOperateEnum.SUCCESS.code);
salesOrderHeader.setStatus(CommonConstant.STATUS_PENDING);
BigDecimal payAmount = BigDecimal.valueOf(payer_total).divide(BigDecimal.valueOf(100)).setScale(2, BigDecimal.ROUND_HALF_UP);
salesOrderHeader.setPayAmount(payAmount);
salesOrderHeader.setModifiedDate(LocalDateTimeUtil.getLocalDateTime());
salesOrderHeaderService.update(salesOrderHeader);
} else {
map.put("code", "ERROR_AMOUNT");
map.put("message", "支付金额与订单金额不匹配");
return map;
}
} else {
salesOrderHeader.setPayTime(localDateTime);
salesOrderHeader.setRefNo(transactionId);
//支付状态code
Integer oneCode = OrdersOperateEnum.getOneCode(notifyResourceVO.getTrade_state());
salesOrderHeader.setPaid(oneCode);
salesOrderHeader.setModifiedDate(LocalDateTimeUtil.getLocalDateTime());
salesOrderHeaderService.update(salesOrderHeader);
map.put("code", "SUCCESS");
map.put("message", "");
return map;
}
}
} else {
log.info("微信返回支付错误摘要:" + wxPayNotifyVO.getSummary());
}
//通知微信正常接收到消息,否则微信会轮询该接口
map.put("code", "SUCCESS");
map.put("message", "");
return map;
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (RequestWechatException e) {
e.printStackTrace();
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
map.put("code", "ERROR_SIGN");
map.put("message", "验签不通过");
return map;
}
/**
* 微信支付获取用户open_id的回调
*/
@ApiOperation(value = "微信支付获取用户open_id的回调")
@PostMapping("/callback/wxauth")
public String wxauth(@RequestParam("code") String code) {
String geturl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + wxPayConfigEntity.getApppid() + "&secret=" + wxPayConfigEntity.getAppserect() + "&code=" + code + "&grant_type=authorization_code";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(geturl, String.class);
log.info(result);
return result;
}
@ApiOperation(value = "微信H5下单,返回微信H5_url", notes = "传入付款金额,订单号 ,商品信息")
@PostMapping("/create/wxH5order")
public Object createWXH5Order(@RequestBody WeiXinPayPo weiXinPayPo) {
String out_trade_no = weiXinPayPo.getOut_trade_no();//商品订单号
SalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(out_trade_no);
if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId())) {
return new ResultUtils<AppSalesOrderHeaderVo>().setErrorMsg("订单不存在!");
}
if (!CommonConstant.STATUS_DRAFT.equals(salesOrderHeader.getStatus())) {
return new ResultUtils<AppSalesOrderHeaderVo>().setErrorMsg("订单状态异常,请确认订单状态!");
}
SortedMap<String, Object> sortedMap = new TreeMap<String, Object>();
sortedMap.put("sp_appid", wxPayConfigEntity.getApppid()); //应用ID
sortedMap.put("sp_mchid", wxPayConfigEntity.getMch_id()); //商户号
sortedMap.put("sub_mchid", wxPayConfigEntity.getSub_mchid());//子商户号
sortedMap.put("description", "珠宝纯定制-H5-" + weiXinPayPo.getBody()); //商品描述
sortedMap.put("out_trade_no", out_trade_no); //商户订单号
sortedMap.put("notify_url", wxPayConfigEntity.getNotify_url()); //通知回调地址
//总金额
int totalFee = weiXinPayPo.getTotal_fee().multiply(new BigDecimal(100)).intValue();//总金额 需要乘以100 ,微信支付默认单位分
Map<String, Object> map1 = new HashMap<>();
map1.put("total", totalFee);
map1.put("currency", "CNY");
//订单金额
sortedMap.put("amount", map1);
Map map2 = new HashMap();
map2.put("payer_client_ip", wxPayConfigEntity.getPayer_client_ip());
Map map3 = new HashMap();
map3.put("type", "iOS, Android, Wap");
map2.put("h5_info", map3);
//场景信息
sortedMap.put("scene_info", map2);
JSONObject json = new JSONObject(sortedMap);
String reqdata = json.toJSONString();
log.info(reqdata);
//校验支付路径
// HttpUrl httpurl = HttpUrl.parse(wxPayConfigEntity.getPayurl());
// 请求body参数
//String Sign = null;
// Sign = wxPayUtil.getToken(nonceStr,timestamp,POST,httpurl,reqdata);
//======设置请求头========
AutoUpdateCertificatesVerifier verifier = wxPayUtil.getAutoUpdateCertificatesVerifier();
//通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
CloseableHttpClient httpClient = wxPayUtil.getCloseableHttpClient();
HttpPost httpPost = new HttpPost(wxPayConfigEntity.getPayH5url());
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();
httpPost.setConfig(requestConfig);
httpPost.addHeader("Content-Type", "application/json");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36");
String authType = wxPayConfigEntity.getAuthType();
//String authorization = wxPayUtil.getAuthorization(Sign, authType);
//httpPost.addHeader("Authorization",authorization);
StringEntity entity = new StringEntity(reqdata, "UTF-8");
httpPost.setEntity(entity);
//======发起请求========
CloseableHttpResponse response = null;
String result = null;
try {
response = httpClient.execute(httpPost);
result = EntityUtils.toString(response.getEntity());
} catch (IOException e) {
e.printStackTrace();
}
Header[] allHeaders = response.getAllHeaders();
Header firstHeader = response.getFirstHeader("Request-ID");
String s = firstHeader.toString();
log.info("Request-ID:" + s);
int statusCode = response.getStatusLine().getStatusCode();
Map<String, Object> map = new HashMap<>();
if (statusCode == 200) {
Map<String, Object> resultMap = JSONObject.parseObject(result, Map.class);
String h5_url = (String) resultMap.get("h5_url");//支付跳转链接
log.info("h5_url:" + h5_url);
salesOrderHeader.setPayAmount(weiXinPayPo.getTotal_fee());
String prepay_id = PayResponseUtils.getParam(h5_url, "prepay_id");
salesOrderHeader.setRefNo(prepay_id);
salesOrderHeader.setPaid(OrdersOperateEnum.SUBMIT.code);
salesOrderHeader.setIsChannel(weiXinPayPo.getIsChannel());
salesOrderHeader.setPayType(weiXinPayPo.getPayType());
salesOrderHeaderService.update(salesOrderHeader); //根据付款单号修改订单表保存微信需要付款总金额,支付微信下单号
map.put("return_code", "操作成功");
map.put("h5_url", h5_url);
return map;
}
map.put("return_code", result);
return map;
}
@ApiOperation(value = "微信JSAPI下单", notes = "传入付款金额,订单号 ,商品信息")
@PostMapping("/create/wxJSAPIorder")
public Object createWXJSAPIOrder(@RequestBody WeiXinPayPo weiXinPayPo) {
String out_trade_no = weiXinPayPo.getOut_trade_no();//商品订单号
SalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(out_trade_no);
if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId())) {
return new ResultUtils<AppSalesOrderHeaderVo>().setErrorMsg("订单不存在!");
}
if (!CommonConstant.STATUS_DRAFT.equals(salesOrderHeader.getStatus())) {
return new ResultUtils<AppSalesOrderHeaderVo>().setErrorMsg("订单状态异常,请确认订单状态!");
}
SortedMap<String, Object> sortedMap = new TreeMap<String, Object>();
sortedMap.put("sp_appid", wxPayConfigEntity.getApppid()); //应用ID
sortedMap.put("sp_mchid", wxPayConfigEntity.getMch_id()); //商户号
sortedMap.put("sub_mchid", wxPayConfigEntity.getSub_mchid());//子商户号
sortedMap.put("description", "珠宝纯定制-JSAPI-" + weiXinPayPo.getBody()); //商品描述
sortedMap.put("out_trade_no", out_trade_no); //商户订单号
sortedMap.put("notify_url", wxPayConfigEntity.getNotify_url()); //通知回调地址
//总金额
int totalFee = weiXinPayPo.getTotal_fee().multiply(new BigDecimal(100)).intValue();//总金额 需要乘以100 ,微信支付默认单位分
Map<String, Object> map1 = new HashMap<>();
map1.put("total", totalFee);
map1.put("currency", "CNY");
//订单金额
sortedMap.put("amount", map1);
Map map2 = new HashMap();
map2.put("sp_openid", weiXinPayPo.getSpOpenid());
//支付者
sortedMap.put("payer", map2);
JSONObject json = new JSONObject(sortedMap);
String reqdata = json.toJSONString();
log.info(reqdata);
//校验支付路径
// HttpUrl httpurl = HttpUrl.parse(wxPayConfigEntity.getPayurl());
// 请求body参数
//String Sign = null;
// Sign = wxPayUtil.getToken(nonceStr,timestamp,POST,httpurl,reqdata);
//获取最新的平台证书
//AutoUpdateCertificatesVerifier verifier = wxPayUtil.getAutoUpdateCertificatesVerifier();
//通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
CloseableHttpClient httpClient = wxPayUtil.getCloseableHttpClient();
HttpPost httpPost = new HttpPost(wxPayConfigEntity.getPayJSAPIurl());
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();
httpPost.setConfig(requestConfig);
httpPost.addHeader("Content-Type", "application/json");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36");
//String authType = wxPayConfigEntity.getAuthType();
//String authorization = wxPayUtil.getAuthorization(Sign, authType);
//httpPost.addHeader("Authorization",authorization);
StringEntity entity = new StringEntity(reqdata, "UTF-8");
httpPost.setEntity(entity);
//======发起请求========
CloseableHttpResponse response = null;
String result = null;
try {
response = httpClient.execute(httpPost);
result = EntityUtils.toString(response.getEntity());
} catch (IOException e) {
e.printStackTrace();
}
Header firstHeader = response.getFirstHeader("Request-ID");
String s = firstHeader.toString();
log.info("Request-ID:" + s);
int statusCode = response.getStatusLine().getStatusCode();
Map<String, Object> map = new HashMap<>();
if (statusCode == 200) {
Map<String, Object> resultMap = JSONObject.parseObject(result, Map.class);
String prepayId = (String) resultMap.get("prepay_id");//微信下单号
salesOrderHeader.setPayAmount(weiXinPayPo.getTotal_fee());
salesOrderHeader.setRefNo(prepayId);
salesOrderHeader.setPaid(OrdersOperateEnum.SUBMIT.code);
salesOrderHeader.setIsChannel(weiXinPayPo.getIsChannel());
salesOrderHeader.setPayType(weiXinPayPo.getPayType());
salesOrderHeaderService.update(salesOrderHeader); //根据付款单号修改订单表保存微信需要付款总金额,支付微信下单号
String packages = "prepay_id=" + prepayId;
String sign2 = null;
try {
sign2 = wxPayUtil.getToken3(packages, timestamp, nonceStr, wxPayConfigEntity.getApppid());
} catch (IOException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
map.put("return_code", "操作成功");
map.put("appId", wxPayConfigEntity.getApppid());
map.put("timeStamp", timestamp);
map.put("nonceStr", nonceStr);
map.put("package", packages);
map.put("signType", "RSA");
map.put("paySign", sign2);
return map;
}
map.put("return_code", "操作失败");
return map;
}
@ApiOperation(value = "关闭订单")
@PostMapping("/create/closeOrder")
public Result closeOrder(@RequestParam("orderId") String orderId) throws RequestWechatException {
//查询订单 可以优化把订单放入redis
SalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(orderId);
//如果是付款 返回成功
if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId()) ||
salesOrderHeader.getStatus().equals(CommonConstant.STATUS_DELETE)) {
return new ResultUtils<>().setErrorMsg("订单不存在");
} else if (salesOrderHeader != null && OrdersOperateEnum.CLOSED.code == salesOrderHeader.getPaid()) {
return new ResultUtils<>().setSuccessMsg("订单已关闭");
} else {
//未付款 请求微信 获取订单状态
String url = String.format("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/", salesOrderHeader.getId(), "/close");
AutoUpdateCertificatesVerifier verifier = wxPayUtil.getAutoUpdateCertificatesVerifier();
//通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
CloseableHttpClient httpClient = wxPayUtil.getCloseableHttpClient();
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();
httpPost.setConfig(requestConfig);
httpPost.addHeader("Content-Type", "application/json");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36");
Map map = new HashMap();
map.put("sp_mchid", wxPayConfigEntity.getMch_id());
map.put("sub_mchid", wxPayConfigEntity.getSub_mchid());
JSONObject json = new JSONObject(map);
String reqdata = json.toJSONString();
StringEntity entity = new StringEntity(reqdata, "UTF-8");
httpPost.setEntity(entity);
HttpResponse response = null;
try {
response = httpClient.execute(httpPost);
} catch (IOException e) {
throw new RequestWechatException();
}
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 204) {
salesOrderHeader.setPaid(OrdersOperateEnum.CLOSED.code);
salesOrderHeaderService.update(salesOrderHeader);
return new ResultUtils<>().setErrorMsg("订单关闭成功");
}
return new ResultUtils<>().setErrorMsg("订单关闭失败");
}
}
@ApiOperation(value = "查验订单")
@GetMapping("/create/selectOrder")
@Transactional
public Result selectOrder(@RequestParam("orderId") String orderId) throws RequestWechatException {
// return CallbackResult.getSuccessResult("500");
//查询订单 可以优化把订单放入redis
SalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(orderId);
//如果是付款 返回成功
if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId()) ||
salesOrderHeader.getStatus().equals(CommonConstant.STATUS_DELETE)) {
return new ResultUtils<>().setErrorMsg("订单不存在");
} else if (salesOrderHeader != null && OrdersOperateEnum.SUCCESS.code == salesOrderHeader.getPaid()) {
return new ResultUtils<>().setSuccessMsg("付款成功");
} else {
//未付款 请求微信 获取订单状态
String url = String.format(wxPayConfigEntity.getPaySelectUrl(), salesOrderHeader.getRefNo(),
"?", "sp_mchid=", wxPayConfigEntity.getMch_id(), "&", "sub_mchid=", wxPayConfigEntity.getSub_mchid());
AutoUpdateCertificatesVerifier verifier = wxPayUtil.getAutoUpdateCertificatesVerifier();
//通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
CloseableHttpClient httpClient = wxPayUtil.getCloseableHttpClient();
HttpGet httpGet = new HttpGet(url);
httpGet.addHeader("Content-Type", "application/json;charset=UTF-8");
httpGet.addHeader("Accept", "application/json");
HttpResponse response = null;
try {
response = httpClient.execute(httpGet);
} catch (IOException e) {
throw new RequestWechatException();
}
//如果状态码为200,就是正常返回
if (response.getStatusLine().getStatusCode() == 200) {
String result = "";
try {
result = EntityUtils.toString(response.getEntity(), "UTF-8");
JSONObject jsonObject = JSON.parseObject(result);
NotifyResourceVO notifyResourceVO = JSONObject.parseObject(jsonObject.toString(), NotifyResourceVO.class);
//支付完成时间
String success_time = notifyResourceVO.getSuccess_time();
LocalDateTime localDateTime = LocalDateTimeUtil.string3LocalDateTime(success_time);
//微信支付订单号
String transactionId = notifyResourceVO.getTransaction_id();
//如果付款成功
if ("SUCCESS".equals(notifyResourceVO.getTrade_state())) {
Integer total = notifyResourceVO.getAmount().getTotal();
Integer payer_total = notifyResourceVO.getAmount().getPayer_total();
int totalAmount = salesOrderHeader.getTotalAmount().multiply(new BigDecimal(100)).intValue();
if (totalAmount == total && payer_total == total) {
salesOrderHeader.setPayTime(localDateTime);
salesOrderHeader.setRefNo(transactionId);
salesOrderHeader.setPaid(OrdersOperateEnum.SUCCESS.code);
salesOrderHeader.setStatus(CommonConstant.STATUS_PENDING);
BigDecimal payAmount = BigDecimal.valueOf(payer_total).divide(BigDecimal.valueOf(100)).setScale(2, BigDecimal.ROUND_HALF_UP);
salesOrderHeader.setPayAmount(payAmount);
salesOrderHeader.setModifiedDate(LocalDateTimeUtil.getLocalDateTime());
salesOrderHeaderService.update(salesOrderHeader);
return new ResultUtils<>().setSuccessMsg(notifyResourceVO.getTrade_state_desc());
} else {
return new ResultUtils<>().setSuccessMsg("支付金额与订单金额不匹配");
}
} else {
return new ResultUtils<>().setSuccessMsg(notifyResourceVO.getTrade_state_desc());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
return new ResultUtils<>().setSuccessMsg("500");
}
@ApiOperation(value = "微信退款")
@PostMapping("/create/refundOrder")
public Object refundOrder(@RequestBody WeiXinPayPo weiXinPayPo) throws Exception {
//查询订单 可以优化把订单放入redis
SalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(weiXinPayPo.getOut_trade_no());
//如果是付款 返回成功
if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId()) ||
salesOrderHeader.getStatus().equals(CommonConstant.STATUS_DELETE)) {
return new ResultUtils<>().setErrorMsg("订单不存在");
} else if (salesOrderHeader != null && OrdersOperateEnum.CLOSED.code == salesOrderHeader.getPaid()) {
return new ResultUtils<>().setSuccessMsg("订单已关闭");
} else {
SortedMap<String, String> sortedMap = new TreeMap<String, String>();
BigDecimal totalAmount = salesOrderHeader.getTotalAmount();
//订单总金额 金额单位为分 *100
String totalFee = totalAmount.multiply(new BigDecimal(100)).toString().replace(".00","");
sortedMap.put("appid", wxPayConfigEntity.getApppid()); //应用ID
sortedMap.put("mch_id", wxPayConfigEntity.getMch_id()); //商户号
sortedMap.put("sub_mch_id", wxPayConfigEntity.getSub_mchid());//子商户号
sortedMap.put("nonce_str", nonceStr);
sortedMap.put("transaction_id", salesOrderHeader.getRefNo());
//sortedMap.put("out_trade_no", salesOrderHeader.getId());
sortedMap.put("out_refund_no", salesOrderHeader.getId());
sortedMap.put("total_fee", totalFee);
sortedMap.put("refund_fee", totalFee);
sortedMap.put("notify_url",wxPayConfigEntity.getRefundnotify_url());
//生成签名
String sign = wxPayUtil.generateSignature(sortedMap, wxPayConfigEntity.getApiKey(),WXPayConfigEntity.SignType.MD5);
sortedMap.put("sign", sign);
log.info("发送给微信的参数:" + sortedMap);
String xml = WXPayUtil.getRequestXml(sortedMap);
log.info("===>>>发送给微信的xml:" + xml);
String string = clientCustomSSL.doRefund(wxPayConfigEntity.getPayRefundurl(), xml);
System.out.println("微信返回的xml===>>>" + string);
//AutoUpdateCertificatesVerifier verifier = wxPayUtil.getAutoUpdateCertificatesVerifier();
//因为退款是V2接口,所以先不用CloseableHttpClient自动签名处理发起请求,就用普通的HttpClient发起请求
boolean bool = wxPayUtil.isSignatureValid(string, wxPayConfigEntity.getApiKey());//验证签名是否正确
Map<String, String> map = new HashMap<String, String>();
if (bool) {
Map<String, String> resultMap = wxPayUtil.xmlToMap(string);
if ("SUCCESS".equals(resultMap.get("return_code"))) { //返回成功 退款成功
map.put("refund_fee", resultMap.get("refund_fee"));//退款金额
map.put("out_refund_no", resultMap.get("out_refund_no")); //退款单号
map.put("message", "SUCCESS");//退款成功
return map;
}
}
map.put("message", "faild"); //退款失败
return map;
}
}
@ApiOperation(value = "微信退款回调地址")
@PostMapping("/callback/refundOrder")
public Object refundCallBack(HttpServletRequest request) throws Exception {
String resultxml = wxPayUtil.getRequestBody(request);
Map<String,String> resultMap = wxPayUtil.xmlToMap(resultxml);
SortedMap<String, String> map = new TreeMap<String, String>(); //返回给微信的map
if ("SUCCESS".equals(resultMap.get("return_code"))) { //返回成功
//获取加密信息
String reqInfo = resultMap.get("req_info").toString();
byte[] asBytes = Base64.getDecoder().decode(reqInfo);
String md5Key = wxPayUtil.md5(wxPayConfigEntity.getApiKey()); //md5加密后的key
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec key = new SecretKeySpec(md5Key.toLowerCase().getBytes(), "AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] doFinal = cipher.doFinal(asBytes);
String result = new String(doFinal, "utf-8");
if(org.apache.commons.lang3.StringUtils.isNotEmpty(result)){
Map<String, String> res = wxPayUtil.xmlToMap(result);
String refundStatus = res.get("refund_status"); //退款状态
log.info("退款状态:-->"+refundStatus);
String outRefundNo = res.get("out_refund_no");//退款单号
log.info("退款单号:-->"+outRefundNo);
String refund_id = res.get("refund_id"); //微信退款单号 可以保存到数据库
// String settlementRefundFee = res.get("settlement_refund_fee");//退款金额
// String refundFee = res.get("refund_fee");//申请退款金额
// String successTime = res.get("success_time"); // 退款成功时间
if("SUCCESS".equals(refundStatus)){ //退款成功
Long refund_fee = Long.parseLong(res.get("refund_fee"));
log.info("退款金额:(单位:分)-->"+outRefundNo);
String refund_success_time = res.get("success_time");
log.info("退款时间:-->"+refund_success_time);
LocalDateTime refundsuccesstime =LocalDateTimeUtil.string2LocalDateTime(refund_success_time);
log.info("退款时间:-->"+refundsuccesstime.toString());
BigDecimal refundfee = BigDecimal.valueOf(refund_fee).divide(BigDecimal.valueOf(100)).setScale(2, BigDecimal.ROUND_HALF_UP);
SalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(outRefundNo);
salesOrderHeader.setRefundPrice(refundfee);
salesOrderHeader.setRefundTime(refundsuccesstime);
salesOrderHeader.setPaid(OrdersOperateEnum.REFUNDSUCCEE.code);
salesOrderHeader.setStatus(CommonConstant.STATUS_RETURN);
salesOrderHeaderService.update(salesOrderHeader);
SalesOrderDetailQuery sdetailQuery = new SalesOrderDetailQuery();
sdetailQuery.setSalesOrderHeader(salesOrderHeader.getId());
sdetailQuery.setStatus(CommonConstant.FINISHED_GOODS);
List<SalesOrderDetail> orderDetails = salesOrderDetailService.findAll(sdetailQuery);
try {
lock.lock();
for (SalesOrderDetail order : orderDetails){
Sku sku = appSkuService.get(order.getSkuId());
if (sku != null && !StringUtils.isEmpty(sku.getId())){
sku.setStock(sku.getStock()+order.getQty());
sku.setSalesCount(sku.getSalesCount()-order.getQty());
appSkuService.update(sku);
}
}
}catch (Exception e){
throw new BusinessException("库存修改异常!");
}finally {
lock.unlock();
}
map.put("return_code", "SUCCESS");
map.put("return_msg", "OK"); //正确返回给微信
return wxPayUtil.getRequestXml(map);
}else if("CHANGE".equals(refundStatus)){ //退款异常
}else if ("REFUNDCLOSE".equals(refundStatus)){ //退款关闭
}
}
}
map.put("return_code", "FAIL");
map.put("return_msg", "return_code不正确");
return wxPayUtil.getRequestXml(map);
}
@ApiOperation(value = "查询退款")
@PostMapping("/create/refundquery")
@Transactional
public Object refundquery(@RequestParam("orderId") String orderId) throws Exception {
//查询订单 可以优化把订单放入redis
SalesOrderHeader salesOrderHeader = salesOrderHeaderService.get(orderId);
//如果是付款 返回成功
if (salesOrderHeader == null || StringUtils.isEmpty(salesOrderHeader.getId()) ||
salesOrderHeader.getStatus().equals(CommonConstant.STATUS_DELETE)) {
return new ResultUtils<>().setErrorMsg("订单不存在");
} else if (salesOrderHeader != null && OrdersOperateEnum.REFUNDSUCCEE.code == salesOrderHeader.getPaid()) {
return new ResultUtils<>().setSuccessMsg("退款成功");
} else {
SortedMap<String, String> sortedMap = new TreeMap<String, String>();
sortedMap.put("appid", wxPayConfigEntity.getApppid()); //应用ID
sortedMap.put("mch_id", wxPayConfigEntity.getMch_id()); //商户号
sortedMap.put("sub_mch_id", wxPayConfigEntity.getSub_mchid());//子商户号
sortedMap.put("nonce_str", nonceStr);
sortedMap.put("transaction_id", salesOrderHeader.getRefNo());
//sortedMap.put("out_trade_no", salesOrderHeader.getId());
sortedMap.put("out_refund_no", salesOrderHeader.getId());
//生成签名
String sign = wxPayUtil.generateSignature(sortedMap, wxPayConfigEntity.getApiKey(),WXPayConfigEntity.SignType.MD5);
sortedMap.put("sign", sign);
log.info("发送给微信的参数:" + sortedMap);
String xml = WXPayUtil.getRequestXml(sortedMap);
log.info("===>>>发送给微信的xml:" + xml);
String string = clientCustomSSL.doRefund(wxPayConfigEntity.getRefundSelecturl(), xml);
System.out.println("微信返回的xml===>>>" + string);
//因为退款是V2接口,所以先不用CloseableHttpClient自动签名处理发起请求,就用普通的HttpClient发起请求
boolean bool = wxPayUtil.isSignatureValid(string, wxPayConfigEntity.getApiKey());//验证签名是否正确
Map<String, String> map = new HashMap<String, String>();
if (bool) {
Map<String, String> resultMap = wxPayUtil.xmlToMap(string);
if ("SUCCESS".equals(resultMap.get("return_code"))) { //返回成功 退款成功
String result_code = resultMap.get("result_code");//业务结果
String refund_id = resultMap.get("refund_id_0");//微信退款单号
String refund_status_0 = resultMap.get("refund_status_0");//退款状态
Long refund_fee = Long.parseLong(resultMap.get("refund_fee_0"));//退款金额
BigDecimal refundfee = BigDecimal.valueOf(refund_fee).divide(BigDecimal.valueOf(100)).setScale(2, BigDecimal.ROUND_HALF_UP);
salesOrderHeader.setPaid(OrdersOperateEnum.REFUNDSUCCEE.code);
salesOrderHeader.setRefundId(refund_id);
salesOrderHeader.setRefundPrice(refundfee);
map.put("result_code",result_code);
map.put("refund_id",refund_id);
map.put("refund_fee", refundfee.toString());//退款金额
map.put("out_refund_no", resultMap.get("out_refund_no_0")); //退款单号
map.put("refund_status_0",refund_status_0);
map.put("message", "SUCCESS");//退款成功
return map;
}
}
map.put("message", "faild"); //退款失败
return map;
}
}
}
WXPayUtil
特别需要注意的,我在这栽跟头栽了两天,如果你使用了下面的方法,就不需要自己再去生成签名了,一定要记住,不让会报401.
文档第一步就是生成签名,我就是跟着文档开发,后面又发现了下面的方法,也用上了,结果就是一直401,虽然我在用这个方法的时候就在想既然自动签名了那为什么我还自己生成签名呢的疑问下,但是一直都没舍得把自己生成签名的步骤删掉,直到我绝望了.
这个是微信支付提供的自动获取证书和自动签名验证的方法路径,我已经封装在下面的工具类里面了,这里是把地址贴出来
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Slf4j
@Component
public class WXPayUtil {
@Autowired
private WXPayConfigEntity wxPayConfigEntity; //支付参数
private static final int byteLowHalfFlag = 0x0F;
private static final int byteHalfShift = 4;
private final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
'F' };
/**
* 获取证书。
*
* @param filename 证书文件路径 (required)
* @return X509证书
*/
public X509Certificate getCertificate(String filename) throws IOException {
InputStream fis = new FileInputStream(filename);
BufferedInputStream bis = new BufferedInputStream(fis);
try {
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
cert.checkValidity();
return cert;
} catch (CertificateExpiredException e) {
throw new RuntimeException("证书已过期", e);
} catch (CertificateNotYetValidException e) {
throw new RuntimeException("证书尚未生效", e);
} catch (CertificateException e) {
throw new RuntimeException("无效的证书文件", e);
} finally {
bis.close();
}
}
/**
* 获取私钥。
*
* @param filename 私钥文件路径 (required)
* @return 私钥对象
*/
public PrivateKey getPrivateKey(String filename) throws IOException {
String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
try {
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
}
}
public String getToken(String nonceStr,long timestamp,String method, HttpUrl url, String body) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {
String message = buildMessage(method, url, timestamp, nonceStr, body);
String signature = sign(message.getBytes("utf-8"));
return "mchid=\"" + wxPayConfigEntity.getMch_id() + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "serial_no=\"" + wxPayConfigEntity.getSerialNo() + "\","
+ "signature=\"" + signature + "\"";
}
//生成签名
public String sign(byte[] message) throws NoSuchAlgorithmException, IOException, InvalidKeyException, SignatureException {
Signature sign = Signature.getInstance("SHA256withRSA");
PrivateKey privateKey1 = getPrivateKey(wxPayConfigEntity.getKeyPath());
sign.initSign(privateKey1);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
//待签名串
//生成签名串
public 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";
}
//生成Authorization
public String getAuthorization(String Sign,String authType) {
return authType.concat(" ").concat(Sign);
}
//APP支付二次生成签名
public String getToken2(String nonceStr,long timestamp,String prepayid,String subAppid) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {
String message2 = buildMessage2(prepayid, timestamp, nonceStr, subAppid);
String signature2 = sign(message2.getBytes("utf-8"));
return signature2;
}
//APP支付二次生成签名串
public String buildMessage2(String prepayid, long timestamp, String nonceStr, String subAppid) {
return subAppid + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ prepayid + "\n";
}
//JSAPI支付二次生成签名
public String getToken3(String packages, long timestamp, String nonceStr, String appId) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {
String message2 = buildMessage3(packages, timestamp, nonceStr, appId);
String signature2 = sign(message2.getBytes("utf-8"));
return signature2;
}
//JSAPI支付二次生成签名串
public String buildMessage3(String packages, long timestamp, String nonceStr, String appId) {
return appId + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ packages + "\n";
}
/**
* 获取请求文体
* @param request
* @return
* @throws IOException
*/
public String getRequestBody(HttpServletRequest request) throws IOException {
ServletInputStream stream = null;
BufferedReader reader = null;
StringBuffer sb = new StringBuffer();
try {
stream = request.getInputStream();
// 获取响应
reader = new BufferedReader(new InputStreamReader(stream));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
throw new IOException("读取返回支付接口数据流出现异常!");
} finally {
reader.close();
}
return sb.toString();
}
/**
* 验证微信签名
* @param request
* @param body
* @return
* @throws GeneralSecurityException
* @throws IOException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ParseException
*/
public boolean verifiedSign(HttpServletRequest request, String body,PrivateKey privateKey) throws GeneralSecurityException, ParseException, RequestWechatException {
//微信返回的证书序列号
String serialNo = request.getHeader("Wechatpay-Serial");
log.info("Wechatpay-Serial:---"+serialNo);
//微信返回的随机字符串
String nonceStr = request.getHeader("Wechatpay-Nonce");
log.info("Wechatpay-Nonce:---"+nonceStr);
//微信返回的时间戳
String timestamp = request.getHeader("Wechatpay-Timestamp");
log.info("Wechatpay-Timestamp:---"+timestamp);
//微信返回的签名
String wechatSign = request.getHeader("Wechatpay-Signature");
log.info("Wechatpay-Signature:---"+wechatSign);
//组装签名字符串
String signStr = Stream.of(timestamp, nonceStr, body)
.collect(Collectors.joining("\n", "", "\n"));
//自动获取最新平台证书
AutoUpdateCertificatesVerifier verifier = getAutoUpdateCertificatesVerifier();
X509Certificate certificate = verifier.getValidCertificate();
//SHA256withRSA签名
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(certificate);
signature.update(signStr.getBytes());
//返回验签结果
return signature.verify(Base64Utils.decodeFromString(wechatSign));
}
//自动获取最新平台证书
public AutoUpdateCertificatesVerifier getAutoUpdateCertificatesVerifier() {
PrivateKey privateKey1 = null;
try {
privateKey1 = getPrivateKey(wxPayConfigEntity.getKeyPath());
} catch (IOException e) {
e.printStackTrace();
}
AutoUpdateCertificatesVerifier verifier = null;
try {
verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(wxPayConfigEntity.getMch_id(), new PrivateKeySigner(wxPayConfigEntity.getSerialNo(), privateKey1)),
wxPayConfigEntity.getApiV3Key().getBytes("utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return verifier;
}
public CloseableHttpClient getCloseableHttpClient() {
PrivateKey privateKey1 = null;
AutoUpdateCertificatesVerifier verifier = null;
try {
privateKey1 = getPrivateKey(wxPayConfigEntity.getKeyPath());
verifier = getAutoUpdateCertificatesVerifier();
} catch (IOException e) {
e.printStackTrace();
}
//通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(wxPayConfigEntity.getMch_id(), wxPayConfigEntity.getSerialNo(), privateKey1)
.withValidator(new WechatPay2Validator(verifier)).build();
return httpClient;
}
//=======================微信支付V2工具类 因为V3接口暂时没有退款接口所以暂时还是需要用V2的退款接口=========================
/**
* 生成签名
*
* @param data 待签名数据
* @param key API密钥
* @return 签名
*/
public String generateSignature(final Map<String, String> data, String key) throws Exception {
return generateSignature(data, key, WXPayConfigEntity.SignType.MD5);
}
/**
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
*
* @param data 待签名数据
* @param key API密钥
* @param signType 签名方式
* @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key, WXPayConfigEntity.SignType signType) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals("sign")) {
continue;
}
if (data.get(k).toString().trim().length() > 0){
// 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).toString().trim()).append("&");
}
}
sb.append("key=").append(key);
if (WXPayConfigEntity.SignType.MD5.equals(signType)) {
return MD5(sb.toString()).toUpperCase();
} else if (WXPayConfigEntity.SignType.HMACSHA256.equals(signType)) {
return HMACSHA256(sb.toString(), key);
} else {
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
/**
* 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public static String MD5(String data) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 生成 HMACSHA256
*
* @param data 待处理数据
* @param key 密钥
* @return 加密结果
* @throws Exception
*/
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 转为xml格式
* @param parameters
* @return
*/
public static String getRequestXml(SortedMap<String,String> parameters){
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
String v = (String)entry.getValue();
if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {
sb.append("<"+k+">"+v+"</"+k+">");
}else {
sb.append("<"+k+">"+v+"</"+k+">");
}
}
sb.append("</xml>");
return sb.toString();
}
/**
* XML格式字符串转换为Map
*
* @param strXML XML字符串
* @return XML数据转换后的Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
try {
stream.close();
} catch (Exception ex) {
// do nothing
}
return data;
} catch (Exception ex) {
log.info("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
throw ex;
}
}
public String md5(String s) throws Exception {
try {
byte[] btInput = s.getBytes();
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
mdInst.update(btInput);
// 获得密文
byte[] md = mdInst.digest();
// 把密文转换成十六进制的字符串形式
int j = md.length;
char[] str = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[(byte0 >>> byteHalfShift) & byteLowHalfFlag];
str[k++] = hexDigits[byte0 & byteLowHalfFlag];
}
return new String(str);
} catch (Exception e) {
throw e;
}
}
public String md5(byte[] btInput) throws Exception {
try {
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
mdInst.update(btInput);
// 获得密文
byte[] md = mdInst.digest();
// 把密文转换成十六进制的字符串形式
int j = md.length;
char[] str = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> byteHalfShift & byteLowHalfFlag];
str[k++] = hexDigits[byte0 & byteLowHalfFlag];
}
return new String(str);
} catch (Exception e) {
throw e;
}
}
/**
* 判断签名是否正确
*
* @param xmlStr XML格式数据
* @param key API密钥
* @return 签名是否正确
* @throws Exception
*/
public boolean isSignatureValid(String xmlStr, String key) throws Exception {
Map<String, String> data = xmlToMap(xmlStr);
if (!data.containsKey("sign")) {
return false;
}
String sign = (String) data.get("sign");
return generateSignature(data, key).equals(sign);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
*
* @param data Map类型数据
* @param key API密钥
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
return isSignatureValid(data, key, WXPayConfigEntity.SignType.MD5);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。
*
* @param data Map类型数据
* @param key API密钥
* @param signType 签名方式
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key, WXPayConfigEntity.SignType signType) throws Exception {
if (!data.containsKey("sign")) {
return false;
}
String sign = (String) data.get("sign");
return generateSignature(data, key, signType).equals(sign);
}
}
PayResponseUtils
解密回调响应加密数据的工具类
import com.google.common.base.Splitter;
import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Map;
/**
* 微信付款返回数据工具类
* @author shenlu
* @date 2020/12/22 13:46
*/
public class PayResponseUtils {
/**
* 用微信V3密钥解密响应体.
*
* @param associatedData response.body.data[i].encrypt_certificate.associated_data
* @param nonce response.body.data[i].encrypt_certificate.nonce
* @param ciphertext response.body.data[i].encrypt_certificate.ciphertext
* @return the string
* @throws GeneralSecurityException the general security exception
*/
public static String decryptResponseBody(String v3key,String associatedData, String nonce, String ciphertext) {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec key = new SecretKeySpec(v3key.getBytes(StandardCharsets.UTF_8), "AES");
GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.DECRYPT_MODE, key, spec);
cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));
byte[] bytes;
try {
bytes = cipher.doFinal(Base64Utils.decodeFromString(ciphertext));
} catch (GeneralSecurityException e) {
throw new IllegalArgumentException(e);
}
return new String(bytes, StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new IllegalStateException(e);
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IllegalArgumentException(e);
}
}
public static String getParam(String url, String name) {
String params = url.substring(url.indexOf("?") + 1, url.length());
Map<String, String> split = Splitter.on("&").withKeyValueSeparator("=").split(params);
return split.get(name);
}
}
OrdersOperateEnum
/**
* 订单操作枚举类
* @author shenlu
* @date 2020/12/15 9:27
*/
public enum OrdersOperateEnum {
UNPAID(0,"待支付"),
SUBMIT(1,"提交支付"),
SUCCESS(2,"支付成功"),
REFUND(3,"转入退款"),
NOTPAY(4,"未支付"),
CLOSED(5,"已关闭"),
REVOKED(6,"已撤销(付款码支付)"),
USERPAYING(7,"用户支付中(付款码支付)"),
PAYERROR(8,"支付失败"),
REFUNDSUCCEE(9,"退款成功");
public int code;
public String msg;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
OrdersOperateEnum(Integer code, String msg){
this.code = code;
this.msg = msg;
}
/**
* 根据code获得msg
* @param code
* @return
*/
@SuppressWarnings("unlikely-arg-type")
public static String getValueByCode(Integer code){
for(OrdersOperateEnum platformFree:OrdersOperateEnum.values()){
if(code.equals(platformFree.getCode())){
return platformFree.getMsg();
}
}
return "未知";
}
public static OrdersOperateEnum getByCode(Integer code){
for(OrdersOperateEnum transactType : values()){
if (code.equals(transactType.getCode())) {
//获取指定的枚举
return transactType;
}
}
return null;
}
public static Integer getOneCode(String trade_state){
switch (trade_state){
case "SUBMIT" :
return OrdersOperateEnum.SUBMIT.code;
case "SUCCESS":
return OrdersOperateEnum.SUCCESS.code;
case "REFUND" :
return OrdersOperateEnum.REFUND.code;
case "NOTPAY" :
return OrdersOperateEnum.NOTPAY.code;
case "CLOSED" :
return OrdersOperateEnum.CLOSED.code;
case "REVOKED" :
return OrdersOperateEnum.REVOKED.code;
case "USERPAYING" :
return OrdersOperateEnum.USERPAYING.code;
default:
return OrdersOperateEnum.PAYERROR.code;
}
}
}
ClientCustomSSL
调用退款接口会用的生成请求的工具类
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
/**
* @ Author :
* @ Date :
**/
@Component
public class ClientCustomSSL {
@Autowired
private WXPayConfigEntity wxPayConfigEntity;
@SuppressWarnings("deprecation")
public String doRefund(String url,String data) throws Exception {
/**
* 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的
*/
KeyStore keyStore = KeyStore.getInstance("PKCS12");
/**
*
*wxconfig.SSLCERT_PATH : 指向你的证书的绝对路径,带着证书去访问
*/
//你下载的证书,解压后的apiclient_cert.p12这个文件全路径加全名
FileInputStream instream = new FileInputStream(new File(wxPayConfigEntity.getP12()));//P12文件目录
try {
/**
*
*
* 下载证书时的密码、默认密码是你的MCHID mch_id
* */
keyStore.load(instream, wxPayConfigEntity.getMch_id().toCharArray());//这里写密码.默认商户id
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
/**
*
* 下载证书时的密码、默认密码是你的MCHID mch_id
* */
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, wxPayConfigEntity.getMch_id().toCharArray())//这里也是写密码的,默认商户id
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
try {
HttpPost httpost = new HttpPost(url); // 设置响应头信息
httpost.addHeader("Connection", "keep-alive");
httpost.addHeader("Accept", "*/*");
httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httpost.addHeader("Host", "api.mch.weixin.qq.com");
httpost.addHeader("X-Requested-With", "XMLHttpRequest");
httpost.addHeader("Cache-Control", "max-age=0");
httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httpost);
try {
HttpEntity entity = response.getEntity();
String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
EntityUtils.consume(entity);
return jsonStr;
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
}
NotifyResourceVO
这里是微信回调通知里面的加密数据解密之后用来封装解密数据的Vo 参考的上面那位老哥的
/**
* 微信通知数据解密后对象
* @author shenlu
* @date 2020/12/18 11:17
*/
public class NotifyResourceVO {
/**
* 服务商公众号ID
*/
private String sp_appid;
/**
* 服务商户号
*/
private String sp_mchid;
/**
* 子商户号
*/
private String sub_mchid;
/**
* 商户订单号
*/
private String out_trade_no;
/**
* 微信支付订单号
*/
private String transaction_id;
/**
* 交易类型
*/
private String trade_type;
/**
* 交易状态
*/
private String trade_state;
/**
* 交易状态描述
*/
private String trade_state_desc;
/**
* 付款银行
*/
private String bank_type;
/**
* 支付完成时间
*/
private String success_time;
/**
* 支付者
*/
private Payer payer;
/**
* 订单金额
*/
private Amount amount;
/**
* 支付者
*/
public class Payer{
/**
* 用户标识
*/
private String openid;
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
}
/**
* 订单金额
*/
public class Amount{
/**
* 总金额
*/
private Integer total;
/**
* 用户支付金额
*/
private Integer payer_total;
/**
* 货币类型
*/
private String currency;
/**
* 用户支付币种
*/
private String payer_currency;
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
public Integer getPayer_total() {
return payer_total;
}
public void setPayer_total(Integer payer_total) {
this.payer_total = payer_total;
}
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
public String getPayer_currency() {
return payer_currency;
}
public void setPayer_currency(String payer_currency) {
this.payer_currency = payer_currency;
}
}
public String getSp_appid() {
return sp_appid;
}
public void setSp_appid(String sp_appid) {
this.sp_appid = sp_appid;
}
public String getSp_mchid() {
return sp_mchid;
}
public void setSp_mchid(String sp_mchid) {
this.sp_mchid = sp_mchid;
}
public String getSub_mchid() {
return sub_mchid;
}
public void setSub_mchid(String sub_mchid) {
this.sub_mchid = sub_mchid;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public String getTransaction_id() {
return transaction_id;
}
public void setTransaction_id(String transaction_id) {
this.transaction_id = transaction_id;
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
public String getTrade_state() {
return trade_state;
}
public void setTrade_state(String trade_state) {
this.trade_state = trade_state;
}
public String getTrade_state_desc() {
return trade_state_desc;
}
public void setTrade_state_desc(String trade_state_desc) {
this.trade_state_desc = trade_state_desc;
}
public String getBank_type() {
return bank_type;
}
public void setBank_type(String bank_type) {
this.bank_type = bank_type;
}
public String getSuccess_time() {
return success_time;
}
public void setSuccess_time(String success_time) {
this.success_time = success_time;
}
public Payer getPayer() {
return payer;
}
public void setPayer(Payer payer) {
this.payer = payer;
}
public Amount getAmount() {
return amount;
}
public void setAmount(Amount amount) {
this.amount = amount;
}
}
WXPayNotifyVO
这里是接收回调通知的Vo,
/**
* 接收微信支付通知VO
* @author shenlu
* @date
*/
public class WXPayNotifyVO {
/**
* 通知的唯一ID
*/
private String id;
/**
* 通知创建时间
*/
private String create_time;
/**
* 通知类型 支付成功通知的类型为TRANSACTION.SUCCESS
*/
private String event_type;
/**
* 通知数据类型 支付成功通知为encrypt-resource
*/
private String resource_type;
/**
* 通知资源数据
*/
private Resource resource;
/**
* 回调摘要
*/
private String summary;
/**
* 通知资源数据
*/
public class Resource{
/**
* 加密算法类型
*/
private String algorithm;
/**
* 数据密文
*/
private String ciphertext;
/**
* 附加数据
*/
private String associated_data;
/**
* 随机串
*/
private String nonce;
public String getAlgorithm() {
return algorithm;
}
public void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
}
public String getCiphertext() {
return ciphertext;
}
public void setCiphertext(String ciphertext) {
this.ciphertext = ciphertext;
}
public String getAssociated_data() {
return associated_data;
}
public void setAssociated_data(String associated_data) {
this.associated_data = associated_data;
}
public String getNonce() {
return nonce;
}
public void setNonce(String nonce) {
this.nonce = nonce;
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCreate_time() {
return create_time;
}
public void setCreate_time(String create_time) {
this.create_time = create_time;
}
public String getEvent_type() {
return event_type;
}
public void setEvent_type(String event_type) {
this.event_type = event_type;
}
public String getResource_type() {
return resource_type;
}
public void setResource_type(String resource_type) {
this.resource_type = resource_type;
}
public Resource getResource() {
return resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
}
**```
RequestWechatException**
异常类
```java
/**
* 请求微信异常,只有在请求微信地址不通时才会抛出该异常
* @author shenlu
* @date 2020/12/29 9:37
*/
public class RequestWechatException extends Exception {
private static final long serialVersionUID = -6477104653316285034L;
public RequestWechatException() {
super("请求微信异常");
}
}
WXPayXmlUtil
xml转换工具类
/**
*
*/
public final class WXPayXmlUtil {
public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
documentBuilderFactory.setXIncludeAware(false);
documentBuilderFactory.setExpandEntityReferences(false);
return documentBuilderFactory.newDocumentBuilder();
}
public static Document newDocument() throws ParserConfigurationException {
return newDocumentBuilder().newDocument();
}
}