From 1567647be2f3e68279b4b08e2316e6a39519a0f6 Mon Sep 17 00:00:00 2001
From: joylink_zhangsai <1021828630@qq.com>
Date: Fri, 15 Jan 2021 09:59:01 +0800
Subject: [PATCH] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 5 +
.../controller/pay/WechatPayController.java | 24 ++
.../BusinessExceptionAssertEnum.java | 5 +-
.../rtss/services/pay/bean/OrderPay.java | 39 ++
.../services/pay/wechat/WechatPayService.java | 129 +++++++
.../services/pay/wechat/bean/WxPayResult.java | 355 ++++++++++++++++++
.../pay/wechat/bean/WxPayResultReply.java | 36 ++
.../pay/wechat/bean/WxUnifiedOrder.java | 221 +++++++++++
.../pay/wechat/bean/WxUnifiedOrderResult.java | 183 +++++++++
.../pay/wechat/constant/WechatConstants.java | 73 ++++
.../controller/PayResultController.java | 25 ++
.../services/pay/wechat/util/SignUtil.java | 39 ++
.../rtss/vo/client/order/OrderDetailVO.java | 4 +-
13 files changed, 1135 insertions(+), 3 deletions(-)
create mode 100644 src/main/java/club/joylink/rtss/controller/pay/WechatPayController.java
create mode 100644 src/main/java/club/joylink/rtss/services/pay/bean/OrderPay.java
create mode 100644 src/main/java/club/joylink/rtss/services/pay/wechat/WechatPayService.java
create mode 100644 src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxPayResult.java
create mode 100644 src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxPayResultReply.java
create mode 100644 src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxUnifiedOrder.java
create mode 100644 src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxUnifiedOrderResult.java
create mode 100644 src/main/java/club/joylink/rtss/services/pay/wechat/constant/WechatConstants.java
create mode 100644 src/main/java/club/joylink/rtss/services/pay/wechat/controller/PayResultController.java
create mode 100644 src/main/java/club/joylink/rtss/services/pay/wechat/util/SignUtil.java
diff --git a/pom.xml b/pom.xml
index 4625083e1..385e34768 100644
--- a/pom.xml
+++ b/pom.xml
@@ -98,6 +98,11 @@
+
+ com.github.wechatpay-apiv3
+ wechatpay-apache-httpclient
+ 0.2.1
+
diff --git a/src/main/java/club/joylink/rtss/controller/pay/WechatPayController.java b/src/main/java/club/joylink/rtss/controller/pay/WechatPayController.java
new file mode 100644
index 000000000..0ac0d4ee4
--- /dev/null
+++ b/src/main/java/club/joylink/rtss/controller/pay/WechatPayController.java
@@ -0,0 +1,24 @@
+package club.joylink.rtss.controller.pay;
+
+import club.joylink.rtss.services.pay.wechat.WechatPayService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@Slf4j
+@RestController
+@RequestMapping("/api/wechatPay")
+public class WechatPayController {
+
+ @Autowired
+ private WechatPayService wechatPayService;
+
+ @PostMapping("/receive")
+ public void receive(@RequestBody String data) {
+ log.info(String.format("微信回调信息:%s", data));
+ wechatPayService.receive(data);
+ }
+}
diff --git a/src/main/java/club/joylink/rtss/exception/BusinessExceptionAssertEnum.java b/src/main/java/club/joylink/rtss/exception/BusinessExceptionAssertEnum.java
index becbe300f..217b65cb4 100644
--- a/src/main/java/club/joylink/rtss/exception/BusinessExceptionAssertEnum.java
+++ b/src/main/java/club/joylink/rtss/exception/BusinessExceptionAssertEnum.java
@@ -53,7 +53,10 @@ public enum BusinessExceptionAssertEnum implements BusinessExceptionAssert {
WECHAT_CODE_EXPIRED(40029, "wechat code expired"),
INVALID_CLIENT(40031, "invalid client"),
INCORRECT_VERIFICATION_CODE(40051, "incorrect verification code"),
- THIRD_SERVICE_CALL_EXCEPTION(40071, "the third service call exception")
+ THIRD_SERVICE_CALL_EXCEPTION(40071, "the third service call exception"),
+
+ //支付异常
+ PAY_ERROR(50000, "pay error")
;
int code;
diff --git a/src/main/java/club/joylink/rtss/services/pay/bean/OrderPay.java b/src/main/java/club/joylink/rtss/services/pay/bean/OrderPay.java
new file mode 100644
index 000000000..5c61046b9
--- /dev/null
+++ b/src/main/java/club/joylink/rtss/services/pay/bean/OrderPay.java
@@ -0,0 +1,39 @@
+package club.joylink.rtss.services.pay.bean;
+
+import club.joylink.rtss.vo.client.order.OrderDetailVO;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.OffsetDateTime;
+import java.util.List;
+
+@Getter
+@Setter
+public class OrderPay {
+
+ /**
+ * 订单编号*
+ */
+ private String orderNo;
+
+ /**
+ * 订单描述*
+ */
+ private String description;
+
+ /**
+ * 订单总价*
+ */
+ private Integer totalFee;
+
+ /**
+ * 交易结束时间
+ */
+ private OffsetDateTime timeExpire;
+
+ /**
+ * 订单中包含的商品详情
+ */
+ private List orderDetail;
+
+}
diff --git a/src/main/java/club/joylink/rtss/services/pay/wechat/WechatPayService.java b/src/main/java/club/joylink/rtss/services/pay/wechat/WechatPayService.java
new file mode 100644
index 000000000..786966203
--- /dev/null
+++ b/src/main/java/club/joylink/rtss/services/pay/wechat/WechatPayService.java
@@ -0,0 +1,129 @@
+package club.joylink.rtss.services.pay.wechat;
+
+import club.joylink.rtss.exception.BusinessExceptionAssertEnum;
+import club.joylink.rtss.services.IOrderService;
+import club.joylink.rtss.services.pay.bean.OrderPay;
+import club.joylink.rtss.services.pay.wechat.bean.WxUnifiedOrder;
+import club.joylink.rtss.util.JsonUtils;
+import club.joylink.rtss.vo.client.order.OrderDetailVO;
+import club.joylink.rtss.websocket.StompMessageService;
+import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
+import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
+import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
+import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
+import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
+import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.time.OffsetDateTime;
+import java.util.List;
+
+import static club.joylink.rtss.services.pay.wechat.constant.WechatConstants.*;
+
+@Service
+@Slf4j
+public class WechatPayService {
+
+ @Autowired
+ private IOrderService iorderService;
+
+ @Autowired
+ private StompMessageService stompMessageService;
+
+ private CloseableHttpClient httpClient;
+
+ /**
+ * 统一下单
+ * @param orderPay
+ * @return 支付二维码url
+ * @throws IOException
+ */
+ public String unifiedOrder(OrderPay orderPay) throws IOException {
+ CloseableHttpResponse response = null;
+ try {
+ if (httpClient == null)
+ buildHttpClient();
+ //构建并发送请求
+ HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
+ httpPost.setHeader("Accept", "application/json");
+ WxUnifiedOrder wxUnifiedOrder = new WxUnifiedOrder(orderPay, "http://2i38984j47.qicp.vip/api/wechatPay/receive");
+ String json = JsonUtils.writeValueAsString(wxUnifiedOrder);
+ StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
+ entity.setContentType("application/json");
+ httpPost.setEntity(entity);
+ response = httpClient.execute(httpPost);
+ //处理请求结果
+ int statusCode = response.getStatusLine().getStatusCode();
+ String result = EntityUtils.toString(response.getEntity());
+ if (statusCode == 200) { //处理成功
+ log.info("微信支付下单成功,return body = " + result);
+ return result;
+ } else if (statusCode == 204) { //处理成功,无返回Body
+ System.out.println("微信支付下单成功");
+ return result;
+ } else {
+ log.error("failed,resp code = " + statusCode+ ",return body = " + result);
+ throw BusinessExceptionAssertEnum.PAY_ERROR.exception();
+ }
+ } catch (Exception e) {
+ throw BusinessExceptionAssertEnum.PAY_ERROR.exception("微信Native支付异常", e);
+ } finally {
+ if (httpClient != null)
+ httpClient.close();
+ if (response != null)
+ response.close();
+ }
+ }
+
+ private void buildHttpClient() {
+ try {
+ PrivateKey privateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(MCH_PRIVATE_KEY.getBytes("utf-8")));
+ AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
+ new WechatPay2Credentials(MCH_ID, new PrivateKeySigner(MCH_SERIAL_NO, privateKey)), APIV3_KEY.getBytes("utf-8"));
+ httpClient = WechatPayHttpClientBuilder.create()
+ .withMerchant(MCH_ID, MCH_SERIAL_NO, privateKey)
+ .withValidator(new WechatPay2Validator(verifier))
+ .build();
+ } catch (Exception e) {
+ throw BusinessExceptionAssertEnum.PAY_ERROR.exception("微信支付httpClient构建异常", e);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ OrderPay pay = new OrderPay();
+ pay.setOrderNo("2018081400001");
+ pay.setDescription("标准1号线ATS现地工作站实操");
+ pay.setTotalFee(1);
+ pay.setTimeExpire(OffsetDateTime.now().plusMinutes(10));
+ OrderDetailVO orderDetailVO = new OrderDetailVO();
+ orderDetailVO.setGoodsId(1L);
+ orderDetailVO.setGoodsName("测试");
+ orderDetailVO.setGoodsAmount(1);
+ orderDetailVO.setPrice(1L);
+ pay.setOrderDetail(List.of(orderDetailVO));
+ WechatPayService wps = new WechatPayService();
+ String result = wps.unifiedOrder(pay);
+ System.out.println(result);
+ }
+
+ /**
+ * 支付结果处理
+ */
+ public String receive(String data) {
+ String result = "支付结果:" + data;
+ log.info(result);
+ return result;
+ }
+
+}
diff --git a/src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxPayResult.java b/src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxPayResult.java
new file mode 100644
index 000000000..ab219829c
--- /dev/null
+++ b/src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxPayResult.java
@@ -0,0 +1,355 @@
+package club.joylink.rtss.services.pay.wechat.bean;
+
+public class WxPayResult {
+
+ /**
+ * 返回状态码
+ */
+ private String returnCode;
+
+ /**
+ * 返回信息
+ */
+ private String returnMsg;
+
+ /**
+ * 公众账号ID*
+ */
+ private String appId;
+
+ /**
+ * 商户号*
+ */
+ private String mchId;
+
+ /**
+ * 设备号
+ */
+ private String deviceInfo;
+
+ /**
+ * 随机字符串*
+ */
+ private String nonceStr;
+
+ /**
+ * 签名*
+ */
+ private String sign;
+
+ /**
+ * 签名类型
+ */
+ private String signType;
+
+ /**
+ * 业务结果
+ */
+ private String resultCode;
+
+ /**
+ * 错误代码
+ */
+ private String errCode;
+
+ /**
+ * 错误代码描述
+ */
+ private String errCodeDes;
+
+ /**
+ * 用户标识
+ */
+ private String openId;
+
+ /**
+ * 是否关注公众账号
+ */
+ private String isSubscribe;
+
+ /**
+ * 交易类型*
+ */
+ private String tradeType;
+
+ /**
+ * 付款银行
+ */
+ private String bankType;
+
+ /**
+ * 订单金额
+ */
+ private Integer totalFee;
+
+ /**
+ * 应结订单金额
+ */
+ private Integer settlementTotalFee;
+
+ /**
+ * 货币种类
+ */
+ private String feeType;
+
+ /**
+ * 现金支付金额
+ */
+ private Integer cashFee;
+
+ /**
+ * 现金支付货币类型
+ */
+ private String cashFeeType;
+
+ /**
+ * 总代金券金额
+ */
+ private Integer couponFee;
+
+ /**
+ * 代金券使用数量
+ */
+ private Integer couponCount;
+
+ /**
+ * 微信支付订单号
+ */
+ private String transactionId;
+
+ /**
+ * 商户订单号
+ */
+ private String outTradeNo;
+
+ /**
+ * 商家数据包
+ */
+ private String attach;
+
+ /**
+ * 支付完成时间
+ */
+ private String timeEnd;
+
+ public String getReturnCode() {
+ return returnCode;
+ }
+
+ public void setReturnCode(String returnCode) {
+ this.returnCode = returnCode;
+ }
+
+ public String getReturnMsg() {
+ return returnMsg;
+ }
+
+ public void setReturnMsg(String returnMsg) {
+ this.returnMsg = returnMsg;
+ }
+
+ public String getAppId() {
+ return appId;
+ }
+
+ public void setAppId(String appId) {
+ this.appId = appId;
+ }
+
+ public String getMchId() {
+ return mchId;
+ }
+
+ public void setMchId(String mchId) {
+ this.mchId = mchId;
+ }
+
+ public String getDeviceInfo() {
+ return deviceInfo;
+ }
+
+ public void setDeviceInfo(String deviceInfo) {
+ this.deviceInfo = deviceInfo;
+ }
+
+ public String getNonceStr() {
+ return nonceStr;
+ }
+
+ public void setNonceStr(String nonceStr) {
+ this.nonceStr = nonceStr;
+ }
+
+ public String getSign() {
+ return sign;
+ }
+
+ public void setSign(String sign) {
+ this.sign = sign;
+ }
+
+ public String getSignType() {
+ return signType;
+ }
+
+ public void setSignType(String signType) {
+ this.signType = signType;
+ }
+
+ public String getResultCode() {
+ return resultCode;
+ }
+
+ public void setResultCode(String resultCode) {
+ this.resultCode = resultCode;
+ }
+
+ public String getErrCode() {
+ return errCode;
+ }
+
+ public void setErrCode(String errCode) {
+ this.errCode = errCode;
+ }
+
+ public String getErrCodeDes() {
+ return errCodeDes;
+ }
+
+ public void setErrCodeDes(String errCodeDes) {
+ this.errCodeDes = errCodeDes;
+ }
+
+ public String getOpenId() {
+ return openId;
+ }
+
+ public void setOpenId(String openId) {
+ this.openId = openId;
+ }
+
+ public String getIsSubscribe() {
+ return isSubscribe;
+ }
+
+ public void setIsSubscribe(String isSubscribe) {
+ this.isSubscribe = isSubscribe;
+ }
+
+ public String getTradeType() {
+ return tradeType;
+ }
+
+ public void setTradeType(String tradeType) {
+ this.tradeType = tradeType;
+ }
+
+ public String getBankType() {
+ return bankType;
+ }
+
+ public void setBankType(String bankType) {
+ this.bankType = bankType;
+ }
+
+ public Integer getTotalFee() {
+ return totalFee;
+ }
+
+ public void setTotalFee(Integer totalFee) {
+ this.totalFee = totalFee;
+ }
+
+ public Integer getSettlementTotalFee() {
+ return settlementTotalFee;
+ }
+
+ public void setSettlementTotalFee(Integer settlementTotalFee) {
+ this.settlementTotalFee = settlementTotalFee;
+ }
+
+ public String getFeeType() {
+ return feeType;
+ }
+
+ public void setFeeType(String feeType) {
+ this.feeType = feeType;
+ }
+
+ public Integer getCashFee() {
+ return cashFee;
+ }
+
+ public void setCashFee(Integer cashFee) {
+ this.cashFee = cashFee;
+ }
+
+ public String getCashFeeType() {
+ return cashFeeType;
+ }
+
+ public void setCashFeeType(String cashFeeType) {
+ this.cashFeeType = cashFeeType;
+ }
+
+ public Integer getCouponFee() {
+ return couponFee;
+ }
+
+ public void setCouponFee(Integer couponFee) {
+ this.couponFee = couponFee;
+ }
+
+ public Integer getCouponCount() {
+ return couponCount;
+ }
+
+ public void setCouponCount(Integer couponCount) {
+ this.couponCount = couponCount;
+ }
+
+ public String getTransactionId() {
+ return transactionId;
+ }
+
+ public void setTransactionId(String transactionId) {
+ this.transactionId = transactionId;
+ }
+
+ public String getOutTradeNo() {
+ return outTradeNo;
+ }
+
+ public void setOutTradeNo(String outTradeNo) {
+ this.outTradeNo = outTradeNo;
+ }
+
+ public String getAttach() {
+ return attach;
+ }
+
+ public void setAttach(String attach) {
+ this.attach = attach;
+ }
+
+ public String getTimeEnd() {
+ return timeEnd;
+ }
+
+ public void setTimeEnd(String timeEnd) {
+ this.timeEnd = timeEnd;
+ }
+
+ @Override
+ public String toString() {
+ return "WxPayResult [returnCode=" + returnCode + ", returnMsg=" + returnMsg + ", appId=" + appId + ", mchId="
+ + mchId + ", deviceInfo=" + deviceInfo + ", nonceStr=" + nonceStr + ", sign=" + sign + ", signType="
+ + signType + ", resultCode=" + resultCode + ", errCode=" + errCode + ", errCodeDes=" + errCodeDes
+ + ", openId=" + openId + ", isSubscribe=" + isSubscribe + ", tradeType=" + tradeType + ", bankType="
+ + bankType + ", totalFee=" + totalFee + ", settlementTotalFee=" + settlementTotalFee + ", feeType="
+ + feeType + ", cashFee=" + cashFee + ", cashFeeType=" + cashFeeType + ", couponFee=" + couponFee
+ + ", couponCount=" + couponCount + ", transactionId=" + transactionId + ", outTradeNo=" + outTradeNo
+ + ", attach=" + attach + ", timeEnd=" + timeEnd + "]";
+ }
+
+}
diff --git a/src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxPayResultReply.java b/src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxPayResultReply.java
new file mode 100644
index 000000000..6a79428f5
--- /dev/null
+++ b/src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxPayResultReply.java
@@ -0,0 +1,36 @@
+package club.joylink.rtss.services.pay.wechat.bean;
+
+public class WxPayResultReply {
+
+ /**
+ * 返回状态码
+ */
+ private String returnCode;
+
+ /**
+ * 返回信息
+ */
+ private String returnMsg;
+
+ public String getReturnCode() {
+ return returnCode;
+ }
+
+ public void setReturnCode(String returnCode) {
+ this.returnCode = returnCode;
+ }
+
+ public String getReturnMsg() {
+ return returnMsg;
+ }
+
+ public void setReturnMsg(String returnMsg) {
+ this.returnMsg = returnMsg;
+ }
+
+ @Override
+ public String toString() {
+ return "WxPayResultReply [returnCode=" + returnCode + ", returnMsg=" + returnMsg + "]";
+ }
+
+}
diff --git a/src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxUnifiedOrder.java b/src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxUnifiedOrder.java
new file mode 100644
index 000000000..12def51d0
--- /dev/null
+++ b/src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxUnifiedOrder.java
@@ -0,0 +1,221 @@
+package club.joylink.rtss.services.pay.wechat.bean;
+
+import club.joylink.rtss.services.pay.bean.OrderPay;
+import club.joylink.rtss.services.pay.wechat.constant.WechatConstants;
+import club.joylink.rtss.vo.client.order.OrderDetailVO;
+import lombok.Getter;
+
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Getter
+public class WxUnifiedOrder {
+
+ /**
+ * 公众账号ID*
+ */
+ private String appid;
+
+ /**
+ * 商户号*
+ */
+ private String mchid;
+
+ /**
+ * 商品描述
+ */
+ private String description;
+
+ /**
+ * 商户订单号
+ */
+ private String out_trade_no;
+
+ /**
+ * 交易结束时间
+ */
+ private String time_expire;
+
+ /**
+ * 附加数据
+ */
+ private String attach;
+
+ /**
+ * 通知地址
+ */
+ private String notify_url;
+
+ /**
+ * 订单优惠标记
+ */
+ private String goods_tag;
+
+ /**
+ * 订单金额信息
+ */
+ private WxPayOrderAmountInfo amount;
+
+ /**
+ * 优惠功能(没懂为啥这个参数要叫优惠功能)
+ */
+ private WxPayOrderDetail detail;
+
+ /**
+ * 场景信息,支付场景描述
+ */
+ private WxPaySceneInfo scene_info;
+
+ public WxUnifiedOrder(OrderPay orderPay, String notify_url) {
+ this.appid = WechatConstants.APP_ID;
+ this.mchid = WechatConstants.MCH_ID;
+ this.description = orderPay.getDescription();
+ this.out_trade_no = orderPay.getOrderNo();
+ if (orderPay.getTimeExpire() != null) {
+ this.time_expire = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(orderPay.getTimeExpire().withNano(0));
+ }
+// this.attach =
+ this.notify_url = notify_url;
+// this.goods_tag =
+ this.amount = new WxPayOrderAmountInfo(orderPay.getTotalFee());
+// this.detail = new WxPayOrderDetail(out_trade_no, orderPay.getOrderDetail());
+// this.scene_info = new WxPaySceneInfo();
+ }
+
+ /**
+ * 订单金额信息
+ */
+ @Getter
+ class WxPayOrderAmountInfo {
+ /**
+ * 订单总金额,单位/分
+ */
+ private int total;
+
+ /**
+ * 货币类型
+ */
+ private String currency = "CNY";
+
+ public WxPayOrderAmountInfo(int total) {
+ this.total = total;
+ }
+ }
+
+ @Getter
+ class WxPayOrderDetail {
+ /**
+ * 订单原价
+ * 该字段主要用于防止同一张小票分多次支付,以享受多次优惠的情况,正常支付订单不必上传此参数。
+ */
+ private int cost_price;
+
+ /**
+ * 商家小票ID
+ */
+ private String invoice_id;
+
+ /**
+ * 单品列表
+ */
+ private List goods_detail;
+
+ public WxPayOrderDetail(String invoice_id, List orderDetailVOS) {
+ this.invoice_id = invoice_id;
+ this.goods_detail = orderDetailVOS.stream().map(GoodsDetail::new).collect(Collectors.toList());
+ }
+
+ @Getter
+ class GoodsDetail {
+ /**
+ * 商户侧商品编码
+ * 由半角的大小写字母、数字、中划线、下划线中的一种或几种组成。
+ */
+ private String merchant_goods_id;
+
+ /**
+ * 微信侧商品编码
+ * 微信支付定义的统一商品编号(没有可不传)
+ */
+ private String wechatpay_goods_id;
+
+ /**
+ * 商品名称
+ */
+ private String goods_name;
+
+ /**
+ * 商品数量
+ */
+ private int quantity;
+
+ /**
+ * 商品单价,单位为分
+ */
+ private int unit_price;
+
+ public GoodsDetail(OrderDetailVO orderDetail) {
+ this.merchant_goods_id = orderDetail.getGoodsId().toString();
+ this.goods_name = orderDetail.getGoodsName();
+ this.quantity = orderDetail.getGoodsAmount();
+ this.unit_price = orderDetail.getPrice().intValue();
+ }
+ }
+ }
+
+ /**
+ * 场景信息
+ */
+ @Getter
+ class WxPaySceneInfo {
+ /**
+ * 用户终端ip。调用微信支付API的机器IP,支持IPv4和IPv6两种格式的IP地址。
+ */
+ private String payer_client_ip;
+
+ /**
+ * 商户端设备号。商户端设备号(门店号或收银设备ID)。
+ */
+ private String device_id;
+
+ /**
+ * 商户门店信息
+ */
+ private StoreInfo store_info;
+
+ public WxPaySceneInfo() {
+ this.payer_client_ip = WechatConstants.SPBILL_CREATE_IP;
+ this.device_id = "1";
+ this.store_info = new StoreInfo();
+ }
+
+ @Getter
+ class StoreInfo {
+ /**
+ * 门店编号,商户侧门店编号
+ */
+ private String id;
+
+ /**
+ * 门店名称,商户侧门店名称
+ */
+ private String name;
+
+ /**
+ * 地区编码,地区编码,详细请见省市区编号对照表。
+ */
+ private String area_code;
+
+ /**
+ * 详细地址,详细的商户门店地址
+ */
+ private String address;
+
+ public StoreInfo() {
+ }
+ }
+ }
+
+
+}
diff --git a/src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxUnifiedOrderResult.java b/src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxUnifiedOrderResult.java
new file mode 100644
index 000000000..1b82840b4
--- /dev/null
+++ b/src/main/java/club/joylink/rtss/services/pay/wechat/bean/WxUnifiedOrderResult.java
@@ -0,0 +1,183 @@
+package club.joylink.rtss.services.pay.wechat.bean;
+
+
+public class WxUnifiedOrderResult {
+
+ /**
+ * 返回状态码*
+ */
+ private String returnCode;
+
+ /**
+ * 返回信息*
+ */
+ private String returnMsg;
+
+ /**
+ * 公众账号ID*
+ */
+ private String appId;
+
+ /**
+ * 商户号*
+ */
+ private String mchId;
+
+ /**
+ * 设备号
+ */
+ private String deviceInfo;
+
+ /**
+ * 随机字符串*
+ */
+ private String nonceStr;
+
+ /**
+ * 签名*
+ */
+ private String sign;
+
+ /**
+ * 业务结果*
+ */
+ private String resultCode;
+
+ /**
+ * 错误代码
+ */
+ private String errCode;
+
+ /**
+ * 错误代码描述
+ */
+ private String errCodeDes;
+
+ /**
+ * 交易类型*
+ */
+ private String tradeType;
+
+ /**
+ * 预支付交易会话标识*
+ */
+ private String prepayId;
+
+ /**
+ * 二维码链接
+ */
+ private String codeUrl;
+
+ public String getReturnCode() {
+ return returnCode;
+ }
+
+ public void setReturnCode(String returnCode) {
+ this.returnCode = returnCode;
+ }
+
+ public String getReturnMsg() {
+ return returnMsg;
+ }
+
+ public void setReturnMsg(String returnMsg) {
+ this.returnMsg = returnMsg;
+ }
+
+ public String getAppId() {
+ return appId;
+ }
+
+ public void setAppId(String appId) {
+ this.appId = appId;
+ }
+
+ public String getMchId() {
+ return mchId;
+ }
+
+ public void setMchId(String mchId) {
+ this.mchId = mchId;
+ }
+
+ public String getDeviceInfo() {
+ return deviceInfo;
+ }
+
+ public void setDeviceInfo(String deviceInfo) {
+ this.deviceInfo = deviceInfo;
+ }
+
+ public String getNonceStr() {
+ return nonceStr;
+ }
+
+ public void setNonceStr(String nonceStr) {
+ this.nonceStr = nonceStr;
+ }
+
+ public String getSign() {
+ return sign;
+ }
+
+ public void setSign(String sign) {
+ this.sign = sign;
+ }
+
+ public String getResultCode() {
+ return resultCode;
+ }
+
+ public void setResultCode(String resultCode) {
+ this.resultCode = resultCode;
+ }
+
+ public String getErrCode() {
+ return errCode;
+ }
+
+ public void setErrCode(String errCode) {
+ this.errCode = errCode;
+ }
+
+ public String getErrCodeDes() {
+ return errCodeDes;
+ }
+
+ public void setErrCodeDes(String errCodeDes) {
+ this.errCodeDes = errCodeDes;
+ }
+
+ public String getTradeType() {
+ return tradeType;
+ }
+
+ public void setTradeType(String tradeType) {
+ this.tradeType = tradeType;
+ }
+
+ public String getPrepayId() {
+ return prepayId;
+ }
+
+ public void setPrepayId(String prepayId) {
+ this.prepayId = prepayId;
+ }
+
+ public String getCodeUrl() {
+ return codeUrl;
+ }
+
+ public void setCodeUrl(String codeUrl) {
+ this.codeUrl = codeUrl;
+ }
+
+ @Override
+ public String toString() {
+ return "WxUnifiedOrderResult [returnCode=" + returnCode + ", returnMsg=" + returnMsg + ", appId=" + appId
+ + ", mchId=" + mchId + ", deviceInfo=" + deviceInfo + ", nonceStr=" + nonceStr + ", sign=" + sign
+ + ", resultCode=" + resultCode + ", errCode=" + errCode + ", errCodeDes=" + errCodeDes + ", tradeType="
+ + tradeType + ", prepayId=" + prepayId + ", codeUrl=" + codeUrl + "]";
+ }
+
+}
diff --git a/src/main/java/club/joylink/rtss/services/pay/wechat/constant/WechatConstants.java b/src/main/java/club/joylink/rtss/services/pay/wechat/constant/WechatConstants.java
new file mode 100644
index 000000000..fb4f42a29
--- /dev/null
+++ b/src/main/java/club/joylink/rtss/services/pay/wechat/constant/WechatConstants.java
@@ -0,0 +1,73 @@
+package club.joylink.rtss.services.pay.wechat.constant;
+
+public interface WechatConstants {
+
+ public static interface PayUrl {
+ /** 统一下单接口地址 */
+ String UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
+ }
+
+ String DATE_FORMAT_STR = "yyyyMMddHHmmss";
+
+ /** 公众账号ID */
+ String APP_ID = "wx41cb66db5faf330f";
+
+ /** 商户号 */
+ String MCH_ID = "1511854231";
+
+ /** 机器IP */
+ String SPBILL_CREATE_IP = "58.87.66.250";
+
+ /** 商户平台设置的密钥key(应该已经没用了) */
+ String MCH_KEY = "a6502d608c64fb2f7e6dbcf854cda32a";
+
+ /** 商户证书序列号 */
+ String MCH_SERIAL_NO = "40980EBE9204E4D8956215FE72A1C89A8480FA27";
+
+ /** APIv3密钥,用于解密接口返回的数据(平台证书公钥) */
+ String APIV3_KEY = "c0666cec1cb15d915f802ce5a3df5c2d";
+
+ /** 商户证书私钥 */
+ String MCH_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n" +
+ "MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDAzmBwXZX7p+bl\n" +
+ "IiSl/5CukfAxOmYTVgCbLQ1SBJMd9c52VsPgWED4o/llkvwWXY9EUTDX09qOBroF\n" +
+ "+iCQBu9iWvStfN2VTmy7ECC3ROv1HzAK2U/aWOsCxv/svpqUoWImqAVaUtOd+kQK\n" +
+ "RtMVvz/kE+BUZO1RFA3YS7dmYPtAuZQOhLXTvg7uS5k/PaSg39Bx+ZNcymUQtHHs\n" +
+ "6nH0RmneY2LBtmLZHw3aeb2VCydEPY9tON/vhBbLXe/+Lq3C8Lx8R2IdURCcR2Wl\n" +
+ "92UlLwR5nBK/kKcgOPA7DjBf4zPGBHgFovytW5gKRY+UscrPgSEHxa9fmRy/SLQS\n" +
+ "k7AjGZC5AgMBAAECggEBALGIFiLA+Y5sbt3DD43OAbHMbSdXB5B8WziHRkGkNraa\n" +
+ "lI5AnEHh4YlQqx7NNdN+OKIGRHwm9ZJbPUStqPgVeqzM5YktdXa6bMHeOtGl48Kk\n" +
+ "Af+rU6zQvSykghjC9OEwrIi9o5gktfg77hSsXEck/7aKWsA64o4KcikcpvXdDNzt\n" +
+ "qrLumHzJ32WLN6q5JClc4zPfpc3CuRUH3SJevDC7b0E+BFth8c1cX1QDbSl1v8kA\n" +
+ "anolAIcHqhFvJHzpY+WjHqGVdN4RbPXl+xsOoo3mXsTHybSWgHd2ZJGAAqtQlaSV\n" +
+ "Y9dRLSU4MQBITNGe5j+rngyRowDkrYqbtAfFqtYQhwUCgYEA5iDlSqu69pr50xf4\n" +
+ "oQmyOk7ep9U8JOTH+C+OOtbVzpS51oLnq95wJ8cu/QV8qhOUVSZ6KKhD37o7XWhZ\n" +
+ "vGjBGrlv/C0anAsZ4S10GmL/gbqpCckSe4ITz4VI03oy/fdRK+VtulgMFvjKEr9F\n" +
+ "cYOkct4IkKAclAECYLhX7VUEKrMCgYEA1ntX1kA4objy7+bgfmxmFkAk50otHJwR\n" +
+ "huCEOMe9MlgAAc9NYk+GEF1tQPzTQVdVL6jovtrZ+L5HP1SXJGQWY6N06XJThN9+\n" +
+ "oks027gQWBoXnTQJ8/wXPQpXq3EIgCThzKNkTLq4fsnGIUgRUH2/W+IjlNEThw26\n" +
+ "KqmuBiXjfOMCgYEAxe6lSIRMWq8RES8c+eWNFfmgKFqPUGw2UpEUlCcT3oqtDIOr\n" +
+ "H3hCnvQCxj1h7CbK/jIJ/846EsPrK3wFMrgm3wV//DYPHQevSq39nnRnrv0NRw1a\n" +
+ "iEBpKaRJ7xq7oRSHDGpY5l20iE2UAGvjHq9LUkEGvN35tpLnqKjld4wX+WECgYEA\n" +
+ "urCwGzDZWoOXCpTHMaP/FD0PIjehnraGVwWUcawClhCdKPYdoIYh5pq734ZyB/0R\n" +
+ "jCOVO5NZibduYsSprqZkCqSbvhuicRTssC2QO/QyXc2QYmiKhVIXlC0tdHA1+vyf\n" +
+ "grVyN4uLzeipygxl7c8Wws7LM9ztB3A+bKY3cOiH5AsCgYEA4XOMKmJeucNOrXTY\n" +
+ "3NdHCb8Yx5JgCYqgdq5uIY7MQ8f3+OlXNwdhh/ftjk8DejWzyY7jpqgj2W3qZFu+\n" +
+ "BxGJZfCDzAqUS9OufMY8tOO/PaIABzSkU4gliBAuxovj9sU6oSSa7aYD+34QW2UU\n" +
+ "L83fUdwBuNbwX6vZM45hIASP6C8=\n" +
+ "-----END PRIVATE KEY-----";
+
+ /**
+ * 支付类型
+ * @author sheng
+ */
+ public static interface TradeType {
+ /** 扫码支付 */
+ String NATIVE = "NATIVE";
+ /** 公众号支付 */
+ String JSAPI = "JSAPI";
+ /** APP支付 */
+ String APP = "APP";
+ }
+
+}
diff --git a/src/main/java/club/joylink/rtss/services/pay/wechat/controller/PayResultController.java b/src/main/java/club/joylink/rtss/services/pay/wechat/controller/PayResultController.java
new file mode 100644
index 000000000..82ffcf783
--- /dev/null
+++ b/src/main/java/club/joylink/rtss/services/pay/wechat/controller/PayResultController.java
@@ -0,0 +1,25 @@
+package club.joylink.rtss.services.pay.wechat.controller;
+
+import club.joylink.rtss.services.pay.wechat.WechatPayService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api/wechatpay")
+@Slf4j
+public class PayResultController {
+
+ @Autowired
+ private WechatPayService wechatPayService;
+
+ @RequestMapping(path="/receive", method= {RequestMethod.POST})
+ public String receive(@RequestBody String data) {
+ log.info("支付结果通知:" + data);
+ return this.wechatPayService.receive(data);
+ }
+
+}
diff --git a/src/main/java/club/joylink/rtss/services/pay/wechat/util/SignUtil.java b/src/main/java/club/joylink/rtss/services/pay/wechat/util/SignUtil.java
new file mode 100644
index 000000000..4dc54d855
--- /dev/null
+++ b/src/main/java/club/joylink/rtss/services/pay/wechat/util/SignUtil.java
@@ -0,0 +1,39 @@
+package club.joylink.rtss.services.pay.wechat.util;
+
+import club.joylink.rtss.util.EncryptUtil;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+public class SignUtil {
+
+ public static String sign(Map map, String mchKey) {
+ Set keysSet = map.keySet();
+ Object[] keys = keysSet.toArray();
+ // 排序
+ Arrays.sort(keys);
+ StringBuffer temp = new StringBuffer();
+ boolean first = true;
+ for (Object key : keys) {
+ Object value = map.get(key);
+ if(null != value) {
+ if (first) {
+ first = false;
+ } else {
+ temp.append("&");
+ }
+ temp.append(key).append("=");
+ String valueString = "";
+ if (null != value) {
+ valueString = value.toString();
+ }
+ temp.append(valueString);
+ }
+ }
+ String tempStr = temp.toString()+"&key="+mchKey;
+ System.out.println(tempStr);
+ return EncryptUtil.md5(tempStr).toUpperCase();
+ }
+
+}
diff --git a/src/main/java/club/joylink/rtss/vo/client/order/OrderDetailVO.java b/src/main/java/club/joylink/rtss/vo/client/order/OrderDetailVO.java
index 84da120e0..dca3c6ebc 100644
--- a/src/main/java/club/joylink/rtss/vo/client/order/OrderDetailVO.java
+++ b/src/main/java/club/joylink/rtss/vo/client/order/OrderDetailVO.java
@@ -31,7 +31,7 @@ public class OrderDetailVO {
private Integer monthAmount;
- private Float price;
+ private Long price;
public OrderDetailVO(SaleOrderDetail detail) {
id = detail.getId();
@@ -41,7 +41,7 @@ public class OrderDetailVO {
startTime = detail.getStartTime();
goodsAmount = detail.getGoodsAmount();
monthAmount = detail.getMonthAmount();
- price = (float) detail.getPrice() / 100;
+ price = detail.getPrice();
}
}