700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 微信扫码支付 回调和退款(附带完整代码)

微信扫码支付 回调和退款(附带完整代码)

时间:2019-08-18 11:35:49

相关推荐

微信扫码支付 回调和退款(附带完整代码)

微信扫码支付

最近重构项目时,负责了支付模块,微信扫码支付(NATIVE)和 支付宝扫码支付,也是第一次接触,虽然根据官方文档和一些博客写出来了,但是遇到的问题却很多,走了很多弯路,浪费了很多精力和时间,抽出时间来记录一下,以后难免还是会用到。

支付宝扫码支付传送门

微信API

SDK与DEMO下载

微信开发流程:

开发前,商户必须在公众平台后台设置支付回调URL。URL实现的功能:接收用户扫码后微信支付系统回调的productidopenid

官网提供的流程图:

业务流程说明:

(1)商户后台系统根据微信支付规定格式生成二维码(规则见下文),展示给用户扫码。(2)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。(3)微信支付系统收到客户端请求,发起对商户后台系统支付回调URL的调用。调用请求将带productid和用户的openid等参数,并要求商户系统返回交数据包,详细请见"本节3.1回调数据输入参数"(4)商户后台系统收到微信支付系统的回调请求,根据productid生成商户系统的订单。(5)商户系统调用微信支付【统一下单API】请求下单,获取交易会话标识(prepay_id)(6)微信支付系统根据商户系统的请求生成预支付交易,并返回交易会话标识(prepay_id)。(7)商户后台系统得到交易会话标识prepay_id(2小时内有效)。(8)商户后台系统将prepay_id返回给微信支付系统。返回数据见"本节3.2回调数据输出参数"(9)微信支付系统根据交易会话标识,发起用户端授权支付流程。(10)用户在微信客户端输入密码,确认支付后,微信客户端提交支付授权。(11)微信支付系统验证后扣款,完成支付交易。(12)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。(13)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。(14)未收到支付通知的情况,商户后台系统调用【查询订单API】。(15)商户确认订单已支付后给用户发货

整个流程看下来其实就是用户通过订单信息和微信提供的统一下单API的接口直接进行http请求,然后微信会生成一个预支付交易,之后后返回一个url,将这里的url埋入二维码就可以了。到这里二维码就生成了,各位就可以消费了,消费之后微信会调支付回调接口,通知支付结果。这里提到的支付接口和回调接口需要在微信商户平台进行配置。

获取二维码:

将二维码返回前端展示 也二维码以文件格式输出到本地

