前言:

现在的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));
}

Java+微信支付(下预购单+回调+退款+查询账单)的更多相关文章

  1. JAVA微信支付~

    1,简单说明 现在好多项目上都需要用到微信支付接口,官方文档上也是简单的描述了下,技术不高深的真的难以理解(我自己看官方文档就看不懂),还是需要自己收集,总结, 网上看了好多 有些照着弄最后还是没法成 ...

  2. 通知url必须为直接可访问的url,不能携带参数 异步接收微信支付结果通知的回调地址 不能携带参数。 回调地址后是否可以加自定义参数 同步回调地址 异步回调地址 return_url和notify_url的区别

    [微信支付]微信小程序支付开发者文档 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_7 通知url必须为直接可访问的 ...

  3. 微信支付重复回调,java微信支付回调问题

    这几天一直在研究微信支付回调这个问题,发现之前微信支付回调都是正常的也没怎么在意,今天在自己项目上测试的时候发现相同的代码在我这个项目上微信支付回调老是重复执行导致支付成功之后的回调逻辑一直在执行,很 ...

  4. JAVA微信支付多次回调方法解决方案

    @WebServlet("/ActionServlet")public class PayWxOrderingReqCBS extends HttpServlet { public ...

  5. Java 微信支付分对接记录 (先享后付)

    微信支付分(先享后付)对接记录: 微信支付分对接步骤 填写开通支付分的申请表格 此步骤大概需要审核 1-3 个工作日; (模板-服务信息配置表-[先享后付免确认]-[商户名].xls) 填写商户信息 ...

  6. JAVA微信支付——微信公众号内支付 代码

    官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 微信PC二维码支付方式参考:https://www.cnblogs. ...

  7. JAVA微信支付——企业付款(企业向微信用户个人付款、转账)

    本地开发环境支付回调调试方法可以参考:https://www.cnblogs.com/pxblog/p/11623053.html 需要自行引入相关依赖 官方文档地址:https://pay.weix ...

  8. JAVA微信支付接口开发——支付

    微信支付接口开发--支付 这几天在做支付服务,系统接入了支付宝.微信.银联三方支付接口.个人感觉支付宝的接口开发较为简单,并且易于测试. 关于数据传输,微信是用xml,所以需要对xml进行解析. 1. ...

  9. JAVA微信支付代码(WeChatPay.java 才是调用类)

    微信官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html MD5Util.java package weixin; import java.se ...

随机推荐

  1. PROJECT | 四则运算UI设计 - PSP表格&需求分析

    PSP表格(TP版) 需求分析 [GUI编程语言选择] 考虑到Java编写GUI效率偏低且界面不算特别美观(即使有Windowbuilder插件帮助),所以我们使用控件更多,开发效率更高,具有集成开发 ...

  2. dubbo重连机制会不会造成错误

    dubbo在调用服务不成功时,默认会重试2次. Dubbo的路由机制,会把超时的请求路由到其他机器上,而不是本机尝试,所以 dubbo的重试机器也能一定程度的保证服务的质量. 但是如果不合理的配置重试 ...

  3. ECharts (mark)

    首页 文档 下载 实例 社区 工具 关于 2.0 EN ECharts 特性 特性 丰富的可视化类型 多种数据格式无需转换直接使用 千万数据的前端展现 移动端优化 多渲染方案,跨平台使用! 深度的交互 ...

  4. pd.Panel转化成json,然后再还原回来

    在使用tornado的write时候有一个需求,是将panel转化成json;而接收端再将json还原成panel格式. 尝试了很久,终于实现了. panel1 =pd.Panel({"on ...

  5. Delphi定时模拟键盘按键例程

    delphi模拟键盘按键实例delphi模拟键盘按键实例,只是模拟一个按键的例子而已.到一定时间按下模拟按下一个按键,delphi7编译通过. 10秒点击一下H键,其他键你们去找数值替换吧,网上大把的 ...

  6. duilib库分析2.第一篇UIManager

    DUiLib 源码分析 ——以UiLib 1.01版为分析目标--------------------------------------------------------------------- ...

  7. 单层感知机_线性神经网络_BP神经网络

    单层感知机 单层感知机基础总结很详细的博客 关于单层感知机的视频 最终y=t,说明经过训练预测值和真实值一致.下面图是sign函数 根据感知机规则实现的上述题目的代码 import numpy as ...

  8. vuecli脚手架+vue+vuex实现vue驱动的demo。

    哎呀呀呀,现在大家都要会Vue ||  React,否则感觉跟这个前端的世界脱节了一样. start: vue-cli这个构建工具大大降低了webpack的使用难度,支持热更新,有webpack-de ...

  9. 出现不不能引java.util.Date包的情况

    出现不不能引java.util.Date包的情况 那个时间段不能引,IDE的bug,等一会儿就好了 心得:很多时候没必要和bug死磕,因为真的不是你的问题.

  10. JAVA POI XSSFWorkbook导出扩展名为xlsx的Excel,附带weblogic 项目导出Excel文件错误的解决方案

    现在很多系统都有导出excel的功能,总结一下自己之前写的,希望能帮到其他人,这里我用的是XSSFWorkbook,我们项目在winsang 用的Tomcat,LInux上用的weblogic服务器, ...