WxPayServiceImpl.java 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925
  1. package com.dtb.portal.service.impl;
  2. import com.dtb.portal.config.WxPayAppConfig;
  3. import com.dtb.portal.config.WxPayMiniprogramConfig;
  4. import com.dtb.portal.config.interceptor.TokenUtils;
  5. import com.dtb.portal.controller.view.response.ResultMap;
  6. import com.dtb.portal.entity.AppPayData;
  7. import com.dtb.portal.entity.MiniprogramPayData;
  8. import com.dtb.portal.entity.VipGoodsData;
  9. import com.dtb.portal.entity.VipOrderData;
  10. import com.dtb.portal.entity.VipUserData;
  11. import com.dtb.portal.entity.SysUser;
  12. import com.dtb.portal.mapper.SysUserMapper;
  13. import com.dtb.portal.service.VipGoodsService;
  14. import com.dtb.portal.service.WxPayService;
  15. import com.dtb.portal.util.DateUtils;
  16. import com.dtb.portal.util.PrimaryIdUtils;
  17. import com.github.wxpay.sdk.WXPay;
  18. import com.github.wxpay.sdk.WXPayConstants;
  19. import com.github.wxpay.sdk.WXPayUtil;
  20. import com.wechat.pay.java.core.Config;
  21. import com.wechat.pay.java.core.RSAAutoCertificateConfig;
  22. import com.wechat.pay.java.core.cipher.SignatureResult;
  23. import com.wechat.pay.java.service.payments.app.AppService;
  24. import com.wechat.pay.java.service.payments.app.AppServiceExtension;
  25. import com.wechat.pay.java.service.payments.app.model.*;
  26. import com.wechat.pay.java.service.payments.jsapi.JsapiService;
  27. import com.wechat.pay.java.service.payments.jsapi.model.*;
  28. import com.wechat.pay.java.service.payments.jsapi.model.Payer;
  29. import com.wechat.pay.java.service.payments.model.Transaction;
  30. import org.apache.commons.lang3.StringUtils;
  31. import org.slf4j.Logger;
  32. import org.slf4j.LoggerFactory;
  33. import org.springframework.beans.factory.annotation.Autowired;
  34. import org.springframework.beans.factory.annotation.Value;
  35. import org.springframework.stereotype.Service;
  36. import java.io.IOException;
  37. import java.time.LocalDateTime;
  38. import java.util.*;
  39. import static java.util.Objects.requireNonNull;
  40. @Service
  41. public class WxPayServiceImpl implements WxPayService {
  42. private final Logger logger = LoggerFactory.getLogger(WxPayServiceImpl.class);
  43. @Value("${wxpay.app.v3-key}")
  44. private String v3Key;
  45. @Autowired
  46. private SysUserMapper sysUserMapper;
  47. /* private HttpClient httpClient;
  48. private WxPayServiceImpl(HttpClient httpClient) {
  49. this.httpClient = httpClient;
  50. }*/
  51. @Autowired
  52. private WxPayAppConfig wxPayAppConfig;
  53. @Autowired
  54. private WxPayMiniprogramConfig wxPayMiniprogramConfig;
  55. @Autowired
  56. private VipGoodsService vipGoodsService;
  57. /**
  58. * @param vipGoodsId : 订单编号
  59. * @return
  60. */
  61. @Override
  62. public ResultMap unifiedOrder(String vipGoodsId) {
  63. Map<String, String> returnMap = new HashMap<>();
  64. Map<String, String> responseMap = new HashMap<>();
  65. Map<String, String> requestMap = new HashMap<>();
  66. String orderNo = "";
  67. try {
  68. VipGoodsData vipGoodsData = vipGoodsService.getById(vipGoodsId);
  69. if (Objects.isNull(vipGoodsData)) {
  70. throw new RuntimeException("商品不存在");
  71. }
  72. String vipMoney = vipGoodsData.getVipMoney();
  73. double amount = Double.parseDouble(vipMoney);
  74. orderNo = PrimaryIdUtils.getId().toString();
  75. WXPay wxpay = new WXPay(wxPayAppConfig);
  76. requestMap.put("body", vipGoodsData.getName()); // 商品描述
  77. requestMap.put("out_trade_no", orderNo); // 商户订单号
  78. requestMap.put("total_fee", String.valueOf((int) (amount * 100))); // 总金额
  79. //requestMap.put("spbill_create_ip", HttpContextUtils.getIpAddr()); // 终端IP
  80. requestMap.put("trade_type", "APP"); // App支付类型
  81. requestMap.put("notify_url", wxPayAppConfig.getPayNotifyUrl()); // 接收微信支付异步通知回调地址
  82. Map<String, String> resultMap = wxpay.unifiedOrder(requestMap);
  83. logger.info("下单返回的信息是:{}", resultMap);
  84. //获取返回码
  85. String returnCode = resultMap.get("return_code");
  86. //String returnMsg = resultMap.get("return_msg");
  87. //若返回码为SUCCESS,则会返回一个result_code,再对该result_code进行判断
  88. if ("SUCCESS".equals(returnCode)) {
  89. String resultCode = resultMap.get("result_code");
  90. String errCodeDes = resultMap.get("err_code_des");
  91. if ("SUCCESS".equals(resultCode)) {
  92. responseMap = resultMap;
  93. }
  94. }
  95. if (responseMap.isEmpty()) {
  96. return ResultMap.error("获取预支付交易会话标识失败");
  97. }
  98. // 3、签名生成算法
  99. Long time = System.currentTimeMillis() / 1000;
  100. String timestamp = time.toString();
  101. returnMap.put("appid", wxPayAppConfig.getAppID());
  102. returnMap.put("partnerid", wxPayAppConfig.getMchID());
  103. returnMap.put("prepayid", responseMap.get("prepay_id"));
  104. returnMap.put("noncestr", responseMap.get("nonce_str"));
  105. returnMap.put("timestamp", timestamp);
  106. returnMap.put("package", "Sign=WXPay");
  107. returnMap.put("sign", responseMap.get("sign"));
  108. returnMap.put("out_trade_no", orderNo);
  109. String signMD5 = WXPayUtil.generateSignature(returnMap, wxPayAppConfig.getKey(), WXPayConstants.SignType.MD5);
  110. String signSHA256 = WXPayUtil.generateSignature(returnMap, wxPayAppConfig.getKey(), WXPayConstants.SignType.HMACSHA256);
  111. returnMap.put("signMD5", signMD5);
  112. returnMap.put("signSHA256", signSHA256);
  113. returnMap.put("base64SignMD5", Base64.getEncoder().encodeToString(signMD5.getBytes()));
  114. returnMap.put("base64SignSHA256", Base64.getEncoder().encodeToString(signSHA256.getBytes()));
  115. returnMap.put("key", wxPayAppConfig.getKey());
  116. logger.info("返回的信息是:{}", returnMap);
  117. return ResultMap.ok().put("data", returnMap);
  118. } catch (Exception e) {
  119. logger.error("订单号:{},错误信息:{}", orderNo, e.getMessage());
  120. return ResultMap.error("微信支付统一下单失败");
  121. } finally {
  122. //生成订单在数据库
  123. VipOrderData data = new VipOrderData();
  124. data.setId(PrimaryIdUtils.getId().toString());
  125. data.setOutTradeNo(orderNo);
  126. data.setStatus("1");
  127. data.setVipGoodsId(vipGoodsId);
  128. data.setCreateTime(LocalDateTime.now());
  129. data.setUpdateTime(LocalDateTime.now());
  130. vipGoodsService.insertVipOrderData(data);
  131. }
  132. }
  133. @Override
  134. public String notify(String notifyStr) {
  135. String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
  136. try {
  137. // 转换成map
  138. Map<String, String> resultMap = WXPayUtil.xmlToMap(notifyStr);
  139. WXPay wxpayApp = new WXPay(wxPayAppConfig);
  140. if (wxpayApp.isPayResultNotifySignatureValid(resultMap)) {
  141. String returnCode = resultMap.get("return_code"); //状态
  142. String outTradeNo = resultMap.get("out_trade_no");//商户订单号
  143. String transactionId = resultMap.get("transaction_id");
  144. if (returnCode.equals("SUCCESS")) {
  145. if (StringUtils.isNotBlank(outTradeNo)) {
  146. /**
  147. * 注意!!!
  148. * 请根据业务流程,修改数据库订单支付状态,和其他数据的相应状态
  149. *
  150. */
  151. logger.info("微信手机支付回调成功,订单号:{}", outTradeNo);
  152. xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
  153. }
  154. }
  155. }
  156. } catch (Exception e) {
  157. e.printStackTrace();
  158. }
  159. return xmlBack;
  160. }
  161. @Override
  162. public ResultMap refund(String orderNo, double amount, String refundReason) {
  163. if (StringUtils.isBlank(orderNo)) {
  164. return ResultMap.error("订单编号不能为空");
  165. }
  166. if (amount <= 0) {
  167. return ResultMap.error("退款金额必须大于0");
  168. }
  169. Map<String, String> responseMap = new HashMap<>();
  170. Map<String, String> requestMap = new HashMap<>();
  171. WXPay wxpay = new WXPay(wxPayAppConfig);
  172. requestMap.put("out_trade_no", orderNo);
  173. requestMap.put("out_refund_no", UUID.randomUUID().toString().replaceAll("-", ""));
  174. requestMap.put("total_fee", "订单支付时的总金额,需要从数据库查");
  175. requestMap.put("refund_fee", String.valueOf((int) (amount * 100)));//所需退款金额
  176. requestMap.put("refund_desc", refundReason);
  177. try {
  178. responseMap = wxpay.refund(requestMap);
  179. } catch (Exception e) {
  180. e.printStackTrace();
  181. }
  182. String return_code = responseMap.get("return_code"); //返回状态码
  183. String return_msg = responseMap.get("return_msg"); //返回信息
  184. if ("SUCCESS".equals(return_code)) {
  185. String result_code = responseMap.get("result_code"); //业务结果
  186. String err_code_des = responseMap.get("err_code_des"); //错误代码描述
  187. if ("SUCCESS".equals(result_code)) {
  188. //表示退款申请接受成功,结果通过退款查询接口查询
  189. //修改用户订单状态为退款申请中或已退款。退款异步通知根据需求,可选
  190. return ResultMap.ok("退款申请成功");
  191. } else {
  192. logger.info("订单号:{}错误信息:{}", orderNo, err_code_des);
  193. return ResultMap.error(err_code_des);
  194. }
  195. } else {
  196. logger.info("订单号:{}错误信息:{}", orderNo, return_msg);
  197. return ResultMap.error(return_msg);
  198. }
  199. }
  200. /**
  201. * @param vipGoodsId 商品主键
  202. * @return
  203. */
  204. @Override
  205. public AppPayData unifiedOrder2(String vipGoodsId) {
  206. logger.info("微信APP支付基础信息:{}", wxPayAppConfig);
  207. //查询商品是否存在,存在则下单,不存在则不下单
  208. VipGoodsData data = vipGoodsService.getById(vipGoodsId);
  209. if (Objects.isNull(data)) {
  210. throw new RuntimeException("VIP商品不存在");
  211. }
  212. // 使用自动更新平台证书的RSA配置
  213. // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
  214. Config config =
  215. new RSAAutoCertificateConfig.Builder()
  216. .merchantId(wxPayAppConfig.getMchID())
  217. .privateKeyFromPath(wxPayAppConfig.getCertPath())
  218. .merchantSerialNumber(wxPayAppConfig.getMerchantSerialNumber())
  219. //.apiV3Key(wxPayAppConfig.getKey())
  220. .apiV3Key(v3Key)
  221. .build();
  222. // 构建service
  223. AppService service = new AppService.Builder().config(config).build();
  224. String orderNo = PrimaryIdUtils.getId().toString();
  225. com.wechat.pay.java.service.payments.app.model.PrepayRequest request = new com.wechat.pay.java.service.payments.app.model.PrepayRequest();
  226. com.wechat.pay.java.service.payments.app.model.Amount amount = new com.wechat.pay.java.service.payments.app.model.Amount();
  227. //金额计算 ,乘以100
  228. String money = data.getVipMoney();
  229. //int orderAmount = Integer.parseInt(money) * 100;
  230. int orderAmount = (int) (Double.parseDouble(money) * 100);
  231. amount.setTotal(orderAmount);
  232. request.setAmount(amount);
  233. request.setAppid(wxPayAppConfig.getAppID());
  234. request.setMchid(wxPayAppConfig.getMchID());
  235. request.setDescription(data.getName());
  236. request.setNotifyUrl(wxPayAppConfig.getPayNotifyUrl());
  237. request.setOutTradeNo(orderNo);
  238. // 调用下单方法,得到应答
  239. com.wechat.pay.java.service.payments.app.model.PrepayResponse response = service.prepay(request);
  240. logger.info("调用微信app下单返回的信息是:{}", response);
  241. String prepayId = response.getPrepayId();
  242. AppPayData appPayData = new AppPayData();
  243. appPayData.setAppid(wxPayAppConfig.getAppID());
  244. appPayData.setPrepayid(prepayId);
  245. appPayData.setPartnerid(wxPayAppConfig.getMchID());
  246. String noncestr = UUID.randomUUID().toString().replaceAll("-", "");
  247. appPayData.setNoncestr(noncestr);
  248. //appPayData.setTimestamp(String.valueOf(System.currentTimeMillis()));
  249. //生成签名
  250. Map<String, String> returnMap = new HashMap<>();
  251. Long time = System.currentTimeMillis() / 1000;
  252. String timestamp = time.toString();
  253. returnMap.put("appid", wxPayAppConfig.getAppID());
  254. returnMap.put("timestamp", timestamp);
  255. returnMap.put("noncestr", noncestr);
  256. returnMap.put("prepayid", prepayId);
  257. //returnMap.put("package", "Sign=WXPay");
  258. //returnMap.put("package", "prepay_id="+prepayId);
  259. AppServiceExtension appServiceExtension = new AppServiceExtension.Builder().config(config).build();
  260. com.wechat.pay.java.service.payments.app.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.app.model.PrepayRequest();
  261. prepayRequest.setAmount(amount);
  262. prepayRequest.setAppid(wxPayAppConfig.getAppID());
  263. prepayRequest.setMchid(wxPayAppConfig.getMchID());
  264. prepayRequest.setDescription(data.getName());
  265. prepayRequest.setNotifyUrl(wxPayAppConfig.getPayNotifyUrl());
  266. prepayRequest.setOutTradeNo(orderNo);
  267. com.wechat.pay.java.service.payments.app.model.PrepayWithRequestPaymentResponse prepayWithRequestPaymentResponse = appServiceExtension.prepayWithRequestPayment(prepayRequest);
  268. appPayData.setPrepayWithRequestPaymentResponse(prepayWithRequestPaymentResponse);
  269. SignatureResult signatureResult = config.createSigner().sign(wxPayAppConfig.getAppID() + "\n" + timestamp + "\n" + noncestr + "\n" + "prepay_id=" + prepayId + "\n");
  270. String sign = signatureResult.getSign();
  271. logger.info("签名值是:{}", sign);
  272. appPayData.setSign(sign);
  273. appPayData.setBase64Sign(Base64.getEncoder().encodeToString(sign.getBytes()));
  274. try {
  275. String signMD5 = WXPayUtil.generateSignature(returnMap, v3Key, WXPayConstants.SignType.MD5);
  276. String signSHA256 = WXPayUtil.generateSignature(returnMap, v3Key, WXPayConstants.SignType.HMACSHA256);
  277. //生成微信签名
  278. appPayData.setSignMD5(signMD5);
  279. appPayData.setBase64SignMD5(Base64.getEncoder().encodeToString(signMD5.getBytes()));
  280. appPayData.setSignSHA256(signSHA256);
  281. appPayData.setBase64SignSHA256(Base64.getEncoder().encodeToString(signSHA256.getBytes()));
  282. appPayData.setTimestamp(timestamp);
  283. } catch (Exception e) {
  284. logger.error("生成微信签名失败:", e);
  285. throw new RuntimeException("生成微信签名失败");
  286. } finally {
  287. }
  288. return appPayData;
  289. }
  290. /**
  291. * APP支付成功后调用,主要计算VIP时长
  292. *
  293. * @param vipGoodsId 商品主键
  294. * @param out_trade_no
  295. * @return
  296. */
  297. @Override
  298. public SysUser success(String vipGoodsId, String out_trade_no) {
  299. //查询商品是否存在,存在则下单,不存在则不下单
  300. VipGoodsData data = vipGoodsService.getById(vipGoodsId);
  301. if (Objects.isNull(data)) {
  302. throw new RuntimeException("VIP商品不存在");
  303. }
  304. VipOrderData vipOrderData = vipGoodsService.getOrderByOutTradeNo(out_trade_no);
  305. if (Objects.isNull(vipOrderData)) {
  306. throw new RuntimeException("订单不存在");
  307. }
  308. //查询订单是否支付成功,支付成功:生成VIP时长,失败异常
  309. /* QueryOrderByOutTradeNoRequest queryOrderByOutTradeNoRequest = new QueryOrderByOutTradeNoRequest();
  310. queryOrderByOutTradeNoRequest.setOutTradeNo(out_trade_no);
  311. queryOrderByOutTradeNoRequest.setMchid(wxPayAppConfig.getMchID());
  312. Transaction transaction = queryOrderByOutTradeNo(queryOrderByOutTradeNoRequest);*/
  313. Transaction transaction = queryOrderByOutTradeNo(out_trade_no);
  314. Transaction.TradeStateEnum tradeState = transaction.getTradeState();
  315. //logger.info("支付返回的信息是:{}", transaction);
  316. //logger.info("根据订单号查询是否支付成功:{}", tradeState.name());
  317. if ("SUCCESS".equals(tradeState.name())) {
  318. logger.info("支付成功:{}", transaction);
  319. //查询用户是否已经开通过,开通过则续费,没有开通过怎插新数据
  320. String userId = TokenUtils.getUserId();
  321. VipUserData vipUserData = vipGoodsService.selectByUserId(userId, vipGoodsId);
  322. //VIP时长
  323. String vipStartDate = "";
  324. String vipEndDate = "";
  325. String vipDate = data.getVipDate();
  326. if (Objects.isNull(vipUserData)) {
  327. //没有开通过,新开通,开始时间=当天时间,结束时间=会员时长+当前时间
  328. vipStartDate = DateUtils.toStringYMD(new Date());
  329. vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
  330. } else {
  331. //开通过,查看有没有过期,过期:新开通,不过期,续费
  332. String vipEndDateStr = vipUserData.getVipEndDate();
  333. Date vipEndDate1 = DateUtils.toDateYMDHMS(vipEndDateStr);
  334. Date todayDate = DateUtils.toDateYMDHMS(DateUtils.toStringYMD(new Date()));
  335. int compareTo = todayDate.compareTo(vipEndDate1);
  336. //0:到当天 1:过期 -1:不过期
  337. if (compareTo == 0 || compareTo == -1) {
  338. //不过期,续费,计算VIP结束时间
  339. vipEndDate = DateUtils.getDesignDateStr(vipEndDate1, DateUtils.sdfyyyyMMdd, Integer.parseInt(vipDate));
  340. vipStartDate = vipUserData.getVipStartDate();
  341. } else {
  342. //过期,删除原来的数据,在重新添加
  343. vipGoodsService.deleteVipUser(vipUserData.getId());
  344. //过期重新开通,设置新的开始和结束时间
  345. vipStartDate = DateUtils.toStringYMD(new Date());
  346. vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
  347. }
  348. }
  349. VipUserData saveVipUserData = new VipUserData();
  350. saveVipUserData.setId(PrimaryIdUtils.getId().toString());
  351. saveVipUserData.setUserId(userId);
  352. saveVipUserData.setVipGoodsId(vipGoodsId);
  353. saveVipUserData.setVipStartDate(vipStartDate);
  354. saveVipUserData.setVipEndDate(vipEndDate);
  355. saveVipUserData.setCreateTime(LocalDateTime.now());
  356. saveVipUserData.setUpdateTime(LocalDateTime.now());
  357. vipGoodsService.saveVipUserData(saveVipUserData);
  358. //修改用户表,添加是VIP用户,VIP最后时间,剩余时长
  359. Long vipResidueDate = DateUtils.getDifferenceTime(DateUtils.toDateYMDHMS(vipStartDate), DateUtils.toDateYMDHMS(vipEndDate));
  360. SysUser sysUser = sysUserMapper.selectOne(userId);
  361. sysUser.setIsVip(true);
  362. sysUser.setVipResidueDate(vipResidueDate);
  363. sysUser.setVipEndDate(vipEndDate);
  364. sysUser.setUpdateTime(new Date());
  365. sysUserMapper.update(sysUser);
  366. //更新订单状态为已支付
  367. vipOrderData.setStatus("2"); // 1:待支付 2:已支付 3:已取消
  368. vipOrderData.setUpdateTime(LocalDateTime.now());
  369. vipGoodsService.updateVipOrderData(vipOrderData);
  370. logger.info("VIP支付成功处理完成,用户ID:{},订单号:{},VIP结束时间:{}", userId, out_trade_no, vipEndDate);
  371. return sysUser;
  372. } else {
  373. throw new RuntimeException("订单支付失败");
  374. }
  375. }
  376. @Override
  377. public Transaction queryOrderByOutTradeNo(String out_trade_no) {
  378. return queryOrderByOutTradeNo(out_trade_no, "app");
  379. }
  380. /**
  381. * 根据订单号和支付类型查询订单状态
  382. * @param out_trade_no 订单号
  383. * @param payType 支付类型:app 或 miniprogram
  384. * @return 交易信息
  385. */
  386. public Transaction queryOrderByOutTradeNo(String out_trade_no, String payType) {
  387. Config config;
  388. if ("miniprogram".equals(payType)) {
  389. // 使用小程序配置
  390. config = new RSAAutoCertificateConfig.Builder()
  391. .merchantId(wxPayMiniprogramConfig.getMchID())
  392. .privateKeyFromPath(wxPayMiniprogramConfig.getCertPath())
  393. .merchantSerialNumber(wxPayMiniprogramConfig.getMerchantSerialNumber())
  394. .apiV3Key(wxPayMiniprogramConfig.getV3Key())
  395. .build();
  396. } else {
  397. // 使用APP配置
  398. config = new RSAAutoCertificateConfig.Builder()
  399. .merchantId(wxPayAppConfig.getMchID())
  400. .privateKeyFromPath(wxPayAppConfig.getCertPath())
  401. .merchantSerialNumber(wxPayAppConfig.getMerchantSerialNumber())
  402. .apiV3Key(v3Key)
  403. .build();
  404. }
  405. // 构建service - 对于查询订单,使用JsapiService即可(小程序和APP都支持)
  406. JsapiService service = new JsapiService.Builder().config(config).build();
  407. com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest queryOrderByOutTradeNoRequest = new com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest();
  408. queryOrderByOutTradeNoRequest.setOutTradeNo(out_trade_no);
  409. queryOrderByOutTradeNoRequest.setMchid("miniprogram".equals(payType) ?
  410. wxPayMiniprogramConfig.getMchID() : wxPayAppConfig.getMchID());
  411. Transaction transaction = service.queryOrderByOutTradeNo(queryOrderByOutTradeNoRequest);
  412. Transaction.TradeStateEnum tradeState = transaction.getTradeState();
  413. logger.info("{}支付订单查询结果 - 订单号:{},状态:{}", payType, out_trade_no, tradeState.name());
  414. if ("SUCCESS".equals(tradeState.name())) {
  415. logger.info("支付成功:{}", transaction);
  416. }
  417. return transaction;
  418. }
  419. // ============= 小程序支付相关实现 =============
  420. @Override
  421. public Object miniprogramUnifiedOrder(String vipGoodsId, String openid) {
  422. logger.info("小程序支付基础信息:{}", wxPayMiniprogramConfig);
  423. logger.info("用户openid:{}", openid);
  424. //查询商品是否存在,存在则下单,不存在则不下单
  425. VipGoodsData data = vipGoodsService.getById(vipGoodsId);
  426. if (Objects.isNull(data)) {
  427. throw new RuntimeException("VIP商品不存在");
  428. }
  429. if (StringUtils.isBlank(openid)) {
  430. throw new RuntimeException("用户openid不能为空");
  431. }
  432. try {
  433. // 使用自动更新平台证书的RSA配置
  434. Config config = new RSAAutoCertificateConfig.Builder()
  435. .merchantId(wxPayMiniprogramConfig.getMchID())
  436. .privateKeyFromPath(wxPayMiniprogramConfig.getCertPath())
  437. .merchantSerialNumber(wxPayMiniprogramConfig.getMerchantSerialNumber())
  438. .apiV3Key(wxPayMiniprogramConfig.getV3Key())
  439. .build();
  440. // 构建service
  441. JsapiService service = new JsapiService.Builder().config(config).build();
  442. String orderNo = PrimaryIdUtils.getId().toString();
  443. // 构建小程序支付请求
  444. com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest request = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();
  445. com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();
  446. //金额计算,乘以100转换为分
  447. String money = data.getVipMoney();
  448. int orderAmount = (int) (Double.parseDouble(money) * 100);
  449. amount.setTotal(orderAmount);
  450. request.setAmount(amount);
  451. request.setAppid(wxPayMiniprogramConfig.getAppID());
  452. request.setMchid(wxPayMiniprogramConfig.getMchID());
  453. request.setDescription(data.getName());
  454. request.setNotifyUrl(wxPayMiniprogramConfig.getPayNotifyUrl());
  455. request.setOutTradeNo(orderNo);
  456. // 设置支付者信息
  457. Payer payer = new Payer();
  458. payer.setOpenid(openid);
  459. request.setPayer(payer);
  460. // 调用下单方法,得到应答
  461. com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse response = service.prepay(request);
  462. logger.info("调用微信小程序下单返回的信息是:{}", response);
  463. String prepayId = response.getPrepayId();
  464. // 构建小程序支付参数
  465. MiniprogramPayData payData = new MiniprogramPayData();
  466. // 生成时间戳
  467. long timestamp = System.currentTimeMillis() / 1000;
  468. String timeStamp = String.valueOf(timestamp);
  469. // 生成随机字符串
  470. String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");
  471. // 构建package参数
  472. String packageParam = "prepay_id=" + prepayId;
  473. // 生成签名
  474. String signStr = wxPayMiniprogramConfig.getAppID() + "\n" + timeStamp + "\n" + nonceStr + "\n" + packageParam + "\n";
  475. SignatureResult signatureResult = config.createSigner().sign(signStr);
  476. String paySign = signatureResult.getSign();
  477. // 设置小程序支付参数
  478. payData.setTimeStamp(timeStamp);
  479. payData.setNonceStr(nonceStr);
  480. payData.setPackage_(packageParam);
  481. payData.setPackageParam(packageParam); // 设置用于uni.requestPayment的package字段
  482. payData.setSignType("MD5");
  483. payData.setPaySign(paySign);
  484. payData.setOut_trade_no(orderNo);
  485. payData.setPrepay_id(prepayId);
  486. payData.setDescription(data.getName());
  487. payData.setTotal(orderAmount);
  488. payData.setRawData(response);
  489. logger.info("小程序支付参数:{}", payData);
  490. // 通过openid查询用户ID
  491. String userId = null;
  492. try {
  493. SysUser user = sysUserMapper.selectByOpenId(openid);
  494. if (user != null) {
  495. userId = user.getId();
  496. }
  497. } catch (Exception e) {
  498. logger.warn("根据openid查询用户失败: {}", openid, e);
  499. }
  500. // 生成订单记录
  501. VipOrderData orderData = new VipOrderData();
  502. orderData.setId(PrimaryIdUtils.getId().toString());
  503. orderData.setOutTradeNo(orderNo);
  504. orderData.setStatus("1");
  505. orderData.setVipGoodsId(vipGoodsId);
  506. orderData.setUserId(userId); // 记录用户ID
  507. orderData.setOpenid(openid); // 记录openid
  508. orderData.setCreateTime(LocalDateTime.now());
  509. orderData.setUpdateTime(LocalDateTime.now());
  510. vipGoodsService.insertVipOrderData(orderData);
  511. return payData;
  512. } catch (Exception e) {
  513. logger.error("小程序支付下单失败 - vipGoodsId: {}, openid: {}", vipGoodsId, openid, e);
  514. String errorMsg = e.getMessage();
  515. if (errorMsg == null || errorMsg.trim().isEmpty()) {
  516. errorMsg = e.getClass().getSimpleName() + ": " + e.toString();
  517. }
  518. throw new RuntimeException("小程序支付下单失败: " + errorMsg);
  519. }
  520. }
  521. @Override
  522. public String miniprogramNotify(String notifyStr) {
  523. String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml>";
  524. try {
  525. // 转换成map
  526. Map<String, String> resultMap = WXPayUtil.xmlToMap(notifyStr);
  527. WXPay wxpayMiniprogram = new WXPay(wxPayMiniprogramConfig);
  528. if (wxpayMiniprogram.isPayResultNotifySignatureValid(resultMap)) {
  529. String returnCode = resultMap.get("return_code"); //状态
  530. String outTradeNo = resultMap.get("out_trade_no");//商户订单号
  531. String transactionId = resultMap.get("transaction_id");
  532. if ("SUCCESS".equals(returnCode)) {
  533. if (StringUtils.isNotBlank(outTradeNo)) {
  534. logger.info("小程序支付异步通知成功,订单号:{}", outTradeNo);
  535. try {
  536. // 查询订单信息
  537. VipOrderData vipOrderData = vipGoodsService.getOrderByOutTradeNo(outTradeNo);
  538. if (vipOrderData != null && "1".equals(vipOrderData.getStatus())) {
  539. // 订单状态为待支付,进行VIP处理
  540. String vipGoodsId = vipOrderData.getVipGoodsId();
  541. String userId = vipOrderData.getUserId();
  542. if (StringUtils.isNotBlank(userId)) {
  543. // 有用户ID,执行完整的VIP处理逻辑
  544. logger.info("开始处理小程序支付异步通知VIP逻辑 - 用户ID: {}, 订单号: {}", userId, outTradeNo);
  545. // 查询VIP商品信息
  546. VipGoodsData vipGoodsData = vipGoodsService.getById(vipGoodsId);
  547. if (vipGoodsData != null) {
  548. // 处理VIP逻辑(复用success方法的逻辑)
  549. handleVipLogic(userId, vipGoodsId, vipGoodsData, outTradeNo);
  550. logger.info("小程序支付异步通知VIP处理完成 - 用户ID: {}, 订单号: {}", userId, outTradeNo);
  551. } else {
  552. logger.warn("VIP商品不存在 - vipGoodsId: {}, 订单号: {}", vipGoodsId, outTradeNo);
  553. }
  554. } else {
  555. logger.warn("订单中缺少用户ID,无法处理VIP逻辑 - 订单号: {}", outTradeNo);
  556. }
  557. // 更新订单状态为已支付
  558. vipOrderData.setStatus("2"); // 已支付
  559. vipOrderData.setUpdateTime(LocalDateTime.now());
  560. vipGoodsService.updateVipOrderData(vipOrderData);
  561. logger.info("小程序支付异步通知处理完成,订单号:{}", outTradeNo);
  562. } else {
  563. logger.info("小程序支付异步通知 - 订单已处理或不存在,订单号:{}", outTradeNo);
  564. }
  565. } catch (Exception e) {
  566. logger.error("小程序支付异步通知业务处理失败,订单号:{}", outTradeNo, e);
  567. // 即使业务处理失败,也要返回SUCCESS,避免微信重复通知
  568. }
  569. xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
  570. }
  571. }
  572. } else {
  573. logger.warn("小程序支付异步通知签名验证失败");
  574. }
  575. } catch (Exception e) {
  576. logger.error("小程序支付通知处理失败", e);
  577. }
  578. return xmlBack;
  579. }
  580. @Override
  581. public SysUser miniprogramSuccess(String vipGoodsId, String out_trade_no) {
  582. //查询商品是否存在,存在则下单,不存在则不下单
  583. VipGoodsData data = vipGoodsService.getById(vipGoodsId);
  584. if (Objects.isNull(data)) {
  585. throw new RuntimeException("VIP商品不存在");
  586. }
  587. VipOrderData vipOrderData = vipGoodsService.getOrderByOutTradeNo(out_trade_no);
  588. if (Objects.isNull(vipOrderData)) {
  589. throw new RuntimeException("订单不存在");
  590. }
  591. //查询订单是否支付成功,支付成功:生成VIP时长,失败异常
  592. Transaction transaction = queryOrderByOutTradeNo(out_trade_no, "miniprogram");
  593. Transaction.TradeStateEnum tradeState = transaction.getTradeState();
  594. logger.info("小程序支付订单查询结果 - 订单号:{},状态:{}", out_trade_no, tradeState.name());
  595. if ("SUCCESS".equals(tradeState.name())) {
  596. logger.info("小程序支付成功:{}", transaction);
  597. //查询用户是否已经开通过,开通过则续费,没有开通过怎插新数据
  598. String userId = TokenUtils.getUserId();
  599. VipUserData vipUserData = vipGoodsService.selectByUserId(userId, vipGoodsId);
  600. //VIP时长
  601. String vipStartDate = "";
  602. String vipEndDate = "";
  603. String vipDate = data.getVipDate();
  604. if (Objects.isNull(vipUserData)) {
  605. //没有开通过,新开通,开始时间=当天时间,结束时间=会员时长+当前时间
  606. vipStartDate = DateUtils.toStringYMD(new Date());
  607. vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
  608. } else {
  609. //开通过,查看有没有过期,过期:新开通,不过期,续费
  610. String vipEndDateStr = vipUserData.getVipEndDate();
  611. Date vipEndDate1 = DateUtils.toDateYMDHMS(vipEndDateStr);
  612. Date todayDate = DateUtils.toDateYMDHMS(DateUtils.toStringYMD(new Date()));
  613. int compareTo = todayDate.compareTo(vipEndDate1);
  614. //0:到当天 1:过期 -1:不过期
  615. if (compareTo == 0 || compareTo == -1) {
  616. //不过期,续费,计算VIP结束时间
  617. vipEndDate = DateUtils.getDesignDateStr(vipEndDate1, DateUtils.sdfyyyyMMdd, Integer.parseInt(vipDate));
  618. vipStartDate = vipUserData.getVipStartDate();
  619. } else {
  620. //过期,删除原来的数据,在重新添加
  621. vipGoodsService.deleteVipUser(vipUserData.getId());
  622. //过期重新开通,设置新的开始和结束时间
  623. vipStartDate = DateUtils.toStringYMD(new Date());
  624. vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
  625. }
  626. }
  627. VipUserData saveVipUserData = new VipUserData();
  628. saveVipUserData.setId(PrimaryIdUtils.getId().toString());
  629. saveVipUserData.setUserId(userId);
  630. saveVipUserData.setVipGoodsId(vipGoodsId);
  631. saveVipUserData.setVipStartDate(vipStartDate);
  632. saveVipUserData.setVipEndDate(vipEndDate);
  633. saveVipUserData.setCreateTime(LocalDateTime.now());
  634. saveVipUserData.setUpdateTime(LocalDateTime.now());
  635. vipGoodsService.saveVipUserData(saveVipUserData);
  636. //修改用户表,添加是VIP用户,VIP最后时间,剩余时长
  637. Long vipResidueDate = DateUtils.getDifferenceTime(DateUtils.toDateYMDHMS(vipStartDate), DateUtils.toDateYMDHMS(vipEndDate));
  638. SysUser sysUser = sysUserMapper.selectOne(userId);
  639. sysUser.setIsVip(true);
  640. sysUser.setVipResidueDate(vipResidueDate);
  641. sysUser.setVipEndDate(vipEndDate);
  642. sysUser.setUpdateTime(new Date());
  643. sysUserMapper.update(sysUser);
  644. //更新订单状态为已支付
  645. vipOrderData.setStatus("2"); // 1:待支付 2:已支付 3:已取消
  646. vipOrderData.setUpdateTime(LocalDateTime.now());
  647. vipGoodsService.updateVipOrderData(vipOrderData);
  648. logger.info("小程序VIP支付成功处理完成,用户ID:{},订单号:{},VIP结束时间:{}", userId, out_trade_no, vipEndDate);
  649. return sysUser;
  650. } else {
  651. throw new RuntimeException("小程序订单支付失败,状态:" + tradeState.name());
  652. }
  653. }
  654. /**
  655. * 处理VIP逻辑(抽取的公共方法,供success和异步通知使用)
  656. * @param userId 用户ID
  657. * @param vipGoodsId VIP商品ID
  658. * @param vipGoodsData VIP商品信息
  659. * @param outTradeNo 订单号
  660. */
  661. private void handleVipLogic(String userId, String vipGoodsId, VipGoodsData vipGoodsData, String outTradeNo) {
  662. try {
  663. //查询用户是否已经开通过,开通过则续费,没有开通过则插新数据
  664. VipUserData vipUserData = vipGoodsService.selectByUserId(userId, vipGoodsId);
  665. //VIP时长
  666. String vipStartDate = "";
  667. String vipEndDate = "";
  668. String vipDate = vipGoodsData.getVipDate();
  669. if (Objects.isNull(vipUserData)) {
  670. //没有开通过,新开通,开始时间=当天时间,结束时间=会员时长+当前时间
  671. vipStartDate = DateUtils.toStringYMD(new Date());
  672. vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
  673. } else {
  674. //开通过,查看有没有过期,过期:新开通,不过期,续费
  675. String vipEndDateStr = vipUserData.getVipEndDate();
  676. Date vipEndDate1 = DateUtils.toDateYMDHMS(vipEndDateStr);
  677. Date todayDate = DateUtils.toDateYMDHMS(DateUtils.toStringYMD(new Date()));
  678. int compareTo = todayDate.compareTo(vipEndDate1);
  679. //0:到当天 1:过期 -1:不过期
  680. if (compareTo == 0 || compareTo == -1) {
  681. //不过期,续费,计算VIP结束时间
  682. vipEndDate = DateUtils.getDesignDateStr(vipEndDate1, DateUtils.sdfyyyyMMdd, Integer.parseInt(vipDate));
  683. vipStartDate = vipUserData.getVipStartDate();
  684. } else {
  685. //过期,删除原来的数据,在重新添加
  686. vipGoodsService.deleteVipUser(vipUserData.getId());
  687. //过期重新开通,设置新的开始和结束时间
  688. vipStartDate = DateUtils.toStringYMD(new Date());
  689. vipEndDate = DateUtils.getAfterDate(Integer.parseInt(vipDate));
  690. }
  691. }
  692. VipUserData saveVipUserData = new VipUserData();
  693. saveVipUserData.setId(PrimaryIdUtils.getId().toString());
  694. saveVipUserData.setUserId(userId);
  695. saveVipUserData.setVipGoodsId(vipGoodsId);
  696. saveVipUserData.setVipStartDate(vipStartDate);
  697. saveVipUserData.setVipEndDate(vipEndDate);
  698. saveVipUserData.setCreateTime(LocalDateTime.now());
  699. saveVipUserData.setUpdateTime(LocalDateTime.now());
  700. vipGoodsService.saveVipUserData(saveVipUserData);
  701. //修改用户表,添加是VIP用户,VIP最后时间,剩余时长
  702. Long vipResidueDate = DateUtils.getDifferenceTime(DateUtils.toDateYMDHMS(vipStartDate), DateUtils.toDateYMDHMS(vipEndDate));
  703. SysUser sysUser = sysUserMapper.selectOne(userId);
  704. if (sysUser != null) {
  705. sysUser.setIsVip(true);
  706. sysUser.setVipResidueDate(vipResidueDate);
  707. sysUser.setVipEndDate(vipEndDate);
  708. sysUser.setUpdateTime(new Date());
  709. sysUserMapper.update(sysUser);
  710. logger.info("VIP处理完成 - 用户ID: {}, 订单号: {}, VIP结束时间: {}", userId, outTradeNo, vipEndDate);
  711. } else {
  712. logger.warn("用户不存在,无法更新VIP状态 - 用户ID: {}", userId);
  713. }
  714. } catch (Exception e) {
  715. logger.error("VIP逻辑处理失败 - 用户ID: {}, 订单号: {}", userId, outTradeNo, e);
  716. throw e;
  717. }
  718. }
  719. public static void main(String[] args) throws IOException {
  720. /** Native 支付下单为例 */
  721. /* *//** 商户号 *//*
  722. String merchantId = "1659536133";
  723. *//** 商户API私钥路径 *//*
  724. String privateKeyPath = "D:\\soft\\WXCertUtil\\WXCertUtil\\cert\\1659536133_20240224_cert\\apiclient_key.pem";
  725. *//** 商户证书序列号 *//*
  726. String merchantSerialNumber = "2C9364545EF9C4C82700563B8C398451E14743B3";
  727. *//** 商户APIV3密钥 *//*
  728. String apiV3Key = "A123456789B123456789C1234567890D";
  729. // 使用自动更新平台证书的RSA配置
  730. // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
  731. Config config =
  732. new RSAAutoCertificateConfig.Builder()
  733. .merchantId(merchantId)
  734. .privateKeyFromPath(privateKeyPath)
  735. .merchantSerialNumber(merchantSerialNumber)
  736. .apiV3Key(apiV3Key)
  737. .build();
  738. // 构建service
  739. AppService service = new AppService.Builder().config(config).build();
  740. // request.setXxx(val)设置所需参数,具体参数可见Request定义
  741. String orderNo = PrimaryIdUtils.getId().toString();
  742. PrepayRequest request = new PrepayRequest();
  743. Amount amount = new Amount();
  744. amount.setTotal(100);
  745. request.setAmount(amount);
  746. request.setAppid("wx8fdb27b97ed17533");
  747. request.setMchid("1659536133");
  748. request.setDescription("VIP一个月商品");
  749. request.setNotifyUrl("http://120.46.40.61:18885/api/swagger-ui.html");
  750. request.setOutTradeNo(orderNo);
  751. // 调用下单方法,得到应答
  752. PrepayResponse response = service.prepay(request);
  753. // 使用微信扫描 code_url 对应的二维码,即可体验Native支付
  754. System.out.println(">>>>>>>>>>>:" + response);*/
  755. String noncestr = UUID.randomUUID().toString().replaceAll("-", "");
  756. long currentTimeMillis = System.currentTimeMillis() / 1000;
  757. System.out.println(currentTimeMillis);
  758. // System.out.println(noncestr.length());
  759. /* Calendar calendar = Calendar.getInstance();
  760. calendar.add(Calendar.DAY_OF_MONTH, 2);
  761. int year = calendar.get(Calendar.YEAR);
  762. int month = calendar.get(Calendar.MONTH) + 1;
  763. int day = calendar.get(Calendar.DAY_OF_MONTH);
  764. System.out.println("7天后的日期为:" + year + "-" + month + "-" + day);*/
  765. //Base64.getDecoder().decode("NDU1ODAwNjk2M0QyOTE2QTMwNjBGNUQwMTEwNkU4ODA0OTcyQTkyOTVDRkJGRTYzQUMzOUYyRkE5Q0RGQ0Y1Qg==");
  766. String vipEndDateStr = "2024-03-3";
  767. Date vipEndDate = DateUtils.toDateYMDHMS(vipEndDateStr);
  768. Date date = DateUtils.toDateYMDHMS(DateUtils.toStringYMD(new Date()));
  769. int compareTo = vipEndDate.compareTo(date);
  770. System.out.println(compareTo);
  771. }
  772. /* public Transaction queryOrderByOutTradeNo(QueryOrderByOutTradeNoRequest request) {
  773. String requestPath =
  774. "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}";
  775. QueryOrderByOutTradeNoRequest realRequest = request;
  776. // 添加 path param
  777. requestPath =
  778. requestPath.replace("{" + "out_trade_no" + "}", urlEncode(realRequest.getOutTradeNo()));
  779. // 添加 query param
  780. QueryParameter queryParameter = new QueryParameter();
  781. if (realRequest.getMchid() != null) {
  782. queryParameter.add("mchid", urlEncode(realRequest.getMchid()));
  783. }
  784. requestPath += queryParameter.getQueryStr();
  785. HttpHeaders headers = new HttpHeaders();
  786. headers.addHeader(Constant.ACCEPT, MediaType.APPLICATION_JSON.getValue());
  787. headers.addHeader(Constant.CONTENT_TYPE, MediaType.APPLICATION_JSON.getValue());
  788. HttpRequest httpRequest =
  789. new HttpRequest.Builder()
  790. .httpMethod(HttpMethod.GET)
  791. .url(requestPath)
  792. .headers(headers)
  793. .build();
  794. HttpResponse<Transaction> httpResponse = httpClient.execute(httpRequest, Transaction.class);
  795. return httpResponse.getServiceResponse();
  796. }*/
  797. }