private Map<String, Object> getPayCodeByWeChat(PayCodeRequest payCodeRequest) {log.info("获取微信二维码请求*******");Map<String, Object> resultMap = new HashMap<String, Object>();String currentUserID = null;String orderID = null;ResultParams makeCode = null;BizContentByWeChatPay bizContent = null;try {orderID = payCodeRequest.getOrderID();currentUserID = payCodeRequest.getCurrentUserID();SystemConfig queryOneSystemConfig = orderPayService.queryOneSystemConfig("cm2_weChatAccount");PayRecord queryOrderPayRecordByOrderID = orderPayService.queryPayRecordByOrderID(orderID);if (null != queryOrderPayRecordByOrderID && OrderPayStatusEnum.PAID.getValue() == queryOrderPayRecordByOrderID.getPayStatus()) {log.info("支付的类型:" + queryOrderPayRecordByOrderID.getPayType());log.debug("订单已存在支付过记录");resultMap.put("code", -20003);resultMap.put("desc", "订单已支付,无法再次操作");return resultMap;}bizContent = new BizContentByWeChatPay();String payAmount = payCodeRequest.getPayAmount().replace(",", "");bizContent.setBody(payCodeRequest.getValidityPeriod());bizContent.setNonce_str(CommonUtils.genId());bizContent.setOut_trade_no(CommonUtils.genPayId());bizContent.setSubject(payCodeRequest.getProductName());bizContent.setTotal_amount(new BigDecimal(payAmount));String configValue = queryOneSystemConfig.getConfigValue();makeCode = WeChatPayUtil.makeCode(configValue, bizContent);if (null == makeCode || !makeCode.getReturn_code().equals("SUCCESS") || !makeCode.getResult_code().equals("SUCCESS")) {resultMap.put("code", -20018);resultMap.put("desc", "获取微信支付二维码失败");payLogService.createPayLog(currentUserID, new Gson().toJson(bizContent), orderID, new Gson().toJson(makeCode), -1);return resultMap;}queryOneSystemConfig = orderPayService.queryOneSystemConfig("qrCodebyAddress");String code_url = makeCode.getCode_url();// 生成二维码String base64Code = QRCodeUtil.getBase64Code(code_url);// 有记录,且非已支付if (null != queryOrderPayRecordByOrderID) {queryOrderPayRecordByOrderID.setOrderID(payCodeRequest.getOrderID());queryOrderPayRecordByOrderID.setPayType(OrderPayTypeEnum.WECHAT.getValue());queryOrderPayRecordByOrderID.setSign(makeCode.getSign());queryOrderPayRecordByOrderID.setEditor(payCodeRequest.getCurrentUserID());queryOrderPayRecordByOrderID.setEditTime(new Date());queryOrderPayRecordByOrderID.setPayStatus(OrderPayStatusEnum.UNPAID.getValue());queryOrderPayRecordByOrderID.setQr_code(code_url);queryOrderPayRecordByOrderID.setNonce_str(bizContent.getNonce_str());queryOrderPayRecordByOrderID.setReturn_nonce_str(makeCode.getNonce_str());long longValue = (new BigDecimal(payAmount).multiply(new BigDecimal("100"))).longValue();queryOrderPayRecordByOrderID.setPayAmount(longValue);queryOrderPayRecordByOrderID.setPrepay_id(makeCode.getPrepay_id());queryOrderPayRecordByOrderID.setTrade_type(makeCode.getTrade_type());queryOrderPayRecordByOrderID.setOut_trade_no(bizContent.getOut_trade_no());orderPayService.updateOrderPayRecord(queryOrderPayRecordByOrderID);} else {PayRecord payRecord = new PayRecord();payRecord.setOrderID(payCodeRequest.getOrderID());payRecord.setPayType(OrderPayTypeEnum.WECHAT.getValue());payRecord.setSign(makeCode.getSign());payRecord.setOperator(payCodeRequest.getCurrentUserID());payRecord.setCreateTime(new Date());payRecord.setPayStatus(OrderPayStatusEnum.UNPAID.getValue());long longValue = (new BigDecimal(payAmount).multiply(new BigDecimal("100"))).longValue();payRecord.setPayAmount(longValue);payRecord.setQr_code(code_url);payRecord.setNonce_str(bizContent.getNonce_str());payRecord.setReturn_nonce_str(makeCode.getNonce_str());payRecord.setPrepay_id(makeCode.getPrepay_id());payRecord.setTrade_type(makeCode.getTrade_type());payRecord.setOut_trade_no(bizContent.getOut_trade_no());orderPayService.createOrderPayRecord(payRecord);}resultMap.put("code", 0);resultMap.put("desc", "获取成功");resultMap.put("data", base64Code);resultMap.put("out_trade_no", bizContent.getOut_trade_no());payLogService.createPayLog(currentUserID, new Gson().toJson(bizContent), orderID, new Gson().toJson(makeCode), 0);return resultMap;} catch (Exception e) {log.error("获取微信二维码处理异常*******", e);resultMap.put("code", -20018);resultMap.put("desc", "获取微信支付二维码失败");payLogService.createPayLog(currentUserID, new Gson().toJson(bizContent), orderID, "获取微信二维码处理异常", 1);return resultMap;}}

微信支付生成二维码和签名

签名生成的通用步骤如下:

第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。 特别注意以下重要规则: ◆ 参数名ASCII码从小到大排序(字典序); ◆ 如果参数的值为空不参与签名; ◆ 参数名区分大小写; ◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。 ◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段

第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。

key设置路径:微信商户平台(pay.)-->账户设置-->API安全-->密钥设置

代码展示:

public static ResultParams makeCode(String weChatAccountData, BizContentByWeChatPay bizContent){log.info("获取微信支付二维码********");log.info("aliPayAccountData:"+weChatAccountData);log.info("bizContent:"+new Gson().toJson(bizContent));try{WeChatAccount weChatAccount = new Gson().fromJson(weChatAccountData, WeChatAccount.class);String app_id = weChatAccount.getApp_id();String merchant_number = weChatAccount.getMerchant_number();String notifyUrl = weChatAccount.getNotifyUrl();String secret_key = weChatAccount.getSecret_key();String pay_path = weChatAccount.getPay_path();//商户订单号,也是订单号String out_trade_no = bizContent.getOut_trade_no();String body = bizContent.getBody();BigDecimal total_amount = bizContent.getTotal_amount();String nonce_str = bizContent.getNonce_str();int intAmount = total_amount.multiply(new BigDecimal("100")).intValue();String timeout_express = weChatAccount.getTimeout_express();//签名所需要的参数SortedMap<String,String> parameters = new TreeMap<String,String>();parameters.put("appid",app_id);//商户号parameters.put("mch_id",merchant_number);parameters.put("body",body);//SAPI -JSAPI支付 NATIVE -Native支付 APP -APP支付parameters.put("trade_type","NATIVE");//商户订单号parameters.put("out_trade_no",out_trade_no);//订单总金额(单位分)parameters.put("total_fee",String.valueOf(intAmount));//终端IP //支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IPparameters.put("spbill_create_ip",GetIp());//商品ID trade_type=NATIVE时,此参数必传。此参数为二维码中包含的商品ID,商户自行定义。parameters.put("product_id",out_trade_no);//异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数parameters.put("notify_url",notifyUrl);//随机字符串parameters.put("nonce_str",nonce_str);//接口所需要的参数PayParams order = new PayParams();order.setAppid(app_id);order.setMch_id(merchant_number);order.setBody(body);order.setTrade_type("NATIVE");order.setOut_trade_no(out_trade_no);order.setTotal_fee(intAmount);order.setSpbill_create_ip(GetIp());order.setProduct_id(out_trade_no);order.setNotify_url(notifyUrl);order.setNonce_str(nonce_str);if(null!=timeout_express && !"".equals(timeout_express)){Calendar c = Calendar.getInstance();c.setTime(new Date());int parseTime = Integer.parseInt(timeout_express);c.add(Calendar.SECOND, parseTime);long time = c.getTime().getTime();parameters.put("time_expire", String.valueOf(time));order.setTime_expire(String.valueOf(time));}//签名 String sign = WXPayUtil.getPaySign(parameters,secret_key);order.setSign(sign);//调用微信支付统一下单接口,让微信也生成一个预支付订单String xmlResult = HttpUtil.sendPost(pay_path,XMLUtil.convertToXml(order));log.info("xmlResult:"+xmlResult);//把返回的xml字符串转成对象ResultParams entity = (ResultParams)XMLUtil.convertXmlStrToObject(ResultParams.class,xmlResult);log.info("ResultParams:"+new Gson().toJson(entity));return entity;}catch(Exception e){log.info("获取微信支付二维码,调用接口异常********",e);return null;}}

微信支付工具类 (微信提供的demo 可以下载使用)

public class WXPayUtil {private static final String SYMBOLS = "xxx";private static final Random RANDOM = new SecureRandom();/*** XML格式字符串转换为Map** @param strXML XML字符串* @return XML数据转换后的Map* @throws Exception*/public static Map<String, String> xmlToMap(String strXML) throws Exception {try {Map<String, String> data = new HashMap<String, String>();DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));org.w3c.dom.Document doc = documentBuilder.parse(stream);doc.getDocumentElement().normalize();NodeList nodeList = doc.getDocumentElement().getChildNodes();for (int idx = 0; idx < nodeList.getLength(); ++idx) {Node node = nodeList.item(idx);if (node.getNodeType() == Node.ELEMENT_NODE) {org.w3c.dom.Element element = (org.w3c.dom.Element) node;data.put(element.getNodeName(), element.getTextContent());}}try {stream.close();} catch (Exception ex) {// do nothing}return data;} catch (Exception ex) {log.error("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);throw ex;}}/*** 将Map转换为XML格式的字符串** @param data Map类型数据* @return XML格式的字符串* @throws Exception*/public static String mapToXml(Map<String, String> data) {log.info("mapToXml开始转换**************");StringWriter writer = null;try {org.w3c.dom.Document document = WXPayXmlUtil.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");writer = new StringWriter();StreamResult result = new StreamResult(writer);transformer.transform(source, result);String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");log.info("转换完:", output);writer.flush();return output;} catch (Exception ex) {log.error("mapToXml转换异常", ex);return null;} finally {if (null != writer) {try {writer.close();} catch (IOException e) {log.error("关闭writer异常", e);}}}}/*** 生成带有 sign 的 XML 格式字符串** @param data Map类型数据* @param key API密钥* @return 含有sign字段的XML*/public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {return generateSignedXml(data, key, WXPayConstants.SignType.MD5);}/*** 生成带有 sign 的 XML 格式字符串** @param dataMap类型数据* @param keyAPI密钥* @param signType 签名类型* @return 含有sign字段的XML*/public static String generateSignedXml(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {String sign = generateSignature(data, key, signType);data.put(WXPayConstants.FIELD_SIGN, sign);return mapToXml(data);}/*** 判断签名是否正确** @param xmlStr XML格式数据* @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, WXPayConstants.SignType.MD5);}/*** 判断签名是否正确,必须包含sign字段,否则返回false。** @param dataMap类型数据* @param keyAPI密钥* @param signType 签名类型,默认为MD5,支持HMAC-SHA256和MD5。* @return 签名是否正确* @throws Exception*/public static boolean isSignatureValid(Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {log.info("开始验证******");if (!data.containsKey(WXPayConstants.FIELD_SIGN)) {log.debug("验证失败******");return false;}String sign = data.get(WXPayConstants.FIELD_SIGN);return generateSignature(data, key, signType).equals(sign);}/*** 生成签名** @param data 待签名数据* @param key API密钥* @return 签名*/public static String generateSignature(final Map<String, String> data, String key) throws Exception {return generateSignature(data, key, WXPayConstants.SignType.MD5);}/*** 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。** @param data待签名数据* @param keyAPI密钥* @param signType 签名方式* @return 签名*/public static String generateSignature(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {Set<String> keySet = data.keySet();String[] keyArray = keySet.toArray(new String[keySet.size()]);Arrays.sort(keyArray);StringBuilder sb = new StringBuilder();for (String k : keyArray) {if (k.equals(WXPayConstants.FIELD_SIGN)) {continue;}if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名sb.append(k).append("=").append(data.get(k).trim()).append("&");}sb.append("key=").append(key);if (WXPayConstants.SignType.MD5.equals(signType)) {return MD5(sb.toString()).toUpperCase();} else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) {return HMACSHA256(sb.toString(), key);} else {throw new Exception(String.format("Invalid sign_type: %s", signType));}}/*** 获取随机字符串 Nonce Str** @return String 随机字符串*/public static String generateNonceStr() {char[] nonceChars = new char[32];for (int index = 0; index < nonceChars.length; ++index) {nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));}return new String(nonceChars);}/*** 生成 MD5** @param data 待处理数据* @return MD5结果*/public static String MD5(String data) throws Exception {MessageDigest md = MessageDigest.getInstance("MD5");byte[] array = md.digest(data.getBytes("UTF-8"));StringBuilder sb = new StringBuilder();for (byte item : array) {sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));}return sb.toString().toUpperCase();}/*** 生成 HMACSHA256** @param data 待处理数据* @param key 密钥* @return 加密结果* @throws Exception*/public static String HMACSHA256(String data, String key) throws Exception {Mac sha256_HMAC = Mac.getInstance("HmacSHA256");SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");sha256_HMAC.init(secret_key);byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));StringBuilder sb = new StringBuilder();for (byte item : array) {sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));}return sb.toString().toUpperCase();}/*** 日志** @return*//* public static log getlog() {log log = logFactory.getlog("wxpay java sdk");return log;}*//*** 获取当前时间戳,单位秒** @return*/public static long getCurrentTimestamp() {return System.currentTimeMillis() / 1000;}/*** 获取当前时间戳,单位毫秒** @return*/public static long getCurrentTimestampMs() {return System.currentTimeMillis();}/*** 获取签名** @param obj* @return* @throws IllegalAccessException* @throws IOException*/public static String getPaySign(Object obj, String secret_key) throws Exception {StringBuilder sb = new StringBuilder();//把对象转为TreeMap集合(按照key的ASCII 码从小到大排序)TreeMap<String, Object> map = (TreeMap) obj;Set<Map.Entry<String, Object>> entrySet = map.entrySet();//遍历键值对for (Map.Entry<String, Object> entry : entrySet) {//如果值为空,不参与签名if (entry.getValue() != null) {sb.append(entry.getKey() + "=" + entry.getValue() + "&");}}//最后拼接商户的API密钥String stringSignTemp = sb.toString() + "key=" + secret_key;return MD5(stringSignTemp);}/*** 处理 HTTPS API返回数据,转换成Map对象。return_code为SUCCESS时,验证签名。** @param xmlStr API返回的XML格式数据* @param weChatAccountData 微信账号信息 json* @return Map类型数据* @throws Exception*/public static Map<String, String> processResponseXml(String weChatAccountData, String xmlStr) throws Exception {log.info("开始处理xml文件***转换Map***");String RETURN_CODE = "return_code";String return_code;Map<String, String> respData = WXPayUtil.xmlToMap(xmlStr);log.info("转换完的map:" + respData);if (respData.containsKey(RETURN_CODE)) {return_code = respData.get(RETURN_CODE);} else {throw new Exception(String.format("No `return_code` in XML: %s", xmlStr));}if (return_code.equals(WXPayConstants.FAIL)) {return respData;} else if (return_code.equals(WXPayConstants.SUCCESS)) {WeChatAccount weChatAccount = new Gson().fromJson(weChatAccountData, WeChatAccount.class);if (isResponseSignatureValid(respData, weChatAccount.getSecret_key())) {log.debug("验证成功******");return respData;} else {log.debug("验证异常******");throw new Exception(String.format("Invalid sign value in XML: %s", xmlStr));}} else {throw new Exception(String.format("return_code value %s is invalid in XML: %s", return_code, xmlStr));}}/*** 判断xml数据的sign是否有效,必须包含sign字段,否则返回false。** @param reqData 向wxpay post的请求数据* @return 签名是否有效* @throws Exception*/public static boolean isResponseSignatureValid(Map<String, String> reqData, String secret_key) throws Exception {// 返回数据的签名方式和请求中给定的签名方式是一致的return isSignatureValid(reqData, secret_key, WXPayConstants.SignType.MD5);}/* public static String genSandBoxSign(String mch_id,String nonce_str,String key){Map<String, String> param = new HashMap<String, String>();try {param.put("mch_id", mch_id);// 商户号param.put("nonce_str", nonce_str);// 随机字符串param.put("sign", WXPayUtil.generateSignature(param, key,WXPayConstants.SignType.MD5));// 沙盒测试貌似只支持MD5加密//String xml = mapToXml(param);TestM m = new TestM();m.setMch_id(mch_id);m.setNonce_str(nonce_str);m.setSign(param.get("sign"));String xmlResult = requestWithoutCert("/sandboxnew/pay/getsignkey",param,1000,1000);return xmlResult;} catch (Exception e) {e.printStackTrace();return null;}}public static String requestWithoutCert(String urlSuffix, Map<String, String> reqData,int connectTimeoutMs, int readTimeoutMs) throws Exception {String msgUUID = reqData.get("nonce_str");String reqBody = WXPayUtil.mapToXml(reqData);String resp = requestWithoutCert(urlSuffix, msgUUID, reqBody, connectTimeoutMs, readTimeoutMs, autoReport);return resp;}*//*** 可重试的,非双向认证的请求** @return*//*public static String requestWithoutCert(String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs, boolean autoReport) throws Exception {return request(urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, false, autoReport);}private static String request(String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs, boolean useCert, boolean autoReport) throws Exception {Exception exception = null;long elapsedTimeMillis = 0;long startTimestampMs = WXPayUtil.getCurrentTimestampMs();boolean firstHasDnsErr = false;boolean firstHasConnectTimeout = false;boolean firstHasReadTimeout = false;DomainInfo domainInfo = config.getWXPayDomain().getDomain(config);if(domainInfo == null){throw new Exception("WXPayConfig.getWXPayDomain().getDomain() is empty or null");}try {String result = requestOnce(domainInfo.domain, urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, useCert);elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;config.getWXPayDomain().report(domainInfo.domain, elapsedTimeMillis, null);WXPayReport.getInstance(config).report(uuid,elapsedTimeMillis,domainInfo.domain,domainInfo.primaryDomain,connectTimeoutMs,readTimeoutMs,firstHasDnsErr,firstHasConnectTimeout,firstHasReadTimeout);return result;}catch (UnknownHostException ex) { // dns 解析错误,或域名不存在exception = ex;firstHasDnsErr = true;elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;WXPayUtil.getlog().warn("UnknownHostException for domainInfo {}", domainInfo);WXPayReport.getInstance(config).report(uuid,elapsedTimeMillis,domainInfo.domain,domainInfo.primaryDomain,connectTimeoutMs,readTimeoutMs,firstHasDnsErr,firstHasConnectTimeout,firstHasReadTimeout);}catch (ConnectTimeoutException ex) {exception = ex;firstHasConnectTimeout = true;elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;WXPayUtil.getlog().warn("connect timeout happened for domainInfo {}", domainInfo);WXPayReport.getInstance(config).report(uuid,elapsedTimeMillis,domainInfo.domain,domainInfo.primaryDomain,connectTimeoutMs,readTimeoutMs,firstHasDnsErr,firstHasConnectTimeout,firstHasReadTimeout);}catch (SocketTimeoutException ex) {exception = ex;firstHasReadTimeout = true;elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;WXPayUtil.getlog().warn("timeout happened for domainInfo {}", domainInfo);WXPayReport.getInstance(config).report(uuid,elapsedTimeMillis,domainInfo.domain,domainInfo.primaryDomain,connectTimeoutMs,readTimeoutMs,firstHasDnsErr,firstHasConnectTimeout,firstHasReadTimeout);}catch (Exception ex) {exception = ex;elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;WXPayReport.getInstance(config).report(uuid,elapsedTimeMillis,domainInfo.domain,domainInfo.primaryDomain,connectTimeoutMs,readTimeoutMs,firstHasDnsErr,firstHasConnectTimeout,firstHasReadTimeout);}config.getWXPayDomain().report(domainInfo.domain, elapsedTimeMillis, exception);throw exception;}*/}class TestM {private String mch_id;private String nonce_str;private String sign;public String getMch_id() {return mch_id;}public void setMch_id(String mch_id) {this.mch_id = mch_id;}public String getNonce_str() {return nonce_str;}public void setNonce_str(String nonce_str) {this.nonce_str = nonce_str;}public String getSign() {return sign;}public void setSign(String sign) {this.sign = sign;}}class DomainInfo {public String domain; //域名public boolean primaryDomain;//该域名是否为主域名。例如:api.mch.为主域名public DomainInfo(String domain, boolean primaryDomain) {this.domain = domain;this.primaryDomain = primaryDomain;}@Overridepublic String toString() {return "DomainInfo{" +"domain='" + domain + '\'' +", primaryDomain=" + primaryDomain +'}';}}

扫码支付完成后需要微信回调:

回调参数:

回调返回:

代码展示:

public Map<String, String> doWeChatNotify() {log.info("微信回调支付返回结果:");Map<String, String> resultMap = new HashMap<String, String>();Map<String, String> params = new HashMap<String, String>();InputStream inStream = null;ByteArrayOutputStream outSteam = null;String orderID = null;try {inStream = request.getInputStream();outSteam = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len = 0;while ((len = inStream.read(buffer)) != -1) {outSteam.write(buffer, 0, len);}String resultxml = new String(outSteam.toByteArray(), "utf-8");log.info("获取的xml:" + resultxml);outSteam.flush();SystemConfig queryOneSystemConfig = orderPayService.queryOneSystemConfig("cm2_weChatAccount");params = WXPayUtil.processResponseXml(queryOneSystemConfig.getConfigValue(), resultxml);log.info("xml转换map结果:" + params);/*** 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一*/String out_trade_no = params.get("out_trade_no");PayRecord queryOrderPayRecordByOrderID = this.orderPayService.queryOrderPayRecordByOut_Trade_NoAndType(out_trade_no, OrderPayTypeEnum.WECHAT.getValue());if (null == queryOrderPayRecordByOrderID) {resultMap.put("return_code", "FAIL");resultMap.put("return_msg", "当前订单未找微信支付记录");return resultMap;}orderID = queryOrderPayRecordByOrderID.getOrderID();String return_code = params.get("return_code");String return_msg = params.get("return_msg");if ("FAIL".equals(return_code)) {resultMap.put("return_code", "FAIL");resultMap.put("return_msg", return_msg);this.payLogService.createPayLogByReceive(orderID, new Gson().toJson(params), 1, "微信异步调用,返回失败");return resultMap;}/*** 订单金额(分)*/String total_fee = params.get("total_fee");/*** 商户号*///String mch_id = params.get("mch_id");String nonce_str = params.get("nonce_str");String sign = params.get("sign");/*** SUCCESS/FAIL*/String result_code = params.get("result_code");/*** 用户在商户appid下的唯一标识*/String openid = params.get("openid");/*** 微信支付订单号*/String transaction_id = params.get("transaction_id");/*** 支付完成的时间*/String time_end = params.get("time_end");String trade_type = params.get("trade_type");queryOrderPayRecordByOrderID.setPayType(OrderPayTypeEnum.WECHAT.getValue());queryOrderPayRecordByOrderID.setComment(queryOrderPayRecordByOrderID.getComment() + "时间:" + new Date() + "==>微信异步数据");queryOrderPayRecordByOrderID.setEditTime(new Date());Date payTime = null;if (null != time_end && !"".equals(time_end)) {SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyyMMddhhmm");payTime = sDateFormat.parse(time_end);}queryOrderPayRecordByOrderID.setPayTime(payTime);queryOrderPayRecordByOrderID.setPayAmount(Long.parseLong(total_fee));queryOrderPayRecordByOrderID.setSign(sign);queryOrderPayRecordByOrderID.setBuyer_logon_id(openid);queryOrderPayRecordByOrderID.setTrade_type(trade_type);/*** 交易状态:WAIT_BUYER_PAY(交易创建,等待买家付款)、TRADE_CLO*/if ("SUCCESS".equals(result_code)) {queryOrderPayRecordByOrderID.setNonce_str(nonce_str);queryOrderPayRecordByOrderID.setPayStatus(OrderPayStatusEnum.PAID.getValue());queryOrderPayRecordByOrderID.setTrade_no(transaction_id);this.orderPayService.updateOrderPayRecord(queryOrderPayRecordByOrderID);resultMap.put("return_code", "SUCCESS");resultMap.put("return_msg", "OK");this.payLogService.createPayLogByReceive(orderID, new Gson().toJson(params), 0, "微信异步调用成功");return resultMap;}queryOrderPayRecordByOrderID.setPayStatus(OrderPayStatusEnum.PAYMENT_FAILED.getValue());this.orderPayService.updateOrderPayRecord(queryOrderPayRecordByOrderID);resultMap.put("return_code", "FAIL");resultMap.put("return_msg", "微信支付回调失败,返回result_code不为SUCCESS");this.payLogService.createPayLogByReceive(orderID, new Gson().toJson(params), 1, "微信异步调用失败");return resultMap;} catch (Exception e) {log.error("微信支付回调返回异常:+", e);resultMap.put("return_code", "FAIL");resultMap.put("return_msg", "微信支付回调失败");this.payLogService.createPayLogByReceive(orderID, new Gson().toJson(params), 1, "微信异步调用异常");return resultMap;} finally {if (null != inStream) {try {inStream.close();} catch (IOException e) {log.error("关闭inStream异常");}}if (null != outSteam) {try {outSteam.close();} catch (IOException e) {log.error("关闭outSteam异常");}}}}

微信退款:

退款需要证书的,这个上边提到过,去微信商户平台生成下载,微信是通过微信订单号(transaction_id)来退款的,transaction_id这个参数是支付回调时候返回的,注意保存

private Map<String, Object> getPayCodeByWeChat(PayRecord queryPayRecordByOrderID, long transactionPrice, TradeRefundRequest tradeRefundRequest) {log.info("微信退款请求*******");Map<String, Object> resultMap = new HashMap<>(16);try {SystemConfig queryOneSystemConfig = orderPayService.queryOneSystemConfig("cm2_weChatAccount");RefundParams refundParams = new RefundParams();refundParams.setNonce_str(CommonUtils.genId());refundParams.setOut_refund_no(CommonUtils.genId());String refund_amount = tradeRefundRequest.getRefund_amount().replace(",", "");int intValue = new BigDecimal(refund_amount).multiply(new BigDecimal("100")).intValue();refundParams.setRefund_fee(intValue);refundParams.setTotal_fee(new BigDecimal(transactionPrice).intValue());refundParams.setOut_trade_no(tradeRefundRequest.getOrderID());RefundResultParams refundResponse = WeChatPayUtil.refund(queryOneSystemConfig.getConfigValue(), refundParams);log.debug("微信退款结果:" + new Gson().toJson(refundResponse));if (null == refundResponse || !refundResponse.getReturn_code().equals("SUCCESS") || !refundResponse.getResult_code().equals("SUCCESS")) {log.error("微信退款失败*******");resultMap.put("code", -20010);resultMap.put("desc", "退款失败");return resultMap;}queryPayRecordByOrderID.setRefund_request_no(refundParams.getOut_refund_no());queryPayRecordByOrderID.setRefund_amount(new BigDecimal(refund_amount).multiply(new BigDecimal("100")).longValue());queryPayRecordByOrderID.setRefundTime(new Date());queryPayRecordByOrderID.setRefund_operator(tradeRefundRequest.getCurrentUserID());queryPayRecordByOrderID.setRefund_reason(tradeRefundRequest.getRefund_reason());queryPayRecordByOrderID.setPayStatus(OrderPayStatusEnum.REFUNDING.getValue());orderPayService.updateOrderPayRecord(queryPayRecordByOrderID);resultMap.put("code", 0);resultMap.put("desc", "操作成功");resultMap.put("request_no", queryPayRecordByOrderID.getRefund_request_no());resultMap.put("refund_amount", queryPayRecordByOrderID.getRefund_amount());return resultMap;} catch (Exception e) {log.error("微信退款处理异常*******", e);resultMap.put("code", -20010);resultMap.put("desc", "退款失败");return resultMap;}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。