微信支付签名后台APP支付错误
根据微信官网提供的SDK创建微信配置类:
public class WxConfig extends WXPayConfig {
private byte[] certData;
private IWXPayDomain iwxPayDomain;
public WxConfig() throws Exception {
// pay/apiclient_cert.p12商户密钥证书在classpath:下的地址
ClassPathResource resource = new ClassPathResource("pay/apiclient_cert.p12");
InputStream certStream = resource.getInputStream();
//使用commonio工具类提供的IO流转为字节数组的方法
certData = IOUtils.toByteArray(certStream);
certStream.read(this.certData);
certStream.close();
}
//微信开放平台APPID
public String getAppID() {
return "xx";
}
// 微信商户ID
public String getMchID() {
return "xx";
}
//微信商户密钥
public String getKey() {
return "xx";
}
public InputStream getCertStream() {
ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
return certBis;
}
public int getHttpConnectTimeoutMs() {
return 8000;
}
public int getHttpReadTimeoutMs() {
return 10000;
}
public IWXPayDomain getWXPayDomain() {
return iwxPayDomain;
}
public void setIwxPayDomain(IWXPayDomain iwxPayDomain) {
this.iwxPayDomain = iwxPayDomain;
}
}
public class WXPayDomain implements IWXPayDomain {
private String domain = WXPayConstants.DOMAIN_API;
private boolean primaryDomain = true;
public void report(String domain, long elapsedTimeMillis, Exception ex) {
this.domain = domain;
}
public DomainInfo getDomain(WXPayConfig config) {
DomainInfo domainInfo = new DomainInfo(domain,primaryDomain);
return domainInfo;
}
public boolean isPrimaryDomain() {
return primaryDomain;
}
public void setPrimaryDomain(boolean primaryDomain) {
this.primaryDomain = primaryDomain;
}
}
APP下单生成预支付id,返回Android和IOS端微信支付参数
wxpay.unifiedOrder(data);
统一下单接口会根据 WXPay的 signType字段选择加密方式,
WXPay wxpay = new WXPay(config);构造函数,其中useSandbox默认为false
if (useSandbox) {
this.signType = SignType.MD5; // 沙箱环境
}
else {
this.signType = SignType.HMACSHA256;
}
所以new WXPay(config);此构造函数unifiedOrder方法使用的加密方式是SignType.HMACSHA256;
微信预下单,微信支付参数,微信回调,需要保持加密方法一致,所以给Android和IOS参数是加密方法应该用
public static String generateSignature(final Map<String, String> data, String key, SignType signType);
//其中signType= SignType.HMACSHA256.
public static String generateSignature(final Map<String, String> data, String key) throws Exception ;
//该方法用的加密方式默认为MD5使用此方法,需要使用WXPay的其他构造函数使
//this.signType = SignType.MD5;
private ResponseBean<Map<String,String>> toWxPay(PayVo payVo) throws Exception {
WxConfig config = new WxConfig();
WXPayDomain myIWXPayDomain = new WXPayDomain();
config.setIwxPayDomain(myIWXPayDomain);
myIWXPayDomain.setPrimaryDomain(true);
WXPay wxpay = new WXPay(config);
IPayOrderService iPayOrderService = payOrderServiceMap.get(ORDERMAPSERVICE.get(payVo.getOrderType()));
ResponseBean<Map<String, String>> wxPayMap = iPayOrderService.createWxPayMap(payVo.getOrderId());
if (wxPayMap.getCode() != Constant.RESP_APP_CODE_SUCCESS) {
return wxPayMap;
}
Map<String, String> data = wxPayMap.getData();
data.put("fee_type", "CNY");
data.put("spbill_create_ip", IPUtil.getIPAddress(httpServletRequest));
data.put("notify_url", HttpRequestUtil.getURLRemoveServletPath(httpServletRequest)+"/pay/wxpaynotify");
data.put("trade_type", "APP"); // 此处指定为APP支付
/**
* WXPay wxpay = new WXPay(config);通过该构造方法useSandbox=false
* 该方法会unifiedOrder方法生成预订单的时候会使用HMACSHA256加密方式
*/
Map<String, String> resp = wxpay.unifiedOrder(data);
if (Constant.WX_SUCCESS.equals(resp.get("return_code")) && Constant.WX_SUCCESS.equals(resp.get("result_code"))) {
Map<String, String> resData = new HashMap<>();
resData.put("appid",config.getAppID());
resData.put("partnerid",config.getMchID());
resData.put("prepayid",resp.get("prepay_id"));
/**
* package为Android端关键字所以用package1代替
*/
resData.put("package","Sign=WXPay");
resData.put("noncestr",resp.get("nonce_str"));
resData.put("timestamp", DateUtil.getTimeStamp());
/**
* 正式环境下单默认为HMACSHA256
* 通过generateSignature
*/
String sign = WXPayUtil.generateSignature(resData, config.getKey(), WXPayConstants.SignType.HMACSHA256);
resData.put("sign",sign);
resData.put("package1","Sign=WXPay");
return new ResponseBean<>().successApp(resData);
}else {
ResponseBean<Map<String,String>> bean = new ResponseBean<>();
bean.setCode(-1);
bean.setMsg(resp.get("err_code_des"));
return bean;
}
}
验签接口
验签时加密方法要和生成微信prepayid和微信支付参数加密算法一致
public static boolean isSignatureValid(Map<String, String> data, String key) 默认使用MD5
public static boolean isSignatureValid(Map<String, String> data, String key, SignType signType)可以选填:
public enum SignType {
MD5, HMACSHA256
}
根据前边的接口可以确定需要验签时需要使用第二种方法并且入参signType=HMACSHA256
/**
* 微信统一支付回调
* @param request
* @param response
* @throws IOException
*/
@RequestMapping("/wxpaynotify")
public void wxpaynotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
log.debug("===============微信APP支付===============");
String result = "FAIL";
String msg = "";
String xml = HttpRequestUtil.readXmlFromRequest(request);
log.info("===============微信回调xml数据===============\n{}", xml);
try {
Map<String, String> wxnotify = WXPayUtil.xmlToMap(xml.toString());
WxConfig config = new WxConfig();
/**
* 下单,订单,验签 加密方式统一使用HMACSHA256
*/
boolean signatureValid = WXPayUtil.isSignatureValid(wxnotify, config.getKey(), WXPayConstants.SignType.HMACSHA256);
if (signatureValid && "SUCCESS".equalsIgnoreCase(wxnotify.get("return_code"))) {
boolean pay = payService.dosuccessPay(wxnotify, Constant.PAY_WX);
if (pay) {
result = "SUCCESS";
msg = "OK";
} else {
msg = wxnotify.get("return_msg");
}
}
} catch (Exception e) {
log.error("微信解析认证失败:{}", e.getMessage());
}
response.setHeader("Content-Type", "text/html");
response.getWriter().write("<xml><return_code><![CDATA[" + result + "]]></return_code><return_msg><![CDATA[" + msg + "]]></return_msg></xml>");
}