目录
前言
当前开发的主流项目涉及到实体商品,虚拟商品等付费服务的基本都会用到微信支付,本篇对于对接微信商户平台的三大主流支付方式的Java对接方式列举一下。
介绍
官方文档给到:
微信扫码支付:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1
微信公众号支付:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1
微信H5支付:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_1
微信小程序支付:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1
微信官方提供了一套Java、.NET、PHP等三个版本的开发SDK
地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
其实这几种支付方式所调用api接口参数和方式都大同小异,详细的参数介绍可以直接访问官方文档查看,下面直接列实战代码复制即用。
代码
业务层代码
//上面可以写你的业务代码(一般在ServiceImpl层)
String unifiedorder = WeiXinUtil.getUnifiedorder("用户openid", "商品名","支付金额", "订单号", "下单方式eg: xcx");// 统一下单
Map<String, String> map = PayUtil.doXMLParse(unifiedorder);//解析xml转map
SortedMap<Object, Object> param = WeiXinUtil.prepayIdApp(map);//调取支付,最后返回的param直接给到前端去拉取支付就可以了
工具类(官方给到的sdk里包含)
WeiXinUtil :
package com.ruoyi.web.jewelry.WxPay;
import com.alibaba.fastjson.JSONObject;
import com.github.wxpay.sdk.WXPayUtil;
import com.ruoyi.web.util.AlipayConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
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.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.springframework.core.io.ClassPathResource;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.util.*;
import static com.ruoyi.web.jewelry.WxPay.PayUtil.mapToXml;
import static com.ruoyi.web.util.AlipayConstant.*;
/**
* @Description
* @Author
* @Date
*/
@Slf4j
public class WeiXinUtil {
/**
* 商户号
*/
private final static String mchId = "";
//p12文件地址
public final static String certUrlPath = "";
/**
* @param openid
* @param ProductName
* @param money
* @Description
* @Return java.lang.String
* @Author
* @Date
**/
// 统一下单》》
public static String getUnifiedorder(String openid, String ProductName, Double money, String OrderId , String FromType,Integer classType) throws Exception {
System.out.println("FromType>;"+FromType);
SortedMap<Object, Object> params = new TreeMap<>();
if ("APP".equals(FromType)){
params.put("appid","appid");//app id
}else if ("XCX".equals(FromType)){
params.put("appid", "xcxid");//小程序appid/app id
}else if ("GZH".equals(FromType)){
params.put("appid", GongzhonghaoappId);//公众号appid
}
params.put("body", ProductName);//商品名称
params.put("mch_id", WXMerchantId);//商户mch_id
params.put("nonce_str", PayUtil.makeUUID(32));//随机字符串
//http://8.142.102.62:8083
//http://8e049480514d.ngrok.io
if (1==classType)
params.put("notify_url","http://localhost:9099/huidiao");直购商品回调地址
else
params.put("notify_url","http://localhost:9099/huidiao");手续费回调地址
System.out.println("走的回调地址:"+params.get("notify_url"));
if ("XCX".equals(FromType)) {
params.put("openid", openid);//微信用户唯一标识>>>小程序支付时必填
}
if ("GZH".equals(FromType)) {
params.put("openid", openid);//微信用户唯一标识>>>公众号
}
// params.put("out_trade_no", PayUtil.generateOrderNo());//商品订单号
params.put("out_trade_no", "WX_"+OrderId);//商品订单号
params.put("spbill_create_ip", PayUtil.getLocalIp());//服务部署的ip
params.put("total_fee", PayUtil.moneyToIntegerStr(money));//费用的参数转型
if ("APP".equals(FromType)){
params.put("trade_type", "APP");//对接类型
}else{
params.put("trade_type", "JSAPI");//对接类型
}
params.put("sign", PayUtil.createSign("UTF-8", params));//MD5签名 (传入key:商户密钥)
//转换成xml
String xmlData = PayUtil.getRequestXml(params);
//请求微信后台,获取支付id
System.out.println("xmlData:"+xmlData);
String resXml = HttpUtil.doPost("https://api.mch.weixin.qq.com/pay/unifiedorder", xmlData);
return resXml;
}
// 小程序调取支付
public static SortedMap<Object, Object> prepayId(Map<String, String> map) {
System.out.println("小程序支付map》》:"+map);
SortedMap<Object, Object> parameters = new TreeMap<>();
parameters.put("appId","");//微信小程序appid
parameters.put("timeStamp", PayUtil.create_timestamp());
parameters.put("nonceStr", map.get("nonce_str"));
parameters.put("package", "prepay_id=" + map.get("prepay_id"));
parameters.put("signType", "MD5");
String sign = PayUtil.createSign("UTF-8", parameters,"");//商户秘钥
parameters.put("prepayId", map.get("prepay_id"));
parameters.put("paySign", sign);
parameters.put("resultCode", "SUCCESS");
parameters.put("mchId","");//微信支付商户号
return parameters;
}
public static SortedMap<Object, Object> GzhprepayId(Map<String, String> map) {
System.out.println("公众号支付map》》:"+map);
SortedMap<Object, Object> parameters = new TreeMap<>();
parameters.put("appId",GongzhonghaoappId);//公众号appid
parameters.put("timeStamp", PayUtil.create_timestamp());
parameters.put("nonceStr", map.get("nonce_str"));
parameters.put("package", "prepay_id=" + map.get("prepay_id"));
parameters.put("signType", "MD5");
String sign = PayUtil.createSign("UTF-8", parameters,WXMerchantSecret);//商户秘钥
parameters.put("prepayId", map.get("prepay_id"));
parameters.put("paySign", sign);
parameters.put("resultCode", "SUCCESS");
parameters.put("mchId",WXMerchantId);//微信支付商户号
return parameters;
}
// app调取支付
public static SortedMap<Object, Object> prepayIdApp(Map<String, String> map) {
System.out.println("map>>:"+map);
SortedMap<Object, Object> parameters = new TreeMap<>();
parameters.put("appid","");//微信appid
parameters.put("partnerid", "");//微信支付商户号
parameters.put("prepayid", map.get("prepay_id"));//预支付交易会话ID
parameters.put("package", "Sign=WXPay");
parameters.put("noncestr", map.get("nonce_str"));
parameters.put("timestamp", PayUtil.create_timestamp());
String sign = PayUtil.createSign("UTF-8", parameters);//商户秘钥
System.out.println("map.get(\"prepay_id\")>>>"+map.get("prepay_id"));
parameters.put("sign", sign);//签名
return parameters;
}
/**
* 验证回调签名
*
* @return
*/
public static boolean isTenpaySign(Map<String, String> map) {
String characterEncoding = "utf-8";
String charset = "utf-8";
String signFromAPIResponse = map.get("sign");
if (signFromAPIResponse == null || signFromAPIResponse.equals("")) {
System.out.println("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
return false;
}
// log.debug("服务器回包里面的签名是:" + signFromAPIResponse);
//过滤空 设置 TreeMap
SortedMap<String, String> packageParams = new TreeMap();
for (String parameter : map.keySet()) {
String parameterValue = map.get(parameter);
String v = "";
if (null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
StringBuffer sb = new StringBuffer();
Set es = packageParams.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (!"sign".equals(k) && null != v && !"".equals(v)) {
sb.append(k + "=" + v + "&");
}
}
// TODO: 2019/1/25 微信支付的key
sb.append("key=" +WXMerchantSecret);
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
//算出签名
String resultSign = "";
String tobesign = sb.toString();
if (null == charset || "".equals(charset)) {
resultSign = MD5Util.passmd5(tobesign).toUpperCase();
} else {
try {
resultSign = MD5Util.passmd5(tobesign).toUpperCase();
} catch (Exception e) {
resultSign = MD5Util.passmd5(tobesign).toUpperCase();
}
}
String tenpaySign = packageParams.get("sign").toUpperCase();
return tenpaySign.equals(resultSign);
}
/**
* @param request 微信返回request
* @return String 通知微信
* @MethodName: notifyWeiXinPay
* @Description: 微信支付回调校验
* @author
* @date
*/
public static Boolean notifyWeiXinPay(HttpServletRequest request) throws Exception {
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
String resultXml = new String(outSteam.toByteArray(), "utf-8");
Map<String, String> params = XMLUtil.parseMap(resultXml);
outSteam.close();
inStream.close();
Map<String, String> return_data = new HashMap<>();
if (!isTenpaySign(params)) {
// 支付失败
log.debug("支付回调校验失败==>{}", JSONObject.toJSONString(params));
return false;
}
log.info("===============付款成功==============");
return true;
}
/**
* xml -> map
*
* @param strXML
* @return
* @throws Exception
* @content 说明:
* @auther
* @time 2020年8月15日 上午10:30:43
* @git
* @filename
* @result Map<String, String>
*/
private static Map<String, String> xmlToMap(String strXML) throws Exception {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
try {
stream.close();
} catch (Exception ex) {
}
return data;
}
/**
* 微信退款 -> https://api.mch.weixin.qq.com/secapi/pay/refund
*
* @param out_trade_no >>> 订单号
* @param price >>> 退款金额
* @return true 调用成功 false 调用失败
* @content 说明:
* @auther
* @time
* @git
* @filename
* @result boolean
*/
public static boolean payRefund(String out_trade_no, double price) {
return payRefund(out_trade_no, price, null);
}
/**
* 微信退款 -> https://api.mch.weixin.qq.com/secapi/pay/refund
*
* @param out_trade_no >>> 订单号
* @param price >>> 退款金额
* @param refund_desc >>> 退款说明
* @return true 调用成功 false 调用失败
* @content 说明:
* @auther
* @time
* @git
* @filename
* @result boolean
*/
public static boolean payRefund(String out_trade_no, double price, String refund_desc) {
Map map = new HashMap<String, String>();
map.put("appid", "");
map.put("mch_id", "");
map.put("nonce_str", UUID.randomUUID().toString().replaceAll("-", "").toUpperCase());
map.put("out_trade_no", out_trade_no);
map.put("out_refund_no", out_trade_no);
map.put("total_fee", ((int) (price * 100)) + "");
map.put("refund_fee", ((int) (price * 100)) + "");
if (refund_desc != null) {
map.put("refund_desc", refund_desc);
}
try {
map.put("sign", WXPayUtil.generateSignature(map, ""));
} catch (Exception e) {
System.out.println("签名错误!");
}
// https://api.mch.weixin.qq.com/secapi/pay/refund 申请退款
try {
CloseableHttpResponse response = Post("https://api.mch.weixin.qq.com/secapi/pay/refund", mapToXml(map));
if (response != null) {
/*String str = new String(response.getEntity().getContent().readAllBytes(), "utf-8");
System.out.println(str);
Map result = xmlToMap(str);
if ("SUCCESS".equals(result.get("return_code"))) {
if ("SUCCESS".equals(result.get("result_code"))) {
return true;
}
} else {
return false;
}*/
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("请求错误");
}
return false;
}
private static CloseableHttpResponse Post(String url, String outputEntity) throws Exception {
HttpPost httpPost = new HttpPost(url);
// 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
httpPost.addHeader("Content-Type", "text/xml");
httpPost.setEntity(new StringEntity(outputEntity, "UTF-8"));
if (certUrlPath != null) {
// 加载含有证书的http请求
return HttpClients.custom().setSSLSocketFactory(initCert()).build().execute(httpPost);
} else {
return HttpClients.custom().build().execute(httpPost);
}
}
/**
* 加载证书
*/
private static SSLConnectionSocketFactory initCert() throws Exception {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
ClassPathResource resource = new ClassPathResource(certUrlPath);
keyStore.load(resource.getInputStream(), mchId.toCharArray());
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();
@SuppressWarnings("deprecation")
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
return sslsf;
}
}
PayUtil :
package com.ruoyi.web.jewelry.WxPay;
import org.jdom2.input.SAXBuilder;
import org.springframework.util.StringUtils;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.*;
import static com.ruoyi.web.util.AlipayConstant.WXMerchantSecret;
/**
* @Description
* @Author
* @Date
*/
public class PayUtil {
/**
* 获取当前机器的ip
*/
public static String getLocalIp() {
InetAddress ia = null;
String localip = null;
try {
ia = ia.getLocalHost();
localip = ia.getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return localip;
}
@SuppressWarnings("rawtypes")
public static String getRequestXml(SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
} else {
sb.append("<" + k + ">" + v + "</" + k + ">");
}
}
sb.append("</xml>");
return sb.toString();
}
/**
* 创建签名Sign
*/
@SuppressWarnings("rawtypes")
public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
Object v = entry.getValue();
if(null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" +WXMerchantSecret);//最后加密时添加商户密钥,由于key值放在最后,所以不用添加到SortMap里面去,单独处理,编码方式采用UTF-8
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
/**
* 创建签名Sign
*/
@SuppressWarnings("rawtypes")
public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters, String key) {
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator<?> it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
if (entry.getValue() != null || !"".equals(entry.getValue())) {
String v = String.valueOf(entry.getValue());
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
}
sb.append("key=" + key);
String sign = MD5Util.encrypt(sb.toString()).toUpperCase();
return sign;
}
/**
* 生成随机数
*/
public static String makeUUID(int len) {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, len);
}
/**
* 生成订单号
*/
public static String generateOrderNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
return sdf.format(new Date()) + makeUUID(16);
}
/**
* 解析xml
*/
public static Map doXMLParse(String strxml) throws Exception {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if (null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
org.jdom2.Document doc = builder.build(in);
org.jdom2.Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
org.jdom2.Element e = (org.jdom2.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);
}
//关闭流
in.close();
return m;
}
/**
* 获取子节点的xml
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if (!children.isEmpty()) {
Iterator it = children.iterator();
while (it.hasNext()) {
org.jdom2.Element e = (org.jdom2.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();
}
/**
* 转换金额到整型
*/
public static String moneyToIntegerStr(Double money) {
BigDecimal decimal = new BigDecimal(money);
int amount = decimal.multiply(new BigDecimal((100)))
.setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
return String.valueOf(amount);
}
/**
* 微信下单,map to xml
* @param params 参数
* @return String
*/
public static String mapToXml(Map<String, String> params) {
StringBuilder xml = new StringBuilder();
xml.append("<xml>");
for (Map.Entry<String, String> entry : params.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// 略过空值
if (StringUtils.isEmpty(value)) continue;
xml.append("<").append(key).append("><![CDATA[");
xml.append(entry.getValue());
xml.append("]]></").append(key).append(">");
}
xml.append("</xml>");
return xml.toString();
}
public static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
}
HttpUtil:
package com.ruoyi.web.jewelry.WxPay;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.*;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* HTTP请求工具类,用于处理HTTP请求
*
* @author
*
*/
public class HttpUtil {
// 连接超时时间,默认10秒
private int socketTimeout = 10000;
// 传输超时时间,默认30秒
private int connectTimeout = 30000;
// HTTP请求器
private static CloseableHttpClient httpClient;
// 请求器的配置
private static RequestConfig requestConfig;
/**
* 连接超时
*/
private static int TIME_OUT = 5000;
/**
* 读取数据超时
*/
private static int READ_OUT = 10000;
/**
* 请求编码
*/
private static String ENCODING = "UTF-8";
// private static Logger logger = Logger.getLogger(HttpUtil.class);
/**
*
* @Title: HttpRecevie @author: LYQ @Description:
* 根据当前的请求对象,获得请求里的数据流 @Createtime: Sep 17, 2012 @param @param request
* 当前请求的对象 @param @return 设定文件 @return String 返回类型 @throws
*/
public static String HttpRecevie(HttpServletRequest request) {
String re = null;
try {
// 读取请求内容
BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
re = sb.toString();
/***
* 注:以下代码是在action中必须写的。否则返回的数据可能有乱码
*/
// response.setCharacterEncoding("UTF-8");
// response.setContentType("text/html;charset=UTF-8");
// PrintWriter writer = response.getWriter();
// String a = "你好";
// writer.write(("{\"result\":" + a + "}"));
} catch (IOException e) {
System.err.println("HttpClientUtils:根据请求对象获取改请求对象里的内容出错:" + e.getMessage());
return null;
}
return re;
}
/**
* @Title: HttpClientPost @author: LYQ @Description:
* 根据发送地址发送json数据到某一个url路径 @Createtime: Sep 17, 2012 @param @param url
* 访问路径 @param @param json 传递的json @param @return 设定文件 @return String
* 返回类型 @throws
*/
public static String HttpClientPost(String url, String json) {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(url);
StringBuilder result = new StringBuilder();
try {
StringEntity s = new StringEntity(json, "application/x-www-form-urlencoded", "UTF-8");
s.setContentType("application/x-www-form-urlencoded");
post.setEntity(s);
HttpResponse res = client.execute(post);
if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
Header[] headers = res.getAllHeaders();
HttpEntity entity = res.getEntity();
Header header = res.getFirstHeader("content-type");
// 读取服务器返回的json数据(接受json服务器数据)
InputStream inputStream = entity.getContent();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
BufferedReader reader = new BufferedReader(inputStreamReader);// 读字符串用的。
String content = "";
while (((content = reader.readLine()) != null)) {
result.append(content);
}
// 关闭输入流
reader.close();
}
} catch (Exception e) {
System.err.println("HttpClientUtils:根据地址发送json数据出错:" + e.getMessage());
e.printStackTrace();
return null;
}
return result.toString();
}
/**
*
* @Title: HttpRequest @author: LYQ @Description:
* 这里用一句话描述这个方法的作用 @Createtime: Sep 17, 2012 @param @param
* reqUrl @param @param parameters @param @param recvEncoding @param @return
* 设定文件 @return String 返回类型 @throws
*/
public static String HttpRequest(String reqUrl, Map parameters, String recvEncoding, String methodType) {
HttpURLConnection urlConnection = null;
String responseContent = null;
InputStream in = null;
BufferedReader rd = null;
try {
StringBuffer params = new StringBuffer();
if (parameters != null) {
for (Iterator iter = parameters.entrySet().iterator(); iter.hasNext();) {
Entry element = (Entry) iter.next();
params.append(element.getKey().toString());
params.append("=");
params.append(URLEncoder.encode(element.getValue().toString(), HttpUtil.ENCODING));
params.append("&");
}
if (params.length() > 0) {
params = params.deleteCharAt(params.length() - 1);
}
}
URL url = new URL(reqUrl);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod(methodType); // GET POST
urlConnection.setConnectTimeout(HttpUtil.TIME_OUT);// (单位:毫秒)
urlConnection.setReadTimeout(HttpUtil.READ_OUT);// (单位:毫秒)
urlConnection.setDoOutput(true);
byte[] b = params.toString().getBytes();
urlConnection.getOutputStream().write(b, 0, b.length);
urlConnection.getOutputStream().flush();
urlConnection.getOutputStream().close();
in = urlConnection.getInputStream();
rd = new BufferedReader(new InputStreamReader(in, recvEncoding));
String tempLine = rd.readLine();
StringBuffer temp = new StringBuffer();
String crlf = System.getProperty("line.separator");
while (tempLine != null) {
temp.append(tempLine);
temp.append(crlf);
tempLine = rd.readLine();
}
responseContent = temp.toString();
rd.close();
in.close();
} catch (IOException e) {
System.out.println("HttpRequest:根据地址以post或者get方式携带参数请求出错:" + e.getMessage());
responseContent = "";
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
try {
if (in != null) {
in.close();
}
if (rd != null) {
rd.close();
}
} catch (IOException e) {
responseContent = "";
}
}
return responseContent;
}
/**
* @Title: HttpSend @author: LYQ @Description:
* 通过想指定地址发送xml,并收获返回的数据。 @Createtime: Sep 17, 2012 @param @param reqUrl
* 指定路径 @param @param xml 指定发送xml @param @return 设定文件 @return String
* 返回类型 @throws
*/
public static String HttpSend(String reqUrl, String xml) {
String rel = "";
try {
URL my_url = new URL(reqUrl);
URLConnection con = my_url.openConnection();
con.setReadTimeout(HttpUtil.READ_OUT);
con.setConnectTimeout(HttpUtil.TIME_OUT);
con.setDoOutput(true);
con.setRequestProperty("Pragma:", "no-cache");
con.setRequestProperty("Cache-Control", "no-cache");
con.setRequestProperty("Content-Type", "text/xml");
OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream());
out.write(new String(xml.getBytes("ISO-8859-1")));
out.flush();
out.close();
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
StringBuffer result = new StringBuffer("");
for (String line = br.readLine(); line != null; line = br.readLine()) {
result.append(line);
}
rel = result.toString();
} catch (Exception ex) {
System.err.println("HttpSend:发送xml到指定目录出错:" + ex.getMessage());
return null;
}
return rel;
}
/**
* @Title: HttpUrl @author: LYQ @Description:
* 根据url路径求情然后获取返回值,最简单的url请求。 @Createtime: Sep 17, 2012 @param @param url
* 请求路径 @param @param ecode 解析字符集 @param @return 设定文件 @return String
* 返回类型 @throws
*/
public static String HttpUrl(String url, String ecode) {
StringBuffer ret = new StringBuffer();
try {
URL getUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) getUrl.openConnection();
InputStream in = connection.getInputStream();
connection.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(in, ecode));
String lines;
while ((lines = reader.readLine()) != null) {
ret.append(lines);
}
reader.close();
connection.disconnect();
} catch (Exception e) {
System.err.println("HttpUrl:根据url路径请求地址出错:" + e.getMessage());
return null;
}
return ret.toString();
}
/**
* 发送get请求
* @param url
* @return
* @throws IOException
*/
public static String GetJson(String url) throws IOException {
JSONObject jsonObject=null;
DefaultHttpClient defaultHttpClient=new DefaultHttpClient();
HttpGet httpGet=new HttpGet(url);
HttpResponse httpResponse = defaultHttpClient.execute(httpGet);
HttpEntity httpEntity=httpResponse.getEntity();
String result = "";
if(httpEntity!=null){
result= EntityUtils.toString(httpEntity,"UTF-8");
//jsonObject=new JSONObject(result);
}
httpGet.releaseConnection();
return result;
}
public static int getTIME_OUT() {
return TIME_OUT;
}
public static void setTIME_OUT(int time_out) {
TIME_OUT = time_out;
}
public static int getREAD_OUT() {
return READ_OUT;
}
public static void setREAD_OUT(int read_out) {
READ_OUT = read_out;
}
public static String getENCODING() {
return ENCODING;
}
public static void setENCODING(String encoding) {
ENCODING = encoding;
}
public static String requestCheckUpdateJson(String adress_Http) {
String returnLine = "";
try {
URL my_url = new URL(adress_Http);
HttpURLConnection connection = (HttpURLConnection) my_url.openConnection();
connection.setConnectTimeout(1000 * 20);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("GET");
connection.setUseCaches(false);
connection.setInstanceFollowRedirects(true);
connection.setRequestProperty("Content-Type", "application/json");
connection.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));
String line = "";
while ((line = reader.readLine()) != null) {
returnLine += line;
}
reader.close();
connection.disconnect();
System.out.println("========返回的结果的为========" + returnLine);
} catch (Exception e) {
returnLine = "";
System.out.println(e.getMessage());
}
return returnLine;
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url
* 发送请求的 URL
* @param param
* 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("Content-Type", "application/json; charset=utf-8");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(), "utf-8"));
// 发送请求参数
out.print(param);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送 POST 请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally块来关闭输出流、输入流
finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
/**
* 发送post请求
*
* @param @param
* url
* @param @param
* param
* @return void
* @throws
*
* @date
*/
public static String doPost(String url, String param) {
StringBuilder sb = new StringBuilder();
InputStream is = null;
BufferedReader br = null;
PrintWriter out = null;
try {
URL uri = new URL(url);
HttpURLConnection connection = (HttpURLConnection) uri.openConnection();
connection.setRequestMethod("POST");
connection.setReadTimeout(5000);
connection.setConnectTimeout(10000);
connection.setRequestProperty("accept", "*/*");
// connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
// 发送参数
connection.setDoOutput(true);
out = new PrintWriter(connection.getOutputStream());
out.print(param);
out.flush();
// 接收结果
is = connection.getInputStream();
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String line;
// 缓冲逐行读取
while ((line = br.readLine()) != null) {
sb.append(line);
}
// System.out.println(sb.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭流
try {
if (is != null) {
is.close();
}
if (br != null) {
br.close();
}
if (out != null) {
out.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return sb.toString();
}
/**
* 向指定URL发送GET方法的请求
*
* @param url
* 发送请求的URL
* @param param
* 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return URL 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param) {
String result = "";
BufferedReader in = null;
try {
String urlNameString = url + "?" + param;
URL realUrl = new URL(urlNameString);
// 打开和URL之间的连接
URLConnection connection = realUrl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立实际的连接
connection.connect();
// 获取所有响应头字段
Map<String, List<String>> map = connection.getHeaderFields();
// 遍历所有的响应头字段
for (String key : map.keySet()) {
System.out.println(key + "--->" + map.get(key));
}
// 定义 BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送GET请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally块来关闭输入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
/**
*
* @param content
* @param url
* @param contentType
* @return
* @throws IOException
*/
public static String doPost(String content, String url, String contentType) throws IOException {
StringBuilder result = new StringBuilder();
URL postUrl = new URL(url);
URLConnection con = postUrl.openConnection();
con.setConnectTimeout(3000);
con.setDoOutput(true);
con.setRequestProperty("Pragma", "no-cache");
con.setRequestProperty("Cache-Control", "no-cache");
if (contentType != null) {
con.setRequestProperty("Content-Type", contentType);
}
OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream());
out.write(new String(content.getBytes("UTF-8")));
out.flush();
out.close();
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
String line = "";
for (line = br.readLine(); line != null; line = br.readLine()) {
result = result.append(line);
}
return result.toString();
}
}
MD5Util:
package com.ruoyi.web.jewelry.WxPay;
import lombok.extern.slf4j.Slf4j;
import java.security.MessageDigest;
/**
* @Description TODO MD5加密工具类
* @param null
* @Return
* @Author
* @Date
**/
@Slf4j
public class MD5Util {
public final static String encrypt(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) {
log.error("generate md5 error, {}", s, e);
return null;
}
}
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" };
public static String passmd5(String source) {
StringBuffer sb = new StringBuffer(32);
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(source.getBytes("utf-8"));
for (int i = 0; i < array.length; i++) {
sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1, 3));
}
} catch (Exception e) {
return null;
}
return sb.toString();
}
/**
* 加密解密算法 执行一次加密,两次解密
*/
public static String convertMD5(String inStr){
char[] a = inStr.toCharArray();
for (int i = 0; i < a.length; i++){
a[i] = (char) (a[i] ^ 't');
}
String s = new String(a);
return s;
}
public static void main(String[] args) {
System.out.println(MD5Util.passmd5("987654321"));
}
}
XMLUtil:
package com.ruoyi.web.jewelry.WxPay;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* @author
*/
public class XMLUtil {
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
*
* @param strxml
*
* @return
*
* @throws JDOMException
* @throws IOException
*/
public static Map<String, String> parseMap(String strxml)
throws JDOMException, IOException {
if (null == strxml || "".equals(strxml)) {
return null;
}
strxml=strxml.trim();
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
// System.out.println("strxml:"+strxml);
SortedMap<String, String> m = new TreeMap<String, String>();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
// System.out.println("in:"+in);
Document doc = builder.build(in);
// System.out.println("doc:"+doc);
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 = XMLUtil.getChildrenText(children);
}
m.put(k, v);
}
// 关闭流
in.close();
return m;
}
/**
* 获取子结点的xml
*
* @param children
*
* @return String
*/
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(XMLUtil.getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
/**
* 输出XML
*
* @param parameters
*
* @return
*/
public static String parseXML(SortedMap parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (null != v && !"".equals(v) && !"appkey".equals(k)) {
sb.append("<" + k + ">" + v + "</" + k + ">\n");
}
}
sb.append("</xml>");
return sb.toString();
}
/**
* 把map里的内容转成xml字符串
*
* @param param xml的map
*
* @return xml字符串
*/
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();
}
}
其中用的的openid 分别来自:公众号:公众号授权获得(前端通过h5访问提供的地址去获取code给到后端去获取openid),小程序:小程序微信授权获得(前端授权code给到后端去获取openid),app:app微信授权获得(前端可以直接获取)这些获取openid的方式可以自行百度。
回调:
拉取完支付后微信官方回通过下单时填写的回调地址去调用接口,来实现一些支付成功后需要处理的需求逻辑。
//支付回调接口》》目的就是截取出来订单号 从而获取订单信息来操作后续逻辑
public String weixinCallback(HttpServletRequest request) throws Exception {
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
String resultXml = new String(outSteam.toByteArray(), "utf-8");
Map<String, String> params = XMLUtil.parseMap(resultXml);
outSteam.close();
inStream.close();
Map<String, String> return_data = new HashMap<>();
if (!WeiXinUtil.isTenpaySign(params)) {
// 支付失败
System.err.println("支付回调校验失败==>" + JSONObject.toJSONString(params));
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "return_code不正确");
System.err.println("返回参数:" + XMLUtil.GetMapToXML(return_data));
return XMLUtil.GetMapToXML(return_data);
}
System.err.println("===============付款成功==============");
String regEx = "[^0-9]";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(params.get("out_trade_no"));
System.out.println("out_trade_no" + m.replaceAll("").trim());
String out_trade_no = m.replaceAll("").trim(); //TODO 这个就是订单号
//TODO 注意处理完业务要做到return 返回给到微信 不然会一直请求这个接口好几次
成功返回:
return_data.put("return_code", "SUCCESS");
return_data.put("return_msg", "OK");
System.err.println("返回参数:" + XMLUtil.GetMapToXML(return_data));
return XMLUtil.GetMapToXML(return_data);
小结
通常按照开发文档开发后运行还是报error多半是粗心导致 可能是参数错误,格式错误 ,确实没有按照官方文档来等等。没有那么多“灵异事件”,更多的是确实是自己错了。
附:要学会复盘能力,从失败中反思自己,使得下次躲掉坑。