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(); } }