微信开发平台 微信支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
package com.qike.webapp.controller;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.qike.sys.entity.AppOrders;
import com.qike.sys.service.IAppOrdersService;
import com.qike.user.entity.UserInfo;
import com.qike.user.service.IUserInfoService;
import com.qike.utils.BigDecimalUtil;
import com.qike.utils.ShortUUIDUtil;
import com.qike.utils.pay.AppWxPayConfig;
import com.qike.utils.pay.HttpRequest;
import com.qike.utils.pay.WXPayUtil;
import com.qike.webapp.response.ResponseData;
import com.qike.webapp.service.IUserUpgradeService;
import com.qike.webapp.vo.AppAlipayVO;
import com.sun.istack.internal.logging.Logger;
/**
*
* @author liuqiyu
* @date 2018年12月6日
*/
@Controller
@RequestMapping("/user/wxpay")
public class AppWxPayController {
// log
private static final Logger logger = Logger.getLogger(AppWxPayController.class);// logger日志
// 用户
@Autowired
private IUserInfoService userInfoService;
// 用户升级
@Autowired
private IUserUpgradeService userUpgradeService;
@Autowired
private IAppOrdersService appOrdersService;
/**
* 微信app支付统一下单
*
* @author liuqiyu
* @param userId 用户编号
* @param orderTotalFee 总金额
* @param memberLevelId 等级id
* @return
*/
@SuppressWarnings("finally")
@ResponseBody
@RequestMapping(value = "/weixinPay")
public synchronized ResponseData weixinPay(AppAlipayVO appAlipayVO, HttpServletRequest request) {
// 定义返回信息对象
ResponseData responseData = new ResponseData();
try {
BigDecimal orderTotalFee = new BigDecimal(appAlipayVO.getTotalAmount());
// 防止抓包修改订单金额造成损失
int flag = orderTotalFee.compareTo(BigDecimal.ZERO);
// 小于0或者等于0
if (flag == 0 || flag == -1) {
responseData.setCode(-999);
responseData.setMsg("付款金额错误!");
return responseData;
}
// 商户订单号生成
String outTradeNo = ShortUUIDUtil.getOrderIdByTime();
// 封装预支付订单信息
Map<String, String> paraMap = new HashMap<String, String>();
paraMap.put("appid", AppWxPayConfig.WX_APPID); // 应用ID
paraMap.put("mch_id", AppWxPayConfig.MCH_ID); // 商户号
paraMap.put("nonce_str", AppWxPayConfig.genNonce(30)); // 随机字符串
paraMap.put("body", appAlipayVO.getBody()); // 商品描述 APP名字-实际商品名称
paraMap.put("out_trade_no", outTradeNo); // 商户订单号
paraMap.put("total_fee", BigDecimalUtil.bigDecimalNullToNumber(orderTotalFee) + ""); // 总金额,单位为分
paraMap.put("spbill_create_ip", AppWxPayConfig.getIp()); // 终端IP 用户端实际ip
// TODO 异步通知地址需要修改
paraMap.put("notify_url", AppWxPayConfig.APP_NOTIFY_URL); // 异步通知地址
paraMap.put("trade_type", AppWxPayConfig.WX_JSAPI); // 交易类型
/* paraMap.put("limit_pay", "no_credit"); // 指定支付方式 no_credit--指定不能使用信用卡支付 */
// 签名生成
String sign = WXPayUtil.generateSignature(paraMap, AppWxPayConfig.PARTNER_KEY);
paraMap.put("sign", sign);
// 将所有参数(map)转xml格式
String xml = WXPayUtil.mapToXml(paraMap);
// 统一下单、发送post请求"统一下单接口"返回预支付id:pr=
String xmlStr = HttpRequest.sendPost(AppWxPayConfig.WX_PAY_URL, xml);
// 以下内容是返回前端页面的json数据
String prepay_id = ""; // 预支付id
if (xmlStr.indexOf("SUCCESS") != -1) {
// xml数据转储map对象
Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
prepay_id = map.get("prepay_id");
}
// 二次签名数据封装
Map<String, String> payMap = new HashMap<String, String>();
payMap.put("appId", AppWxPayConfig.WX_APPID);
payMap.put("timeStamp", AppWxPayConfig.getCurrentTimestamp() + "");
payMap.put("nonceStr", AppWxPayConfig.genNonce(30));
payMap.put("signType", AppWxPayConfig.TYPE_MD5);
payMap.put("package", "prepay_id=" + prepay_id);
// 二次签名生成
String paySign = WXPayUtil.generateSignature(payMap, AppWxPayConfig.PARTNER_KEY);
payMap.put("paySign", paySign);
payMap.put("out_trade_no", outTradeNo);
// payMap 是前台调起支付的凭证,将payMap传给前台即可★★★★★
// ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼业务处理▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
// 创建交易记录
int insFlasg = this.appOrderCreate(appAlipayVO); // 创建预支付订单(贴合自己的订单写)
if (insFlasg <= 0) {
// 封装code、msg、data
responseData.setCode(300);
responseData.setMsg("失败");
return responseData;
}
// 封装code、msg、data
responseData.setCode(200);
responseData.setMsg("成功");
responseData.setData(payMap);
// ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲业务处理▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
} catch (Exception e) {
// 异常抛出
e.printStackTrace();
responseData.setCode(300);
responseData.setMsg("服务器内部错误");
} finally {
return responseData;
}
}
/**
* 微信支付通知地址
*
* @author liuqiyu
* @param request
* @return
*/
@ResponseBody
@RequestMapping(value = "/weixinPayNotify", produces = { "application/json;charset=utf-8" })
public synchronized String weixinPayNotify(HttpServletRequest request, HttpServletResponse response) {
try {
// 读取参数
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);
}
outSteam.close();
inStream.close();
// 获取微信调用我们notify_url的返回信息
String notityXml = new String(outSteam.toByteArray(), "utf-8");
logger.info("微信支付回调通知--------" + notityXml);
// 判断返回信息是否为空
if (null == notityXml || "" == notityXml) {
/*
* 返回状态码 return_code 是 String(16) SUCCESS 请按示例值填写
*
* 返回信息 return_msg 是 String(128) OK 请按示例值填写
*/
return AppWxPayConfig.setXml("fail", "xml为空");
}
// 将xml转为map形式数据
Map<Object, Object> map = WXPayUtil.xml2Map(notityXml);
// 判断返回状态和业务结果是否成功
if (map.get("result_code").toString().equalsIgnoreCase("SUCCESS")
&& map.get("return_code").toString().equalsIgnoreCase("SUCCESS")) {
logger.info("微信支付回调通知----返回成功");
/*
* 特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。
*/
// 验证签名
if (WXPayUtil.isSignatureValid(notityXml)) {
// 订单处理 操作 orderConroller 的回写操作?
logger.info("微信支付----验证签名成功");
// ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼业务处理▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
/*
* 验证订单金额是否与商户侧的订单金额一致
*/
// 根据订单号获取订单
AppOrders appOrders = appOrdersService.getByOrderNum(request.getParameter("out_trade_no"));
// 判断订单是否存在
if (null == appOrders) {
return AppWxPayConfig.setXml("fail", "该订单号不存在");
}
appOrders.setTransactionId((String) map.get("transaction_id")); // 微信交易id 可用于查询账单
appOrders.setPayMoney(new BigDecimal(request.getParameter("cash_fee").toString())); // 实际支付金额
// 订单金额(数据库存放的)
BigDecimal orderMoney = appOrders.getOrderMoney();
// 订单金额(微信回调返回的)
BigDecimal totalMoney = new BigDecimal(request.getParameter("total_fee").toString());
// 判断订单金额是否与商户侧的订单金额一致
if (orderMoney.compareTo(totalMoney) != 0) {
// 微信支付订单号 用户查看订单。。。
// 支付金额
// 设定失败返回信息
return AppWxPayConfig.setXml("fail", "订单金额与商户侧的订单金额不一致");
}
// 进行升级
userUpgradeService.upgradeSuccess(appOrders);
// ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲业务处理▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
// ====================================================================
// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
return AppWxPayConfig.setXml("SUCCESS", "OK");
} else {
return AppWxPayConfig.setXml("fail", "签名验证失败");
}
} else {
return AppWxPayConfig.setXml("fail", "报文为空");
}
} catch (Exception e) {
e.printStackTrace();
logger.info("errorMsg:--- 微信通知失败" + e.getMessage());
return AppWxPayConfig.setXml("fail", "微信通知异常");
}
}
工具类分享 WXPayUtil
package com.qike.utils.pay;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.qike.utils.pay.WXPayConstants.SignType;
public class WXPayUtil {
/**
* XML格式字符串转换为Map
*
* @param strXML XML字符串
* @return XML数据转换后的Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
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) {
// do nothing
}
return data;
} catch (Exception ex) {
WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}",
ex.getMessage(), strXML);
throw ex;
}
}
/**
* 将Map转换为XML格式的字符串
*
* @param data Map类型数据
* @return XML格式的字符串
* @throws Exception
*/
public static String mapToXml(Map<String, String> data) throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
org.w3c.dom.Document document = documentBuilder.newDocument();
org.w3c.dom.Element root = document.createElement("xml");
document.appendChild(root);
for (String key : data.keySet()) {
String value = data.get(key);
if (value == null) {
value = "";
}
value = value.trim();
org.w3c.dom.Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
String output = writer.getBuffer().toString(); // .replaceAll("\n|\r", "");
try {
writer.close();
} catch (Exception ex) {
}
return output;
}
/**
* 生成带有 sign 的 XML 格式字符串
*
* @param data Map类型数据
* @param key API密钥
* @return 含有sign字段的XML
*/
public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
return generateSignedXml(data, key, WXPayConstants.SignType.MD5);
}
/**
* 生成带有 sign 的 XML 格式字符串
*
* @param data Map类型数据
* @param key API密钥
* @param signType 签名类型
* @return 含有sign字段的XML
*/
public static String generateSignedXml(final Map<String, String> data, String key, WXPayConstants.SignType signType)
throws Exception {
String sign = generateSignature(data, key, signType);
data.put(WXPayConstants.FIELD_SIGN, sign);
return mapToXml(data);
}
/**
* 判断签名是否正确
*
* @param xmlStr XML格式数据
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(String xmlStr) throws Exception {
Map<String, String> data = xmlToMap(xmlStr);
if (!data.containsKey(WXPayConstants.FIELD_SIGN)) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, AppWxPayConfig.PARTNER_KEY).equals(sign);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
*
* @param data Map类型数据
* @param key API密钥
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
return isSignatureValid(data, key, SignType.MD5);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。
*
* @param data Map类型数据
* @param key API密钥
* @param signType 签名方式
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key, SignType signType) throws Exception {
if (!data.containsKey(WXPayConstants.FIELD_SIGN)) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, key, signType).equals(sign);
}
/**
* 生成签名
*
* @param data 待签名数据
* @param key API密钥
* @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
return generateSignature(data, key, WXPayConstants.SignType.MD5);
}
/**
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
*
* @param data 待签名数据
* @param key API密钥
* @param signType 签名方式
* @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key, WXPayConstants.SignType signType)
throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(WXPayConstants.FIELD_SIGN)) {
continue;
}
if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
if (WXPayConstants.SignType.MD5.equals(signType)) {
return MD5(sb.toString()).toUpperCase();
} else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) {
return HMACSHA256(sb.toString(), key);
} else {
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
/**
* 获取随机字符串 Nonce Str
*
* @return String 随机字符串
*/
public static String generateNonceStr() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
/**
* 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public static String MD5(String data) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 生成 HMACSHA256
*
* @param data 待处理数据
* @param key 密钥
* @return 加密结果
* @throws Exception
*/
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 日志
*
* @return
*/
public static Logger getLogger() {
Logger logger = LoggerFactory.getLogger("wxpay java sdk");
return logger;
}
/**
* 获取当前时间戳,单位秒
*
* @return
*/
public static long getCurrentTimestamp() {
return System.currentTimeMillis() / 1000;
}
/**
* 获取当前时间戳,单位毫秒
*
* @return
*/
public static long getCurrentTimestampMs() {
return System.currentTimeMillis();
}
/**
* 生成 uuid, 即用来标识一笔单,也用做 nonce_str
*
* @return
*/
public static String generateUUID() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
/**
* 把原始的xml转换成map key为节点的路径,值为节点值 ,遇到重复节点用”[数字]“标识第几个节点
* 如:<xml><list><test>test1</test><test>test2</test></list></xml> 会转换成
* /xml/list[1]/test=test1 /xml/list[2]/test=test2
*
* @param message
* @return
*/
public static Map<Object, Object> xml2Map(String message) {
Map<Object, Object> data = new LinkedHashMap<Object, Object>();
Document doc = null;
try {
doc = DocumentHelper.parseText(message);
} catch (DocumentException e) {
e.printStackTrace();
}
Element root = doc.getRootElement();
String path = "/" + root.getName();
element2Map(root, data, path);
return data;
}
private static void element2Map(Element ele, Map<Object, Object> data, String path) {
if (ele == null) {
return;
}
List<Element> childrens = ele.elements();
if (childrens != null && childrens.size() > 0) {
Element pre = null;
Element cur = null;
Element next = null;
int nodeIndex = 1;
int length = childrens.size();
for (int i = 0; i < length; i++) {
cur = childrens.get(i);
String nodePath = cur.getName();
if (pre == null) {
next = childrens.get(i + 1);
if (next.getName().equals(cur.getName())) {
nodePath += "[" + nodeIndex + "]";
nodeIndex++;
}
} else {
if (pre.getName().equals(cur.getName())) {
nodePath += "[" + nodeIndex + "]";
nodeIndex++;
} else {
nodeIndex = 1;
}
}
element2Map(cur, data, nodePath);
pre = cur;
}
} else {
data.put(path, ele.getText());
}
}
}
HttpRequest
package com.qike.utils.pay;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
public class HttpRequest {
/**
* 向指定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;
System.out.println(urlNameString);
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;
}
/**
* 向指定 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)");
// 发送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()));
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;
}
}
常量
package com.qike.utils.pay;
/**
* 常量
*/
public class WXPayConstants {
public enum SignType {
MD5, HMACSHA256
}
public static final String DOMAIN_API = "api.mch.weixin.qq.com";
public static final String DOMAIN_API2 = "api2.mch.weixin.qq.com";
public static final String DOMAIN_APIHK = "apihk.mch.weixin.qq.com";
public static final String DOMAIN_APIUS = "apius.mch.weixin.qq.com";
public static final String FAIL = "FAIL";
public static final String SUCCESS = "SUCCESS";
public static final String HMACSHA256 = "HMAC-SHA256";
public static final String MD5 = "MD5";
public static final String FIELD_SIGN = "sign";
public static final String FIELD_SIGN_TYPE = "sign_type";
public static final String MICROPAY_URL_SUFFIX = "/pay/micropay";
public static final String UNIFIEDORDER_URL_SUFFIX = "/pay/unifiedorder";
public static final String ORDERQUERY_URL_SUFFIX = "/pay/orderquery";
public static final String REVERSE_URL_SUFFIX = "/secapi/pay/reverse";
public static final String CLOSEORDER_URL_SUFFIX = "/pay/closeorder";
public static final String REFUND_URL_SUFFIX = "/secapi/pay/refund";
public static final String REFUNDQUERY_URL_SUFFIX = "/pay/refundquery";
public static final String DOWNLOADBILL_URL_SUFFIX = "/pay/downloadbill";
public static final String REPORT_URL_SUFFIX = "/payitil/report";
public static final String SHORTURL_URL_SUFFIX = "/tools/shorturl";
public static final String AUTHCODETOOPENID_URL_SUFFIX = "/tools/authcodetoopenid";
// sandbox
public static final String SANDBOX_MICROPAY_URL_SUFFIX = "/sandboxnew/pay/micropay";
public static final String SANDBOX_UNIFIEDORDER_URL_SUFFIX = "/sandboxnew/pay/unifiedorder";
public static final String SANDBOX_ORDERQUERY_URL_SUFFIX = "/sandboxnew/pay/orderquery";
public static final String SANDBOX_REVERSE_URL_SUFFIX = "/sandboxnew/secapi/pay/reverse";
public static final String SANDBOX_CLOSEORDER_URL_SUFFIX = "/sandboxnew/pay/closeorder";
public static final String SANDBOX_REFUND_URL_SUFFIX = "/sandboxnew/secapi/pay/refund";
public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX = "/sandboxnew/pay/refundquery";
public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
public static final String SANDBOX_REPORT_URL_SUFFIX = "/sandboxnew/payitil/report";
public static final String SANDBOX_SHORTURL_URL_SUFFIX = "/sandboxnew/tools/shorturl";
public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";
}
ShortUUIDUtil
/***
* 生成订单流水号 根据时间戳+随机数+进程编程生成订单号
*
* @date 2018年11月17日
* @return
*/
public static String getOrderIdByTime() throws Exception {
// 获取进程编号
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
Field jvm = runtime.getClass().getDeclaredField("jvm");
jvm.setAccessible(true);
VMManagement mgmt = (VMManagement) jvm.get(runtime);
Method pidMethod = mgmt.getClass().getDeclaredMethod("getProcessId");
pidMethod.setAccessible(true);
int pid = (Integer) pidMethod.invoke(mgmt);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String newDate = sdf.format(new Date());
String result = "";
Random random = new Random();
for (int i = 0; i < 3; i++) {
result += random.nextInt(10);
}
// 时间戳+随机数+进程编号
return newDate + result + pid;
}
package com.qike.utils.pay;
import java.net.InetAddress;
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;
public class AppWxPayConfig {
/** 微信支付请求URL */
public static final String WX_PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
/** 微信申请退款URL */
public static final String WX_PAY_REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
/** 微信申请转账URL */
public static final String WX_PAY_TRANSFERS_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
/** 微信订单查询请求URL */
public static final String WX_PAY_ORDER_URL = "https://api.mch.weixin.qq.com/pay/orderquery";
/** 微信商户平台自己配置的密钥key */
// public static final String PARTNER_KEY = "*****************************";
public static final String PARTNER_KEY = "*****************************";
/** 应用ID */
// public static final String WX_APPID = "****************";
public static final String WX_APPID = "****************";
public static final String WX_JSAPI = "JSAPI";
public static final String WX_APP = "APP";
/** 微信商户账号 */
public static final String MCH_ID = "***************";
public static final String TYPE_MD5 = "MD5";
/** The Constant UTC_FORMAT. */
public static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
// app支付回调地址(微信)
public static final String APP_NOTIFY_URL = "http://****/wechat/weixinPayNotify";
/** The Constant CHAR_ARRAY. */
private static final char CHAR_ARRAY[] = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b',
'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
/**
* 发送请求时生成的一个随机数
* @param length 生成长度
* @return
*/
public static String genNonce(int length) {
Random random = new Random();
StringBuffer sb = new StringBuffer();
int nextPos = CHAR_ARRAY.length;
int tmp = 0;
for (int i = 0; i < length; i++) {
tmp = random.nextInt(nextPos);
sb.append(CHAR_ARRAY[tmp]);
}
return sb.toString();
}
/**
* 获取当前时间戳,单位秒
*
* @return
*/
public static long getCurrentTimestamp() {
return System.currentTimeMillis() / 1000;
}
/**
* 获取当前时间戳,单位毫秒
* @return
*/
public static long getCurrentTimestampMs() {
return System.currentTimeMillis();
}
/**
* 获取当前IP
*
* @return
*/
@SuppressWarnings("finally")
public static String getIp() {
// 定义返回的ip
String localip = null;
try {
// 取得当前ip
InetAddress ia = InetAddress.getLocalHost();
localip = ia.getHostAddress();
} catch (Exception e) {
e.printStackTrace();
} finally {
return localip;
}
}
/**
* 设置xml 发给微信消息(用户微信支付成功后的异步回调)
*
* @return
*/
public static String setXml(String return_code, String return_msg) {
SortedMap<String, String> parameters = new TreeMap<String, String>();
parameters.put("return_code", return_code);
parameters.put("return_msg", return_msg);
return "<xml><return_code><![CDATA[" + return_code + "]]>" + "</return_code><return_msg><![CDATA[" + return_msg
+ "]]></return_msg></xml>";
}
}