1.pom.xml(引入微信支付sdk)
com.github.wxpay
wxpay-sdk
0.0.3
2.Wxpay-Config.properties
# 微信公众账号或开放平台APP的唯一标识
app_id =
# 财付通平台的商户账号
partner =
# 财付通平台的商户密钥
partner_key =
#下面地址需要公网可以访问
# 微信支付结果通知的回调地址
notify_URL = http://qq1334713380.xicp.net:36576/pay/wxPayController/weChatPayNotify
# 微信退款异步回调
refund_notify_URL= http://qq1334713380.xicp.net:36576/pay/wxPayController/refundResult
3.WxPayConfigImpl.java
import com.github.wxpay.sdk.WXPayConfig;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class WxPayConfigImpl implements WXPayConfig {
/**
* 加载证书 这里证书需要到微信商户平台进行下载
*/
private byte[] certData;
private static WxPayConfigImpl INSTANCE;
/**
* 配置文件加载
*/
private static Properties prop;
/**
* 配置文件名称
*/
public static String CONFIG_FILE = "WxPay-Config.properties";
static {
initProp();
}
/**
* 私有构造,使其通过公共方法创建对象
*
* @throws IOException
*/
private WxPayConfigImpl() {
InputStream certStream = WxPayConfigImpl.class.getClassLoader().getResourceAsStream("apiclient_cert.p12");
try {
certStream.read(this.certData);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
certStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 获得WxPayConfigImpl实例
*
* @return WxPayConfigImpl实例
* @throws IOException
*/
public static WxPayConfigImpl getInstance() {
if (INSTANCE == null) {
synchronized (WxPayConfigImpl.class) {
if (INSTANCE == null) {
INSTANCE = new WxPayConfigImpl();
}
}
}
return INSTANCE;
}
/**
* 初始化配置值
*/
private static void initProp() {
prop = new Properties();
try {
synchronized (prop) {
InputStream inputStream = AlipayConfig.class.getClassLoader().getResourceAsStream(CONFIG_FILE);
prop.load(inputStream);
inputStream.close();
}
} catch (IOException e) {
log.error("日志 =============》: 配置文件WxPay-Config.properties找不到");
e.printStackTrace();
}
}
/**
* 获取配置文件信息
*
* @return
*/
public static Properties getProperties() {
if (prop == null) {
initProp();
}
return prop;
}
@Override
public String getAppID() {
return prop.getProperty("app_id");
}
@Override
public String getMchID() {
return prop.getProperty("partner");
}
@Override
public String getKey() {
return prop.getProperty("partner_key");
}
@Override
public InputStream getCertStream() {
ByteArrayInputStream certBis;
certBis = new ByteArrayInputStream(this.certData);
return certBis;
}
@Override
public int getHttpConnectTimeoutMs() {
return 2000;
}
@Override
public int getHttpReadTimeoutMs() {
return 10000;
}
public String getSpbillCreateIp() throws UnknownHostException {
return InetAddress.getLocalHost().getHostAddress();
}
4.HttpClient.java
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
@SuppressWarnings("all")
public class HttpClient {
private String url;
private Map param;
private int statusCode;
private String content;
private String xmlParam;
private boolean isHttps;
public boolean isHttps() {
return isHttps;
}
public void setHttps(boolean isHttps) {
this.isHttps = isHttps;
}
public String getXmlParam() {
return xmlParam;
}
public void setXmlParam(String xmlParam) {
this.xmlParam = xmlParam;
}
public HttpClient(String url, Map param) {
this.url = url;
this.param = param;
}
public HttpClient(String url) {
this.url = url;
}
public void setParameter(Map map) {
param = map;
}
public void addParameter(String key, String value) {
if (param == null) {
param = new HashMap();
}
param.put(key, value);
}
public void post() throws ClientProtocolException, IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException {
HttpPost http = new HttpPost(url);
setEntity(http);
execute(http);
}
public void put() throws ClientProtocolException, IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException {
HttpPut http = new HttpPut(url);
setEntity(http);
execute(http);
}
public void get() throws ClientProtocolException, IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException {
if (param != null) {
StringBuilder url = new StringBuilder(this.url);
boolean isFirst = true;
for (String key : param.keySet()) {
if (isFirst) {
url.append("?");
}else{
url.append("&");}
url.append(key).append("=").append(param.get(key));
}
this.url = url.toString();
}
HttpGet http = new HttpGet(url);
execute(http);
}
/**
* set http post,put param
*/
private void setEntity(HttpEntityEnclosingRequestBase http) {
if (param != null) {
List nvps = new LinkedList();
for (String key : param.keySet()) {
// 参数
nvps.add(new BasicNameValuePair(key, param.get(key)));
}
http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数
}
if (xmlParam != null) {
http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
}
}
private void execute(HttpUriRequest http) throws ClientProtocolException,
IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
//退款时需要加载证书文件,详情见微信支付,证书文件使用
//KeyStore keyStore = KeyStore.getInstance("PKCS12");
//keyStore.load(HttpClient.class.getClassLoader().getResourceAsStream("apiclient_cert.p12"), WxPayConstant.PARTNER.toCharArray());
CloseableHttpClient httpClient = null;
try {
if (isHttps) {
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(null, new TrustStrategy() {
// 信任所有
@Override
public boolean isTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
return true;
}
})
//.loadKeyMaterial(keyStore,WxPayConstant.PARTNER.toCharArray())
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext);
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
.build();
} else {
httpClient = HttpClients.createDefault();
}
CloseableHttpResponse response = httpClient.execute(http);
try {
if (response != null) {
if (response.getStatusLine() != null) {
statusCode = response.getStatusLine().getStatusCode();
}
HttpEntity entity = response.getEntity();
// 响应内容
content = EntityUtils.toString(entity, Consts.UTF_8);
}
} finally {
response.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
httpClient.close();
}
}
public int getStatusCode() {
return statusCode;
}
public String getContent() throws ParseException, IOException {
return content;
}
}
5.WxPayServiceImpl.java
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@SuppressWarnings("ALL")
@Service
@Transactional(rollbackFor = Exception.class)
public class WxPayServiceImpl implements IWxPayService {
private WxPayConfigImpl wxPayConfig;
private WXPay wxpay;
private static WxPayServiceImpl INSTANCE;
@Autowired
private OrderDao orderDao;
@Autowired
private OrderService orderService;
@Autowired
private PayOrderFlowDao payOrderFlowDao;
@Autowired
private UserDao userDao;
private Properties prop = WxPayConfigImpl.getProperties();
public WxPayServiceImpl() {
wxPayConfig = WxPayConfigImpl.getInstance();
wxpay = new WXPay(wxPayConfig);
}
/**
* 生成微信支付二维码
*
* @param outTradeNo 订单号
* @return
*/
@Override
public Map unifiedOrder(String outTradeNo) {
//订单号合法性校验
Order order = PayCommonUtil.checkOutTradeNo(outTradeNo, true);
String totalMoney = (order.getPrice().multiply(new BigDecimal("100"))) + "";
//组装请求参数
Map wxPayRequestMap = this.addWxPayParam(outTradeNo);
//商品描述
wxPayRequestMap.put("body", "绿马甲灵活用工平台");
//标价金额
wxPayRequestMap.put("total_fee", totalMoney.substring(0, totalMoney.lastIndexOf(".")));
//终端IP
try {
wxPayRequestMap.put("spbill_create_ip", wxPayConfig.getSpbillCreateIp());
} catch (UnknownHostException e) {
e.printStackTrace();
}
//支付结果通知的回调地址
wxPayRequestMap.put("notify_url", prop.getProperty("notify_URL"));
//交易方式
wxPayRequestMap.put("trade_type", WxPayConstant.NATIVE);
//将要发送的参数,转换为xml
try {
String xmlParam = WXPayUtil.generateSignedXml(wxPayRequestMap, wxPayConfig.getKey());
Map resultMap = this.getContent(xmlParam, "https://api.mch.weixin.qq.com/pay/unifiedorder");
Map responseMap = new HashMap(16);
responseMap.put("codeUrl", resultMap.get("code_url"));
responseMap.put("outTradeNo", outTradeNo);
responseMap.put("totalFee", order.getPrice());
System.out.println(resultMap);
return responseMap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 查询支付状态
*
* @param outTradeNo 订单号
* @return
*/
@Override
public Result orderQuery(String outTradeNo) {
//订单号合法性校验
Order order = PayCommonUtil.checkOutTradeNo(outTradeNo, true);
//组装请求参数
Map wxPayRequestMap = this.addWxPayParam(outTradeNo);
//将要发送的参数,转换为xml
String xmlParam = null;
try {
xmlParam = WXPayUtil.generateSignedXml(wxPayRequestMap, wxPayConfig.getKey());
int i = 0;
//轮询
while (true) {
//查询支付状态
Map map = this.getContent(xmlParam, "https://api.mch.weixin.qq.com/pay/orderquery");
if (map == null) {
return ResultUtil.error("支付错误");
} else if ("FAIL".equals(map.get("trade_state"))) {
return ResultUtil.error((String) map.get("return_msg"));
}
if ("SUCCESS".equals(map.get("trade_state"))) {
return ResultUtil.success("支付成功");
} else if ("CLOSED".equals(map.get("trade_state"))) {
return ResultUtil.error("订单已关闭");
} else if ("PAYERROR".equals(map.get("trade_state"))) {
return ResultUtil.error("支付失败");
}
//间隔三秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
//防止无限的轮询下去,增加一个计数器
if (i >= 20) {
return ResultUtil.error("二维码超时");
}
}
} catch (Exception e) {
e.printStackTrace();
}
return ResultUtil.error("支付错误");
}
/**
* 关闭订单
*
* @param outTradeNo 订单号
* @return
*/
@Override
public Result closeOrder(String outTradeNo) {
//组装请求参数
Map wxPayRequestMap = this.addWxPayParam(outTradeNo);
//将要发送的参数,转换为xml
try {
String xmlParam = WXPayUtil.generateSignedXml(wxPayRequestMap, wxPayConfig.getKey());
Map map = this.getContent(xmlParam, "https://api.mch.weixin.qq.com/pay/closeorder");
if (map == null) {
return ResultUtil.error("支付关闭错误");
}
if (WxPayConstant.SUCCESS.equals(map.get("return_code"))) {
Result result = this.orderQuery(outTradeNo);
return result;
} else {
if (WxPayConstant.ORDERPAID.equals(map.get("err_code"))) {
return ResultUtil.error("订单已支付");
} else if (WxPayConstant.ORDERCLOSED.equals(map.get("err_code"))) {
return ResultUtil.error("订单已关闭");
} else if (WxPayConstant.SYSTEMERROR.equals(map.get("err_code"))) {
return ResultUtil.error("系统错误");
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 退款
* @param refund 退款实体类
* @return
*/
@Override
public Map refund(OrderRefund refund) {
String totalMoney = (refund.getPayPrice().multiply(new BigDecimal("100"))) + "";
String refundMoney = (refund.getRufundPrice().multiply(new BigDecimal("100"))) + "";
//组装请求参数
Map wxPayRequestMap = this.addWxPayParam(refund.getOrderId().getOrderUUID());
//退款订单号
wxPayRequestMap.put("out_refund_no", refund.getOrderRefundId());
//订单金额
wxPayRequestMap.put("total_fee", totalMoney.substring(0, totalMoney.lastIndexOf(".")));
//退款金额
wxPayRequestMap.put("refund_fee", refundMoney.substring(0, refundMoney.lastIndexOf(".")));
//退款结果通知的回调地址
wxPayRequestMap.put("notify_url", prop.getProperty("refund_notify_URL"));
//将要发送的参数,转换为xml
try {
String xmlParam = WXPayUtil.generateSignedXml(wxPayRequestMap, wxPayConfig.getKey());
return this.getContent(xmlParam, "https://api.mch.weixin.qq.com/secapi/pay/refund");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 查询退款
*
* @param outTradeNo 订单号
* @return
*/
@Override
public Map refundQuery(String outTradeNo) {
//组装请求参数
Map wxPayRequestMap = this.addWxPayParam(outTradeNo);
//将要发送的参数,转换为xml
try {
String xmlParam = WXPayUtil.generateSignedXml(wxPayRequestMap, wxPayConfig.getKey());
return this.getContent(xmlParam, "https://api.mch.weixin.qq.com/pay/refundquery");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 组装调用微信支付接口所需的基本参数
*
* @param outTradeNo
* @return
*/
private Map addWxPayParam(String outTradeNo) {
//组装请求参数
Map wxPayRequestMap = new HashMap(16);
//公众账号ID
wxPayRequestMap.put("appid", wxPayConfig.getAppID());
//商户号
wxPayRequestMap.put("mch_id", wxPayConfig.getMchID());
//随机字符串
wxPayRequestMap.put("nonce_str", WXPayUtil.generateNonceStr());
//商户订单号
wxPayRequestMap.put("out_trade_no", outTradeNo);
return wxPayRequestMap;
}
/**
* 发送支付请求,返回结果
*
* @param xmlParam 请求参数
* @param url 请求url
* @return
* @throws Exception
*/
private Map getContent(String xmlParam, String url) throws Exception {
//发送请求
HttpClient httpClient = new HttpClient(url);
httpClient.setHttps(true);
httpClient.setXmlParam(xmlParam);
httpClient.post();
//获得结果
String content = httpClient.getContent();
//使用工具类解析要返回的map
Map responseMap = WXPayUtil.xmlToMap(content);
return responseMap;
}
}
6.WxPayDecodeUtil.java
import cn.exrick.xboot.lvmajia.pay.config.WxPayConfigImpl;
import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class WxPayDecodeUtil {
private static final String ALGORITHM = "AES";
private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS7Padding";
private static SecretKeySpec secretKey = new SecretKeySpec(MD5Util.MD5Encode(WxPayConfigImpl.getInstance().getKey(), "UTF-8").toLowerCase().getBytes(), ALGORITHM);
//退款结果解密
public static String decryptData(String base64Data) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64Utils.decode(base64Data.getBytes())));
}
}
7.WxPayNotifyController.java
import cn.hutool.core.util.StrUtil;
import com.github.wxpay.sdk.WXPayUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
@RestController
@RequestMapping("/xboot/pay/wxPayController")
@Api(value = "微信支付异步回调接口")
@Slf4j
public class WxPayNotifyController {
@Autowired
private IWxPayService wxPayService;
@Autowired
private IPayOrderFlowService payOrderFlowService;
@Autowired
private UserDao userDao;
@Autowired
private OrderService orderService;
@Autowired
private OrderRefundService orderRefundService;
@Autowired
private DictDataDao dictDataDao;
@Autowired
private DictDao dictDao;
/**
* 微信支付结果通知回调接口
*
* @param request
* @param response
* @throws IOException
*/
@RequestMapping("/weChatPayNotify")
@ApiOperation("微信支付结果通知回调接口")
public void weChatPayNotify(HttpServletRequest request, HttpServletResponse response) {
//读取参数
InputStream inputStream = null;
BufferedReader in = null;
BufferedOutputStream out = null;
StringBuffer sb = new StringBuffer();
try {
inputStream = request.getInputStream();
String s;
in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null) {
sb.append(s);
}
//解析xml成map
Map resultMap = new HashMap<>(16);
resultMap = WXPayUtil.xmlToMap(sb.toString());
//过滤空 设置 TreeMap
SortedMap packageParams = new TreeMap<>();
Set keySet = resultMap.keySet();
for (String key : keySet) {
String value = resultMap.get(key);
String v = "";
if (StrUtil.isNotBlank(value)) {
v = value.trim();
}
packageParams.put(key, v);
}
//判断签名是否正确
if (WXPayUtil.isSignatureValid(packageParams, WxPayConfigImpl.getInstance().getKey())) {
//处理业务开始
Map requestMap = new HashMap(16);
if ("SUCCESS".equals((String) packageParams.get("return_code"))) {
//通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
requestMap.put("return_code", "SUCCESS");
requestMap.put("return_msg", "OK");
System.out.println("通知签名验证成功");
/**
* 业务处理
*/
} else {
requestMap.put("return_code", "FAIL");
requestMap.put("return_msg", "报文为空");
}
String xmlParam = WXPayUtil.mapToXml(requestMap);
out = new BufferedOutputStream(
response.getOutputStream());
out.write(xmlParam.getBytes());
out.close();
} else {
log.info("通知签名验证失败");
System.out.println("通知签名验证失败");
}
} catch (Exception e) {
e.printStackTrace();
ExceptionCast.cast(ResultEnum.INVALID_PARAM);
} finally {
try {
in.close();
inputStream.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 微信退款结果通知接口
*
* @return
*/
@RequestMapping("/refundResult")
@ApiOperation("微信退款结果通知接口")
public void refundResult(HttpServletRequest request, HttpServletResponse response) {
//读取参数
InputStream inputStream = null;
BufferedReader in = null;
BufferedOutputStream out = null;
try {
StringBuffer sb = new StringBuffer();
inputStream = request.getInputStream();
String s;
in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null) {
sb.append(s);
}
//解析xml成map
Map resultMap = new HashMap<>(16);
resultMap = WXPayUtil.xmlToMap(sb.toString());
//处理业务开始
Map requestMap = new HashMap(16);
if (resultMap != null && resultMap.size() > 0) {
//退款结果解密
String reqInfo = WxPayDecodeUtil.decryptData(resultMap.get("req_info"));
//得到退款结果信息
Map map = WXPayUtil.xmlToMap(reqInfo);
if ("SUCCESS".equals(map.get("refund_status"))) {
/**
* 业务处理
*/
//通知微信.异步确认成功
requestMap.put("return_code", "SUCCESS");
requestMap.put("return_msg", "OK");
} else{
}
} else {
requestMap.put("return_code", "FAIL");
requestMap.put("return_msg", "报文为空");
}
String xmlParam = WXPayUtil.mapToXml(requestMap);
out = new BufferedOutputStream(
response.getOutputStream());
out.write(xmlParam.getBytes());
} catch (Exception e) {
e.printStackTrace();
ExceptionCast.cast(ResultEnum.INVALID_PARAM);
} finally {
try {
in.close();
inputStream.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}