1、导入maven依赖
com.github.wxpay
wxpay-sdk
0.0.3
2、调用官方SDK获取预支付订单等参数(注意生签名的参数顺序不能乱,如果生成的签名对但是报签名错误可以把密钥全改为大写试试。微信支付接口签名校验工具:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=20_1)
public Result unifiedOrder(Map param, HttpServletRequest request) {
Map returnMap = new HashMap<>();
Map responseMap = new HashMap<>();
Map data = new HashMap<>();
String orderNo = param.get("orderNo").toString();
try {
//支付参数
WXPay wxpay = new WXPay(wxPayAppConfig);
data.put("appid", wxPayAppConfig.getAppID());
data.put("body", "订单支付");
data.put("mch_id", wxPayAppConfig.getMchID());
data.put("notify_url", wxPayAppConfig.getPayNotifyUrl());//异步通知地址(请注意必须是外网)
data.put("out_trade_no", orderNo);
data.put("spbill_create_ip", getIpAddress(request)); //自己的服务器IP地址
Double amount = Double.parseDouble(param.get("amount").toString());
data.put("total_fee", String.valueOf((int) (amount * 100)));
data.put("trade_type", "APP");//交易类型
String s = WXPayUtil.generateSignature(data, wxPayAppConfig.getKey()); //签名
data.put("sign", s);//签名
//使用官方API请求预付订单
Map resultMap = wxpay.unifiedOrder(data);
//获取返回码
String returnCode = resultMap.get("return_code");
//若返回码为SUCCESS,则会返回一个result_code,再对该result_code进行判断
if ("SUCCESS".equals(returnCode)) {
String resultCode = resultMap.get("result_code");
if ("SUCCESS".equals(resultCode)) {
responseMap = resultMap;
}
}
if (responseMap == null || responseMap.isEmpty()) {
return ResultUtil.error("获取预支付交易会话标识失败");
}
// 3、签名生成算法
Long time = System.currentTimeMillis() / 1000;
String timestamp = time.toString();
returnMap.put("appid", wxPayAppConfig.getAppID());
returnMap.put("partnerid", wxPayAppConfig.getMchID());
returnMap.put("prepayid", responseMap.get("prepay_id"));
returnMap.put("noncestr", responseMap.get("nonce_str"));
returnMap.put("timestamp", timestamp);
returnMap.put("package", "Sign=WXPay");
returnMap.put("sign", WXPayUtil.generateSignature(returnMap, wxPayAppConfig.getKey()));//微信支付签名
return ResultUtil.success(returnMap);
} catch (Exception e) {
logger.error("订单号:{},错误信息:{}", orderNo, e.getStackTrace());
return ResultUtil.error("微信支付统一下单失败");
}
}
3、回调(注:如果回调成功就不会再次回调,否则会进行多次回调)
@RequestMapping(value = "/notify")
@ResponseBody
public String payNotify(HttpServletRequest request, HttpServletResponse response) {
InputStream is = null;
String xmlBack = " ";
try {
is = request.getInputStream();
// 将InputStream转换成String
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
xmlBack = wxPayService.payBack(sb.toString());
// 告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可
response.getWriter().write(xmlBack);
} catch (Exception e) {
logger.error("微信手机支付回调通知失败:", e);
}
finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
/**
* @param notifyData 异步通知后的XML数据
* @return
*/
public String payBack(String notifyData) {
WXPay wxpay = new WXPay(wxPayAppConfig);
String xmlBack = " ";
Map notifyMap = new HashMap<>();
try {
notifyMap = WXPayUtil.xmlToMap(notifyData); // 调用官方SDK转换成map类型数据
if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {//验证签名是否有效,有效则进一步处理
String return_code = notifyMap.get("return_code");//状态
String out_trade_no = notifyMap.get("out_trade_no");//商户订单号
String total_fee = notifyMap.get("total_fee");//订单总金额,单位为分
if (return_code.equals("SUCCESS")) {
//业务代码
}
return xmlBack;
} else {
// 签名错误,如果数据里没有sign字段,也认为是签名错误
//失败的数据要不要存储?
logger.error("手机支付回调通知签名错误");
xmlBack = "" + " " + " " + " ";
return xmlBack;
}
} catch (Exception e) {
logger.error("手机支付回调通知失败", e);
xmlBack = "" + " " + " " + " ";
}
return xmlBack;
}
其他配置信息
#微信APP支付参数
#wxpayconfig:
#商户应用appId
wxpayconfig.appid=
#商户ID
wxpayconfig.mchid=
#商户key:api秘钥(32位)
wxpayconfig.key=
#回调接口
wxpayconfig.payNotifyUrl=
/**
* 获取服务器IP工具类
*/
public static String getIpAddress(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
/**
* 微信参数配置
*/
package com.test.service.pay;
import com.github.wxpay.sdk.WXPayConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.InputStream;
@Component
@ConfigurationProperties(prefix = "wxpayconfig")
public class WxPayAppConfig implements WXPayConfig {
/**
* appID
*/
private String appID;
/**
* 商户号
*/
private String mchID;
/**
* API 密钥
*/
private String key;
/**
* API证书绝对路径 (本项目放在了 resources/cert/wxpay/apiclient_cert.p12")
*/
private String certPath;
/**
* HTTP(S) 连接超时时间,单位毫秒
*/
private int httpConnectTimeoutMs = 8000;
/**
* HTTP(S) 读数据超时时间,单位毫秒
*/
private int httpReadTimeoutMs = 10000;
/**
* 微信支付异步通知地址
*/
private String payNotifyUrl;
/**
* 微信退款异步通知地址
*/
private String refundNotifyUrl;
/**
* 获取商户证书内容(这里证书需要到微信商户平台进行下载)
*
* @return 商户证书内容
*/
@Override
public InputStream getCertStream() {
InputStream certStream =getClass().getClassLoader().getResourceAsStream(certPath);
return certStream;
}
public String getAppID() {
return appID;
}
public void setAppID(String appID) {
this.appID = appID;
}
public String getMchID() {
return mchID;
}
public void setMchID(String mchID) {
this.mchID = mchID;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getCertPath() {
return certPath;
}
public void setCertPath(String certPath) {
this.certPath = certPath;
}
public int getHttpConnectTimeoutMs() {
return httpConnectTimeoutMs;
}
public void setHttpConnectTimeoutMs(int httpConnectTimeoutMs) {
this.httpConnectTimeoutMs = httpConnectTimeoutMs;
}
public int getHttpReadTimeoutMs() {
return httpReadTimeoutMs;
}
public void setHttpReadTimeoutMs(int httpReadTimeoutMs) {
this.httpReadTimeoutMs = httpReadTimeoutMs;
}
public String getPayNotifyUrl() {
return payNotifyUrl;
}
public void setPayNotifyUrl(String payNotifyUrl) {
this.payNotifyUrl = payNotifyUrl;
}
public String getRefundNotifyUrl() {
return refundNotifyUrl;
}
public void setRefundNotifyUrl(String refundNotifyUrl) {
this.refundNotifyUrl = refundNotifyUrl;
}
}