|
@@ -0,0 +1,356 @@
|
|
|
+package com.futu.course.common.utils;
|
|
|
+
|
|
|
+import cn.hutool.core.util.IdUtil;
|
|
|
+import com.futu.course.common.vo.ResultVo;
|
|
|
+import com.futu.course.pay.config.WxConfigV2;
|
|
|
+import com.futu.course.pay.dto.CreateOrderDto;
|
|
|
+import com.futu.course.pay.vo.OrderWxPayVo;
|
|
|
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
|
|
|
+import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
|
|
|
+import com.github.binarywang.wxpay.exception.WxPayException;
|
|
|
+import com.github.binarywang.wxpay.service.WxPayService;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.xml.sax.Attributes;
|
|
|
+import org.xml.sax.SAXException;
|
|
|
+import org.xml.sax.helpers.DefaultHandler;
|
|
|
+
|
|
|
+import javax.net.ssl.HostnameVerifier;
|
|
|
+import javax.net.ssl.HttpsURLConnection;
|
|
|
+import javax.net.ssl.SSLSession;
|
|
|
+import javax.xml.parsers.SAXParser;
|
|
|
+import javax.xml.parsers.SAXParserFactory;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.io.OutputStreamWriter;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.net.HttpURLConnection;
|
|
|
+import java.net.URL;
|
|
|
+import java.security.MessageDigest;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+public class PayUtil {
|
|
|
+ public static ResultVo createOrderPreId(CreateOrderDto dto, WxPayService wxPayService) throws WxPayException {
|
|
|
+
|
|
|
+ // 订单参数
|
|
|
+
|
|
|
+ String openId = dto.getOpenId();
|
|
|
+ // 按业务场景填写
|
|
|
+ String attach = "微信支付";
|
|
|
+
|
|
|
+ String outTradeNo =dto.getOrderId().toString();
|
|
|
+ BigDecimal orderAmount = dto.getOrderAmount();
|
|
|
+ String description =dto.getDescription();
|
|
|
+
|
|
|
+ // 失效时间
|
|
|
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
|
|
+ Long expireTime = System.currentTimeMillis() + (60 * 1000 * 15);
|
|
|
+ String timeExpire = format.format(expireTime) + "+08:00";
|
|
|
+
|
|
|
+ // 订单信息
|
|
|
+ WxPayUnifiedOrderV3Request wxPayUnifiedOrderV3Request = new WxPayUnifiedOrderV3Request();
|
|
|
+ wxPayUnifiedOrderV3Request.setOutTradeNo(outTradeNo);
|
|
|
+ wxPayUnifiedOrderV3Request.setDescription("微信支付");
|
|
|
+ wxPayUnifiedOrderV3Request.setTimeExpire(timeExpire);
|
|
|
+ wxPayUnifiedOrderV3Request.setAttach(attach);
|
|
|
+
|
|
|
+
|
|
|
+ wxPayUnifiedOrderV3Request.setNotifyUrl("https://mini.workervip.com/pay/notify");
|
|
|
+ log.info("################# 回调地址: {}", wxPayUnifiedOrderV3Request.getNotifyUrl());
|
|
|
+
|
|
|
+ // 订单金额
|
|
|
+ WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
|
|
|
+ amount.setTotal(orderAmount.multiply(new BigDecimal("100")).intValue());
|
|
|
+ amount.setCurrency("CNY");
|
|
|
+ wxPayUnifiedOrderV3Request.setAmount(amount);
|
|
|
+
|
|
|
+ // 付款人员
|
|
|
+ WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
|
|
|
+ payer.setOpenid(openId);
|
|
|
+
|
|
|
+ // H5平台
|
|
|
+ TradeTypeEnum tradeTypeEnum = TradeTypeEnum.JSAPI;
|
|
|
+
|
|
|
+//
|
|
|
+ System.out.println(wxPayUnifiedOrderV3Request.toString());
|
|
|
+ // 发起订单
|
|
|
+ wxPayUnifiedOrderV3Request.setPayer(payer);
|
|
|
+
|
|
|
+
|
|
|
+ Object obj=wxPayService.createOrderV3(tradeTypeEnum, wxPayUnifiedOrderV3Request);
|
|
|
+ return ResultVo.success(obj);
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public static OrderWxPayVo getPreIdV2(CreateOrderDto dto,WxConfigV2 configV2){
|
|
|
+
|
|
|
+ Map map = new HashMap();
|
|
|
+ map.put("orderId", dto.getOrderId());
|
|
|
+// map.put("payType", );
|
|
|
+
|
|
|
+ //如果没有生成支付订单去支付
|
|
|
+
|
|
|
+
|
|
|
+ Map<String, String> setting = new TreeMap<>();
|
|
|
+ String key = configV2.getApiKey();
|
|
|
+ setting.put("timeStamp", System.currentTimeMillis() / 1000 + "");
|
|
|
+ String nonceStr = IdUtil.getSnowflake().nextIdStr();
|
|
|
+ setting.put("nonce_str", nonceStr);
|
|
|
+// setting.put("openid", "4zeWDXewwKm5vKpvBIFhS2U");
|
|
|
+ setting.put("openid", dto.getOpenId());
|
|
|
+
|
|
|
+ BigDecimal payMoney = dto.getOrderAmount();
|
|
|
+
|
|
|
+
|
|
|
+ try {
|
|
|
+ String getPreId= wxMiniAppPrePayId("47.240.226.34",dto.getOrderId().toString(),payMoney,setting,configV2);
|
|
|
+ setting.put("package", "prepay_id=" + getPreId);
|
|
|
+ String paySign= getPaySign(setting,configV2);
|
|
|
+ OrderWxPayVo responseData=new OrderWxPayVo();
|
|
|
+ responseData.setAppid(configV2.getAppId());
|
|
|
+ responseData.setPartnerid(configV2.getMchId());
|
|
|
+ responseData.setPaySign(paySign);
|
|
|
+ responseData.setNonceStr(nonceStr);
|
|
|
+ responseData.setOrderId(dto.getOrderId());
|
|
|
+ responseData.setOrderNumber(dto.getOrderId().toString());
|
|
|
+ responseData.setPayAmount(payMoney.setScale(2, BigDecimal.ROUND_HALF_UP));
|
|
|
+ responseData.setSigntype("MD5");
|
|
|
+ responseData.setTimestart(setting.get("timeStamp"));
|
|
|
+ responseData.setPackages("prepay_id=" + getPreId);
|
|
|
+ return responseData;
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ public static String wxMiniAppPrePayId(String remoteIP, String orderNumber, BigDecimal amount,
|
|
|
+ Map<String, String> settingn,WxConfigV2 configV2) throws Exception {
|
|
|
+
|
|
|
+ Map<String, String> setting = new TreeMap<>();
|
|
|
+ String key = configV2.getApiKey();
|
|
|
+ setting.put("appid", configV2.getAppId());
|
|
|
+ setting.put("mch_id", configV2.getMchId());
|
|
|
+ setting.put("notify_url", configV2.getNotiyUrl());
|
|
|
+ setting.put("body",configV2.getWxBody());
|
|
|
+ setting.put("trade_type", "JSAPI");
|
|
|
+
|
|
|
+ setting.put("out_trade_no", orderNumber);
|
|
|
+ setting.put("total_fee",
|
|
|
+ amount.multiply(BigDecimal.valueOf(100)).setScale(0, BigDecimal.ROUND_HALF_UP).toPlainString());
|
|
|
+ setting.put("spbill_create_ip", remoteIP);
|
|
|
+ setting.put("nonce_str", settingn.get("nonce_str"));
|
|
|
+ setting.put("timeStamp", settingn.get("timeStamp"));
|
|
|
+ if (!StringUtils.isBlank(settingn.get("openid"))) {
|
|
|
+ setting.put("openid", settingn.get("openid"));
|
|
|
+ }
|
|
|
+// setting.put("time_start",settingn.get("time_start"));
|
|
|
+// setting.put("time_expire",settingn.get("time_expire"));
|
|
|
+ StringBuilder builder = new StringBuilder();
|
|
|
+ String sign = "";
|
|
|
+ {
|
|
|
+ // 签名
|
|
|
+ boolean notFirst = false;
|
|
|
+ for (Map.Entry<String, String> entry : setting.entrySet()) {
|
|
|
+ String k = entry.getKey();
|
|
|
+ String v = entry.getValue();
|
|
|
+ if (v == null || v.trim().isEmpty()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (notFirst) {
|
|
|
+ builder.append('&');
|
|
|
+ } else {
|
|
|
+ notFirst = true;
|
|
|
+ }
|
|
|
+ builder.append(k).append('=').append(v);
|
|
|
+ }
|
|
|
+ builder.append('&').append("key=").append(key);
|
|
|
+
|
|
|
+ System.out.println("vvv="+builder.toString());
|
|
|
+ try {
|
|
|
+ sign = EraHelper
|
|
|
+ .byte2HexStr(MessageDigest.getInstance("MD5").digest(builder.toString().getBytes("UTF-8")));
|
|
|
+ log.info("sign========="+sign);
|
|
|
+ setting.put("sign", sign);
|
|
|
+ log.info(builder.toString());
|
|
|
+
|
|
|
+ } catch (Throwable e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 发送请求
|
|
|
+ try {
|
|
|
+ // 生成请求内容
|
|
|
+ builder.setLength(0);
|
|
|
+ builder.append("<xml>");
|
|
|
+ for (Map.Entry<String, String> entry : setting.entrySet()) {
|
|
|
+ String k = entry.getKey();
|
|
|
+ String v = entry.getValue();
|
|
|
+ if (v == null || v.trim().isEmpty()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ builder.append('<').append(k).append('>');
|
|
|
+ builder.append("<![CDATA[").append(v).append("]]>");
|
|
|
+ builder.append("</").append(k).append('>');
|
|
|
+ }
|
|
|
+ builder.append("</xml>");
|
|
|
+
|
|
|
+ log.info("H5微信支付请求数据:" + builder.toString());
|
|
|
+
|
|
|
+ URL location = new URL("https://api.mch.weixin.qq.com/pay/unifiedorder");
|
|
|
+ HttpsURLConnection connection = (HttpsURLConnection) location.openConnection();
|
|
|
+ connection.setHostnameVerifier(new HostnameVerifier() {
|
|
|
+ @Override
|
|
|
+ public boolean verify(String hostname, SSLSession session) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ connection.setRequestProperty("Connection", "Close");
|
|
|
+ connection.setRequestProperty("Accept-Language", "*");
|
|
|
+ connection.setRequestProperty("Accept-Encoding", "identity");
|
|
|
+ connection.setRequestProperty("User-Agent", "MYTIM-PAY-GATE");
|
|
|
+ connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
|
|
+ connection.setDoOutput(true);
|
|
|
+ connection.setDoInput(true);
|
|
|
+ connection.setUseCaches(false);
|
|
|
+ try (OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), "UTF-8")) {
|
|
|
+ writer.write(builder.toString());
|
|
|
+ writer.flush();
|
|
|
+ }
|
|
|
+ builder.setLength(0);
|
|
|
+
|
|
|
+ if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
|
|
+ try (InputStream inputStream = connection.getInputStream()) {
|
|
|
+ String responseStr = parseResult(inputStream);
|
|
|
+ log.debug("H5微信支付返回预付款id数据:" + responseStr);
|
|
|
+ return responseStr;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw new Exception("错误的响应代码:" + connection.getResponseCode());
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+
|
|
|
+ } catch (Throwable e) {
|
|
|
+ e.printStackTrace();
|
|
|
+
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ private static String parseResult(InputStream inputStream) throws Exception {
|
|
|
+ try {
|
|
|
+ Map<String, String> values = parseMap(inputStream);
|
|
|
+ log.info("pay-wei-xin return : " + values.toString());
|
|
|
+ if ("SUCCESS".equalsIgnoreCase(values.get("return_code"))
|
|
|
+ && "SUCCESS".equalsIgnoreCase(values.get("result_code"))) {
|
|
|
+ String prepay_id = values.get("prepay_id");
|
|
|
+ if (prepay_id == null || prepay_id.isEmpty()) {
|
|
|
+ throw new Exception("响应成功,但解析预交易单号失败");
|
|
|
+ }
|
|
|
+ return prepay_id;
|
|
|
+ } else {
|
|
|
+ if ("OUT_TRADE_NO_USED".equals(values.get("err_code"))) {
|
|
|
+ throw new Exception("微信支付不支持金额修改,请选用其他方式");
|
|
|
+ }
|
|
|
+ throw new Exception(values.get("return_msg"));
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ throw e;
|
|
|
+ } catch (Throwable e) {
|
|
|
+
|
|
|
+ e.printStackTrace();
|
|
|
+ throw new Exception("解析响应数据失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ public static Map<String, String> parseMap(InputStream inputStream) throws Throwable {
|
|
|
+
|
|
|
+ Map<String, String> notifies = new TreeMap<>();
|
|
|
+ SAXParserFactory factory = SAXParserFactory.newInstance();
|
|
|
+ factory.setNamespaceAware(false);
|
|
|
+ SAXParser parser = factory.newSAXParser();
|
|
|
+ Set<String> ignoreFields = new HashSet<>();
|
|
|
+ ignoreFields.add("xml");
|
|
|
+ parser.parse(inputStream, new DefaultHandler() {
|
|
|
+
|
|
|
+ String currentElem = null;
|
|
|
+ String value = null;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
|
|
|
+ throws SAXException {
|
|
|
+ currentElem = qName;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void endElement(String uri, String localName, String qName) throws SAXException {
|
|
|
+ if (ignoreFields.contains(currentElem) || currentElem == null || value == null || value.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ notifies.put(currentElem, value);
|
|
|
+ currentElem = null;
|
|
|
+ value = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void characters(char[] ch, int start, int length) throws SAXException {
|
|
|
+ if (ignoreFields.contains(currentElem)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ value = new String(ch, start, length);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return notifies;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public static String getPaySign( Map<String, String> settingn,WxConfigV2 configV2)
|
|
|
+ throws Exception {
|
|
|
+ Map<String, String> setting = new TreeMap<>();
|
|
|
+ String key = configV2.getApiKey();
|
|
|
+ setting.put("appId", configV2.getAppId());
|
|
|
+ setting.put("timeStamp", settingn.get("timeStamp"));
|
|
|
+ setting.put("nonceStr", settingn.get("nonce_str"));
|
|
|
+ setting.put("package", settingn.get("package"));
|
|
|
+ setting.put("signType", "MD5");
|
|
|
+
|
|
|
+ StringBuilder builder = new StringBuilder();
|
|
|
+ String sign = "";
|
|
|
+ {
|
|
|
+ // 签名
|
|
|
+ boolean notFirst = false;
|
|
|
+ for (Map.Entry<String, String> entry : setting.entrySet()) {
|
|
|
+ String k = entry.getKey();
|
|
|
+ String v = entry.getValue();
|
|
|
+ if (v == null || v.trim().isEmpty()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (notFirst) {
|
|
|
+ builder.append('&');
|
|
|
+ } else {
|
|
|
+ notFirst = true;
|
|
|
+ }
|
|
|
+ builder.append(k).append('=').append(v);
|
|
|
+ }
|
|
|
+ builder.append('&').append("key=").append(key);
|
|
|
+ System.out.println("vvv="+builder.toString());
|
|
|
+ try {
|
|
|
+ sign = EraHelper
|
|
|
+ .byte2HexStr(MessageDigest.getInstance("MD5").digest(builder.toString().getBytes("UTF-8")));
|
|
|
+ log.info("sign========="+sign);
|
|
|
+
|
|
|
+ } catch (Throwable e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 发送请求
|
|
|
+
|
|
|
+ return sign;
|
|
|
+ }
|
|
|
+}
|