前言:
现在的APP的离不开微信支付, 现在项目里接入微信支付 , 微信支付的官方文档是:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1 。 也很明了,这里是我的示例。
作为客户端, 需要接入的功能大概四个:
1 下预购单, 这里生成签名去请求微信, 返回预购单交易号。 再拼接客户端需要的对象,让客户端发起支付。
2 回调接口, 当APP完成支付, 微信会调用商户的回调接口, 告诉回调结果。
3 退款, 发起的支付 需要退款, 这里需要下载证书。
4 查询订单结果, 回调可能失败, 这时候需要商户服务端主动去查询 订单处理结果。
代码:
1.1 微信工具类 生成签名, 验签等
package com.yupaopao.trade.api.gateway.utils; import com.yupaopao.trade.api.gateway.code.SignType; import com.yupaopao.trade.api.gateway.code.WXPayConstants; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.input.SAXBuilder; import org.w3c.dom.Node; import org.w3c.dom.NodeList; 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 java.io.*; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.URL; import java.security.MessageDigest; import java.util.*; public class WxUtil { /** * XML格式字符串转换为Map * * @param strXML XML字符串 * @return XML数据转换后的Map * @throws Exception */ public static Map<String, String> xmlToMap(String strXML) throws Exception { Map<String, String> data = new HashMap<String, String>(); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder(); InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8")); org.w3c.dom.Document doc = documentBuilder.parse(stream); doc.getDocumentElement().normalize(); NodeList nodeList = doc.getDocumentElement().getChildNodes(); for (int idx=0; idx<nodeList.getLength(); ++idx) { Node node = nodeList.item(idx); if (node.getNodeType() == Node.ELEMENT_NODE) { org.w3c.dom.Element element = (org.w3c.dom.Element) node; data.put(element.getNodeName(), element.getTextContent()); } } try { stream.close(); } catch (Exception ex) { } return data; } /** * 将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, 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, SignType signType) throws Exception { String sign = generateSignature(data, key, signType); data.put(WXPayConstants.FIELD_SIGN, sign); return mapToXml(data); } /** * 判断签名是否正确 * * @param xmlStr XML格式数据 * @param key API密钥 * @return 签名是否正确 * @throws Exception */ public static boolean isSignatureValid(String xmlStr, String key) 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, 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, SignType.MD5); } /** * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。 * * @param data 待签名数据 * @param key API密钥 * @param signType 签名方式 * @return 签名 */ public static String generateSignature(final Map<String, String> data, String key, 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 (SignType.MD5.equals(signType)) { return MD5(sb.toString()).toUpperCase(); } else if (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(); } /** * xml解析 * @param strxml * @return * @throws Exception */ public static Map<String, String> doXMLParse(String strxml) throws Exception { Map<String,String> m = new HashMap<String,String>(); try { strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); if(null == strxml || "".equals(strxml)) { return null; } 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 k = e.getName(); String v = ""; List children = e.getChildren(); if(children.isEmpty()) { v = e.getTextNormalize(); } else { v = getChildrenText(children); } m.put(k, v); } //关闭流 in.close(); } catch (Exception e) { e.printStackTrace(); } return m; } /** * 获取子节点数据 * @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(getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } //发起微信支付请求 public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) { try { URL url = new URL(requestUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 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) { ce.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 返回给微信服务端的xml * @param return_code * @return */ public static String returnXML(String return_code) { return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"; } //Map转xml数据 public static String GetMapToXML(Map<String,String> param){ StringBuffer sb = new StringBuffer(); sb.append("<xml>"); for (Map.Entry<String,String> entry : param.entrySet()) { sb.append("<"+ entry.getKey() +">"); sb.append(entry.getValue()); sb.append("</"+ entry.getKey() +">"); } sb.append("</xml>"); return sb.toString(); } /** * 获取Ip * @return */ public static String GetIp() { InetAddress ia=null; try { ia= InetAddress.getLocalHost(); String localip=ia.getHostAddress(); return localip; } catch (Exception e) { return null; } } public static String getRequestXml(SortedMap<String,String> parameters){ StringBuffer sb = new StringBuffer(); sb.append("<xml>"); Set es = parameters.entrySet(); Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); String v = (String)entry.getValue(); if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) { sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">"); }else { sb.append("<"+k+">"+v+"</"+k+">"); } } sb.append("</xml>"); return sb.toString(); } }
2 service实现类
package com.yupaopao.trade.gateway.service.impl; import com.yupaopao.trade.api.account.response.WXGzhResponse; import com.yupaopao.trade.api.account.response.WXPayResponse; import com.yupaopao.trade.api.gateway.code.*; import com.yupaopao.trade.api.gateway.request.PayRequest; import com.yupaopao.trade.api.gateway.request.RefundRequest; import com.yupaopao.trade.api.gateway.request.WeixinOrderFindRequest; import com.yupaopao.trade.api.gateway.sdk.qqpay.config.ClientCustomSSL; import com.yupaopao.trade.api.gateway.utils.WxUtil; import com.yupaopao.trade.api.payment.code.PaymentConstant; import com.yupaopao.trade.api.payment.dto.YppException; import com.yupaopao.trade.api.utils.IdWorker; import com.yupaopao.trade.common.config.ThirdUrlConfig; import com.yupaopao.trade.common.exception.TradeException; import com.yupaopao.trade.domain.mapper.PayMapper; import com.yupaopao.trade.domain.mapper.PaymentDetailMapper; import com.yupaopao.trade.domain.mapper.RefundDetailMapper; import com.yupaopao.trade.domain.model.PaymentDetail; import com.yupaopao.trade.domain.model.RefundDetail; import com.yupaopao.trade.gateway.service.WxPayService; import com.yupaopao.trade.payment.service.TradeService; 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.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.ResourceUtils; import javax.net.ssl.SSLContext; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.security.KeyStore; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; /** * Created by zhuliangxing on 2017/4/21. */ @Service public class WxpayServiceImpl implements WxPayService { private static final Logger LOGGER = LoggerFactory.getLogger(WxpayServiceImpl.class); @Autowired private PayMapper payMapper; @Autowired private TradeService tradeService; @Value("${third.callback.net_url}") private String notifyUrl; @Autowired private PaymentDetailMapper paymentDetailsMapper; @Autowired private RefundDetailMapper refundDetailMapper; /** * 统一下单 * * @param request * @return */ public WXPayResponse wxPay(PayRequest request) { WXPayResponse response = new WXPayResponse(); PaymentDetail detail = null; Map<String, String> data = null; try { // 组装XML String xml = WXParamGenerate(request.getSubject(), request.getOutTradeNo(), request.getTotalFee()); // 发送http请求到微信服务端,获取返回的参数 String res = WxUtil.httpsRequest(WXPayConstants.WX_PAYURL, "POST", xml); data = WxUtil.doXMLParse(res); detail = new PaymentDetail(); detail.setCallbackStatus(CallbackType.unCall.getStatusValue()); detail.setUserId(request.getUserId()); detail.setType(PayType.wxPay.getStatusValue()); detail.setPaymentId(request.getPaymentId()); detail.setOrderType(request.getOrderType()); detail.setTotalFee(request.getTotalFee()); detail.setStatus(PayStatus.unPay.getStatusValue()); detail.setOutTradeNo(request.getOutTradeNo()); detail.setSubject(request.getSubject()); Map<String, String> param = new HashMap<String, String>(); param.put("appid", WXPayConstants.APP_ID); param.put("partnerid", WXPayConstants.MCH_ID); param.put("prepayid", data.get("prepay_id")); param.put("noncestr", data.get("nonce_str")); String timeStamp = System.currentTimeMillis() / 1000 + ""; param.put("timestamp", timeStamp); param.put("package", "Sign=WXPay"); String sign = WxUtil.generateSignature(param, WXPayConstants.WX_PARTNERKEY, SignType.MD5); response.setAppId(WXPayConstants.APP_ID); response.setPartnerId(WXPayConstants.MCH_ID); response.setTimestamp(timeStamp); response.setNoncestr(data.get("nonce_str")); response.setSign(sign); response.setPrepayId(data.get("prepay_id")); paymentDetailsMapper.insertPayment(detail); return response; } catch (Exception e) { throw new YppException(PayException.xmlParseError); } } /** * 微信统一下单参数设置 * * @param description * @param orderOutId * @param totalFee * @return * @throws Exception */ public String WXParamGenerate(String description, String orderOutId, Long totalFee) throws Exception { Map<String, String> param = new HashMap<String, String>(); param.put("appid", WXPayConstants.APP_ID); param.put("mch_id", WXPayConstants.MCH_ID); param.put("nonce_str", WxUtil.generateNonceStr()); param.put("body", description); param.put("out_trade_no", orderOutId); param.put("total_fee", totalFee + ""); param.put("spbill_create_ip", WxUtil.GetIp()); param.put("notify_url", notifyUrl + WXPayConstants.NOTIFU_URL); param.put("trade_type", "APP"); String sign = WxUtil.generateSignature(param, WXPayConstants.WX_PARTNERKEY, SignType.MD5); param.put("sign", sign); return WxUtil.GetMapToXML(param); } /** * 拼接微信退款XML * * @param request * @return * @throws Exception */ public static SortedMap<String, String> WXRefundParamGenerate(RefundRequest request) { SortedMap<String, String> param = new TreeMap<String, String>(); try { param.put("appid", WXPayConstants.APP_ID); param.put("mch_id", WXPayConstants.MCH_ID); param.put("nonce_str", WxUtil.generateNonceStr()); param.put("out_trade_no", request.getOutTradeNo()); // 支付网关生成订单流水 param.put("out_refund_no", String.valueOf(IdWorker.nextId())); param.put("total_fee", String.valueOf(request.getTotalFee()));// 单位为分 param.put("refund_fee", String.valueOf(request.getRefundFee()));// 单位为分 param.put("op_user_id", WXPayConstants.MCH_ID);// 操作人员,默认为商户账号 String sign = WxUtil.generateSignature(param, WXPayConstants.WX_PARTNERKEY, SignType.MD5); param.put("sign", sign); } catch (Exception e) { throw new YppException(PayException.signError); } return param; } /** * 解析微信返回的xml 参数 * * @param request */ public Map<String, String> xmlParserCallback(HttpServletRequest request) { Map<String, String> map = null; BufferedReader reader = null; String line = ""; String xmlString = null; try { reader = request.getReader(); StringBuffer inputString = new StringBuffer(); while ((line = reader.readLine()) != null) { inputString.append(line); } xmlString = inputString.toString(); request.getReader().close(); LOGGER.info("----接收到的数据如下:---" + xmlString); map = WxUtil.doXMLParse(xmlString); } catch (Exception e) { throw new YppException(PayException.xmlParseError); } return map; } /** * IO解析获取微信的数据 * * @param request * @return */ public String getXmlString(HttpServletRequest request) { BufferedReader reader = null; String line = ""; String xmlString = null; try { reader = request.getReader(); StringBuffer inputString = new StringBuffer(); while ((line = reader.readLine()) != null) { inputString.append(line); } xmlString = inputString.toString(); } catch (Exception e) { throw new YppException(PayException.xmlParseError); } return xmlString; } /** * 微信回调 * * @param request * @return */ public String wxNotify(HttpServletRequest request) { LOGGER.info("微信正在回调》》》》》》》》》"); PaymentDetail detail = null; String xmlString = ""; String lastXml = ""; try { xmlString = getXmlString(request); LOGGER.info("微信返回的回调结果是:::::::" + xmlString); // 先解析返回的数据 Map<String, String> dataMap = WxUtil.xmlToMap(xmlString); String returnCode = dataMap.get("return_code"); // 通信成功 if (ReturnStatus.success.getStatusValue().equals(returnCode)) { LOGGER.info("通信成功++++++++++++"); // 验证通过才能记到流水表中,否则不计入 if (WxUtil.isSignatureValid(xmlString, WXPayConstants.WX_PARTNERKEY)) { try { detail = new PaymentDetail(); detail.setTradeNo(dataMap.get("transaction_id")); detail.setOutTradeNo(dataMap.get("out_trade_no")); if (dataMap.get("result_code").equals(ReturnStatus.success)) { detail.setStatus(PayStatus.paySuccess.getStatusValue()); } else { detail.setStatus(PayStatus.payFail.getStatusValue()); } detail.setType(PayType.wxPay.getStatusValue()); // 设置为已经回调 detail.setCallbackStatus(CallbackType.callable.getStatusValue()); paymentDetailsMapper.updatePayment(detail); } catch (Exception e) { e.printStackTrace(); } } tradeService.dealWithPayCallBack(dataMap.get("out_trade_no").toString(), dataMap.get("transaction_id").toString(), PaymentConstant.PayCallBackStatus.SUCCESS, PaymentConstant.TradeAction.WEIXIN_ACCOUNT); lastXml = WxUtil.returnXML(ReturnStatus.success.getStatusValue()); } else { lastXml = WxUtil.returnXML(ReturnStatus.fail.getStatusValue()); } } catch (Exception e) { throw new TradeException(GatewayCode.PayBackError); } LOGGER.info("最终给微信的结果是:" + lastXml); return lastXml; } /** * 微信退款 * * @param request * @return */ public String wxRefund(RefundRequest request) { RefundDetail detail = null; // 拼装Map SortedMap<String, String> map = WXRefundParamGenerate(request); // 拼装XML String requestXml = WxUtil.getRequestXml(map); // 获取返回数据 String refundResult = ""; try { refundResult = ClientCustomSSL.doRefund(WXPayConstants.WX_REFUND, requestXml); } catch (Exception e) { throw new TradeException(GatewayCode.RefundBackXmlError); } System.out.println("退款产生的json字符串:" + refundResult); // 转换为Map Map<String, String> responseMap = new HashMap<>(); try { responseMap = WxUtil.doXMLParse(refundResult); } catch (Exception e) { throw new TradeException(GatewayCode.RefundBackXmlError); } String returnCode = responseMap.get("return_code"); // 通信正常 if (returnCode.equals(ReturnStatus.success.getStatusValue())) { String resultCode = responseMap.get("result_code"); detail = new RefundDetail(); // 微信生成的退款ID String tradeNo = responseMap.get("out_refund_no"); detail.setTradeNo(tradeNo); detail.setRefundId(IdWorker.unique()); detail.setType(PayType.wxPay.getStatusValue()); detail.setUserId(request.getUserId()); detail.setSubject(responseMap.get("err_code_des")); detail.setorderOutId(request.getOutTradeNo()); detail.setRefundFee(request.getRefundFee()); detail.setTotalFee(request.getTotalFee()); detail.setOriginTradeNo(tradeNo); // 退款成功 if (resultCode.equals(ReturnStatus.success)) { detail.setStatus(PayStatus.paySuccess.getStatusValue()); payMapper.saveRefund(detail); return ReturnStatus.success.getStatusValue(); } else { detail.setStatus(PayStatus.payFail.getStatusValue()); refundDetailMapper.insertRefund(detail); return ReturnStatus.fail.getStatusValue(); } } else { throw new TradeException(GatewayCode.RefundHasGone); } } /** * 微信公众号支付下单 */ @Override public WXGzhResponse wxGZHPay(PayRequest request) { WXGzhResponse response = new WXGzhResponse(); PaymentDetail detail = null; Map<String, String> data = null; try { // 组装XML String xml = WXGZHParamGenerate(request.getSubject(), request.getOutTradeNo(), request.getTotalFee(), request.getPayExt().get("openId").toString()); // 发送http请求到微信服务端,获取返回的参数 String res = WxUtil.httpsRequest(WXPayConstants.WX_PAYURL, "POST", xml); data = WxUtil.doXMLParse(res); detail = new PaymentDetail(); detail.setCallbackStatus(CallbackType.unCall.getStatusValue()); detail.setUserId(request.getUserId()); detail.setType(PayType.wxPay.getStatusValue()); detail.setPaymentId(request.getPaymentId()); detail.setOrderType(request.getOrderType()); detail.setTotalFee(request.getTotalFee()); detail.setStatus(PayStatus.unPay.getStatusValue()); detail.setOutTradeNo(request.getOutTradeNo()); detail.setSubject(request.getSubject()); String timeStamp = System.currentTimeMillis() / 1000 + ""; Map<String, String> param = new HashMap<String, String>(); param.put("appId", WXPayConstants.WEIXIN_GZH_APPID); param.put("signType", "MD5"); param.put("nonceStr", data.get("nonce_str")); param.put("timeStamp", timeStamp); param.put("package", "prepay_id=" + data.get("prepay_id")); String sign = WxUtil.generateSignature(param, WXPayConstants.WEIXIN_GZH_KEY, SignType.MD5); response.setAppId(WXPayConstants.WEIXIN_GZH_APPID); response.setNonceStr(data.get("nonce_str")); response.setPaySign(sign); response.setTimeStamp(timeStamp); response.setWxpackage("prepay_id=" + data.get("prepay_id")); response.setSignType("MD5"); payMapper.saveDetail(detail); return response; } catch (Exception e) { e.printStackTrace(); throw new YppException(PayException.xmlParseError); } } /** * 微信公众号统一下单 * * @param description * @param orderOutId * @param totalFee * @return * @throws Exception */ public String WXGZHParamGenerate(String description, String orderOutId, Long totalFee, String openId) throws Exception { Map<String, String> param = new HashMap<String, String>(); param.put("appid", WXPayConstants.WEIXIN_GZH_APPID); param.put("mch_id", WXPayConstants.WEIXIN_GZH_MACHID); param.put("nonce_str", WxUtil.generateNonceStr()); param.put("body", description); param.put("out_trade_no", orderOutId); param.put("openid", openId); param.put("total_fee", totalFee + ""); param.put("spbill_create_ip", WxUtil.GetIp()); param.put("notify_url", notifyUrl + WXPayConstants.NOTIFU_URL); param.put("trade_type", "JSAPI"); String sign = WxUtil.generateSignature(param, WXPayConstants.WEIXIN_GZH_KEY, SignType.MD5); param.put("sign", sign); return WxUtil.GetMapToXML(param); } /** * 微信公众号账单查询 */ @Override public void wxGZHOrderFind(WeixinOrderFindRequest request) { Map<String, String> data = null; try { String xml = WXGZHOrderGenerate(request.getOutTradeNo()); String res = WxUtil.httpsRequest(WXPayConstants.WEIXIN_ORDER_FIND_URL, "POST", xml); data = WxUtil.doXMLParse(res); // 通信异常 if (!data.get("return_code").equals("SUCCESS")) { throw new TradeException(GatewayCode.CommunicationError); } if (!data.get("result_code").equals("SUCCESS")) { throw new TradeException(GatewayCode.OrderFindErrror); } if (!data.get("trade_state").equals("SUCCESS")) { throw new TradeException(GatewayCode.OrderPayError); } } catch (Exception e) { throw new TradeException(GatewayCode.PayXmlError); } } /** * 微信公众号生成订单。 * * @param orderOutId * @return * @throws Exception */ public String WXGZHOrderGenerate(String orderOutId) throws Exception { Map<String, String> param = new HashMap<String, String>(); param.put("appid", WXPayConstants.WEIXIN_GZH_APPID); param.put("mch_id", WXPayConstants.WEIXIN_GZH_MACHID); param.put("nonce_str", WxUtil.generateNonceStr()); param.put("out_trade_no", orderOutId); String sign = WxUtil.generateSignature(param, WXPayConstants.WEIXIN_GZH_KEY, SignType.MD5); param.put("sign", sign); return WxUtil.GetMapToXML(param); } }
3下单返回的参数model 给 APP
package com.yupaopao.trade.api.account.response; import com.alibaba.fastjson.JSONObject; public class WXPayResponse { private String appId; private String partnerId; private String wxpackage="Sign=WXPay"; private String noncestr; private String timestamp; private String sign; private String prepayId; public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public String getPartnerId() { return partnerId; } public void setPartnerId(String partnerId) { this.partnerId = partnerId; } public String getWxpackage() { return wxpackage; } public void setWxpackage(String wxpackage) { this.wxpackage = wxpackage; } public String getNoncestr() { return noncestr; } public void setNoncestr(String noncestr) { this.noncestr = noncestr; } public String getTimestamp() { return timestamp; } public void setTimestamp(String timestamp) { this.timestamp = timestamp; } public String getSign() { return sign; } public void setSign(String sign) { this.sign = sign; } public String getPrepayId() { return prepayId; } public void setPrepayId(String prepayId) { this.prepayId = prepayId; } @Override public String toString() { JSONObject json = new JSONObject(); json.put("appId", appId); json.put("partnerId", partnerId); json.put("wxpackage", wxpackage); json.put("noncestr", noncestr); json.put("timestamp", timestamp); json.put("sign", sign); json.put("prepayId", prepayId); return json.toString(); } }
/** * 微信支付回调 * * @param request * @param response * @return * @throws Exception */ @RequestMapping(method = RequestMethod.POST, value = "/wx/wxCallback") public YppResponse wxCallback(HttpServletRequest request, HttpServletResponse response) throws Exception { return YppResponse.success(wxPayService.wxNotify(request)); }