From a0a090dc6d71f06dd8ed1b75a3f3ca1f501b20e4 Mon Sep 17 00:00:00 2001 From: gxwebsoft Date: Sat, 11 May 2024 19:11:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E5=BE=AE=E4=BF=A1Na?= =?UTF-8?q?tive=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/core/security/SecurityConfig.java | 1 + .../system/controller/OrderController.java | 28 +++- .../system/controller/WxLoginController.java | 24 ++-- .../controller/WxNativePayController.java | 132 ++++++++++++++++++ .../controller/WxPayNotifyController.java | 2 +- .../gxwebsoft/common/system/entity/User.java | 2 - .../system/service/impl/OrderServiceImpl.java | 6 +- src/main/resources/cert/apiclient_cert.pem | 25 ++++ src/main/resources/cert/apiclient_key.pem | 28 ++++ 9 files changed, 229 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/gxwebsoft/common/system/controller/WxNativePayController.java create mode 100644 src/main/resources/cert/apiclient_cert.pem create mode 100644 src/main/resources/cert/apiclient_key.pem diff --git a/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java b/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java index 5725652..b74261b 100644 --- a/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java +++ b/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java @@ -51,6 +51,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { "/api/parseToken/*", "/api/login-alipay/*", "/api/wx-login/loginByMpWxPhone", + "/api/system/wx-native-pay/**", "/api/wxWorkQrConnect", "/api/sys/user-plan-log/wx-pay/**", "/api/wx-official/**", diff --git a/src/main/java/com/gxwebsoft/common/system/controller/OrderController.java b/src/main/java/com/gxwebsoft/common/system/controller/OrderController.java index 04fa874..2cb02c8 100644 --- a/src/main/java/com/gxwebsoft/common/system/controller/OrderController.java +++ b/src/main/java/com/gxwebsoft/common/system/controller/OrderController.java @@ -1,9 +1,11 @@ package com.gxwebsoft.common.system.controller; import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.gxwebsoft.common.core.web.BaseController; import com.gxwebsoft.common.system.entity.Company; +import com.gxwebsoft.common.system.entity.OrderInfo; import com.gxwebsoft.common.system.entity.User; import com.gxwebsoft.common.system.service.CompanyService; import com.gxwebsoft.common.system.service.OrderInfoService; @@ -21,6 +23,7 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import java.math.BigDecimal; import java.util.Date; import java.util.List; @@ -75,13 +78,30 @@ public class OrderController extends BaseController { public ApiResult save(@RequestBody Order order) { // 记录当前登录用户id User loginUser = getLoginUser(); + if (loginUser == null) { + return fail("请先登录"); + } + // 微信openid(必填) + if(StrUtil.isBlank(loginUser.getOpenid())){ + return fail("微信openid(必填)"); + } + // 商品描述(必填) + if(StrUtil.isBlank(order.getComments())){ + return fail("商品描述(必填)"); + } + // 微信支付(商品金额不能为0) + if(order.getPayType().equals(1)){ + if (order.getTotalPrice().compareTo(BigDecimal.ZERO) == 0) { + return fail("商品金额不能为0"); + } + } + + // 订单信息 long timeMillis = System.currentTimeMillis(); long orderNo = IdUtil.getSnowflakeNextId(); - if (loginUser != null) { - order.setUserId(loginUser.getUserId()); - order.setOpenid(loginUser.getOpenid()); - } order.setOrderNo(Long.toString(orderNo)); + order.setUserId(loginUser.getUserId()); + order.setOpenid(loginUser.getOpenid()); order.setRealName(loginUser.getRealName()); order.setStartTime(timeMillis / 1000); order.setAddTime(timeMillis / 1000); diff --git a/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java b/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java index a3e1f84..7265767 100644 --- a/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java +++ b/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java @@ -253,9 +253,14 @@ public class WxLoginController extends BaseController { @PostMapping("/getWxOpenId") public ApiResult getWxOpenId(@RequestBody UserParam userParam) { final User loginUser = getLoginUser(); - if(loginUser.getUsername().equals("www")){ - return fail("游客"); + if(loginUser == null){ + return fail("请先登录"); } + // 已存在直接返回 + if(StrUtil.isNotBlank(loginUser.getOpenid())){ + return success(loginUser); + } + // 请求微信接口获取openid String apiUrl = "https://api.weixin.qq.com/sns/jscode2session"; final HashMap map = new HashMap<>(); final JSONObject setting = settingService.getBySettingKey("mp-weixin"); @@ -266,13 +271,16 @@ public class WxLoginController extends BaseController { map.put("js_code",userParam.getCode()); map.put("grant_type","authorization_code"); final String response = HttpUtil.get(apiUrl,map); - System.out.println("response = " + response); final JSONObject jsonObject = JSONObject.parseObject(response); - final String openid = jsonObject.getString("openid"); - jsonObject.getString("session_key"); - jsonObject.getString("unionid"); - System.out.println("openid = " + openid); - System.out.println("loginUser = " + loginUser); + String openid = jsonObject.getString("openid"); + String sessionKey = jsonObject.getString("session_key"); + String unionid = jsonObject.getString("unionid"); + // 保存openID + if(loginUser.getOpenid() == null || StrUtil.isBlank(loginUser.getOpenid())){ + loginUser.setOpenid(openid); + loginUser.setUnionid(unionid); + userService.updateById(loginUser); + } return success("获取成功",jsonObject); } diff --git a/src/main/java/com/gxwebsoft/common/system/controller/WxNativePayController.java b/src/main/java/com/gxwebsoft/common/system/controller/WxNativePayController.java new file mode 100644 index 0000000..dc8ec27 --- /dev/null +++ b/src/main/java/com/gxwebsoft/common/system/controller/WxNativePayController.java @@ -0,0 +1,132 @@ +package com.gxwebsoft.common.system.controller; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.gxwebsoft.common.core.config.ConfigProperties; +import com.gxwebsoft.common.core.utils.CommonUtil; +import com.gxwebsoft.common.core.web.ApiResult; +import com.gxwebsoft.common.core.web.BaseController; +import com.gxwebsoft.common.system.entity.Order; +import com.gxwebsoft.common.system.entity.Setting; +import com.gxwebsoft.common.system.entity.User; +import com.gxwebsoft.common.system.param.SettingParam; +import com.gxwebsoft.common.system.service.OrderService; +import com.wechat.pay.java.core.Config; +import com.wechat.pay.java.core.RSAAutoCertificateConfig; +import com.wechat.pay.java.core.notification.NotificationConfig; +import com.wechat.pay.java.core.notification.NotificationParser; +import com.wechat.pay.java.core.notification.RSANotificationConfig; +import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction; +import com.wechat.pay.java.service.payments.nativepay.NativePayService; +import com.wechat.pay.java.service.payments.nativepay.model.Amount; +import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest; +import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.io.File; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.util.Map; + +import static com.gxwebsoft.common.core.constants.OrderConstants.PAY_STATUS_NO_PAY; +import static com.gxwebsoft.common.core.constants.OrderConstants.PAY_STATUS_SUCCESS; + + +@Api(tags = "微信Native支付接口") +@RestController +@RequestMapping("/api/system/wx-native-pay") +public class WxNativePayController extends BaseController { + /** 商户号 */ + public static String merchantId = "1246610101"; + /** 商户API私钥路径 */ + public static String privateKeyPath = "/Users/gxwebsoft/JAVA/com.gxwebsoft.core/src/main/resources/cert/apiclient_key.pem"; +// public static String privateKeyPath = "/Users/gxwebsoft/cert/1246610101_20240511_cert/apiclient_cert.pem"; + /** 商户证书序列号 */ + public static String merchantSerialNumber = "48749613B40AA8F1D768583FC352358E13EB5AF0"; + /** 商户APIV3密钥 */ + public static String apiV3Key = "zGufUcqa7ovgxRL0kF5OlPr482EZwtn9"; + // 公众号的AppID + public static String appId = "wx541db955e7a62709"; + private final Config wxPayConfig; + @Resource + private OrderService orderService; + @Resource + private ConfigProperties config; + + + public WxNativePayController() { + File file = new File(privateKeyPath); + if (file.exists()) { + this.wxPayConfig = + new RSAAutoCertificateConfig.Builder() + .merchantId(merchantId) + .privateKeyFromPath(privateKeyPath) + .merchantSerialNumber(merchantSerialNumber) + .apiV3Key(apiV3Key) + .build(); + }else { + System.out.println("无法访问文件"); + this.wxPayConfig = null ; + } + } + + @ApiOperation("生成付款码") + @PostMapping("/codeUrl") + public ApiResult getCodeUrl(@RequestBody Order order) { + order.setMoney(new BigDecimal(0.01)); + order.setOrderNo(CommonUtil.createOrderNo()); + // 使用自动更新平台证书的RSA配置 + // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错 + + // 构建service + NativePayService service = new NativePayService.Builder().config(wxPayConfig).build(); + // request.setXxx(val)设置所需参数,具体参数可见Request定义 + PrepayRequest request = new PrepayRequest(); + // 计算金额 + BigDecimal decimal = order.getMoney(); + final BigDecimal multiply = decimal.multiply(new BigDecimal(100)); + // 将 BigDecimal 转换为 Integer + Integer money = multiply.intValue(); + Amount amount = new Amount(); + amount.setTotal(money); + request.setAmount(amount); + request.setAppid(appId); + request.setMchid(merchantId); + request.setDescription("网宿软件企业版-1年授权"); + request.setNotifyUrl("https://server.gxwebsoft.com/api/system/wx-native-pay/notify/10150"); + request.setOutTradeNo(order.getOrderNo()); + // 调用下单方法,得到应答 + PrepayResponse response = service.prepay(request); + // 使用微信扫描 code_url 对应的二维码,即可体验Native支付 +// System.out.println(response.getCodeUrl()); + // 生成指定url对应的二维码到文件,宽和高都是300像素 +// QrCodeUtil.generate(response.getCodeUrl(), 300, 300, FileUtil.file("/Users/gxwebsoft/Documents/uploads/wx-native-qrcode.jpg")); + return success("生成付款码",response.getCodeUrl()); + } + + + @ApiModelProperty("异步通知") + @PostMapping("/notify/{tenantId}") + public String wxNotify(@RequestHeader Map header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) { + System.out.println("异步通知*************** = "); + System.out.println("request header = " + header); + System.out.println("request body = " + body); + System.out.println("tenantId = " + tenantId); + + // 获取支付配置信息用于解密 + final SettingParam param = new SettingParam(); + param.setSettingKey("payment"); + param.setTenantId(tenantId); + final String uploadPath = config.getUploadPath(); // 服务器本地路径 + + return "fail"; + } + + + +} diff --git a/src/main/java/com/gxwebsoft/common/system/controller/WxPayNotifyController.java b/src/main/java/com/gxwebsoft/common/system/controller/WxPayNotifyController.java index 539f4c5..0915a86 100644 --- a/src/main/java/com/gxwebsoft/common/system/controller/WxPayNotifyController.java +++ b/src/main/java/com/gxwebsoft/common/system/controller/WxPayNotifyController.java @@ -52,7 +52,7 @@ import static com.gxwebsoft.common.core.constants.OrderConstants.PAY_STATUS_SUCC */ @Api(tags = "会员特权购买记录表管理") @RestController -@RequestMapping("/api/love/user-plan-log") +@RequestMapping("/api/system/notify") public class WxPayNotifyController extends BaseController { @Resource private OrderService orderService; diff --git a/src/main/java/com/gxwebsoft/common/system/entity/User.java b/src/main/java/com/gxwebsoft/common/system/entity/User.java index 235becd..e878bac 100644 --- a/src/main/java/com/gxwebsoft/common/system/entity/User.java +++ b/src/main/java/com/gxwebsoft/common/system/entity/User.java @@ -237,11 +237,9 @@ public class User implements UserDetails { private Integer dealerId; @ApiModelProperty("微信openid") - @TableField(exist = false) private String openid; @ApiModelProperty("微信unionid") - @TableField(exist = false) private String unionid; @ApiModelProperty("ico文件") diff --git a/src/main/java/com/gxwebsoft/common/system/service/impl/OrderServiceImpl.java b/src/main/java/com/gxwebsoft/common/system/service/impl/OrderServiceImpl.java index c1d94e5..908a612 100644 --- a/src/main/java/com/gxwebsoft/common/system/service/impl/OrderServiceImpl.java +++ b/src/main/java/com/gxwebsoft/common/system/service/impl/OrderServiceImpl.java @@ -96,7 +96,7 @@ public class OrderServiceImpl extends ServiceImpl implements final String appId = mpWx.getString("appId"); final String mchId = payment.getMchId(); final String openid = order.getOpenid(); // openid - final String notifyUrl = config.getServerUrl() + "/love/user-plan-log/wx-pay/notify/" + order.getTenantId(); // 异步通知地址 + final String notifyUrl = config.getServerUrl() + "/system/notify/" + order.getTenantId(); // 异步通知地址 final String privateKey = uploadPath.concat("file").concat(payment.getApiclientKey()); // 秘钥证书 final String apiclientCert = uploadPath.concat("file").concat(payment.getApiclientCert()); final String merchantSerialNumber = payment.getMerchantSerialNumber(); // 证书序列号 @@ -127,9 +127,7 @@ public class OrderServiceImpl extends ServiceImpl implements request.setAmount(amount); request.setAppid(appId); request.setMchid(mchId); - OrderInfo desc = order.getOrderInfoList().get(0); - - request.setDescription(desc.getSiteName().concat(desc.getFieldName()).concat(desc.getDateTime())); + request.setDescription(order.getComments()); request.setNotifyUrl(notifyUrl); request.setOutTradeNo(order.getOrderNo()); request.setAttach(order.getTenantId().toString()); diff --git a/src/main/resources/cert/apiclient_cert.pem b/src/main/resources/cert/apiclient_cert.pem new file mode 100644 index 0000000..1fcd156 --- /dev/null +++ b/src/main/resources/cert/apiclient_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIUSHSWE7QKqPHXaFg/w1I1jhPrWvAwDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjQwNTExMDk0MzIzWhcNMjkwNTEwMDk0MzIzWjCBhDETMBEGA1UEAwwK +MTI0NjYxMDEwMTEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTAwLgYDVQQL +DCfljZflroHluILnvZHlrr/kv6Hmga/np5HmioDmnInpmZDlhazlj7gxCzAJBgNV +BAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAJSGQstwTNKfEUWGGNRdzAG691PKkpa78IV7SNAaAWdBUohvSGQB +Hxg2JcTjnNifqxWAVj302u0+OEPETQ+teTLeLgRfGp4b8WBKdibn9RzZD964xGhM +NkcMEwUxdqfBK28kGaKYW0zBifkzS1LDGuEVmUo9jE7pAuzDz5mJwcd1fZs4NsjD +7O60QLw4SZCXINW6IYVc41Ln+RlY2XPkm/keBydjrfvMI7Z+DqW/TEWOWshNycYr +3hqVeipz2FnUwK4ruGxEOqTXhYtn0QtvYaMcrfcXJ1U+zuwtZf+kh3RI/Lk+y2rJ +kfnuxZZ+P5K2oG+hcBapYS3q15kmf9RpMH0CAwEAAaOBuTCBtjAJBgNVHRMEAjAA +MAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2 +Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJD +MDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJC +MjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQCK +sgR2Wgb9wyyLX7ltlGXDqT44aMc3n5KI02LXv0mBD1aR4m5TFjlMzJIW2DIe01LF +yxVsUsoGIpjnAkmQOdNPL3tnCfl3bWqdNDDH9B711llNe5y1i4IYOcObhX08dEQd +vBnzuZ7/kH/t2h8q7rd7hqpQ5ZtU2xEY6ZlnohGyzNgVsDkLJI4b9iKRqOxRPVhs +GGbGKrv3JAYiFouSeH/m04xMWARFKhPoWduIeSWEJZmszWfkUBvPXo26+0YOKBVN +5gSkjioeXEX2T4/9K1SHx/iTzWvgN9MjlIJNujbg3Vz4PFU6aw2b8eK3Y0juto96 +2uoUN1fLIqxNOz2E4iSJ +-----END CERTIFICATE----- diff --git a/src/main/resources/cert/apiclient_key.pem b/src/main/resources/cert/apiclient_key.pem new file mode 100644 index 0000000..a08bc93 --- /dev/null +++ b/src/main/resources/cert/apiclient_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCUhkLLcEzSnxFF +hhjUXcwBuvdTypKWu/CFe0jQGgFnQVKIb0hkAR8YNiXE45zYn6sVgFY99NrtPjhD +xE0PrXky3i4EXxqeG/FgSnYm5/Uc2Q/euMRoTDZHDBMFMXanwStvJBmimFtMwYn5 +M0tSwxrhFZlKPYxO6QLsw8+ZicHHdX2bODbIw+zutEC8OEmQlyDVuiGFXONS5/kZ +WNlz5Jv5HgcnY637zCO2fg6lv0xFjlrITcnGK94alXoqc9hZ1MCuK7hsRDqk14WL +Z9ELb2GjHK33FydVPs7sLWX/pId0SPy5PstqyZH57sWWfj+StqBvoXAWqWEt6teZ +Jn/UaTB9AgMBAAECggEAb1Nvj5OeUaUfShBoXg3sU0O0DR9i3w8CCttMyYcklCO3 +XEKlbSgWCYzUpI7DSu/rSdOHUStOSdOAUvM5m824cbNtpKMwjWB+fWFyzFjDNhtR +NO0jctXlPT3Ep/jaaoV1K/pQKLqwfIj5BUw4YlGRvTL2Ulpt59vp8FQZMIm8MOcw +rNwYcUbMPaNKk4q3GF0LGvzW8k+S6wAWcAbx1KINRsLE0127o+shjGIlBiZgMJGZ +nTMz4xdvVbojsMhdM8aEhq6GtmSHgBFKjETQPXiOjRDCGOM5yC/9R/9WsMGJmJ4m +6Ec/RM4k9TZlnMZFsOZYO8S/kM+xgQUcAD8uGT1UgQKBgQDDGVZiqsDjudFcRkG/ +5pJN9PAC/Dk0Wzt6uRPZIhyFo2tDC/uL210Z5QR4hhB2nUSK8ANfAnepTotNzPHO +DC/sO2NzLuZz5EZTLeg9ij9BZDK+0/6AiBT2XdBKR/uGZAffjFCDh+ujm44lbrRK +7MUb9LtvDjPru1WVR0WhpFIwXQKBgQDC4xTQv6x3cPSW2SEglLVrl9CA68yO1g4T +MphCav64Cl9UDk1ov5C2SCvshFbWlIBv2g7tqb/bUk8nj42GuZWBu1spkUt2y7HS +eO89BmnaRNkVtWT8GtSMYYrYYAd23IGiOHPQqMnw/6HXkpjonpBa9c9CfEPwNtdq +84pgqed+oQKBgC6rV/PAPuX6pC87iyzZffPx/JvqM9DnZgIEVdAiDcqV/emK60BY +WBwCoaAnCbcmBahqo5PNpkw0wrP4q3sLhUcwKaj69huQ5pWtLJnUAS+mRVFKqt2a +L9GDPXkXYP6T3SJHkVb1Y5O+eTFRGwW1P61hTJjTP+5K4L0V0H1LLnHtAoGAEDBU +1lJVvUZAyxcWTWKM/3cI9uyffW4ClU2qoDnLFvalnJHjlEP1fW7ZVzhXDlQfpyrx ++oQTT+CyepLOKtbXuIMbu4Q6RI//IYCyPtt9h4gYkFkVHmwMI+0mX3r6o8EFc7hE +xpx+yeoyQ3oGAazKSQQKR3eTHS0xD81TPVxfwoECgYEAvBi3fPvIQ08pxk6kxj+S +bypHo06JHT1Fi8pmKtKCGLduK85dCeBZqHmsorWC/qg4RgCFWFFKfrFTGTxC4nf8 +MRQHmKxq+SAh4SvFgRDA0lyaUWmw7H/JpolbBDIGnXhoDI0CmQU3s2xsQdJnNPIL +azgaJXtOu+wr1MPR7Ij5OTU= +-----END PRIVATE KEY-----