App微信支付
一 :微信支付基本流程
商户系统和微信支付系统主要交互说明:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式为Sign=WXPay
步骤4:商户APP调起微信支付。api参见【app端开发步骤说明】
步骤5:商户后台接收支付通知。api参见【支付结果通知API】
步骤6:商户后台查询支付结果。,api参见【查询订单API】
二 :具体准备步骤
1:填写微信支付的相关信息和app信息,等待审核通过
2:获取 APPID (app 应用id)
3 :申请成功后获取商户号
商户号及密码会发送到申请时的邮箱上
4:登录 微信商户平台 设置API_KEY(API密钥)和下载证书
5:在服务器上安装API证书
三 : 具体demo 代码
1 :工具类
import java.util.Date; /** * * @author jiangbo_lin * @2018-09-14 * @desc:日期工具 */ public class DateUtils { /** * 获取当前系统的时间戳 * * @return */ public static String getCurrentTimestamp() { long timeStamp = new Date().getTime(); return String.valueOf(timeStamp); } public static String getCurrentTimestamp10() { long timeStamp = new Date().getTime() / 1000; String timestr = String.valueOf(timeStamp); return timestr; } public static String getTimeStamp() { int time = (int) (System.currentTimeMillis() / 1000); return String.valueOf(time); } }
import java.util.*; import java.util.Map.Entry; /** *@author jiangbo_lin * 2018-09-18 * @desc:对map的key进行ASCII排序 */ public class MapUtils { /** * 对map根据key进行排序 ASCII 顺序 * * @param map 无序的map * @return */ public static SortedMap<String, Object> sortMap(Map<String, Object> map) { List<Entry<String, Object>> infoIds = new ArrayList<Entry<String, Object>>(map.entrySet()); // 排序 Collections.sort(infoIds, new Comparator<Entry<String, Object>>() { public int compare(Entry<String, Object> o1, Entry<String, Object> o2) { // return (o2.getValue() - o1.getValue());//value处理 return (o1.getKey()).toString().compareTo(o2.getKey()); } }); // 排序后 SortedMap<String, Object> sortmap = new TreeMap<String, Object>(); for (int i = 0; i < infoIds.size(); i++) { Entry<String, Object> data = infoIds.get(i); String key = data.getKey() ; Object value = data.getValue(); if(value==null){ sortmap.put(data.getKey(), data.getValue()); }else{ sortmap.put(data.getKey(),data.getValue().toString()); } } return sortmap; } /** * map to String * * @param map * @return */ public static String toString(Map<String, Object> map) { StringBuffer buf = new StringBuffer(); buf.append("{"); Iterator<Entry<String, Object>> i = map.entrySet().iterator(); boolean hasNext = i.hasNext(); while (hasNext) { Entry<String, Object> e = i.next(); Object key = e.getKey(); Object value = e.getValue(); if (key == MapUtils.class) buf.append("(this Map)"); else buf.append(key); buf.append("="); if (value == MapUtils.class) buf.append("(this Map)"); else buf.append(value); hasNext = i.hasNext(); if (hasNext) buf.append(", "); } buf.append("}"); return buf.toString(); } }
import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Iterator; import java.util.List; import java.util.SortedMap; import java.util.TreeMap; /** * * @desc:XML 解析工具 */ @SuppressWarnings("all") public class XMLUtil { /** * 解析xml,返回第一级元素键值对。 * 如果第一级元素有子节点, * 则此节点的值是子节点的xml数据。 * * @param strxml * @return * @throws JDOMException * @throws IOException */ public static SortedMap<String, Object> doXMLParse(String strxml) throws JDOMException, IOException { strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); if (null == strxml || "".equals(strxml)) { return null; } SortedMap<String, Object> map = new TreeMap<String, Object>(); InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8")); SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(in); Element root = doc.getRootElement(); List list = root.getChildren(); Iterator it = list.iterator(); while (it.hasNext()) { Element e = (Element) it.next(); String key = e.getName(); String value = ""; List children = e.getChildren(); if (children.isEmpty()) { value = e.getTextNormalize(); } else { value = XMLUtil.getChildrenText(children); } map.put(key, value); } // 关闭流 in.close(); return map; } /** * 获取子结点的xml * @param children * @return */ public 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(); } }
import java.security.MessageDigest; /** * * @author jiangbo_lin * @2018/-09-14 * @desc:md5的工具类 */ public class MD5Util { 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" }; }
import javax.net.ssl.X509TrustManager; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; /** * * @author jiangbo_lin * @2018-09-14 * @desc:信任管理中心 */ public class FsTrustManager implements X509TrustManager { // 检查客户端证书 public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 检查服务器端证书 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 返回受信任的X509证书数组 public X509Certificate[] getAcceptedIssuers() { return null; } }
import org.apache.http.HttpEntity; 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.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.xml.parsers.ParserConfigurationException; import java.io.*; import java.net.ConnectException; import java.net.URL; import java.security.KeyStore; import java.text.SimpleDateFormat; import java.util.*; public class WeixinPayUtil { //获取日志记录器Logger,名字为本类类名 private static Logger logger = LoggerFactory.getLogger(WeixinPayUtil.class); /** * 生成随机数的方法 * * @return String 随机数 */ public static String generateNonceStr() { String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; String res = ""; for (int i = 0; i < 16; i++) { Random rd = new Random(); res += chars.charAt(rd.nextInt(chars.length() - 1)); } return res; } /** * 生成签名字符串 * * @param characterEncoding 编码格式 * @param parameters 参数 * @return String 签名字符串 */ public static String createSign(String characterEncoding, SortedMap<String, Object> parameters) { StringBuffer sb = new StringBuffer(); Iterator<Map.Entry<String, Object>> it = parameters.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next(); String key = (String) entry.getKey(); Object value = entry.getValue();//去掉带sign的项 if (null != value && !"".equals(value) && !"sign".equals(key) && !"key".equals(key)) { sb.append(key + "=" + value + "&"); } } sb.append("key=" + WeixinConfig.API_KEY); //注意sign转为大写 return MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); } /** * @param parameters 请求参数 * @return String xml格式的string * @Description:将请求参数转换为xml格式的string */ public static String getRequestXml(SortedMap<String, Object> parameters) { StringBuffer sb = new StringBuffer(); sb.append("<xml>"); Iterator<Map.Entry<String, Object>> iterator = parameters.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Object> entry = iterator.next(); String key = entry.getKey(); String value = (String) entry.getValue(); if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) { sb.append("<" + key + ">" + "<![CDATA[" + value + "]]></" + key + ">"); } else { sb.append("<" + key + ">" + value + "</" + key + ">"); } } sb.append("</xml>"); return sb.toString(); } /** * @param return_code 返回编码 * @param return_msg 返回信息 * @return * @Description:返回给微信的参数 */ public static String setXML(String return_code, String return_msg) { return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>"; } /** * 发送https请求 * * @param requestUrl 请求地址 * @param requestMethod 请求方式(GET、POST) * @param outputStr 提交的数据 * @return 返回微信服务器响应的信息 * @throws Exception */ public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) throws Exception { try { // 创建SSLContext对象,并使用我们指定的信任管理器初始化 TrustManager[] tm = {new FsTrustManager()}; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 从上述SSLContext对象中得到SSLSocketFactory对象 SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); conn.setSSLSocketFactory(ssf); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); // 设置请求方式(GET/POST) conn.setRequestMethod(requestMethod); conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); // 当outputStr不为null时向输出流写数据 if (null != outputStr) { OutputStream outputStream = conn.getOutputStream(); // 注意编码格式 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = conn.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; conn.disconnect(); return buffer.toString(); } catch (ConnectException ce) { logger.error("【微信支付-连接超时】:{}", ce); throw new RuntimeException("链接异常" + ce); } catch (Exception e) { logger.error("微信支付-https请求异常】:{}", e); throw new RuntimeException("https请求异常" + e); } } /** * https双向签名认证,用于支付申请退款 */ public static String payHttps(String url, String requestMethod, String xml) throws Exception { //商户id String MCH_ID = WeixinConfig.MCH_ID; //指定读取证书格式为PKCS12 KeyStore keyStore = KeyStore.getInstance("PKCS12"); String path = "F:\\software\\apache-tomcat-8.0.11\\webapps\\frq-app\\certificate\\apiclient_cert.p12"; //读取本机存放的PKCS12证书文件 FileInputStream instream = new FileInputStream(new File(path)); try { //指定PKCS12的密码(商户ID) keyStore.load(instream, MCH_ID.toCharArray()); } finally { instream.close(); } SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, MCH_ID.toCharArray()).build(); //指定TLS版本 SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); //设置httpclient的SSLSocketFactory CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); try { HttpPost httpost = new HttpPost(url); // 设置响应头信息 httpost.addHeader("Connection", "keep-alive"); httpost.addHeader("Accept", "*/*"); httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); httpost.addHeader("Host", "api.mch.weixin.qq.com"); httpost.addHeader("X-Requested-With", "XMLHttpRequest"); httpost.addHeader("Cache-Control", "max-age=0"); httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); httpost.setEntity(new StringEntity(xml, requestMethod)); CloseableHttpResponse response = httpclient.execute(httpost); try { HttpEntity entity = response.getEntity(); String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); EntityUtils.consume(entity); return jsonStr; } finally { response.close(); } } finally { httpclient.close(); } } /** * 检验API返回的数据里面的签名是否合法,避免数据在传输的过程中被第三方篡改 * * @param responseString API返回的XML数据字符串 * @return API签名是否合法 * @throws ParserConfigurationException * @throws IOException * @throws SAXException */ public static boolean checkIsSignValidFromResponseString(String responseString) { try { SortedMap<String, Object> map = XMLUtil.doXMLParse(responseString); //Map<String, Object> map = XMLParser.getMapFromXML(responseString); logger.debug(map.toString()); String signFromAPIResponse = map.get("sign").toString(); if ("".equals(signFromAPIResponse) || signFromAPIResponse == null) { logger.debug("API返回的数据签名数据不存在,有可能被第三方篡改!!!"); return false; } logger.debug("服务器回包里面的签名是:" + signFromAPIResponse); //清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名 map.put("sign", ""); //将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较 String signForAPIResponse = WeixinPayUtil.createSign("UTF-8", map); if (!signForAPIResponse.equals(signFromAPIResponse)) { //签名验不过,表示这个API返回的数据有可能已经被篡改了 logger.debug("API返回的数据签名验证不通过,有可能被第三方篡改!!!"); return false; } logger.debug("恭喜,API返回的数据签名验证通过!!!"); return true; } catch (Exception e) { return false; } } public static String getOrderIdByTime() { 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; } }
2:常量类
/** * * @desc:微信支付常量类 */ public class WeixinConstant { public static String SUCCESS = "SUCCESS"; //成功return_code public static String FAIL = "FAIL"; //失败return_code public static String PRODUCT_BODY = "xx时长充值卡"; // 商品描述 public static String FEBDA_PAY_BODY = "xx分答支付"; // 商品描述 public static String TRADE_TYPE = "JSAPI";//支付类型 JSAPI NATIVE(扫码支付) APP等等 public static String SIGN_TYPE = "MD5";//签名类型 public static String EPAY_DESC = "xxxx";//企业付款描述 }
/** * 支付方式常量表 */ public interface PayWay { /** 微信支付 */ String PAY_WX = "1"; /** 阿里支付宝支付 */ String PAY_ALI = "2"; }
/** * 微信配置常量类 */ public interface WeixinConfig { /** app应用id */ String APPID = ""; /** 微信支付分配的商户号 */ String MCH_ID = ""; /** API密钥 */ String API_KEY = ""; /** 商品描述 */ String BODY = "小猪佩奇身上纹,掌声送给社会人"; /** 支付回调地址 */ String NOTIFY_URL = ""; /** 交易类型 */ String TRADE_TYPE_APP = "APP"; /** 签名方式 */ String sign_type = "MD5"; /** -------- 微信接口访问地址----------- */ /** 微信统一下单地址 */ String UNIFIEDORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; /** 订单查询地址 */ String ORDERQUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery"; /** 关闭订单的地址 */ String CLOSEORDER_URL = "https://api.mch.weixin.qq.com/pay/closeorder"; /** 申请退款 */ String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund"; }
3:Controller 层代码
import com.alibaba.fastjson.JSONObject; import com.bphl.basic.model.vo.CheckBasicParaVo; import com.bphl.pay.wx.service.impl.WeixinAppPayServiceImpl; import com.bphl.pay.wx.utils.PayWay; import com.bphl.utils.ErrorCodeUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * * app微信支付入口 * @author jiangbo_lin * @date 2018-09-13 */ @Controller @RequestMapping("wxPay") public class WeixinAppPay { //获取日志记录器Logger,名字为本类类名 private static Logger logger = LoggerFactory.getLogger(WeixinAppPay.class); @Autowired private WeixinAppPayServiceImpl weixinAppPayService ; /** * 统一下订单入口 * @param outTradeNo 订单号 * @param checkBasicParaVo 基础校验对象 * @param flag 微信支付为:‘1’ 支付宝支付为:‘2’ * @return JSONObject 下订单的结果以及订单信息 */ @RequestMapping("unifiedOrder") @ResponseBody public JSONObject unifiedOrder(final String outTradeNo, CheckBasicParaVo checkBasicParaVo,String flag){ JSONObject returnValue = null ; try { returnValue = weixinAppPayService.unifiedOrder(outTradeNo,checkBasicParaVo,flag); }catch (Exception e){ returnValue = ErrorCodeUtil.ERROR_FAILED("统一下订单失败"); if(PayWay.PAY_WX.equals(flag)){ logger.error("【微信支付 - 统一下订单失败】原因为:" , e.getMessage()); }else if(PayWay.PAY_ALI.equals(flag)){ logger.error("【支付宝支付 - 统一下订单失败】原因为:" , e.getMessage()); } } return returnValue; } /** * 微信支付结果回调入口 * @param request * @param response */ @RequestMapping("wxCallback") public void wxCallback(HttpServletRequest request, HttpServletResponse response){ String str = weixinAppPayService.wxCallback(request); try { response.getWriter().write(str); } catch (IOException e) { logger.error("【微信支付结果回调 -- 返回失败】" + e.getMessage()); } } /** * 微信关闭订单 * @param outTradeNo 订单号 * @return */ @ResponseBody @RequestMapping("closeOrder") public JSONObject closeOrder(final String outTradeNo){ JSONObject returnValue = null ; try { returnValue = weixinAppPayService.closeOrder(outTradeNo); }catch (Exception e){ returnValue = ErrorCodeUtil.ERROR_FAILED("微信关闭订单失败"); logger.error("【微信支付 - 关闭订单失败】" + e.getMessage()); } return returnValue; } /** * 微信订单查询 * @param outTradeNo 订单号 * @param transactionId 微信订单号 * @return */ @RequestMapping("orderquery") @ResponseBody public JSONObject orderquery(final String outTradeNo,final String transactionId){ JSONObject returnValue = null ; try { returnValue = weixinAppPayService.orderquery(outTradeNo,transactionId); }catch (Exception e){ returnValue = ErrorCodeUtil.ERROR_FAILED("微信查询订单失败"); logger.error("【微信订单查询 -- 返回失败】" + e.getMessage()); } return returnValue; } /** * 微信申请退款 * @param outTradeNo * @param transactionId * @return */ @RequestMapping("weiXinPayOrderRefund") @ResponseBody public JSONObject weiXinPayOrderRefund(final String outTradeNo,final String transactionId){ JSONObject returnValue = null ; try { returnValue = weixinAppPayService.weiXinPayOrderRefund(outTradeNo,transactionId); }catch (Exception e){ returnValue = ErrorCodeUtil.ERROR_FAILED("微信申请退款失败"); logger.error("【微信订单查询 -- 返回失败】" + e.getMessage()); } return returnValue; } }
4 : Service 层代码
import com.alibaba.fastjson.JSONObject; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.domain.AlipayTradeAppPayModel; import com.alipay.api.request.AlipayTradeAppPayRequest; import com.alipay.api.response.AlipayTradeAppPayResponse; import com.bphl.basic.model.vo.CheckBasicParaVo; import com.bphl.constant.ErrorCode; import com.bphl.pay.alipay.utils.AliPayConfig; import com.bphl.pay.wx.mapper.WeixinAppPayMapper; import com.bphl.pay.wx.service.WeixinAppPayService; import com.bphl.pay.wx.utils.*; import com.bphl.utils.ErrorCodeUtil; import com.bphl.utils.LoginUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; @Service public class WeixinAppPayServiceImpl implements WeixinAppPayService { //获取日志记录器Logger,名字为本类类名 private static Logger logger = LoggerFactory.getLogger(WeixinAppPayServiceImpl.class); @Autowired private WeixinAppPayMapper weixinAppPayMapper; @Autowired private LoginUtil loginUtil; @Override public JSONObject unifiedOrder(final String outTradeNo, CheckBasicParaVo checkBasicParaVo, String flag) { JSONObject returnValue = null; if (PayWay.PAY_WX.equals(flag)) {//微信支付 try { //判断该用户是否已登录 /* boolean statusResult = loginUtil.checkUserStatus(checkBasicParaVo); if (!statusResult) { returnValue = ErrorCodeUtil.ERROR_FAILED(); //return returnValue; }*/ /* //查询订单id M_T_Order m_t_order = new M_T_Order(); m_t_order.setFuserid(checkBasicParaVo.getFuserid()); m_t_order.setFproId(proId);*/ InetAddress addr = InetAddress.getLocalHost(); String ip = addr.getHostAddress().toString(); String orderId = WeixinPayUtil.getOrderIdByTime(); Integer price = 1; logger.info("【微信支付-统一下单】开始,用户id:{},订单id:{},价格:{}", checkBasicParaVo.getFuserid(), orderId, price); //String orderId = weixinAppPayMapper.getOrderId(m_t_order); SortedMap<String, Object> parameters = prepareOrder(ip, orderId, price); parameters.put("sign", WeixinPayUtil.createSign("utf-8", parameters)); // sign签名 key String requestXML = WeixinPayUtil.getRequestXml(parameters);// 生成xml格式字符串 String responseStr = WeixinPayUtil.httpsRequest(WeixinConfig.UNIFIEDORDER_URL, "POST", requestXML);// 带上post // 检验API返回的数据里面的签名是否合法,避免数据在传输的过程中被第三方篡改 if (!WeixinPayUtil.checkIsSignValidFromResponseString(responseStr)) { logger.error("【微信统一下单失败,签名可能被篡改】" + responseStr); returnValue = ErrorCodeUtil.ERROR_FAILED("统一下单失败"); return returnValue; } // 解析结果 resultStr SortedMap<String, Object> resutlMap = XMLUtil.doXMLParse(responseStr); if (resutlMap != null && WeixinConstant.FAIL.equals(resutlMap.get("return_code"))) { logger.error("【微信统一下单失败】,订单编号:{},失败原因:{}", orderId, resutlMap.get("return_msg")); returnValue = ErrorCodeUtil.ERROR_FAILED("统一下单失败"); return returnValue; } // 商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再在APP里面调起支付。 SortedMap<String, Object> map = buildClientJson(resutlMap); map.put("outTradeNo", orderId); JSONObject data = (JSONObject) JSONObject.toJSON(map); returnValue = ErrorCodeUtil.ERROR_SUCCESS(data.toString()); logger.info("【微信统一下单成功】订单号为:{}", orderId); } catch (Exception e) { logger.error("【微信统一下单失败】,用户id:{},商品id:{},失败原因:{}", checkBasicParaVo.getFuserid(), outTradeNo, e.getMessage()); returnValue = ErrorCodeUtil.ERROR_FAILED("统一下单失败"); return returnValue; } return returnValue; } else if (PayWay.PAY_ALI.equals(flag)) {//支付宝支付 //实例化客户端 AlipayClient alipayClient = new DefaultAlipayClient(AliPayConfig.GATEWAY_URL, AliPayConfig.APP_ID, AliPayConfig.PRIVATE_KEY, AliPayConfig.FORMAT, AliPayConfig.CHARSET, AliPayConfig.ALI_PUBLIC_KEY, AliPayConfig.SIGN_TYPE); //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。 String orderId = WeixinPayUtil.getOrderIdByTime(); AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); model.setBody("我是测试数据"); model.setSubject("App支付测试Java"); model.setOutTradeNo(orderId); model.setTimeoutExpress("30m"); model.setTotalAmount("0.01"); model.setProductCode("QUICK_MSECURITY_PAY"); request.setBizModel(model); request.setNotifyUrl(AliPayConfig.NOTIFY_URL); try { //这里和普通的接口调用不同,使用的是sdkExecute AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request); //就是orderString 可以直接给客户端请求,无需再做处理。 returnValue = new JSONObject(); returnValue.put("errCode", ErrorCode.SUCCESS_RETRUN); returnValue.put("errMsg", "ok"); returnValue.put("data", response.getBody()); logger.info("【支付宝统一下单 --- 成功 】,订单号为:{}", orderId); return returnValue; } catch (AlipayApiException e) { e.printStackTrace(); returnValue = ErrorCodeUtil.ERROR_FAILED("下单失败"); return returnValue; } } else { returnValue = ErrorCodeUtil.ERROR_FAILED("未知错误"); return returnValue; } } /** * 生成订单信息 * * @param ip * @param orderId * @return */ private SortedMap<String, Object> prepareOrder(String ip, String orderId, int price) { SortedMap<String, Object> orderMap = new TreeMap<>(); orderMap.put("appid", WeixinConfig.APPID); orderMap.put("mch_id", WeixinConfig.MCH_ID); orderMap.put("nonce_str", WeixinPayUtil.generateNonceStr()); orderMap.put("body", WeixinConfig.BODY); orderMap.put("out_trade_no", orderId); //Integer payMoney = new BigDecimal(Integer.parseInt(model.getMoney())).multiply(newBigDecimal(100)).intValue(); orderMap.put("total_fee", price); orderMap.put("spbill_create_ip", ip); orderMap.put("notify_url", WeixinConfig.NOTIFY_URL); orderMap.put("trade_type", WeixinConfig.TRADE_TYPE_APP); orderMap.put("sign_type", WeixinConfig.sign_type); return MapUtils.sortMap(orderMap); } /** * 生成预付快订单完成,返回给android,ios唤起微信所需要的参数。 * * @param resutlMap * @return */ private SortedMap<String, Object> buildClientJson(Map<String, Object> resutlMap) { // 获取微信返回的签名 Map<String, Object> params = new TreeMap<>(); params.put("appid", WeixinConfig.APPID); params.put("noncestr", WeixinPayUtil.generateNonceStr()); params.put("package", "Sign=WXPay"); params.put("partnerid", WeixinConfig.MCH_ID); params.put("prepayid", resutlMap.get("prepay_id")); params.put("timestamp", DateUtils.getTimeStamp()); // 10 位时间戳 // key ASCII排序 // 这里用treemap也是可以的 可以用treemap // TODO SortedMap<String, Object> sortMap = MapUtils.sortMap(params); sortMap.put("package", "Sign=WXPay"); // paySign的生成规则和Sign的生成规则同理 String paySign = WeixinPayUtil.createSign("utf-8", sortMap); sortMap.put("sign", paySign); return MapUtils.sortMap(sortMap); } @Override public String wxCallback(HttpServletRequest request) { try { String responseStr = parseWeixinCallback(request); Map<String, Object> map = XMLUtil.doXMLParse(responseStr); logger.info("【微信支付结果回调】-- 参数集合:{}", map.toString()); // 校验签名 防止数据泄漏导致出现“假通知”,造成资金损失 if (!WeixinPayUtil.checkIsSignValidFromResponseString(responseStr)) { logger.error("微信回调失败,签名可能被篡改 " + responseStr); return WeixinPayUtil.setXML(WeixinConstant.FAIL, "invalid sign"); } if (WeixinConstant.FAIL.equalsIgnoreCase(map.get("result_code").toString())) { logger.error("微信回调失败的原因:" + responseStr); return WeixinPayUtil.setXML(WeixinConstant.FAIL, "weixin pay fail"); } if (WeixinConstant.SUCCESS.equalsIgnoreCase(map.get("result_code").toString())) { // --- 业务逻辑处理 // 商户订单号 String outTradeNo = (String) map.get("out_trade_no"); // 微信支付订单号 String transactionId = (String) map.get("transaction_id"); // 订单总金额,单位为分 String totlaFee = (String) map.get("total_fee"); Integer totalPrice = Integer.valueOf(totlaFee) / 100;//服务器这边记录的是钱的元 logger.info("【微信支付--支付回调】返回微信成功,参数为:{}", map.toString()); /* //获取该订单的处理信息 M_T_Order m_t_order = weixinAppPayMapper.getOrderInfo(outTradeNo); if(totalPrice != Integer.parseInt(m_t_order.getFprice())){ return WeixinPayUtil.setXML(WeixinConstant.FAIL, "totlaFee is error"); } //如果该订单已经处理过 if("已支付".equals(m_t_order.getFstatus())){ return WeixinPayUtil.setXML(WeixinConstant.SUCCESS, "OK"); } M_T_Order order = new M_T_Order(); order.setFid(outTradeNo); order.setFstatus("已支付"); order.setFlag(m_t_order.getFlag()); order.setTransactionId(transactionId); int result = weixinAppPayMapper.updateOrder(order); // 告诉微信服务器,我收到信息了,不要在调用回调action了 logger.info("【微信支付结果回调】 -- 成功:"+responseStr); if (result==1) { return WeixinPayUtil.setXML(WeixinConstant.SUCCESS, "OK"); } else { return WeixinPayUtil.setXML(WeixinConstant.FAIL, "update bussiness outTrade fail"); }*/ return WeixinPayUtil.setXML(WeixinConstant.SUCCESS, "OK"); } } catch (Exception e) { logger.error("【微信支付结果回调异常】-- " + e.getMessage()); return WeixinPayUtil.setXML(WeixinConstant.FAIL, "weixin pay server exception"); } return WeixinPayUtil.setXML(WeixinConstant.FAIL, "weixin pay fail"); } /** * 解析微信回调参数 * * @param request * @return * @throws IOException */ private String parseWeixinCallback(HttpServletRequest request) throws IOException { // 获取微信调用我们notify_url的返回信息 String result = ""; InputStream inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); try { byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } result = new String(outSteam.toByteArray(), "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { if (outSteam != null) { outSteam.close(); outSteam = null; // help GC } if (inStream != null) { inStream.close(); inStream = null;// help GC } } catch (IOException e) { e.printStackTrace(); } } return result; } @Override public JSONObject orderquery(final String outTradeNo, final String transactionId) { JSONObject returnValue = null; try { //组装查询订单的参数 SortedMap<String, Object> orderData = prepareOrderData(outTradeNo, transactionId); String requestXML = WeixinPayUtil.getRequestXml(orderData);// 生成xml格式字符串 String responseStr = WeixinPayUtil.httpsRequest(WeixinConfig.ORDERQUERY_URL, "POST", requestXML); SortedMap<String, Object> responseMap = XMLUtil.doXMLParse(responseStr);// 解析响应xml格式字符串 // 校验响应结果return_code if (WeixinConstant.FAIL.equalsIgnoreCase(responseMap.get("return_code").toString())) { returnValue = ErrorCodeUtil.ERROR_FAILED(responseMap.get("return_msg").toString()); return returnValue; } // 校验业务结果result_code if (WeixinConstant.FAIL.equalsIgnoreCase(responseMap.get("result_code").toString())) { returnValue = ErrorCodeUtil.ERROR_FAILED(responseMap.get("err_code_des").toString()); return returnValue; } // 校验签名 if (!WeixinPayUtil.checkIsSignValidFromResponseString(responseStr)) { returnValue = ErrorCodeUtil.ERROR_FAILED("签名错误"); return returnValue; } // 判断支付状态 String tradeState = responseMap.get("trade_state").toString(); if (tradeState != null && tradeState.equals("SUCCESS")) { returnValue = new JSONObject(); returnValue.put("errCode", ErrorCode.SUCCESS_RETRUN); returnValue.put("errMsg", "订单支付成功"); returnValue.put("data", ""); return returnValue; } else if (tradeState == null) { returnValue = ErrorCodeUtil.ERROR_FAILED("获取订单状态失败"); return returnValue; } else if (tradeState.equals("REFUND")) { returnValue = ErrorCodeUtil.ERROR_FAILED("转入退款"); return returnValue; } else if (tradeState.equals("NOTPAY")) { returnValue = ErrorCodeUtil.ERROR_FAILED("未支付"); return returnValue; } else if (tradeState.equals("CLOSED")) { returnValue = ErrorCodeUtil.ERROR_FAILED("已关闭"); return returnValue; } else if (tradeState.equals("REVOKED")) { returnValue = ErrorCodeUtil.ERROR_FAILED("已撤销(刷卡支付)"); return returnValue; } else if (tradeState.equals("USERPAYING")) { returnValue = ErrorCodeUtil.ERROR_FAILED("用户支付中"); return returnValue; } else if (tradeState.equals("PAYERROR")) { returnValue = ErrorCodeUtil.ERROR_FAILED("支付失败"); return returnValue; } else { returnValue = ErrorCodeUtil.ERROR_FAILED("未知的失败状态"); return returnValue; } } catch (Exception e) { logger.error("【微信支付 - 订单查询失败】,原因为" + e.getMessage()); returnValue = ErrorCodeUtil.ERROR_FAILED("订单查询失败"); return returnValue; } } private SortedMap<String, Object> prepareOrderData(final String outTradeNo, final String transactionId) { Map<String, Object> queryParams = new TreeMap<>(); // 微信的订单号,优先使用 if (null == outTradeNo || outTradeNo.length() == 0) { queryParams.put("appid", WeixinConfig.APPID); queryParams.put("mch_id", WeixinConfig.MCH_ID); queryParams.put("transaction_id", transactionId); queryParams.put("nonce_str", WeixinPayUtil.generateNonceStr()); } else { queryParams.put("appid", WeixinConfig.APPID); queryParams.put("mch_id", WeixinConfig.MCH_ID); queryParams.put("out_trade_no", outTradeNo); queryParams.put("nonce_str", WeixinPayUtil.generateNonceStr()); } // key ASCII 排序 SortedMap<String, Object> sortMap = MapUtils.sortMap(queryParams); // MD5签名 String createSign = WeixinPayUtil.createSign("UTF-8", sortMap); sortMap.put("sign", createSign); return sortMap; } public JSONObject closeOrder(final String outTradeNo) { JSONObject returnValue = null; try { //组装关闭订单的数据 SortedMap<String, Object> closeData = prepareCloseOrderData(outTradeNo); //把集合转成xml格式的字符串 String requestXml = WeixinPayUtil.getRequestXml(closeData); String responseStr = WeixinPayUtil.httpsRequest(WeixinConfig.CLOSEORDER_URL, "POST", requestXml); //把返回的xml字符串转成map集合 SortedMap<String, Object> responseMap = XMLUtil.doXMLParse(responseStr);// 解析响应xml格式字符串 // 校验响应结果return_code if (WeixinConstant.FAIL.equalsIgnoreCase(responseMap.get("return_code").toString())) { returnValue = ErrorCodeUtil.ERROR_FAILED(responseMap.get("return_code").toString()); return returnValue; } //校验签名 if (!WeixinPayUtil.checkIsSignValidFromResponseString(responseStr)) { returnValue = ErrorCodeUtil.ERROR_FAILED("订单关闭失败"); return returnValue; } // 校验业务结果result_code if (WeixinConstant.FAIL.equalsIgnoreCase(responseMap.get("result_code").toString())) { returnValue = ErrorCodeUtil.ERROR_FAILED(responseMap.get("return_code").toString()); return returnValue; } else { returnValue = ErrorCodeUtil.ERROR_FAILED("订单关闭成功"); return returnValue; } } catch (Exception e) { logger.error("【微信支付-关闭订单失败】,原因为:{}", e.getMessage()); returnValue = ErrorCodeUtil.ERROR_FAILED("订单关闭失败"); return returnValue; } } private SortedMap<String, Object> prepareCloseOrderData(String outTradeNo) { SortedMap<String, Object> closeData = new TreeMap<>(); closeData.put("appid", WeixinConfig.APPID); closeData.put("mch_id", WeixinConfig.MCH_ID); closeData.put("out_trade_no", outTradeNo); closeData.put("nonce_str", WeixinPayUtil.generateNonceStr()); SortedMap<String, Object> sortedMap = MapUtils.sortMap(closeData); String createSign = WeixinPayUtil.createSign("utf-8", sortedMap); sortedMap.put("sign", createSign); return sortedMap; } @Override public JSONObject weiXinPayOrderRefund(final String outTradeNo, final String transactionId) { JSONObject returnValue = null; //退款订单号 String outRefundNo = "2018092748152"; //订单金额 int totalFee = 1; //退款金额 int refundFee = 1; try { //组装退款的数据 SortedMap<String, Object> refundData = prepareRefundData(outTradeNo, transactionId, outRefundNo, totalFee, refundFee); //把集合转成xml格式的字符串 String requestXml = WeixinPayUtil.getRequestXml(refundData); String responseStr = WeixinPayUtil.payHttps(WeixinConfig.REFUND_URL, "utf-8", requestXml); //把返回的xml字符串转成map集合 SortedMap<String, Object> responseMap = XMLUtil.doXMLParse(responseStr);// 解析响应xml格式字符串 // 校验响应结果return_code if (WeixinConstant.FAIL.equalsIgnoreCase(responseMap.get("return_code").toString())) { returnValue = ErrorCodeUtil.ERROR_FAILED(responseMap.get("err_code_des").toString()); return returnValue; } //校验签名 if (!WeixinPayUtil.checkIsSignValidFromResponseString(responseStr)) { returnValue = ErrorCodeUtil.ERROR_FAILED("退款失败"); return returnValue; } // 校验业务结果result_code if (WeixinConstant.FAIL.equalsIgnoreCase(responseMap.get("result_code").toString())) { returnValue = ErrorCodeUtil.ERROR_FAILED(responseMap.get("err_code_des").toString()); return returnValue; } else if (WeixinConstant.SUCCESS.equalsIgnoreCase(responseMap.get("result_code").toString())) { returnValue = ErrorCodeUtil.ERROR_FAILED("退款成功"); return returnValue; } } catch (Exception e) { returnValue = ErrorCodeUtil.ERROR_FAILED("退款失败,请稍后再试"); return returnValue; } return returnValue; } private SortedMap<String, Object> prepareRefundData(String outTradeNo, String transactionId, String outRefundNo, int totalFee, int refundFee) { SortedMap<String, Object> refundData = new TreeMap<>(); refundData.put("appid", WeixinConfig.APPID); refundData.put("mch_id", WeixinConfig.MCH_ID); // 微信的订单号,优先使用 if (null == outTradeNo || outTradeNo.length() == 0) { refundData.put("transaction_id", transactionId); } else { refundData.put("out_trade_no", outTradeNo); } refundData.put("out_refund_no", outRefundNo); refundData.put("total_fee", totalFee); refundData.put("refund_fee", refundFee); refundData.put("nonce_str", WeixinPayUtil.generateNonceStr()); //排序 SortedMap<String, Object> sortedMap = MapUtils.sortMap(refundData); String createSign = WeixinPayUtil.createSign("utf-8", sortedMap); sortedMap.put("sign", createSign); return sortedMap; } }