最近工作中遇到的需求需要使用微信支付,这里把代码做一下记录。
以下代码主要实现了 微信支付,二维码支付,微信退款,微信转账(企业付款)
其中退款和转账需要用到证书,官方有详细的指引,这里就不赘述了。
详情请查看微信支付官方文档
支付参数类
package bean;
public class SysPayParams{
private String appId;
private String mchId;
private String apiKey;
private String sslCertPath;
private String certPwd;
private String notifyUrl;
private String refundNotifyUrl;
private String rechargeNotifyUrl;
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 getApiKey() {
return apiKey;
}
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
public String getNotifyUrl() {
return notifyUrl;
}
public void setNotifyUrl(String notifyUrl) {
this.notifyUrl = notifyUrl;
}
public String getSslCertPath() {
return sslCertPath;
}
public void setSslCertPath(String sslCertPath) {
this.sslCertPath = sslCertPath;
}
public String getRefundNotifyUrl() {
return refundNotifyUrl;
}
public void setRefundNotifyUrl(String refundNotifyUrl) {
this.refundNotifyUrl = refundNotifyUrl;
}
public String getCertPwd() {
return certPwd;
}
public void setCertPwd(String certPwd) {
this.certPwd = certPwd;
}
public String getRechargeNotifyUrl() {
return rechargeNotifyUrl;
}
public void setRechargeNotifyUrl(String rechargeNotifyUrl) {
this.rechargeNotifyUrl = rechargeNotifyUrl;
}
}
http请求工具类
package util;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
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.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Description http请求工具类
*/
public class HttpClientUtil {
private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
private static RequestConfig RC = RequestConfig.custom()
.setSocketTimeout(60000)
.setConnectTimeout(60000)
.setConnectionRequestTimeout(60000)
.build();
/**
* 发送Post请求
* @param httpPost
* @return
*/
private static String sendHttpPost(HttpPost httpPost, String charset) {
if(charset == null) {
charset = "UTF-8";
}
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
HttpEntity entity = null;
String responseContent = null;
try {
// 创建默认的httpClient实例.
httpClient = HttpClients.createDefault();
httpPost.setConfig(RC);
// 执行请求
response = httpClient.execute(httpPost);
entity = response.getEntity();
responseContent = EntityUtils.toString(entity, charset);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 关闭连接,释放资源
if (response != null) {
response.close();
}
if (httpClient != null) {
httpClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return responseContent;
}
/**
* 请求String[]参数
* @return 请求结果 str
*/
public static String createPostRequest(String url, String[] names, String[] values, String charset){
if(charset == null) {
charset = "UTF-8";
}
HttpPost httpPost = new HttpPost(url);//创建httpPost
if(names != null && values != null){
// 创建参数队列
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
for(int i=0; i < names.length; i++){
nameValuePairs.add(new BasicNameValuePair(names[i], values[i]));
}
try {
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, charset));
} catch (Exception e) {
e.printStackTrace();
}
}
return sendHttpPost(httpPost, charset);
}
/**
* Map<String, String> params
* @return 请求结果 str
*/
public static String createPostRequest(String url, Map<String, String> params, String charset){
if(charset == null)
charset = "UTF-8";
HttpPost httpPost = new HttpPost(url);//创建httpPost
if(params != null && !params.isEmpty()){
// 创建参数队列
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
for(String name : params.keySet()){
nameValuePairs.add(new BasicNameValuePair(name, params.get(name)));
}
try {
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, charset));
} catch (Exception e) {
e.printStackTrace();
}
}
return sendHttpPost(httpPost, charset);
}
/**
* json
* @return 请求结果 str
*/
public static String createPostRequestJSON(String url,String json, String charset){
if(charset == null)
charset = "UTF-8";
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Content-type","application/json; charset=" + charset);
httpPost.setHeader("Accept", "application/json");
try {
// System.out.println("json:"+json);
httpPost.setEntity(new StringEntity(json, Charset.forName(charset)));
} catch (Exception e) {
e.printStackTrace();
}
return sendHttpPost(httpPost, charset);
}
/**
* xml
* @return 请求结果 str
*/
public static String createPostRequestXML(String url, String xml, String charset){
if(charset == null)
charset = "UTF-8";
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Content-type","application/xml; charset=" + charset);
httpPost.setHeader("Accept", "application/xml");
try {
httpPost.setEntity(new StringEntity(xml, Charset.forName(charset)));
} catch (Exception e) {
e.printStackTrace();
}
return sendHttpPost(httpPost, charset);
}
public static String createSslPostRequest(String url, String data, String sslcertPath, String pwd) throws Exception {
/**
* 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全中下载的
*/
KeyStore keyStore = KeyStore.getInstance("PKCS12");
/**
*此处要改
*wxconfig.SSLCERT_PATH : 指向你的证书的绝对路径,带着证书去访问
*/
FileInputStream instream = new FileInputStream(new File(sslcertPath));//P12文件目录
try {
/**
* 下载证书时的密码、默认密码是你的MCHID mch_id
* */
keyStore.load(instream, pwd.toCharArray());//这里写密码
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
/**
* 下载证书时的密码、默认密码是你的MCHID mch_id
* */
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, pwd.toCharArray())//这里也是写密码的
.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();
}
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url
* 发送请求的 URL
* @param param
* 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @param charset
* 发送和接收的格式
* @return 所代表远程资源的响应结果
*/
public static String createPostRequestStr(String url, String param,String charset) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
String line;
StringBuffer sb=new StringBuffer();
try {
URL realUrl = new URL(url);
//如果是https请求,忽略SSL证书
if("https".equalsIgnoreCase(realUrl.getProtocol())){
//SslUtils.ignoreSsl();
}
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性 设置请求格式
conn.setRequestProperty("contentType", charset);
conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
//设置超时时间
conn.setConnectTimeout( 60000);
conn.setReadTimeout( 60000);
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(param);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应 设置接收格式
in = new BufferedReader(new InputStreamReader(conn.getInputStream(),charset));
while ((line = in.readLine()) != null) {
sb.append(line);
}
result=sb.toString();
} catch (Exception e) {
logger.error("发送 POST请求出现异常!"+e);
//e.printStackTrace();
}
//使用finally块来关闭输出流、输入流
finally{
try{
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
}
catch(IOException ex){
ex.printStackTrace();
}
}
return result;
}
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* Takes the raw bytes from the digest and formats them correct.
*
* @param bytes the raw bytes from the digest.
* @return the formatted bytes.
*/
private static String getFormattedText(byte[] bytes) {
int len = bytes.length;
StringBuilder buf = new StringBuilder(len * 2);
// 把密文转换成十六进制的字符串形式
for (int j = 0; j < len; j++) {
buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
}
return buf.toString();
}
public static String encode(String str) {
if (str == null) {
return null;
}
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
messageDigest.update(str.getBytes());
return getFormattedText(messageDigest.digest());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 发送get请求
* @param url 路径
* @return
*/
@SuppressWarnings({ "deprecation", "resource" })
public static String httpGet(String url){
String strResult = null;
//get请求返回结果
try {
DefaultHttpClient client = new DefaultHttpClient();
//发送get请求
HttpGet request = new HttpGet(url);
HttpResponse response = client.execute(request);
/**请求发送成功,并得到响应**/
if (response.getStatusLine().getStatusCode() == 200) {
/**读取服务器返回过来的json字符串数据**/
strResult = EntityUtils.toString(response.getEntity());
/**把json字符串转换成json对象**/
url = URLDecoder.decode(url, "UTF-8");
} else {
logger.error("get请求提交失败:" + url);
}
} catch (IOException e) {
logger.error("get请求提交失败:" + url);
}
return strResult;
}
}
xml工具类
package util;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.dom4j.*;
import java.util.List;
public class XmlTool {
/**
* String 转 org.dom4j.Document
* @param xml
* @return
* @throws DocumentException
*/
public static Document strToDocument(String xml){
try {
//加上xml标签是为了获取最外层的标签,如果不需要可以去掉
return DocumentHelper.parseText("<xml>"+xml+"</xml>");
} catch (DocumentException e) {
return null;
}
}
/**
* org.dom4j.Document 转 com.alibaba.fastjson.JSONObject
* @param xml
* @return
* @throws DocumentException
*/
public static JSONObject documentToJSONObject(String xml){
return elementToJSONObject(strToDocument(xml).getRootElement());
}
/**
* org.dom4j.Element 转 com.alibaba.fastjson.JSONObject
* @param node
* @return
*/
public static JSONObject elementToJSONObject(Element node) {
JSONObject result = new JSONObject();
// 当前节点的名称、文本内容和属性
List<Attribute> listAttr = node.attributes();// 当前节点的所有属性的list
for (Attribute attr : listAttr) {// 遍历当前节点的所有属性
result.put(attr.getName(), attr.getValue());
}
// 递归遍历当前节点所有的子节点
List<Element> listElement = node.elements();// 所有一级子节点的list
if (!listElement.isEmpty()) {
for (Element e : listElement) {// 遍历所有一级子节点
if (e.attributes().isEmpty() && e.elements().isEmpty()) // 判断一级节点是否有属性和子节点
result.put(e.getName(), e.getTextTrim());// 沒有则将当前节点作为上级节点的属性对待
else {
if (!result.containsKey(e.getName())) // 判断父节点是否存在该一级节点名称的属性
result.put(e.getName(), new JSONArray());// 没有则创建
((JSONArray) result.get(e.getName())).add(elementToJSONObject(e));// 将该一级节点放入该节点名称的属性对应的值中
}
}
}
return result;
}
}
支付方法类
package util;
import bean.SysPayParams;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.collections.MapUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.*;
/**
* @ClassName util.WxPayUtil
* @Description 微信支付工具类
* @Author leo
* @Date 2019/8/7 14:29
**/
public class WxPayUtil {
private static Logger logger = LoggerFactory.getLogger(WxPayUtil.class);
private static final String PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
private static final String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
private static final String TRANS_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS7Padding";
private static final String ALGORITHM = "AES";
/**
* 微信支付
* @param payParams
* @param outOrderId
* @param totalPayment
* @param openId
* @param ip
* @param body
* @return
*/
public Map wxPay(SysPayParams payParams, String outOrderId, BigDecimal totalPayment, String openId, String ip, String body) throws Exception {
JSONObject jobj = new JSONObject();
jobj.put("appid",payParams.getAppId());//小程序id
jobj.put("mch_id",payParams.getMchId());//商户号
jobj.put("device_info","WEB");//设备号,非必填
jobj.put("nonce_str", buildUUID());//随机字符串
//jobj.put("sign_type","MD5");//签名方式
jobj.put("body",body);//商品描述,规范
//jobj.put("detail","");//非必填,规范
//jobj.put("attach","");//非必填,原样返回
jobj.put("out_trade_no",outOrderId);//外部订单号
//计算支付金额,单位分 (总价-钱包付款部分)*100
jobj.put("total_fee",String.valueOf((totalPayment.multiply(BigDecimal.valueOf(100))).intValue()));//金额,单位分
jobj.put("spbill_create_ip",ip);//调用支付api的机器ip
//jobj.put("time_start",DateUtil.formatDateToString(orderInfo.getCreateTime(),DateUtil.yyyyMMddHHmmss2));//非必填,开始时间
//jobj.put("time_expire","yyyyMMddHHmmss");//非必填,失效时间
//jobj.put("goods_tag","WXG");//非必填,优惠标记,规则
jobj.put("notify_url",payParams.getNotifyUrl());//回调地址
jobj.put("trade_type","JSAPI");
//jobj.put("product_id","");//非必填,商品id
//jobj.put("limit_pay","no_credit");//非必填,限制用户不能使用信用卡
jobj.put("openid",openId);//用户唯一标识
//jobj.put("receipt","");//开发票
//jobj.put("scene_info","");//场景信息上报
jobj.put("sign",paramsSign(jobj,payParams.getApiKey()));//签名
String xml =jsonToXmlstr(jobj,new StringBuffer());
xml = "<xml>"+xml+"</xml>";
String charset = "utf-8";
String result = HttpClientUtil.createPostRequestXML(PAY_URL,xml,charset);
JSONObject json = XmlTool.documentToJSONObject(result);
JSONObject data = (JSONObject)json.getJSONArray("xml").get(0);
if ("SUCCESS".equals(MapUtils.getString(data,"return_code"))){
//组装返回参数
String prepayId = MapUtils.getString(data,"prepay_id");
String timeStamp = String.valueOf(System.currentTimeMillis()/1000);
JSONObject map = new JSONObject();
map.put("appId",payParams.getAppId());
map.put("timeStamp",timeStamp);
map.put("nonceStr",buildUUID());
map.put("package","prepay_id="+prepayId);
map.put("signType","MD5");
String paySign = paramsSign(map,payParams.getApiKey());
map.put("paySign",paySign);
return map;
}else {
System.out.println("下单失败"+result);
throw new Exception("下单失败");
}
}
/**
* 发起退款
* @param payParams
* @param outOrderId
* @param backOrderId
* @param totalAmount
* @param backAmount
* @param reason
* @return
*/
public Map refund(SysPayParams payParams,String outOrderId,String backOrderId,BigDecimal totalAmount,BigDecimal backAmount,String reason) throws Exception {
JSONObject jobj = new JSONObject();
jobj.put("appid",payParams.getAppId());//小程序id
jobj.put("mch_id",payParams.getMchId());//商户号
jobj.put("nonce_str", buildUUID());//随机字符串
jobj.put("out_trade_no", outOrderId);//外部订单号
jobj.put("out_refund_no", backOrderId);//商户退款单号
jobj.put("total_fee",String.valueOf(totalAmount.multiply(new BigDecimal(100)).intValue()));//订单金额
jobj.put("refund_fee",String.valueOf(backAmount.multiply(new BigDecimal(100)).intValue()));//退款金额
jobj.put("refund_desc",reason);//退款原因
jobj.put("notify_url",payParams.getRefundNotifyUrl());
jobj.put("sign",paramsSign(jobj,payParams.getApiKey()));//签名
String xml =jsonToXmlstr(jobj,new StringBuffer());
xml = "<xml>"+xml+"</xml>";
String result = null;
try {
result = HttpClientUtil.createSslPostRequest(REFUND_URL,xml,payParams.getSslCertPath(),payParams.getCertPwd());
} catch (Exception e) {
System.out.println("订单:"+outOrderId+"退款异常,错误信息:"+e.getMessage());
}
JSONObject json = XmlTool.documentToJSONObject(result);
JSONObject data = (JSONObject)json.getJSONArray("xml").get(0);
if ("SUCCESS".equals(MapUtils.getString(data,"return_code"))){
Map<String,Object> map = new HashMap<>();
map.put("wxBackId",MapUtils.getString(data,"refund_id"));
map.put("wxBackTime",new Date());
return map;
}else {
System.out.println("退款失败"+result);
throw new Exception("退款失败");
}
}
/**
* 转账
* @param payParams
* @param transferId
* @param openId
* @param realName
* @param amount
* @param ip
* @param remark
* @return
*/
public Map transfer(SysPayParams payParams,String transferId,String openId,String realName,BigDecimal amount,String ip,String remark) throws Exception {
JSONObject jobj = new JSONObject();
jobj.put("mch_appid",payParams.getAppId());//小程序id
jobj.put("mchid",payParams.getMchId());//商户号
jobj.put("nonce_str", buildUUID());//随机字符串
jobj.put("partner_trade_no", transferId);//商户订单号
jobj.put("openid", openId);//用户openid
jobj.put("check_name","NO_CHECK");//姓名校验
//jobj.put("re_user_name",realName);//真实姓名,若需要校验则必填
jobj.put("amount",String.valueOf(amount.multiply(new BigDecimal(100)).intValue()));//金额
jobj.put("desc",remark);
jobj.put("spbill_create_ip",ip);
jobj.put("sign",paramsSign(jobj,payParams.getApiKey()));//签名
String xml =jsonToXmlstr(jobj,new StringBuffer());
xml = "<xml>"+xml+"</xml>";
String result = null;
try {
result = HttpClientUtil.createSslPostRequest(TRANS_URL,xml,payParams.getSslCertPath(),payParams.getCertPwd());
} catch (Exception e) {
System.out.println("转账订单:"+transferId+"发生异常,错误信息:"+e.getMessage());
}
JSONObject json = XmlTool.documentToJSONObject(result);
JSONObject data = (JSONObject)json.getJSONArray("xml").get(0);
if ("SUCCESS".equals(MapUtils.getString(data,"return_code")) && "SUCCESS".equals(MapUtils.getString(data,"result_code"))){
Map<String,Object> map = new HashMap<>();
map.put("payOrderId",MapUtils.getString(data,"payment_no"));
map.put("payTime",MapUtils.getString(data,"payment_time"));
return map;
}else {
System.out.println("转账失败"+result);
throw new Exception("转账失败");
}
}
/**
* 微信支付Native(二维码) 跟wxpay支付方法同一接口,场景不同
* @param payParams
* @param logId
* @param totalPayment
* @param ip
* @param body
* @return
*/
public String recharge(SysPayParams payParams,String logId, BigDecimal totalPayment, String ip, String body) throws Exception {
JSONObject jobj = new JSONObject();
jobj.put("appid",payParams.getAppId());//小程序id
jobj.put("mch_id",payParams.getMchId());//商户号
jobj.put("device_info","WEB");//设备号,非必填
jobj.put("nonce_str", buildUUID());//随机字符串
//jobj.put("sign_type","MD5");//签名方式
jobj.put("body",body);//商品描述,规范
//jobj.put("detail","");//非必填,规范
//jobj.put("attach","");//非必填,原样返回
jobj.put("out_trade_no",logId);//外部订单号
//计算支付金额,单位分 (总价-钱包付款部分)*100
jobj.put("total_fee",String.valueOf((totalPayment.multiply(BigDecimal.valueOf(100))).intValue()));//金额,单位分
jobj.put("spbill_create_ip",ip);//调用支付api的机器ip
jobj.put("notify_url",payParams.getRechargeNotifyUrl());//回调地址
jobj.put("trade_type","NATIVE");
jobj.put("product_id",logId);//必填,商品id
//jobj.put("limit_pay","no_credit");//非必填,限制用户不能使用信用卡
//jobj.put("receipt","");//开发票
//jobj.put("scene_info","");//场景信息上报
jobj.put("sign",paramsSign(jobj,payParams.getApiKey()));//签名
String xml =jsonToXmlstr(jobj,new StringBuffer());
xml = "<xml>"+xml+"</xml>";
String charset = "utf-8";
String result = HttpClientUtil.createPostRequestXML(PAY_URL,xml,charset);
JSONObject json = XmlTool.documentToJSONObject(result);
JSONObject data = (JSONObject)json.getJSONArray("xml").get(0);
if ("SUCCESS".equals(MapUtils.getString(data,"return_code"))){
//组装返回参数
String codeUrl = MapUtils.getString(data,"code_url");
return codeUrl;
}else {
System.out.println("失败"+result);
throw new Exception("下单失败");
}
}
public String buildUUID(){
return UUID.randomUUID().toString().replace("-","");
}
//签名
private String paramsSign(JSONObject jobj,String key){
TreeMap<String,String> map = new TreeMap<String, String>();
//参数名ASCII码从小到大排序,转treeMap排序
map.putAll((Map)jobj);
StringBuffer signStr = new StringBuffer();
for (Map.Entry entry:map.entrySet()) {
if (entry.getValue()!=null && !"".equals(entry.getValue().toString())) {
signStr.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
}
signStr.append("key="+key);
//md5加密转大写
String sign = computeUTF(signStr.toString()).toUpperCase();
return sign;
}
/**
* 验签
* @param jobj
* @return
*/
public boolean checkSign(JSONObject jobj,String apiKey){
String paramSign = MapUtils.getString(jobj,"sign");
jobj.remove("sign");
String sign = paramsSign(jobj,apiKey);
return sign.equals(paramSign);
}
/**
*
* @param str
* @return
*/
public static String computeUTF(String str) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(str.getBytes("utf-8"));
} catch (NoSuchAlgorithmException var5) {
System.out.println("NoSuchAlgorithmException caught!");
System.exit(-1);
} catch (UnsupportedEncodingException var6) {
var6.printStackTrace();
}
byte[] byteArray = messageDigest.digest();
StringBuffer md5StrBuff = new StringBuffer();
for(int i = 0; i < byteArray.length; ++i) {
if (Integer.toHexString(255 & byteArray[i]).length() == 1) {
md5StrBuff.append("0").append(Integer.toHexString(255 & byteArray[i]));
} else {
md5StrBuff.append(Integer.toHexString(255 & byteArray[i]));
}
}
return md5StrBuff.toString();
}
/**
* json转xml
* @param jObj
* @param buffer
* @return
*/
public String jsonToXmlstr(JSONObject jObj,StringBuffer buffer){
Set<Map.Entry<String, Object>> se = jObj.entrySet();
for(Iterator<Map.Entry<String, Object>> it = se.iterator(); it.hasNext(); )
{
Map.Entry<String, Object> en = it.next();
if(en.getValue().getClass().getName().equals("com.alibaba.fastjson.JSONObject")){
buffer.append("<"+en.getKey()+">");
JSONObject jo = jObj.getJSONObject(en.getKey());
jsonToXmlstr(jo,buffer);
buffer.append("</"+en.getKey()+">");
}else if(en.getValue().getClass().getName().equals("com.alibaba.fastjson.JSONArray")){
JSONArray jarray = jObj.getJSONArray(en.getKey());
for (int i = 0; i < jarray.size(); i++) {
buffer.append("<"+en.getKey()+">");
JSONObject jsonobject = jarray.getJSONObject(i);
jsonToXmlstr(jsonobject,buffer);
buffer.append("</"+en.getKey()+">");
}
}else if(en.getValue().getClass().getName().equals("java.lang.String")){
buffer.append("<"+en.getKey()+">"+en.getValue());
buffer.append("</"+en.getKey()+">");
}
}
return buffer.toString();
}
/**
* 退款回调的req_info解密
* @param reqInfo
* @param apiKey
* @return
* @throws Exception
*/
public String decryptReqInfo(String reqInfo,String apiKey) throws Exception {
byte[] decodeBase64Data = Base64.getDecoder().decode(reqInfo);
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING, "BC");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(computeUTF(apiKey).toLowerCase().getBytes(), ALGORITHM));
return new String(cipher.doFinal(decodeBase64Data));
}
}
回调处理
package util;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.collections.MapUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @ClassName util.CallbackController
* @Description 回调处理
* @Author leo
* @Date 2019-08-08 15:02
**/
public class CallbackController {
WxPayUtil wxPayUtil= new WxPayUtil();
public Object rechargeCallBack(HttpServletRequest request) throws IOException {
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
String resultxml = new String(outSteam.toByteArray(), "utf-8");
JSONObject json = XmlTool.documentToJSONObject(resultxml);
outSteam.close();
inStream.close();
JSONObject params = (JSONObject) json.getJSONArray("xml").get(0);
String apiKey = "";
if (wxPayUtil.checkSign(params,apiKey)) {
//todo 处理业务
//返回参数
JSONObject returnParam = new JSONObject();
returnParam.put("return_code", "SUCCESS");
returnParam.put("return_msg", "OK");
return "<xml>" + wxPayUtil.jsonToXmlstr(returnParam, new StringBuffer()) + "</xml>";
} else {
System.out.println("验签失败");
return "fasle";
}
}
public Object refundCallBack(HttpServletRequest request) throws IOException {
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
String resultxml = new String(outSteam.toByteArray(), "utf-8");
JSONObject json = XmlTool.documentToJSONObject(resultxml);
outSteam.close();
inStream.close();
JSONObject params = (JSONObject) json.getJSONArray("xml").get(0);
String apiKey = "";
String reqInfo = params.getString("req_info");
String decodeXml;
try {
decodeXml = wxPayUtil.decryptReqInfo(reqInfo,apiKey);
} catch (Exception e) {
System.out.println("解密失败"+reqInfo);
return "false";
}
JSONObject data = XmlTool.documentToJSONObject(decodeXml);
JSONObject resultJson = (JSONObject) data.getJSONArray("root").get(0);
String flag = MapUtils.getString(resultJson,"refund_status");
if ("SUCCESS".equals(flag)) {
//todo 处理业务
//返回参数
JSONObject returnParam = new JSONObject();
returnParam.put("return_code", "SUCCESS");
returnParam.put("return_msg", "OK");
return "<xml>" + wxPayUtil.jsonToXmlstr(returnParam, new StringBuffer()) + "</xml>";
}
return false;
}
}
测试方法
import bean.SysPayParams;
import util.WxPayUtil;
import java.math.BigDecimal;
/**
* @ClassName DemoTest
* @Description 测试类
* @Author leo
* @Date 2019-08-09 11:04
**/
public class DemoTest {
public static void main(String[] args) throws Exception {
SysPayParams sysPayParams = new SysPayParams();
sysPayParams.setApiKey("");
sysPayParams.setAppId("");
sysPayParams.setMchId("");
sysPayParams.setNotifyUrl("");
sysPayParams.setRefundNotifyUrl("");
sysPayParams.setRechargeNotifyUrl("");
sysPayParams.setSslCertPath("");//证书地址
sysPayParams.setCertPwd("");//证书密码,默认是商户号
String outOrderId = "";
String openId = "";
String ip = "";
String body = "测试-充值";
BigDecimal amount = new BigDecimal("0.01");
WxPayUtil wxPayUtil = new WxPayUtil();
//二维码支付 返回二维码链接
String result = wxPayUtil.recharge(sysPayParams,outOrderId,amount,ip,body);
//微信支付 返回包装过的map方便前端直接调用
//Map result = wxPayUtil.wxPay(sysPayParams,outOrderId,amount,openId,ip,body);
String backOrderId = "";
//退款
//wxPayUtil.refund(sysPayParams,outOrderId,backOrderId,amount,amount,body);
//转账,实时接口
//wxPayUtil.transfer(sysPayParams,outOrderId,openId,null,amount,ip,body);
System.out.println(result);
}
}