微信app支付(后端代码)
一、准备
- 接入步骤: (注意:这些要先申请应用和一个商户号;API调用密钥在微信商户平台(pay.weixin.qq.com)–>账户中心–>账户设置–>API安全设置)
- 接口文档地址: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
二、接入
- 步骤1可以不用管,直接进入步骤2,调用统一下单代码:
//第一次签名
Map<String, String> paraMap = new HashMap<>();
//订单总金额,单位为分
paraMap.put("total_fee", "1");
//微信开放平台审核通过的应用APPID
paraMap.put("appid",WeChatAppPayUtils.APPID);
//商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_2
paraMap.put("out_trade_no", System.currentTimeMillis()+"test");
//附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
paraMap.put("attach", "扩展字段");
//商品描述交易字段格式根据不同的应用场景按照以下格式: APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。
paraMap.put("body", "XXXX-充值");
//微信支付分配的商户号
paraMap.put("mch_id", WeChatAppPayUtils.MCH_ID );
//商品详细描述,对于使用单品优惠的商户,改字段必须按照规范上传,详见
paraMap.put("detail", "XXXX-充值");
//随机字符串,不长于32位。
paraMap.put("nonce_str", WeChatAppPayUtils.getNonceStr());
//接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
paraMap.put("notify_url", WeChatAppPayUtils.NOTIFY_URL);
//用户端实际ip
paraMap.put("spbill_create_ip", "14.111.58.XX");
//交易类型,取值如下:JSAPI,NATIVE,APP
paraMap.put("trade_type", "APP");
//将参数字典序列排序
String stringSignTemp = WeChatAppPayUtils.formatUrlMap(paraMap, false, false);
stringSignTemp = stringSignTemp + "&key=" + WeChatAppPayUtils.MCH_ID_KEY;
//得到签名
String sign = MD5Utils.MD5Encoding(stringSignTemp).toUpperCase();
//拼接xml
StringBuffer xml = new StringBuffer();
xml.append("<xml>");
for (Map.Entry<String, String> entry : paraMap.entrySet()) {
xml.append("<" + entry.getKey() + ">");
xml.append(entry.getValue());
xml.append("</" + entry.getKey() + ">" + "\n");
}
xml.append("<sign>");
xml.append(sign);
xml.append("</sign>");
xml.append("</xml>");
log.info("xml \n {} ", xml.toString());
//请求接口返回prepay_id等等数据
String responseBosy = HttpUtils.sentPost(WeChatAppPayUtils.PAYURL, xml.toString(), "UTF-8");
log.info("responseBosy [ {} ]",responseBosy);
- 下面就是步骤3:步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式为Sign=WXPay
//将返回的xml转为map
Map<String,String> resultMap = WeChatAppPayUtils.readStringXmlOut(responseBosy);
String prepay_id = resultMap.getOrDefault("prepay_id", "");
String return_code = resultMap.getOrDefault("return_code", "");
if(return_code != null && "SUCCESS".equals(return_code) && prepay_id != null && !"".equals(prepay_id)){
log.info(" prepay_id [ {} ]",prepay_id);
//要返回给app端的支付参数
//APP端调起支付的参数列表
Map<String, String> paraMapApp = new HashMap<>();
//微信开放平台审核通过的应用APPID
paraMapApp.put("appid",WeChatAppPayUtils.APPID);
// 微信支付分配的商户号
paraMapApp.put("partnerid",WeChatAppPayUtils.MCH_ID);
paraMapApp.put("prepayid",prepay_id);
paraMapApp.put("package","Sign=WXPay");
paraMapApp.put("noncestr",WeChatAppPayUtils.getNonceStr());
String timeStamp = String.valueOf(WeChatAppPayUtils.getSecondTimestamp(new Date()));
paraMapApp.put("timestamp",timeStamp);
String stringSignTempApp = WeChatAppPayUtils.formatUrlMap(paraMapApp,false,false);
stringSignTempApp = stringSignTempApp + "&key=" + WeChatAppPayUtils.MCH_ID_KEY;
log.info("stringSignTempApp [ {} ]",stringSignTempApp);
//得到app支付签名
String signApp = MD5Utils.MD5Encoding(stringSignTempApp).toUpperCase();
paraMapApp.put("sign",signApp);
log.info("返回给app的参数 [ {} ]",paraMapApp);
return paraMapApp;
}else {
log.info("获取prepay_id失败 [ {} ]",resultMap.getOrDefault("return_msg",""));
}
- 最后就是把结果返回给app端了,下面贴下支付的完整的代码:
private static final Logger log = LoggerFactory.getLogger(WeChatAppPay.class);
/***
*发起支付
* */
public Map<String,String> gateway( Map<String, String> params) {
//第一次签名
Map<String, String> paraMap = new HashMap<>();
//订单总金额,单位为分
paraMap.put("total_fee", "1");
//微信开放平台审核通过的应用APPID
paraMap.put("appid",WeChatAppPayUtils.APPID);
//商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_2
paraMap.put("out_trade_no", System.currentTimeMillis()+"test");
//附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
paraMap.put("attach", "扩展字段");
//商品描述交易字段格式根据不同的应用场景按照以下格式: APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。
paraMap.put("body", "XXXX-充值");
//微信支付分配的商户号
paraMap.put("mch_id", WeChatAppPayUtils.MCH_ID );
//商品详细描述,对于使用单品优惠的商户,改字段必须按照规范上传,详见
paraMap.put("detail", "XXXX-充值");
//随机字符串,不长于32位。
paraMap.put("nonce_str", WeChatAppPayUtils.getNonceStr());
//接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
paraMap.put("notify_url", WeChatAppPayUtils.NOTIFY_URL);
//用户端实际ip
paraMap.put("spbill_create_ip", "14.111.58.XX");
//交易类型,取值如下:JSAPI,NATIVE,APP
paraMap.put("trade_type", "APP");
//将参数字典序列排序
String stringSignTemp = WeChatAppPayUtils.formatUrlMap(paraMap, false, false);
stringSignTemp = stringSignTemp + "&key=" + WeChatAppPayUtils.MCH_ID_KEY;
//得到签名
String sign = MD5Utils.MD5Encoding(stringSignTemp).toUpperCase();
//拼接xml
StringBuffer xml = new StringBuffer();
xml.append("<xml>");
for (Map.Entry<String, String> entry : paraMap.entrySet()) {
xml.append("<" + entry.getKey() + ">");
xml.append(entry.getValue());
xml.append("</" + entry.getKey() + ">" + "\n");
}
xml.append("<sign>");
xml.append(sign);
xml.append("</sign>");
xml.append("</xml>");
log.info("xml \n {} ", xml.toString());
//请求接口返回prepay_id等等数据
String responseBosy = HttpUtils.sentPost(WeChatAppPayUtils.PAYURL, xml.toString(), "UTF-8");
log.info("responseBosy [ {} ]",responseBosy);
//将返回的xml转为map
Map<String,String> resultMap = WeChatAppPayUtils.readStringXmlOut(responseBosy);
String prepay_id = resultMap.getOrDefault("prepay_id", "");
String return_code = resultMap.getOrDefault("return_code", "");
if(return_code != null && "SUCCESS".equals(return_code) && prepay_id != null && !"".equals(prepay_id)){
log.info(" prepay_id [ {} ]",prepay_id);
//要返回给app端的支付参数
//APP端调起支付的参数列表
Map<String, String> paraMapApp = new HashMap<>();
//微信开放平台审核通过的应用APPID
paraMapApp.put("appid",WeChatAppPayUtils.APPID);
// 微信支付分配的商户号
paraMapApp.put("partnerid",WeChatAppPayUtils.MCH_ID);
paraMapApp.put("prepayid",prepay_id);
paraMapApp.put("package","Sign=WXPay");
paraMapApp.put("noncestr",WeChatAppPayUtils.getNonceStr());
String timeStamp = String.valueOf(WeChatAppPayUtils.getSecondTimestamp(new Date()));
paraMapApp.put("timestamp",timeStamp);
String stringSignTempApp = WeChatAppPayUtils.formatUrlMap(paraMapApp,false,false);
stringSignTempApp = stringSignTempApp + "&key=" + WeChatAppPayUtils.MCH_ID_KEY;
log.info("stringSignTempApp [ {} ]",stringSignTempApp);
//得到app支付签名
String signApp = MD5Utils.MD5Encoding(stringSignTempApp).toUpperCase();
paraMapApp.put("sign",signApp);
log.info("返回给app的参数 [ {} ]",paraMapApp);
return paraMapApp;
}else {
log.info("获取prepay_id失败 [ {} ]",resultMap.getOrDefault("return_msg",""));
}
return null;
}
- 步骤4:是app端得到刚才接口的返回调用微信sdk,弹出支付界面。
- 步骤5是接收支付完成后的异步回调(微信端调用notify_url参数的地址):
/***
* 微信支付后异步回调
* */
public String callback(HttpServletRequest request) {
Map<String, String> params = new HashMap<>();
params.put("body", getBody(request));
//读取xml内容转为map
params = WeChatAppPayUtils.readStringXmlOut(params.getOrDefault("body", ""));
log.info("微信支付回调参数 [ {} ]",params);
//商户订单号
String out_trade_no = params.getOrDefault("out_trade_no", "");
//业务结果SUCCESS或者FAIL
String result_code = params.getOrDefault("result_code", "");
//支付金额 (分)
String total_fee = params.getOrDefault("total_fee", "");
//支付完成时间
String time_end = params.getOrDefault("time_end", "");
//签名
String sign = params.getOrDefault("sign","");
//商家数据包,原样返回
String attach = params.getOrDefault("attach","");
//排除sign参数的所有参数,参与签名 , 验证微信回调的签名
Map<String, String> map = new HashMap<>();
for (String s : params.keySet()) {
String value = params.get(s);
if (StringUtils.isNotBlank(value) && !StringUtils.equals(s, "sign")) {
map.put(s, value);
}
}
String comSign = WeChatAppPayUtils.getSign(map , WeChatAppPayUtils.MCH_ID_KEY);
if (null != result_code && "SUCCESS".equals(result_code)) {
if (StringUtils.equals(sign, comSign)) {
//TODO 通过out_trade_no或者attach查询到数据库里的订单,再进行验证等
return "SUCCESS";
}else{
log.info("验证签名失败 [ {} ]",params);
}
}else{
log.info("支付失败 [ {} ]",params);
}
return "FAIL";
}
private String getBody(HttpServletRequest request) {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
CharArrayWriter data = new CharArrayWriter();
char[] buf = new char[8192];
int ret;
while ((ret = in.read(buf, 0, 8192)) != -1) {
data.write(buf, 0, ret);
}
return data.toString();
} catch (Exception e) {
System.err.println("接收BODY内容失败:"+ e);
}
return null;
}
-
步骤6就不说了,一般很少用到的查询订单。
-
下面贴下完整的代码:
WeChatAppPay.java :
package com.rw.pay.services;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.CharArrayWriter;
import java.io.InputStreamReader;
import java.util.*;
/**
* @author Zhou Zhong Qing
* @Description: 微信原生app支付
* @date 2018/6/14 15:16
*/
public class WeChatAppPay {
private static final Logger log = LoggerFactory.getLogger(WeChatAppPay.class);
/***
*发起支付
* */
public Map<String,String> gateway( Map<String, String> params) {
//第一次签名
Map<String, String> paraMap = new HashMap<>();
//订单总金额,单位为分
paraMap.put("total_fee", "1");
//微信开放平台审核通过的应用APPID
paraMap.put("appid",WeChatAppPayUtils.APPID);
//商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_2
paraMap.put("out_trade_no", System.currentTimeMillis()+"test");
//附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
paraMap.put("attach", "扩展字段");
//商品描述交易字段格式根据不同的应用场景按照以下格式: APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。
paraMap.put("body", "XXXX-充值");
//微信支付分配的商户号
paraMap.put("mch_id", WeChatAppPayUtils.MCH_ID );
//商品详细描述,对于使用单品优惠的商户,改字段必须按照规范上传,详见
paraMap.put("detail", "XXXX-充值");
//随机字符串,不长于32位。
paraMap.put("nonce_str", WeChatAppPayUtils.getNonceStr());
//接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
paraMap.put("notify_url", WeChatAppPayUtils.NOTIFY_URL);
//用户端实际ip
paraMap.put("spbill_create_ip", "14.111.58.XX");
//交易类型,取值如下:JSAPI,NATIVE,APP
paraMap.put("trade_type", "APP");
//将参数字典序列排序
String stringSignTemp = WeChatAppPayUtils.formatUrlMap(paraMap, false, false);
stringSignTemp = stringSignTemp + "&key=" + WeChatAppPayUtils.MCH_ID_KEY;
//得到签名
String sign = MD5Utils.MD5Encoding(stringSignTemp).toUpperCase();
//拼接xml
StringBuffer xml = new StringBuffer();
xml.append("<xml>");
for (Map.Entry<String, String> entry : paraMap.entrySet()) {
xml.append("<" + entry.getKey() + ">");
xml.append(entry.getValue());
xml.append("</" + entry.getKey() + ">" + "\n");
}
xml.append("<sign>");
xml.append(sign);
xml.append("</sign>");
xml.append("</xml>");
log.info("xml \n {} ", xml.toString());
//请求接口返回prepay_id等等数据
String responseBosy = HttpUtils.sentPost(WeChatAppPayUtils.PAYURL, xml.toString(), "UTF-8");
log.info("responseBosy [ {} ]",responseBosy);
//将返回的xml转为map
Map<String,String> resultMap = WeChatAppPayUtils.readStringXmlOut(responseBosy);
String prepay_id = resultMap.getOrDefault("prepay_id", "");
String return_code = resultMap.getOrDefault("return_code", "");
if(return_code != null && "SUCCESS".equals(return_code) && prepay_id != null && !"".equals(prepay_id)){
log.info(" prepay_id [ {} ]",prepay_id);
//要返回给app端的支付参数
//APP端调起支付的参数列表
Map<String, String> paraMapApp = new HashMap<>();
//微信开放平台审核通过的应用APPID
paraMapApp.put("appid",WeChatAppPayUtils.APPID);
// 微信支付分配的商户号
paraMapApp.put("partnerid",WeChatAppPayUtils.MCH_ID);
paraMapApp.put("prepayid",prepay_id);
paraMapApp.put("package","Sign=WXPay");
paraMapApp.put("noncestr",WeChatAppPayUtils.getNonceStr());
String timeStamp = String.valueOf(WeChatAppPayUtils.getSecondTimestamp(new Date()));
paraMapApp.put("timestamp",timeStamp);
String stringSignTempApp = WeChatAppPayUtils.formatUrlMap(paraMapApp,false,false);
stringSignTempApp = stringSignTempApp + "&key=" + WeChatAppPayUtils.MCH_ID_KEY;
log.info("stringSignTempApp [ {} ]",stringSignTempApp);
//得到app支付签名
String signApp = MD5Utils.MD5Encoding(stringSignTempApp).toUpperCase();
paraMapApp.put("sign",signApp);
log.info("返回给app的参数 [ {} ]",paraMapApp);
return paraMapApp;
}else {
log.info("获取prepay_id失败 [ {} ]",resultMap.getOrDefault("return_msg",""));
}
return null;
}
public static void main(String[] args) {
new WeChatAppPay().gateway(new HashMap<>());
}
/***
* 微信支付后异步回调回调
* */
public String callback(HttpServletRequest request) {
Map<String, String> params = new HashMap<>();
params.put("body", getBody(request));
//读取xml内容转为map
params = WeChatAppPayUtils.readStringXmlOut(params.getOrDefault("body", ""));
log.info("微信支付回调参数 [ {} ]",params);
//商户订单号
String out_trade_no = params.getOrDefault("out_trade_no", "");
//业务结果SUCCESS或者FAIL
String result_code = params.getOrDefault("result_code", "");
//支付金额 (分)
String total_fee = params.getOrDefault("total_fee", "");
//支付完成时间
String time_end = params.getOrDefault("time_end", "");
//签名
String sign = params.getOrDefault("sign","");
//商家数据包,原样返回
String attach = params.getOrDefault("attach","");
//排除sign参数的所有参数,参与签名 , 验证微信回调的签名
Map<String, String> map = new HashMap<>();
for (String s : params.keySet()) {
String value = params.get(s);
if (StringUtils.isNotBlank(value) && !StringUtils.equals(s, "sign")) {
map.put(s, value);
}
}
String comSign = WeChatAppPayUtils.getSign(map , WeChatAppPayUtils.MCH_ID_KEY);
if (null != result_code && "SUCCESS".equals(result_code)) {
if (StringUtils.equals(sign, comSign)) {
//TODO 通过out_trade_no或者attach查询到数据库里的订单,再进行验证等
return "SUCCESS";
}else{
log.info("验证签名失败 [ {} ]",params);
}
}else{
log.info("支付失败 [ {} ]",params);
}
return "FAIL";
}
private String getBody(HttpServletRequest request) {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
CharArrayWriter data = new CharArrayWriter();
char[] buf = new char[8192];
int ret;
while ((ret = in.read(buf, 0, 8192)) != -1) {
data.write(buf, 0, ret);
}
return data.toString();
} catch (Exception e) {
System.err.println("接收BODY内容失败:"+ e);
}
return null;
}
}
WeChatAppPayUtils.java:
package com.rw.pay.services;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import java.net.URLEncoder;
import java.util.*;
/**
* @author Zhou Zhong Qing
* @Title: ${file_name}
* @Package ${package_name}
* @Description: 微信支付的工具类
* @date 2018/6/14 16:38
*/
public class WeChatAppPayUtils {
public static final String APPID = "这里填写APPID";
public final String APPSECRET = "这里填写秘钥APPSECRET";
//商户号
public static final String MCH_ID = "这里填写商户id MCH_ID";
//商户密钥
public static final String MCH_ID_KEY = "这里填商户密钥MCH_ID_KEY";
//统一下单地址
public final static String PAYURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
/***
* 异步回调地址
* */
public final static String NOTIFY_URL = "http://pay.test.com/notify/6";
/**
* 获取精确到秒的时间戳 10 位数
*
* @return
*/
public static int getSecondTimestamp(Date date) {
if (null == date) {
return 0;
}
String timestamp = String.valueOf(date.getTime());
int length = timestamp.length();
if (length > 3) {
return Integer.valueOf(timestamp.substring(0, length - 3));
} else {
return 0;
}
}
/**
* @param xml
* @return Map
* @description 将xml字符串转换成map
*/
public static Map<String, String> readStringXmlOut(String xml) {
Map<String, String> map = new HashMap<String, String>();
Document doc = null;
try {
doc = DocumentHelper.parseText(xml); // 将字符串转为XML
Element rootElt = doc.getRootElement(); // 获取根节点
@SuppressWarnings("unchecked")
List<Element> list = rootElt.elements();// 获取根节点下所有节点
for (Element element : list) { // 遍历节点
map.put(element.getName(), element.getText()); // 节点的name为map的key,text为map的value
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
/*
* 生成32位随机字符串
* */
public static String getNonceStr() {
UUID uuid = UUID.randomUUID();
return uuid.toString().replace("-", "");
}
/**
* 方法用途: 对所有传入参数按照字段名的Unicode码从小到大排序(字典序),并且生成url参数串<br>
* 实现步骤: <br>
*
* @param paraMap 要排序的Map对象
* @param urlEncode 是否需要URLENCODE
* @param keyToLower 是否需要将Key转换为全小写
* true:key转化成小写,false:不转化
* @return
*/
public static String formatUrlMap(Map<String, String> paraMap, boolean urlEncode, boolean keyToLower) {
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) {
if (StringUtils.isNotBlank(item.getKey())) {
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.isEmpty() == false) {
buff = buff.substring(0, buff.length() - 1);
}
} catch (Exception e) {
return null;
}
return buff;
}
/**
* 获取参数签名
* @param paramMap 签名参数
* @param paySecret 签名密钥
* @return
*/
public static String getSign (Map<String , String> paramMap , String paySecret){
SortedMap<String, String> smap = new TreeMap<String, String>(paramMap);
StringBuffer stringBuffer = new StringBuffer();
for (Map.Entry<String, String> m : smap.entrySet()) {
Object value = m.getValue();
if (value != null && StringUtils.isNotBlank(String.valueOf(value))&&!"sign".equals(m.getKey())){
stringBuffer.append(m.getKey()).append("=").append(value).append("&");
}
}
String argPreSign = stringBuffer.append("key=").append(paySecret).toString();
return MD5Utils.MD5Encoding(argPreSign).toUpperCase();
}
}
MD5Utils.java:
package com.rw.pay.services;
import java.security.MessageDigest;
public class MD5Utils {
private static final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/*
* 先转为utf-8
* */
public static String MD5Encoding(String s) {
byte[] btInput = null;
try {
btInput = s.getBytes("UTF-8");
}catch (Exception e){
}
return MD5(btInput, 32);
}
public static String MD5(String s) {
byte[] btInput = s.getBytes();
return MD5(btInput, 32);
}
public static String MD5_16(String str) {
byte[] btInput = str.getBytes();
return MD5(btInput, 16);
}
private static String MD5(byte[] btInput, int length) {
try {
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// MessageDigest mdInst = MessageDigest.getInstance("SHA-1");
// 使用指定的字节更新摘要
mdInst.update(btInput);
// 获得密文
byte[] md = mdInst.digest();
// 把密文转换成十六进制的字符串形式
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (byte byte0 : md) {
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
String result = new String(str);
return length == 16 ? result.substring(8, 24) : result;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
HttpUtils.java:
package com.rw.pay.services;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.*;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
public class HttpUtils {
private static final int TIME_OUT = 5;
private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
/**
* 通过HTTP GET 发送参数
*
* @param httpUrl
* @param parameter
*/
public static String sendGet(String httpUrl, Map<String, String> parameter) {
if (parameter == null || httpUrl == null) {
return null;
}
StringBuilder sb = new StringBuilder();
Iterator<Entry<String, String>> iterator = parameter.entrySet().iterator();
while (iterator.hasNext()) {
if (sb.length() > 0) {
sb.append('&');
}
Entry<String, String> entry = iterator.next();
String key = entry.getKey();
String value;
try {
value = URLEncoder.encode(entry.getValue(), "UTF-8");
} catch (UnsupportedEncodingException e) {
value = "";
}
sb.append(key).append('=').append(value);
}
String urlStr = null;
if (httpUrl.lastIndexOf('?') != -1) {
urlStr = httpUrl + '&' + sb.toString();
} else {
urlStr = httpUrl + '?' + sb.toString();
}
HttpURLConnection httpCon = null;
String responseBody = null;
try {
URL url = new URL(urlStr);
log.info("request url [ {} ]",urlStr);
httpCon = (HttpURLConnection) url.openConnection();
httpCon.setDoOutput(true);
httpCon.setRequestMethod("GET");
httpCon.setConnectTimeout(TIME_OUT * 1000);
httpCon.setReadTimeout(TIME_OUT * 1000);
// 开始读取返回的内容
InputStream in = httpCon.getInputStream();
byte[] readByte = new byte[1024];
// 读取返回的内容
int readCount = in.read(readByte, 0, 1024);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while (readCount != -1) {
baos.write(readByte, 0, readCount);
readCount = in.read(readByte, 0, 1024);
}
responseBody = new String(baos.toByteArray(), "UTF-8");
baos.close();
} catch (Exception e) {
} finally {
if (httpCon != null)
httpCon.disconnect();
}
return responseBody;
}
/**
* 使用HTTP POST 发送文本
*
* @param httpUrl
* 发送的地址
* @param postBody
* 发送的内容
* @return 返回HTTP SERVER的处理结果,如果返回null,发送失败
*/
public static String sentPost(String httpUrl, String postBody) {
return sentPost(httpUrl, postBody, "UTF-8", null);
}
/**
* 使用HTTP POST 发送文本
*
* @param httpUrl
* 发送的地址
* @param postBody
* 发送的内容
* @return 返回HTTP SERVER的处理结果,如果返回null,发送失败
*/
public static String sentPost(String httpUrl, String postBody, String encoding) {
return sentPost(httpUrl, postBody, encoding, null);
}
/**
* 使用HTTP POST 发送文本
* @param httpUrl 目的地址
* @param postBody post的包体
* @param headerMap 增加的Http头信息
* @return
*/
public static String sentPost(String httpUrl, String postBody, Map<String, String> headerMap) {
return sentPost(httpUrl, postBody, "UTF-8", headerMap);
}
/**
* 使用HTTP POST 发送文本
*
* @param httpUrl
* 发送的地址
* @param postBody
* 发送的内容
* @param encoding
* 发送的内容的编码
* @param headerMap 增加的Http头信息
* @return 返回HTTP SERVER的处理结果,如果返回null,发送失败
* .................
*/
public static String sentPost(String httpUrl, String postBody, String encoding, Map<String, String> headerMap) {
HttpURLConnection httpCon = null;
String responseBody = null;
URL url = null;
try {
url = new URL(httpUrl);
} catch (MalformedURLException e1) {
return null;
}
try {
httpCon = (HttpURLConnection) url.openConnection();
} catch (IOException e1) {
return null;
}
if (httpCon == null) {
return null;
}
httpCon.setDoOutput(true);
httpCon.setConnectTimeout(TIME_OUT * 1000);
httpCon.setReadTimeout(TIME_OUT * 1000);
httpCon.setDoOutput(true);
httpCon.setUseCaches(false);
try {
httpCon.setRequestMethod("POST");
} catch (ProtocolException e1) {
return null;
}
if (headerMap != null) {
Iterator<Entry<String, String>> iterator = headerMap.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, String> entry = iterator.next();
httpCon.addRequestProperty(entry.getKey(), entry.getValue());
}
}
OutputStream output;
try {
output = httpCon.getOutputStream();
} catch (IOException e1) {
return null;
}
try {
output.write(postBody.getBytes(encoding));
} catch (UnsupportedEncodingException e1) {
return null;
} catch (IOException e1) {
return null;
}
try {
output.flush();
output.close();
} catch (IOException e1) {
return null;
}
// 开始读取返回的内容
InputStream in;
try {
in = httpCon.getInputStream();
} catch (IOException e1) {
return null;
}
/**
* 这个方法可以在读写操作前先得知数据流里有多少个字节可以读取。
* 需要注意的是,如果这个方法用在从本地文件读取数据时,一般不会遇到问题,
* 但如果是用于网络操作,就经常会遇到一些麻烦。
* 比如,Socket通讯时,对方明明发来了1000个字节,但是自己的程序调用available()方法却只得到900,或者100,甚至是0,
* 感觉有点莫名其妙,怎么也找不到原因。
* 其实,这是因为网络通讯往往是间断性的,一串字节往往分几批进行发送。
* 本地程序调用available()方法有时得到0,这可能是对方还没有响应,也可能是对方已经响应了,但是数据还没有送达本地。
* 对方发送了1000个字节给你,也许分成3批到达,这你就要调用3次available()方法才能将数据总数全部得到。
*
* 经常出现size为0的情况,导致下面readCount为0使之死循环(while (readCount != -1) {xxxx}),出现死机问题
*/
int size = 0;
try {
size = in.available();
} catch (IOException e1) {
return null;
}
if (size == 0) {
size = 1024;
}
byte[] readByte = new byte[size];
// 读取返回的内容
int readCount = -1;
try {
readCount = in.read(readByte, 0, size);
} catch (IOException e1) {
return null;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while (readCount != -1) {
baos.write(readByte, 0, readCount);
try {
readCount = in.read(readByte, 0, size);
} catch (IOException e) {
return null;
}
}
try {
responseBody = new String(baos.toByteArray(), encoding);
} catch (UnsupportedEncodingException e) {
return null;
} finally {
if (httpCon != null) {
httpCon.disconnect();
}
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
}
}
}
return responseBody;
}
}
到这儿基本结束了,另外文章代码或者我理解有误的地方,希望能批评指出。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现