一:参考网站
微信开发文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
二:准备
去商户平台获取商户账户id(appid)、微信支付商户号、密钥
微信支付网关:https://api.mch.weixin.qq.com/pay/unifiedorder
三:配置文件yml
在yml文件中配置微信付款需要的参数
#微信支付配置
wechat:
#商户账号id
appId: xxxxxxxxxx
#微信支付商户号
mcHid: xxxxxxxx
#微信支付异步回调地址
notifyUrl: xxxxxxxxxxxxxxxxxxxxxxx
#微信退款结果通知地址
notifyRefundUrl: xxxxxxxxxxxxxxxxx
#密钥
wechatPrivateKey: xxxxxxx
#商品描述
body: xxxxxxxxx
#交易类型
tradeType: MWEB
openId:
#微信支付网关
weChatPrepayUrl: https://api.mch.weixin.qq.com/pay/unifiedorder
#微信退款网关
weChatRefundPrepayUrl: https://api.mch.weixin.qq.com/secapi/pay/refund
#微信付款查询订单网关
weChatQueryPrepayUrl: https://api.mch.weixin.qq.com/pay/orderquery
#微信退款查询网关
weChatQueryRefundPrepayUrl: https://api.mch.weixin.qq.com/pay/refundquery
#微信浏览器内调起获取openid时的appid
jsapiAppId: xxxxxxxxxxxx
#JSAPI交易类型
jsapiTradeType: JSAPI
#appSecret
appSecret: xxxxxxxxxxxxxxx
通过实体类可以获取yml文件配置的参数值:
package fly.system.bms.entity.input;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @ProjectName: fly-system-bms
* @Package: fly.system.bms.entity.input
* @ClassName: WeChatConfig
* @Author: hfq
* @Description: 微信支付的配置类
* @Date: 2020/9/4 14:13
* @Version: 1.0
*/
@Component
@Data
@ConfigurationProperties(prefix="wechat") //接收application.yml中的weChat下面的属性
public class WeChatConfig {
//微信账户id
private String appId;
//微信支付商户号
private String mcHid;
//异步接收微信支付结果通知的回调地址
private String notifyUrl;
//密钥
private String wechatPrivateKey;
//商品描述
private String body;
//交易类型
private String tradeType;
private String openId;
//退款结果通知url
private String notifyRefundUrl;
//微信支付网关
private String weChatPrepayUrl;
//微信退款网关
private String weChatRefundPrepayUrl;
//微信付款查询订单网关
private String weChatQueryPrepayUrl;
//微信退款查询网关
private String weChatQueryRefundPrepayUrl;
//微信浏览器内调起获取openid时的appid
private String jsapiAppId;
//JSAPI交易类型
private String jsapiTradeType;
//appSecret
private String appSecret;
}
四、调用第三方接口
private String weCateApiPay(BigDecimal amount) {
String outTradeNo = OrderCodeFactory.getOrderCode(15L);
//用户真实ip:H5支付要求商户在统一下单接口中上传用户真实ip地址“spbill_create_ip”
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String ip = SpbillGreateIp.getIpAddress(request);
//获取商品描述:body
String body = weChatConfig.getBody();
//交易类型
String tradeType = weChatConfig.getTradeType();
String openid = weChatConfig.getOpenId();
//微信支付分配的公众账号ID(企业号corpid即为appid)
String appid = weChatConfig.getAppId();
//微信支付分配的商户号
String mchId = weChatConfig.getMcHid();
//异步接收微信支付结果的回调地址
String NnotifyUrl = weChatConfig.getNotifyUrl();
//密钥
String wechatPlatformApiKey = weChatConfig.getWechatPrivateKey();
//微信支付网关
String weChatPrepayUrl = weChatConfig.getWeChatPrepayUrl();
//调用微信支付的接口
Map<String, String> map = WechatPayUtils.sendPayment(body, outTradeNo, amount, ip, tradeType,
openid, NnotifyUrl, null, null, appid, mchId, wechatPlatformApiKey,weChatPrepayUrl);
log.info("调用微信统一下单返回参数 {}",JSON.toJSONString(map));
//处理业务
//将微信返回的map集合中的地址返回给前台
return map.get("mweb_url");
}
将调用第三方接口返回的地址返回给前端,前端可以通过这个url调起微信支付页面
注:这个url必须在浏览器内打开,无法直接在微信中打开支付页面(微信H5支付的特性)
return_code为FAIL的时候代表未调同第三方支付接口,可以通过return_msg查询到报错信息
result_code为FAIL的时候代表调用第三方支付接口出错,可以通过err_code和err_code_des查询到出错原因
WechatPayUtils
package fly.system.bms.utils;
import com.alibaba.fastjson.JSON;
import fly.cloud.common.base.enums.BusinessCodeEnum;
import fly.cloud.common.base.exception.BusinessException;
import fly.system.bms.entity.db.RefundInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
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.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.math.BigDecimal;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.util.*;
/**
* @ClassName WechatPayUtils
* @Description TODO 微信支付工具类
* @Author liushan
* @Date 2020/1/3 13:39
**/
@Slf4j
public class WechatPayUtils {
// JSAPI支付
public static final String WECHAT_JSAPI = "JSAPI";
// H5支付
public static final String WECHAT_MWEB = "MWEB";
//微信退款
public static final String WECHAT_RETUND = "RETUND";
//微信付款查询
public static final String WECHAT_PAY_QUERY = "PAYQUERY";
//微信退款查询
public static final String WECHAT_RETUND_QUERY = "RETUNDQUEY";
//微信回调签名key
private static final String FIELD_SIGN = "sign";
/*
* 发起支付请求 body 商品描述 out_trade_no 订单号 total_fee 订单金额 单位 元 product_id 商品ID
* appid:微信支付分配的公众账号ID(企业号corpid即为此appId)
* mchid:微信支付分配的商户号
* openid:rade_type=JSAPI时(即JSAPI支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识。
* notify_url:异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数
* sign:通过签名算法计算得出的签名值
* trade_type:交易类型
* spbill_create_ip:支持IPV4和IPV6两种格式的IP地址。用户的客户端IP
*/
public static Map<String, String> sendPayment(String body,
String out_trade_no,
BigDecimal total_fee,
String ip,
String trade_type,
String openId,
String wechatNotifyUrl,
String wechatAppId,
String wechatMchId,
String wechatPlatformAppId,
String wechatPlatformMchId,
String wechatPlatformApiKey,
String weChatPrepayUrl) {
String xml = wXParamGenerate(body, out_trade_no, total_fee, ip, trade_type, openId,
wechatNotifyUrl, wechatAppId, wechatMchId, wechatPlatformAppId, wechatPlatformMchId, wechatPlatformApiKey);
String res = httpsRequest(weChatPrepayUrl, "POST", xml);
Map<String, String> data = null;
try {
data = doXMLParse(res);
} catch (Exception e) {
}
return data;
}
// 微信统一下单参数设置
private static String wXParamGenerate(String description,
String outTradeNo,
BigDecimal total_fee,
String ip,
String trade_type,
String openId,
String wechatNotifyUrl,
String wechatAppId,
String wechatMchId,
String wechatPlatformAppId,
String wechatPlatformMchId,
String wechatPlatformApiKey
) {
int fee = total_fee.multiply(new BigDecimal("100"))
.setScale(0, BigDecimal.ROUND_HALF_UP)
.intValue();
Map<String, String> param = new HashMap<String, String>();
param.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
param.put("body", description);
param.put("out_trade_no", outTradeNo);
param.put("total_fee", fee + "");
param.put("sign_type", "MD5");
param.put("notify_url", wechatNotifyUrl);
// 交易类型
param.put("trade_type", trade_type);
if (WECHAT_JSAPI.equals(trade_type)) {
param.put("openid", openId);
param.put("appid", wechatAppId);
param.put("mch_id", wechatMchId);
param.put("spbill_create_ip", ip);
} else {
param.put("appid", wechatPlatformAppId);
param.put("mch_id", wechatPlatformMchId);
if (WECHAT_MWEB.equals(trade_type)) {
param.put("spbill_create_ip", ip);
}
}
param.put(
"scene_info",
"{\"h5_info\":{\"type\":\"Wap\",\"wap_url\":\"http://" + "www.xueguoxue.com" + "\",\"wap_name\":\"您正在购买商品\"}}");
log.info("验签参数集合 {}", JSON.toJSONString(param));
String sign = getSign(param, wechatPlatformApiKey);
param.put("sign", sign);
return getMapToXML(param);
}
private static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if (!children.isEmpty()) {
Iterator it = children.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if (!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
/**
* @return java.lang.String
* @Author liushan
* @Description Map转xml数据
* @Date 2018/11/29 16:20
* @Param [param]
**/
public static String getMapToXML(Map<String, String> param) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
for (Map.Entry<String, String> entry : param.entrySet()) {
sb.append("<" + entry.getKey() + ">");
sb.append(entry.getValue());
sb.append("</" + entry.getKey() + ">");
}
sb.append("</xml>");
return sb.toString();
}
/**
* @return java.lang.String
* @Author liushan
* @Description 微信回调返回参数
* @Date 2018/11/29 16:18
* @Param [return_code, return_msg]
**/
public static String setXML(HashMap<String,String> return_data) {
String returnCode = return_data.get("return_code");
String returnMsg = return_data.get("return_msg");
return "<xml><return_code><![CDATA[" + returnCode
+ "]]></return_code><return_msg><![CDATA[" + returnMsg
+ "]]></return_msg></xml>";
}
/**
* @return java.lang.String
* @Author liushan
* @Description 获取返回给微信支付的签名
* @Date 2020年1月7日 17:24:37
* @Param [weChatResult]
**/
public static String getPaySign(Map<String, String> weChatResult, String tradeType, String wechatPlatformApiKey) {
String sign = "";
Map<String, String> param = new HashMap<>();
if (tradeType.equals(WECHAT_JSAPI)) {
param.put("appId", weChatResult.get("appid"));
param.put("nonceStr", weChatResult.get("nonce_str"));
param.put("timeStamp", weChatResult.get("timestamp"));
param.put("signType", weChatResult.get("sign_type"));
param.put("package", "prepay_id=" + weChatResult.get("prepay_id"));
} else {
param.put("appid", weChatResult.get("appid"));
param.put("partnerid", weChatResult.get("mch_id"));
param.put("prepayid", weChatResult.get("prepay_id"));
param.put("noncestr", weChatResult.get("nonce_str"));
param.put("timestamp", weChatResult.get("timestamp"));
param.put("package", weChatResult.get("packageInfo"));
}
sign = getSign(param, wechatPlatformApiKey);
return sign;
}
/**
* 微信退款服务方法
* @return
*/
public static Map<String, String> sendRefundPayment(RefundInfo refundInfo) {
log.info("微信原路返回入参 {}", JSON.toJSONString(refundInfo));
String refundFee = String.valueOf(refundInfo.getRefundFee().multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue());
String totalFee = String.valueOf(refundInfo.getTotalFee().multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue());
String xml = wXParam(refundInfo.getAppid(), refundInfo.getMchId(), refundInfo.getNotifyUrl(), refundFee, totalFee, refundInfo.getPayNumber(),
refundInfo.getOutRefundNo(), refundInfo.getWechatPlatformApiKey(),WECHAT_RETUND);
//微信退款发送消息
//加载证书
String res = initCert(refundInfo.getWeChatRefundPrepayUrl(), xml, refundInfo.getMchId());
Map<String, String> data = null;
try {
data = doXMLParse(res);
} catch (Exception e) {
}
return data;
}
/**
* 微信支付查询结果
*/
public static Map<String, String> queryWeChatPay(String appid, String mchId, String serialNumber, String wechatPlatformApiKey,String weChatQueryPrepayUrl) {
String xml = wXParam(appid, mchId,"","","","",serialNumber,wechatPlatformApiKey,WECHAT_PAY_QUERY);
String res = httpsRequest(weChatQueryPrepayUrl, "POST", xml);
Map<String, String> data = null;
try {
data = doXMLParse(res);
} catch (Exception e) {
}
return data;
}
/**
* 微信退款查询结果
*/
public static Map<String, String> queryRetundWeChatPay(String appid, String mchId, String serialNumber, String wechatPlatformApiKey,String weChatQueryRefundPrepayUrl) {
String xml = wXParam(appid, mchId, "", "", "","" ,serialNumber,wechatPlatformApiKey,WECHAT_RETUND_QUERY);
String res = httpsRequest(weChatQueryRefundPrepayUrl, "POST", xml);
Map<String, String> data = null;
try {
data = doXMLParse(res);
} catch (Exception e) {
}
return data;
}
// 微信退款/查询参数设置
private static String wXParam(String appid, String mchId, String refundNotifyUrl, String refundFee,String totalFee,
String payNumber, String outTradeNo, String wechatPlatformApiKey,String type) {
Map<String,String> param = new HashMap<String,String>();
//微信退款
if (WECHAT_RETUND.equals(type)){
param.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
param.put("appid",appid);
param.put("mch_id",mchId);
param.put("notify_url",refundNotifyUrl);
param.put("refund_fee",refundFee);
param.put("total_fee",totalFee);
param.put("transaction_id",payNumber);
param.put("out_refund_no",outTradeNo);
}//微信付款查询
else if (WECHAT_PAY_QUERY.equals(type)){
param.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
param.put("appid",appid);
param.put("mch_id",mchId);
param.put("out_trade_no",outTradeNo);
// param.put("transaction_id","4200000689202009029472000195");
}//微信退款查询
else if (WECHAT_RETUND_QUERY.equals(type)){
param.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
param.put("appid",appid);
param.put("mch_id",mchId);
param.put("out_refund_no",outTradeNo);
}
String sign = getSign(param, wechatPlatformApiKey);
param.put("sign", sign);
return getMapToXML(param);
}
// 发起微信支付请求
private static String httpsRequest(String requestUrl, String requestMethod,
String outputStr) {
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
HttpURLConnection conn = null;
try {
URL url = new URL(requestUrl);
conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
conn.setRequestProperty("content-type",
"application/x-www-form-urlencoded");
// 当outputStr不为null时向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
inputStream = conn.getInputStream();
inputStreamReader = new InputStreamReader(inputStream, "utf-8");
bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
return buffer.toString();
} catch (ConnectException ce) {
System.out.println("连接超时:{}" + ce);
} catch (Exception e) {
System.out.println("https请求异常:{}" + e);
}finally {
// 释放资源
try{
if (Objects.nonNull(bufferedReader)){
bufferedReader.close();
}
if (Objects.nonNull(inputStreamReader)){
inputStreamReader.close();
}
if (Objects.nonNull(inputStream)){
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (Objects.nonNull(conn)){
conn.disconnect();
}
inputStream = null;
}
return null;
}
// 组装参数
public static Map<String, String> doXMLParse(String strxml)
throws Exception {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if (null == strxml || "".equals(strxml)) {
return null;
}
Map<String, String> m = new HashMap<String, String>();
InputStream in = null;
try{
in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
//指向根节点
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (Objects.nonNull(in)){
// 关闭流
in.close();
}
}
return m;
}
/**
* @Author liushan
* @Description //TODO 获取
* @Date 2020/1/3 15:26
* @Param [param, wechatPlatformApiKey]
* @return java.lang.String
**/
public static String getSign(Map<String, String> param, String wechatPlatformApiKey) {
log.info("param {}",JSON.toJSONString(param));
String paramUrl = formatUrlMap(param, true, false);
log.info("StringA {}",paramUrl);
if (!StringUtils.isNotBlank(paramUrl)){
return null;
}
String stringSignTemp = MD5Utils.md5(java.net.URLDecoder.decode(paramUrl) + "&key=" + wechatPlatformApiKey)
.toUpperCase();
return stringSignTemp;
}
/**
* 方法用途: 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序),并且生成url参数串<br>
* 实现步骤: <br>
*
* @param paraMap 要排序的Map对象
* @param urlEncode 是否需要URLENCODE
* @param keyToLower 是否需要将Key转换为全小写 true:key转化成小写,false:不转化
* @return
*/
private static String formatUrlMap(Map<String, String> paraMap,
boolean urlEncode, boolean keyToLower) {
if (paraMap == null) {
return "";
}
String buff = "";
Map<String, String> tmpMap = paraMap;
try {
List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(
tmpMap.entrySet());
// 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
Collections.sort(infoIds,
new Comparator<Map.Entry<String, String>>() {
@Override
public int compare(Map.Entry<String, String> o1,
Map.Entry<String, String> o2) {
return (o1.getKey()).toString().compareTo(
o2.getKey());
}
});
// 构造URL 键值对的格式
StringBuilder buf = new StringBuilder();
for (Map.Entry<String, String> item : infoIds) {
String key = item.getKey();
String val = item.getValue();
if (urlEncode) {
val = URLEncoder.encode(val, "utf-8");
}
if (keyToLower) {
buf.append(key.toLowerCase() + "=" + val);
} else {
buf.append(key + "=" + val);
}
buf.append("&");
}
buff = buf.toString();
if (buff.equals("") == false) {
buff = buff.substring(0, buff.length() - 1);
}
} catch (Exception e) {
return null;
}
return buff;
}
//加载证书
private static String initCert(String url,String xml,String mchId){
RestTemplate restTemplate = null;
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");//eg. PKCS12
ClassPathResource cp = new ClassPathResource("apiclient_cert.p12");
keyStore.load(cp.getInputStream(), mchId.toCharArray());
SSLContext sslcontext = SSLContextBuilder.create()
.loadKeyMaterial(keyStore, mchId.toCharArray())
.build();
// SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, NoopHostnameVerifier.INSTANCE);
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
// HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
// restTemplate = new RestTemplate(factory);
// //将转换器的编码换成utf-8
// restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(Charset.forName("utf-8")));
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(xml, "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();
}
} catch (Exception e) {
throw new BusinessException(BusinessCodeEnum.DATA_IS_NULL.getCode(), "微信退款加载证书失败,请联系管理员!");
}
}
}
MD5Utils
package fly.system.bms.utils;
import java.security.MessageDigest;
public class MD5Utils {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
}
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
// MD5加密
public final static String md5(String s) {
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F' };
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 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
五、微信支付异步回调
@Api(tags = {"【微信支付API】"})
@RestController
@RequestMapping("/wechat")
@Slf4j
public class WeCateApiController {
/**
* @return java.lang.String
* @Author hfq
* @Description
* @Date 2020/1/3 15:49
* @Param [request, response]
**/
@PostMapping("/callback")
public String payNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
InputStream inStream = request.getInputStream();
StringBuffer sffer = new StringBuffer();
BufferedReader buffInt = null;
HashMap<String,String> return_data = null;
try{
String str = new String();
return_data = new HashMap<String,String>();
buffInt = new BufferedReader(new InputStreamReader(inStream, "UTF-8"));
while ((str = buffInt.readLine()) != null) {
sffer.append(str);
}
Map<String, String> results = WechatPayUtils.doXMLParse(sffer.toString());
log.info("微信支付回调参数【" + results + "】");
//微信支付单号
String transactionId = results.get("transaction_id");
//微信订单号
String outTradeNo = results.get("out_trade_no");
/**
*判断该笔订单是否已经处理(去三方流水表中查询当前订单的状态)
*/
if(!"SUCCESS".equals(results.get("return_code").toString())) {
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "return_code不正确");
}
if (!"SUCCESS".equals(results.get("result_code").toString())) {
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "result_code不正确");
}
//判断金额是否正确
long total_amount = new BigDecimal(results.get("total_fee")).longValue();
if ( total_amount != tripartitePay.getAmount().multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP).longValue()){
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "返回金额不正确");
throw new BusinessException(BusinessCodeEnum.RETURN_ERROR.getCode(), "金额total_amount错误");
}
/**
* 第一步:首先通过resultCode判断付款成功还是失败
* 第二步:如果付款失败:
* 1、修改三方流水表
*/
PayCallBackParam payCallBackParam = PayCallBackParam.builder()
.outTradeNo(outTradeNo)
.payNumber(transactionId) .timeEnd(results.get("time_end")) .totalFee(results.get("total_fee")) .returnMsg(results.get("return_msg")) .resultCode(results.get("result_code")) .errCode(results.get("err_code")) .errCodeDesc(results.get("err_code_des")) .payRetundType(PayRefundTypeEnum.PAY_MODE.getCode().toString()) .transactionId(tripartitePay.getTransactionId()) .tripartitePayInfoId(tripartitePay.getId()) .transactionCode(tripartitePay.getTransactionCode())
.result(sffer.toString())
.build();
//处理业务
return_data.put("return_code","SUCCESS");
return_data.put("return_msg","OK");
return WechatPayUtils.setXML(return_data);
}catch(BusinessException e){
if (Objects.nonNull(return_data)){
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "运行时异常");
}
throw new BusinessException(BusinessCodeEnum.SYSTEM_ERROR.getCode(),e.getMessage());
}finally {
if(Objects.nonNull(buffInt)){
buffInt.close();
}
if(Objects.nonNull(inStream)){
inStream.close();
}
}
}
}
可以通过查询result_code的值判断是否支付成功