一、工具类
1、生成订单
package com.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
public class WxOrderUtils {
public static String getOrderNo() {
SimpleDateFormat simpleDateFormat;
simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
Date date = new Date();
String str = simpleDateFormat.format(date);
Random random = new Random();
int rannum = (int) (random.nextDouble() *5);// 获取5位随机数
return str + rannum;// 当前时间 + 系统5随机生成位数
}
public static void main(String[] args) {
String fileName = getOrderNo();
System.out.println(fileName);// 8835920140307
}
}
2、Sign签名
package com.pay.wx.utils;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import com.utils.MD5Utils;
import net.sf.json.JSONObject;
public class WXSignUtils {
public final static String pack = "Sign=WXPay";
public static String unifiedorderMapToSign(String appid, String mch_id, String nonce_str, String body, String detail, String attach, String out_trade_no, int total_fee, String time_start, String time_expire, String notify_url, String spbill_create_ip) {
// 参数:开始生成签名
SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
parameters.put("appid", appid);
parameters.put("mch_id", mch_id);
parameters.put("nonce_str", nonce_str);
parameters.put("body", body);
parameters.put("detail", detail);
parameters.put("attach", attach);
parameters.put("out_trade_no", out_trade_no);
parameters.put("total_fee", total_fee);
parameters.put("time_start", time_start);
parameters.put("time_expire", time_expire);
parameters.put("notify_url", notify_url);
parameters.put("trade_type", "APP");
parameters.put("spbill_create_ip", spbill_create_ip);
String sign = createSign(parameters);
return sign;
}
@SuppressWarnings("rawtypes")
public static String createSign(SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();// 所有参与传参的参数按照accsii排序(升序)
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=" + wechat_pay_secret_key);//微信secret key
System.out.println("字符串拼接后是:" + sb.toString());
String sign = MD5Utils.MD5(sb.toString()).toUpperCase();
return sign;
}
}
3、XML参数封装
package com.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import com.pay.wx.Unifiedorder;
/**
* post提交xml格式的参数
*
* @author iYjrg_xiebin
* @date 2016年10月25日下午3:33:38
*/
public class HttpXmlUtils {
/**
* 开始post提交参数到接口 并接受返回
*
* @param url
* @param xml
* @param method
* @param contentType
* @return
*/
public static String xmlHttpProxy(String url, String xml, String method, String contentType) {
InputStream is = null;
OutputStreamWriter os = null;
try {
URL _url = new URL(url);
HttpURLConnection conn = (HttpURLConnection) _url.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestProperty("Content-type", "text/xml");
conn.setRequestProperty("Pragma:", "no-cache");
conn.setRequestProperty("Cache-Control", "no-cache");
conn.setRequestMethod("POST");
os = new OutputStreamWriter(conn.getOutputStream());
os.write(new String(xml.getBytes(contentType)));
os.flush();
// 返回值
is = conn.getInputStream();
return getContent(is, "utf-8");
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (os != null) {
os.close();
}
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 解析返回的值
*
* @param is
* @param charset
* @return
*/
public static String getContent(InputStream is, String charset) {
String pageString = null;
InputStreamReader isr = null;
BufferedReader br = null;
StringBuffer sb = null;
try {
isr = new InputStreamReader(is, charset);
br = new BufferedReader(isr);
sb = new StringBuffer();
String line = null;
while ((line = br.readLine()) != null) {
sb.append(line + "\n");
}
pageString = sb.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (isr != null) {
isr.close();
}
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
sb = null;
}
return pageString;
}
/**
* 构造xml参数
*
* @param xml
* @return
*/
public static String xmlInfoUnifiedorder(Unifiedorder unifiedorder) {
// 构造xml参数的时候,至少又是个必传参数
/*
* <xml> <appid>wx2421b1c4370ec43b</appid> <attach>支付测试</attach>
* <body>JSAPI支付测试</body> <mch_id>10000100</mch_id>
* <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
* <notify_url>http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php</
* notify_url> <openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
* <out_trade_no>1415659990</out_trade_no>
* <spbill_create_ip>14.23.150.211</spbill_create_ip>
* <total_fee>1</total_fee> <trade_type>JSAPI</trade_type>
* <sign>0CB01533B8C1EF103065174F50BCA001</sign> </xml>
*/
if (unifiedorder != null) {
StringBuffer bf = new StringBuffer();
bf.append("<xml>");
bf.append("<appid><![CDATA[");
bf.append(unifiedorder.getAppid());
bf.append("]]></appid>");
bf.append("<mch_id><![CDATA[");
bf.append(unifiedorder.getMch_id());
bf.append("]]></mch_id>");
bf.append("<nonce_str><![CDATA[");
bf.append(unifiedorder.getNonce_str());
bf.append("]]></nonce_str>");
bf.append("<sign><![CDATA[");
bf.append(unifiedorder.getSign());
bf.append("]]></sign>");
bf.append("<body><![CDATA[");
bf.append(unifiedorder.getBody());
bf.append("]]></body>");
bf.append("<detail><![CDATA[");
bf.append(unifiedorder.getDetail());
bf.append("]]></detail>");
bf.append("<attach><![CDATA[");
bf.append(unifiedorder.getAttach());
bf.append("]]></attach>");
bf.append("<out_trade_no><![CDATA[");
bf.append(unifiedorder.getOut_trade_no());
bf.append("]]></out_trade_no>");
bf.append("<total_fee><![CDATA[");
bf.append(unifiedorder.getTotal_fee());
bf.append("]]></total_fee>");
bf.append("<spbill_create_ip><![CDATA[");
bf.append(unifiedorder.getSpbill_create_ip());
bf.append("]]></spbill_create_ip>");
bf.append("<time_start><![CDATA[");
bf.append(unifiedorder.getTime_start());
bf.append("]]></time_start>");
bf.append("<time_expire><![CDATA[");
bf.append(unifiedorder.getTime_expire());
bf.append("]]></time_expire>");
bf.append("<notify_url><![CDATA[");
bf.append(unifiedorder.getNotify_url());
bf.append("]]></notify_url>");
bf.append("<trade_type><![CDATA[");
bf.append(unifiedorder.getTrade_type());
bf.append("]]></trade_type>");
bf.append("</xml>");
return bf.toString();
}
return "";
}
/**
* post请求并得到返回结果
*
* @param requestUrl
* @param requestMethod
* @param output
* @return
*/
public static String httpsRequest(String requestUrl, String requestMethod, String output) {
try {
URL url = new URL(requestUrl);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setRequestMethod(requestMethod);
if (null != output) {
OutputStream outputStream = connection.getOutputStream();
outputStream.write(output.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = connection.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
connection.disconnect();
return buffer.toString();
} catch (Exception ex) {
ex.printStackTrace();
}
return "";
}
}
4、setPay 请求参数封装
package com.pay.wx.utils;
import java.util.SortedMap;
import java.util.TreeMap;
import com.utils.WXSignUtils;
import net.sf.json.JSONObject;
public class PayUtils {
public final static String pack = "Sign=WXPay";
public static JSONObject setPayMap(String appid, String mch_id, String prepay_id, String nonce_str, String dateline) {
// 参数:开始生成签名
SortedMap<Object, Object> par = new TreeMap<Object, Object>();
par.put("appid", appid);
par.put("partnerid", mch_id);
par.put("prepayid", prepay_id);
par.put("package", pack);
par.put("noncestr", nonce_str);
par.put("timestamp", dateline);
String signnew = WXSignUtils.createSign(par);
par.put("sign", signnew);
par.put("package", pack);
par.remove("package");
JSONObject js = JSONObject.fromObject(par);
return js;
}
}
一、付款
1、后台接口参数封装(APP打开微信支付SDK参数)
package com.service.impl;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.model.PayCustomerDetail;
import com.model.PayUnifiedOrder;
import com.pay.wx.Unifiedorder;
import com.pay.wx.utils.PayUtils;
import com.utils.BeanUtilsExtends;
import com.utils.HttpXmlUtils;
import com.utils.JSONUtils;
import com.utils.RandomUtils;
import com.utils.TimeUtils;
import com.utils.WxOrderUtils;
@Service
public class PayServiceImpl implements PayService {
private final Logger logger = LoggerFactory.getLogger(PayBoServiceImpl.class);
public Map<String, Object> orderPay(Map<String, Object> paramMap) throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
map.put("state", "0");// 创建订单状态,0=失败,1=成功
map.put("msg", "支付交易失败,请稍后再试");
String custid = "1";
String body = "购买商品";
String total_price = "100"; // 订单总金额,单位为分,详见支付金额
String detail = "购买商品,价格" + total_price + "元";
String attach = ""; // 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
String out_trade_no = WxOrderUtils.getOrderNo(); // 商户系统内部的订单号,32个字符内、可包含字母,其他说明见商户订单号
String appid = appID;// 微信开放平台审核通过的应用APPID
String mch_id = wechat_pay_merchant_no;// 微信开放平台审核通过的应用merchant_no
String nonce_str = RandomUtils.randomUserName(16);
String spbill_create_ip = "127.0.0.1";
double totalfee = Double.parseDouble(total_price);// // 单位是分,即是0.01元
int total_fee = (int) (totalfee * 100);
String time_start = TimeUtils.timeStart();
String time_expire = TimeUtils.timeExpire();
String notify_url = callback_order_pay;// 同步回调地址
String sign = PayUtils.unifiedorderMapToSign(appid, mch_id, nonce_str, body, detail, attach, out_trade_no, total_fee, time_start, time_expire, notify_url, spbill_create_ip);
// 拼装支付订单信息
Unifiedorder unifiedorder = new Unifiedorder();
unifiedorder.setAppid(appid);
unifiedorder.setMch_id(mch_id);
unifiedorder.setNonce_str(nonce_str);
unifiedorder.setSign(sign);
unifiedorder.setBody(body);
unifiedorder.setDetail(detail);
unifiedorder.setAttach(attach);
unifiedorder.setOut_trade_no(out_trade_no);
unifiedorder.setTotal_fee(total_fee);
unifiedorder.setSpbill_create_ip(spbill_create_ip);
unifiedorder.setTime_start(time_start);
unifiedorder.setTime_expire(time_expire);
unifiedorder.setNotify_url(notify_url);
unifiedorder.setTrade_type(PayUtils.trade_type);
String xmlInfo = HttpXmlUtils.xmlInfoUnifiedorder(unifiedorder);// 构造xml参数
PayUnifiedOrder payUnifiedOrder = new PayUnifiedOrder();
BeanUtilsExtends.copyProperties(payUnifiedOrder, unifiedorder);
payUnifiedOrder.setUpdatetime(new Date());
payUnifiedOrder.setCustomerid(custid);
payUnifiedOrder.setState(1);
payUnifiedOrder.setOrderstatus(0);
payUnifiedOrder.setPayUnifiedOrderXml(xmlInfo);
payUnifiedOrderService.savePayUnifiedOrder(payUnifiedOrder);// 订单信息保存
PayCustomerDetail payCustomerDetail = new PayCustomerDetail();
payCustomerDetail.setTitle(body);
payCustomerDetail.setContent(detail);
payCustomerDetail.setAmount(totalfee);
payCustomerDetail.setPoundage(0.0);
payCustomerDetail.setCustomerid(custid);
payCustomerDetail.setTradestatus(0); // 交易状态 : 0=初始化,1=成功,2=失败
payCustomerDetail.setIp(spbill_create_ip);
payCustomerDetail.setCreatetime(new Date());
payCustomerDetail.setUpdatetime(new Date());
payCustomerDetail.setOuttradeno(out_trade_no);
payCustomerDetail.setState(1);
payCustomerDetailService.savePayCustomerDetail(payCustomerDetail);// 订单详细信息记录
String method = "POST";
String weixinPost = HttpXmlUtils.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", method, xmlInfo).toString();
logger.info("=============================start============================");
logger.info(weixinPost);
logger.info("=============================end==============================");
String json = JSONUtils.xml2json(weixinPost);
if (StringUtils.isNotBlank(json)) {
// 参数:开始生成签名
JSONObject jsonObject = JSONUtils.getJsonObjectFromJsonString(json);
String prepay_id = jsonObject.getString("prepay_id");
// 时间戳
String dateline = String.valueOf(new Date().getTime());// mysq时间戳只有10位要做处理
dateline = dateline.substring(0, 10);
payUnifiedOrder.setPack(PayUtils.pack);
payUnifiedOrder.setSetpay_timestamp(dateline);
payUnifiedOrder.setPrepayid(prepay_id);
payUnifiedOrder.setSetPayXml(weixinPost);
payUnifiedOrderService.savePayUnifiedOrder(payUnifiedOrder); // 存储回调信息
JSONObject js = PayUtils.setPayMap(appid, mch_id, prepay_id, nonce_str, dateline);
map.put("setpay", js);// 返回支付参数,app客户端使用里面的值用来调用支付
map.put("state", "1");// 创建订单状态,0=失败,1=成功
map.put("msg", "支付申请创建成功");
return map;
} else {
payUnifiedOrder.setSetPayXml(weixinPost);
payUnifiedOrderService.savePayUnifiedOrder(payUnifiedOrder);// 存储回调信息
}
return map;
}
}
2、APP客户端使用API返回参数打开微信支付SDK
后台返回值参数:
{
"setpay": {
"appid": "wxfa234123412897843",
"noncestr": "11232323112205947",
"package_": "Sign=WXPay",
"partnerid": "150523232351",
"prepayid": "wx123232323230a3404882531",
"sign": "87E12411D457944452352352536C4",
"timestamp": "1528902743"
},
"state": "1",
"msg": "支付申请创建成功"
}
3、微信支付成功后,微信回调API服务端,处理业务,更新订单状态
package com.service.impl;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.model.PayCustomerDetail;
import com.model.PayUnifiedOrder;
import com.service.PayCustomerDetailService;
import com.service.PayUnifiedOrderService;
@Service
public class PayCallbackServiceImpl {
private final Logger logger = LoggerFactory.getLogger(PayCallbackServiceImpl.class);
@Autowired
private PayUnifiedOrderService payUnifiedOrderService;
@Autowired
private PayCustomerDetailService payCustomerDetailService;
@SuppressWarnings("unchecked")
public void callbackVipOrderPay(HttpServletRequest request, HttpServletResponse response) throws Exception {
String msg = "error";
// 解析结果存储在HashMap
Map<String, String> map = new HashMap<String, String>();
InputStream inputStream = request.getInputStream();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
// 遍历所有子节点
for (Element e : elementList) {
map.put(e.getName(), e.getText());
}
JSONObject json = JSONObject.fromObject(map);
System.out.println("===消息通知的结果:" + json.toString() + "==========================");
String outTradeNo = map.get("out_trade_no");
PayUnifiedOrder payUnifiedOrder = payUnifiedOrderService.findPayUnifiedOrderByOut_trade_no(outTradeNo);
if (payUnifiedOrder == null) {
logger.info("支付回调根据订单号查询支付信息失败,为空");
// 释放资源
inputStream.close();
inputStream = null;
// 不返回结果,微信会重新调用,总调用5次
return;
}
PayCustomerDetail payCustomerDetail = payCustomerDetailService.findPayCustomerDetailByOut_trade_no(outTradeNo);
if (payCustomerDetail == null) {
logger.info("回调根据订单号查询用户明细失败,为空");
// 释放资源
inputStream.close();
inputStream = null;
// 不返回结果,微信会重新调用,总调用5次
return;
}
payUnifiedOrder.setSetPayCallBackXml(json.toString());// 请求支付回调XML
payUnifiedOrder.setResult_code(map.get("return_code"));// 业务结果
payUnifiedOrder.setUpdatetime(new Date());
payCustomerDetail.setUpdatetime(new Date());
payCustomerDetail.setReachtime(new Date());
// 判断是否支付成功
if (map.get("return_code").equals("SUCCESS")) {
if (payUnifiedOrder != null && payUnifiedOrder.getOrderstatus().intValue() != 0) {
logger.info("订单已处理,无需重复处理");
inputStream.close();
inputStream = null;
msg = "success";
response.setContentType("text/xml");
response.getWriter().println(msg);// 返回结果,微信不会重新调用
return;
}
payUnifiedOrder.setOrderstatus(1);// 交易状态 : 0=初始化,1=成功,2=失败
payUnifiedOrder.setOpenid(map.get("openid"));// 用户标识
payUnifiedOrder.setBank_type(map.get("bank_type"));// 付款银行
payUnifiedOrder.setTransaction_id(map.get("transaction_id"));// 微信支付订单号
payUnifiedOrder.setTime_end(map.get("time_end"));// 支付完成时间
payUnifiedOrderService.savePayUnifiedOrder(payUnifiedOrder);
payCustomerDetail.setTradestatus(1);// 交易状态 : 0=初始化,1=成功,2=失败
payCustomerDetailService.savePayCustomerDetail(payCustomerDetail);
// 释放资源
inputStream.close();
inputStream = null;
logger.info("支付成功,outTradeNo={}", outTradeNo);
msg = "success";
response.setContentType("text/xml");
response.getWriter().println(msg);// 返回结果,微信不会重新调用
return;
} else if (map.get("return_code").equals("FAIL")) {
payUnifiedOrder.setOrderstatus(2);
payUnifiedOrder.setErr_code(map.get("err_code"));// 错误代码
payUnifiedOrder.setErr_code_des(map.get("err_code_des"));// 错误代码描述
payUnifiedOrderService.savePayUnifiedOrder(payUnifiedOrder);
payCustomerDetail.setTradestatus(2);// 交易状态 : 0=初始化,1=成功,2=失败
payCustomerDetailService.savePayCustomerDetail(payCustomerDetail);
logger.error("支付失败,outTradeNo={}", outTradeNo);
// 释放资源
inputStream.close();
inputStream = null;
response.setContentType("text/xml");
response.getWriter().println(msg);// 返回结果,微信不会重新调用
return;
}
// 释放资源
inputStream.close();
inputStream = null;
response.setContentType("text/xml");
response.getWriter().println(msg);
return;
}
}