WeixinServiceImpl.java 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. package com.fuint.common.service.impl;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.alibaba.fastjson.JSONArray;
  5. import com.fuint.common.bean.WxPayBean;
  6. import com.fuint.common.dto.OrderDto;
  7. import com.fuint.common.dto.UserOrderDto;
  8. import com.fuint.common.enums.*;
  9. import com.fuint.common.http.HttpRESTDataClient;
  10. import com.fuint.common.service.*;
  11. import com.fuint.common.util.RedisUtil;
  12. import com.fuint.framework.exception.BusinessCheckException;
  13. import com.fuint.framework.web.ResponseObject;
  14. import com.fuint.repository.model.*;
  15. import com.fuint.utils.StringUtil;
  16. import com.ijpay.core.enums.SignType;
  17. import com.ijpay.core.kit.HttpKit;
  18. import com.ijpay.core.kit.WxPayKit;
  19. import com.ijpay.wxpay.WxPayApi;
  20. import com.ijpay.wxpay.WxPayApiConfig;
  21. import com.ijpay.wxpay.WxPayApiConfigKit;
  22. import com.ijpay.wxpay.model.MicroPayModel;
  23. import com.ijpay.wxpay.model.OrderQueryModel;
  24. import com.ijpay.wxpay.model.RefundModel;
  25. import com.ijpay.wxpay.model.UnifiedOrderModel;
  26. import org.bouncycastle.jce.provider.BouncyCastleProvider;
  27. import org.slf4j.Logger;
  28. import org.slf4j.LoggerFactory;
  29. import org.springframework.beans.factory.annotation.Autowired;
  30. import org.springframework.stereotype.Service;
  31. import org.springframework.transaction.annotation.Transactional;
  32. import org.springframework.core.env.Environment;
  33. import weixin.popular.util.JsonUtil;
  34. import javax.crypto.Cipher;
  35. import javax.crypto.spec.IvParameterSpec;
  36. import javax.crypto.spec.SecretKeySpec;
  37. import javax.servlet.http.HttpServletRequest;
  38. import javax.servlet.http.HttpServletResponse;
  39. import java.io.IOException;
  40. import java.io.OutputStream;
  41. import java.math.BigDecimal;
  42. import java.security.AlgorithmParameters;
  43. import java.security.Security;
  44. import java.util.*;
  45. /**
  46. * 微信相关接口
  47. *
  48. * Created by FSQ
  49. * CopyRight https://www.fuint.cn
  50. */
  51. @Service
  52. public class WeixinServiceImpl implements WeixinService {
  53. private static final Logger logger = LoggerFactory.getLogger(WeixinServiceImpl.class);
  54. @Autowired
  55. private OrderService orderService;
  56. @Autowired
  57. private SettingService settingService;
  58. @Autowired
  59. private MessageService messageService;
  60. @Autowired
  61. private StoreService storeService;
  62. @Autowired
  63. private PaymentService paymentService;
  64. @Autowired
  65. private Environment env;
  66. @Autowired
  67. WxPayBean wxPayBean;
  68. private static final String CALL_BACK_URL = "/clientApi/pay/weixinCallback";
  69. private static final String REFUND_NOTIFY_URL = "/clientApi/pay/weixinRefundNotify";
  70. /**
  71. * 获取微信accessToken
  72. * @param useCache 是否读取缓存
  73. * @return
  74. * */
  75. @Override
  76. public String getAccessToken(boolean useCache) {
  77. String wxAppId = env.getProperty("wxpay.appId");
  78. String wxAppSecret = env.getProperty("wxpay.appSecret");
  79. String wxTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
  80. String url = String.format(wxTokenUrl, wxAppId, wxAppSecret);
  81. String token = "";
  82. if (useCache) {
  83. token = RedisUtil.get("FUINT_ACCESS_TOKEN");
  84. }
  85. if (token == null || StringUtil.isEmpty(token)) {
  86. try {
  87. String response = HttpRESTDataClient.requestGet(url);
  88. JSONObject json = (JSONObject) JSONObject.parse(response);
  89. if (!json.containsKey("errcode")) {
  90. RedisUtil.set("FUINT_ACCESS_TOKEN", json.get("access_token"), 7200);
  91. token = (String) json.get("access_token");
  92. } else {
  93. logger.error("获取微信accessToken出错:" + json.get("errmsg"));
  94. }
  95. } catch (Exception e) {
  96. logger.error("获取微信accessToken异常:" + e.getMessage());
  97. }
  98. }
  99. return token;
  100. }
  101. /**
  102. * 创建支付订单
  103. * @return
  104. * */
  105. @Override
  106. @Transactional(rollbackFor = Exception.class)
  107. public ResponseObject createPrepayOrder(MtUser userInfo, MtOrder orderInfo, Integer payAmount, String authCode, Integer giveAmount, String ip, String platform) throws BusinessCheckException {
  108. logger.info("WeixinService createPrepayOrder inParams userInfo={} payAmount={} giveAmount={} goodsInfo={}", userInfo, payAmount, giveAmount, orderInfo);
  109. String goodsInfo = orderInfo.getOrderSn();
  110. if (orderInfo.getType().equals(OrderTypeEnum.PRESTORE.getKey())) {
  111. goodsInfo = OrderTypeEnum.PRESTORE.getValue();
  112. }
  113. // 1. 调用微信接口生成预支付订单
  114. Map<String, String> reqData = new HashMap<>();
  115. reqData.put("body", goodsInfo);
  116. reqData.put("out_trade_no", orderInfo.getOrderSn());
  117. reqData.put("device_info", "");
  118. reqData.put("fee_type", "CNY");
  119. reqData.put("total_fee", payAmount.toString());
  120. reqData.put("spbill_create_ip", ip);
  121. // JSAPI支付
  122. if (orderInfo.getPayType().equals(PayTypeEnum.JSAPI.getKey())) {
  123. reqData.put("trade_type", PayTypeEnum.JSAPI.getKey());
  124. reqData.put("openid", userInfo.getOpenId() == null ? "" : userInfo.getOpenId());
  125. }
  126. // 刷卡支付
  127. if (StringUtil.isNotEmpty(authCode)) {
  128. reqData.put("auth_code", authCode);
  129. }
  130. // 更新支付金额
  131. BigDecimal payAmount1 = new BigDecimal(payAmount).divide(new BigDecimal("100"));
  132. OrderDto reqDto = new OrderDto();
  133. reqDto.setId(orderInfo.getId());
  134. reqDto.setPayAmount(payAmount1);
  135. reqDto.setPayType(orderInfo.getPayType());
  136. orderService.updateOrder(reqDto);
  137. Map<String, String> respData;
  138. if (reqData.get("auth_code") != null && StringUtil.isNotEmpty(reqData.get("auth_code"))) {
  139. respData = microPay(orderInfo.getStoreId(), reqData, ip, platform);
  140. } else {
  141. respData = jsapiPay(orderInfo.getStoreId(), reqData, ip, platform);
  142. }
  143. if (respData == null) {
  144. logger.error("微信支付接口调用异常......");
  145. return new ResponseObject(3000, "微信支付接口调用异常", null);
  146. }
  147. // 2.更新预支付订单号
  148. if (respData.get("return_code").equals("SUCCESS")) {
  149. if (respData.get("trade_type").equals(PayTypeEnum.JSAPI.getKey())) {
  150. String prepayId = respData.get("prepay_id");
  151. getApiConfig(orderInfo.getStoreId(), platform);
  152. WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
  153. respData = WxPayKit.miniAppPrepayIdCreateSign(wxPayApiConfig.getAppId(), prepayId, wxPayApiConfig.getPartnerKey(), SignType.MD5);
  154. String jsonStr = JSON.toJSONString(respData);
  155. logger.info("小程序支付的参数:" + jsonStr);
  156. }
  157. } else {
  158. logger.error("微信支付接口返回状态失败......" + respData.toString() + "...reason");
  159. return new ResponseObject(3000, "微信支付接口返回状态失败", null);
  160. }
  161. ResponseObject responseObject = new ResponseObject(200, "微信支付接口返回成功", respData);
  162. logger.info("WXService createPrepayOrder outParams {}", responseObject.toString());
  163. return responseObject;
  164. }
  165. public Map<String, String> processResXml(HttpServletRequest request) {
  166. try {
  167. String xmlMsg = HttpKit.readData(request);
  168. logger.info("支付通知=" + xmlMsg);
  169. Map<String, String> result = WxPayKit.xmlToMap(xmlMsg);
  170. String returnCode = result.get("return_code");
  171. getApiConfig(0, PlatformTypeEnum.MP_WEIXIN.getCode());
  172. if (WxPayKit.verifyNotify(result, WxPayApiConfigKit.getWxPayApiConfig().getPartnerKey(), SignType.MD5)) {
  173. if (WxPayKit.codeIsOk(returnCode)) {
  174. return result;
  175. }
  176. }
  177. } catch (Exception e) {
  178. logger.error(e.getMessage(), e);
  179. }
  180. return null;
  181. }
  182. public void processRespXml(HttpServletResponse response, boolean flag){
  183. Map<String,String> respData = new HashMap<>();
  184. if (flag) {
  185. respData.put("return_code", "SUCCESS");
  186. respData.put("return_msg", "OK");
  187. }else{
  188. respData.put("return_code", "FAIL");
  189. respData.put("return_msg", "FAIL");
  190. }
  191. OutputStream outputStream = null;
  192. try {
  193. String respXml = WxPayKit.toXml(respData);
  194. outputStream = response.getOutputStream();
  195. outputStream.write(respXml.getBytes("UTF-8"));
  196. outputStream.flush();
  197. } catch (Exception e) {
  198. e.printStackTrace();
  199. }finally {
  200. if(outputStream!=null){
  201. try {
  202. outputStream.close();
  203. } catch (IOException e) {
  204. logger.error(e.getMessage(), e);
  205. }
  206. }
  207. }
  208. }
  209. /**
  210. * 获取微信个人信息
  211. * @return
  212. * */
  213. @Override
  214. public JSONObject getWxProfile(String code) {
  215. String wxAppId = env.getProperty("wxpay.appId");
  216. String wxAppSecret = env.getProperty("wxpay.appSecret");
  217. String wxAccessUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
  218. String url = String.format(wxAccessUrl, wxAppId, wxAppSecret, code);
  219. try {
  220. String response = HttpRESTDataClient.requestGet(url);
  221. JSONObject json = (JSONObject) JSONObject.parse(response);
  222. if (!json.containsKey("errcode")) {
  223. return json;
  224. } else {
  225. logger.error("获取微信getWxProfile出错:code = " + json.containsKey("errcode") + ",msg="+ json.get("errmsg"));
  226. }
  227. } catch (Exception e) {
  228. logger.error("获取微信getWxProfile异常:" + e.getMessage());
  229. }
  230. return null;
  231. }
  232. /**
  233. * 获取公众号openId
  234. * @return
  235. * */
  236. @Override
  237. public JSONObject getWxOpenId(String code) {
  238. String wxAppId = env.getProperty("weixin.official.appId");
  239. String wxAppSecret = env.getProperty("weixin.official.appSecret");
  240. String wxAccessUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
  241. String url = String.format(wxAccessUrl, wxAppId, wxAppSecret, code);
  242. try {
  243. String response = HttpRESTDataClient.requestGet(url);
  244. JSONObject json = (JSONObject) JSONObject.parse(response);
  245. if (!json.containsKey("errcode")) {
  246. return json;
  247. } else {
  248. logger.error("获取openId出错:code = " + json.containsKey("errcode") + ",msg="+ json.get("errmsg"));
  249. }
  250. } catch (Exception e) {
  251. logger.error("获取微信openId异常:" + e.getMessage());
  252. }
  253. return null;
  254. }
  255. /**
  256. * 获取微信绑定手机号
  257. * @return
  258. * */
  259. @Override
  260. public String getPhoneNumber(String encryptedData, String sessionKey, String iv) {
  261. // 被加密的数据
  262. byte[] dataByte = Base64.getDecoder().decode(encryptedData);
  263. // 加密秘钥
  264. byte[] keyByte = Base64.getDecoder().decode(sessionKey);
  265. // 偏移量
  266. byte[] ivByte = Base64.getDecoder().decode(iv);
  267. try {
  268. // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
  269. int base = 16;
  270. if (keyByte.length % base != 0) {
  271. int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
  272. byte[] temp = new byte[groups * base];
  273. Arrays.fill(temp, (byte) 0);
  274. System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
  275. keyByte = temp;
  276. }
  277. // 初始化
  278. Security.addProvider(new BouncyCastleProvider());
  279. Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
  280. SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
  281. AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
  282. parameters.init(new IvParameterSpec(ivByte));
  283. cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
  284. byte[] resultByte = cipher.doFinal(dataByte);
  285. if (null != resultByte && resultByte.length > 0) {
  286. String result = new String(resultByte, "UTF-8");
  287. JSONObject object = JSONObject.parseObject(result);
  288. return object.getString("phoneNumber");
  289. }
  290. } catch (Exception e) {
  291. e.printStackTrace();
  292. }
  293. return null;
  294. }
  295. /**
  296. * 发送订阅消息
  297. * @return
  298. * */
  299. @Override
  300. public Boolean sendSubscribeMessage(Integer merchantId, Integer userId, String toUserOpenId, String key, String page, Map<String,Object> params, Date sendTime) throws BusinessCheckException {
  301. if (StringUtil.isEmpty(toUserOpenId) || StringUtil.isEmpty(key) || userId < 1) {
  302. return false;
  303. }
  304. MtSetting mtSetting = settingService.querySettingByName(merchantId, key);
  305. if (mtSetting == null) {
  306. return false;
  307. }
  308. JSONObject jsonObject = null;
  309. String templateId = "";
  310. JSONArray paramArray = null;
  311. try {
  312. if (mtSetting != null && mtSetting.getValue().indexOf('}') > 0) {
  313. jsonObject = JSONObject.parseObject(mtSetting.getValue());
  314. }
  315. if (jsonObject != null) {
  316. templateId = jsonObject.get("templateId").toString();
  317. paramArray = (JSONArray) JSONObject.parse(jsonObject.get("params").toString());
  318. }
  319. } catch (Exception e) {
  320. logger.info("WeixinService sendSubscribeMessage parse setting error={}", mtSetting);
  321. }
  322. if (StringUtil.isEmpty(templateId) || paramArray.size() < 1) {
  323. logger.info("WeixinService sendSubscribeMessage setting error={}", mtSetting);
  324. return false;
  325. }
  326. JSONObject jsonData = new JSONObject();
  327. jsonData.put("touser", toUserOpenId); // 接收者的openid
  328. jsonData.put("template_id", templateId);
  329. if (StringUtil.isEmpty(page)) {
  330. page = "pages/index/index";
  331. }
  332. jsonData.put("page", page);
  333. // 组装参数
  334. JSONObject data = new JSONObject();
  335. for (int i = 0; i < paramArray.size(); i++) {
  336. JSONObject para = paramArray.getJSONObject(i);
  337. String value = para.get("value").toString().replaceAll("\\{", "").replaceAll(".DATA}}", "");
  338. String paraKey = para.get("key").toString();
  339. String paraValue = params.get(paraKey).toString();
  340. JSONObject arg = new JSONObject();
  341. arg.put("value", paraValue);
  342. data.put(value, arg);
  343. }
  344. jsonData.put("data", data);
  345. String reqDataJsonStr = JSON.toJSONString(jsonData);
  346. // 存储到消息表里,后续通过定时任务发送
  347. MtMessage mtMessage = new MtMessage();
  348. mtMessage.setUserId(userId);
  349. mtMessage.setType(MessageEnum.SUB_MSG.getKey());
  350. mtMessage.setTitle(WxMessageEnum.getValue(key));
  351. mtMessage.setContent(WxMessageEnum.getValue(key));
  352. mtMessage.setIsRead(YesOrNoEnum.NO.getKey());
  353. mtMessage.setIsSend(YesOrNoEnum.NO.getKey());
  354. mtMessage.setSendTime(sendTime);
  355. mtMessage.setStatus(StatusEnum.ENABLED.getKey());
  356. mtMessage.setParams(reqDataJsonStr);
  357. messageService.addMessage(mtMessage);
  358. return true;
  359. }
  360. @Override
  361. public Boolean doSendSubscribeMessage(String reqDataJsonStr) {
  362. try {
  363. String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + this.getAccessToken(true);
  364. String response = HttpRESTDataClient.requestPost(url, "application/json; charset=utf-8", reqDataJsonStr);
  365. logger.info("WeixinService sendSubscribeMessage response={}", response);
  366. JSONObject json = (JSONObject) JSONObject.parse(response);
  367. if (json.get("errcode").toString().equals("40001")) {
  368. this.getAccessToken(false);
  369. logger.error("发送订阅消息出错error1:" + json.get("errcode").toString());
  370. return false;
  371. } else if (!json.get("errcode").toString().equals("0")) {
  372. logger.error("发送订阅消息出错error2:" + json.get("errcode").toString());
  373. return false;
  374. } else {
  375. return true;
  376. }
  377. } catch (Exception e) {
  378. logger.error("发送订阅消息出错:" + e.getMessage());
  379. }
  380. return true;
  381. }
  382. /**
  383. * 查询支付订单
  384. * */
  385. @Override
  386. public Map<String, String> queryPaidOrder(Integer storeId, String transactionId, String orderSn) {
  387. try {
  388. getApiConfig(storeId, PlatformTypeEnum.MP_WEIXIN.getCode());
  389. WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
  390. Map<String, String> params = OrderQueryModel.builder()
  391. .appid(wxPayApiConfig.getAppId())
  392. .mch_id(wxPayApiConfig.getMchId())
  393. .transaction_id(transactionId)
  394. .out_trade_no(orderSn)
  395. .nonce_str(WxPayKit.generateStr())
  396. .build()
  397. .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5);
  398. logger.info("请求参数:{}", WxPayKit.toXml(params));
  399. String query = WxPayApi.orderQuery(params);
  400. Map<String, String> result = WxPayKit.xmlToMap(query);
  401. logger.info("查询结果: {}", result);
  402. if (result.get("result_code").equals("FAIL")) {
  403. result.put("trade_state", "FAIL");
  404. }
  405. return result;
  406. } catch (Exception e) {
  407. e.printStackTrace();
  408. return null;
  409. }
  410. }
  411. /**
  412. * 刷卡支付
  413. * */
  414. private Map<String, String> microPay(Integer storeId, Map<String, String> reqData, String ip, String platform) {
  415. try {
  416. String orderSn = reqData.get("out_trade_no");
  417. logger.info("调用微信刷卡支付下单接口入参{}", JsonUtil.toJSONString(reqData));
  418. logger.info("请求平台:{}, 订单号:{}", platform, orderSn);
  419. // 支付配置
  420. getApiConfig(storeId, platform);
  421. WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
  422. Map<String, String> params = MicroPayModel.builder()
  423. .appid(wxPayApiConfig.getAppId())
  424. .mch_id(wxPayApiConfig.getMchId())
  425. .nonce_str(WxPayKit.generateStr())
  426. .body(reqData.get("body"))
  427. .attach(reqData.get("body"))
  428. .out_trade_no(orderSn)
  429. .total_fee(reqData.get("total_fee"))
  430. .spbill_create_ip(ip)
  431. .auth_code(reqData.get("auth_code"))
  432. .build()
  433. .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5);
  434. String xmlResult = WxPayApi.microPay(false, params);
  435. // 同步返回结果
  436. logger.info("xmlResult:" + xmlResult);
  437. Map<String, String> respMap = WxPayKit.xmlToMap(xmlResult);
  438. String returnCode = respMap.get("return_code");
  439. String returnMsg = respMap.get("return_msg");
  440. if (!WxPayKit.codeIsOk(returnCode)) {
  441. // 通讯失败
  442. Map<String, String> payResult = null;
  443. String errCode = respMap.get("err_code");
  444. if (StringUtil.isNotEmpty(errCode)) {
  445. // 用户支付中,需要输入密码
  446. if (errCode.equals("USERPAYING")) {
  447. // 等待10秒后查询订单
  448. try {
  449. Thread.sleep(10000);
  450. } catch (InterruptedException e) {
  451. e.printStackTrace();
  452. }
  453. payResult = queryPaidOrder(storeId, respMap.get("transaction_id"), orderSn);
  454. }
  455. }
  456. if (payResult == null || !payResult.get("trade_state").equals("SUCCESS")) {
  457. logger.info("提交刷卡支付失败>>" + xmlResult);
  458. return respMap;
  459. }
  460. }
  461. String resultCode = respMap.get("result_code");
  462. if (!WxPayKit.codeIsOk(resultCode)) {
  463. logger.info("支付失败>>" + xmlResult);
  464. logger.error(returnMsg);
  465. return respMap;
  466. }
  467. // 支付成功
  468. logger.info("刷卡支付返回>>" + respMap.toString());
  469. if (StringUtil.isNotEmpty(orderSn)) {
  470. UserOrderDto orderInfo = orderService.getOrderByOrderSn(orderSn);
  471. if (orderInfo != null) {
  472. if (!orderInfo.getStatus().equals(OrderStatusEnum.DELETED.getKey())) {
  473. paymentService.paymentCallback(orderInfo);
  474. }
  475. }
  476. }
  477. return respMap;
  478. } catch (Exception e) {
  479. logger.error(e.getMessage(), e);
  480. }
  481. return null;
  482. }
  483. /**
  484. * 小程序、公众号支付
  485. * */
  486. private Map<String, String> jsapiPay(Integer storeId, Map<String, String> reqData, String ip, String platform) {
  487. try {
  488. logger.info("调用微信支付下单接口入参{}", JsonUtil.toJSONString(reqData));
  489. logger.info("请求平台:{}", platform);
  490. // 支付配置
  491. getApiConfig(storeId, platform);
  492. WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
  493. Map<String, String> params = UnifiedOrderModel
  494. .builder()
  495. .appid(wxPayApiConfig.getAppId())
  496. .mch_id(wxPayApiConfig.getMchId())
  497. .nonce_str(WxPayKit.generateStr())
  498. .body(reqData.get("body"))
  499. .attach(reqData.get("body"))
  500. .out_trade_no(reqData.get("out_trade_no"))
  501. .total_fee(reqData.get("total_fee"))
  502. .spbill_create_ip(ip)
  503. .notify_url(wxPayApiConfig.getDomain() + CALL_BACK_URL)
  504. .trade_type(reqData.get("trade_type"))
  505. .openid(reqData.get("openid"))
  506. .build()
  507. .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5);
  508. String xmlResult = WxPayApi.pushOrder(false, params);
  509. logger.info(xmlResult);
  510. Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
  511. String returnCode = result.get("return_code");
  512. String returnMsg = result.get("return_msg");
  513. if (!WxPayKit.codeIsOk(returnCode)) {
  514. logger.error(returnMsg);
  515. }
  516. String resultCode = result.get("result_code");
  517. if (!WxPayKit.codeIsOk(resultCode)) {
  518. logger.error(returnMsg);
  519. }
  520. logger.info("调用微信支付下单接口返回{}", JsonUtil.toJSONString(result));
  521. return result;
  522. } catch (Exception e) {
  523. logger.error(e.getMessage(), e);
  524. }
  525. return null;
  526. }
  527. /**
  528. * 发起退款
  529. * @param storeId
  530. * @param orderSn
  531. * @param totalAmount
  532. * @param refundAmount
  533. * @param platform
  534. * @return
  535. * */
  536. public Boolean doRefund(Integer storeId, String orderSn, BigDecimal totalAmount, BigDecimal refundAmount, String platform) throws BusinessCheckException {
  537. try {
  538. logger.info("WeixinService.doRefund orderSn = {}, totalFee = {}, refundFee = {}", orderSn, totalAmount, refundAmount);
  539. if (StringUtil.isEmpty(orderSn)) {
  540. throw new BusinessCheckException("退款订单号不能为空...");
  541. }
  542. BigDecimal totalFee = totalAmount.multiply(new BigDecimal("100"));
  543. BigDecimal refundFee = refundAmount.multiply(new BigDecimal("100"));
  544. Integer totalFeeInt = totalFee.intValue();
  545. Integer refundFeeInt = refundFee.intValue();
  546. // 支付配置
  547. getApiConfig(storeId, platform);
  548. WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
  549. Map<String, String> params = RefundModel.builder()
  550. .appid(wxPayApiConfig.getAppId())
  551. .mch_id(wxPayApiConfig.getMchId())
  552. .nonce_str(WxPayKit.generateStr())
  553. .transaction_id("")
  554. .out_trade_no(orderSn)
  555. .out_refund_no(orderSn)
  556. .total_fee(totalFeeInt.toString())
  557. .refund_fee(refundFeeInt.toString())
  558. .notify_url(wxPayApiConfig.getDomain() + REFUND_NOTIFY_URL)
  559. .build()
  560. .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5);
  561. String refundStr = WxPayApi.orderRefund(false, params, wxPayApiConfig.getCertPath(), wxPayApiConfig.getMchId());
  562. logger.info("WeixinService doRefund params: {}", params);
  563. logger.info("WeixinService doRefund return: {}", refundStr);
  564. Map<String, String> result = WxPayKit.xmlToMap(refundStr);
  565. String returnCode = result.get("return_code");
  566. String returnMsg = result.get("return_msg");
  567. if (!WxPayKit.codeIsOk(returnCode)) {
  568. logger.error(returnMsg);
  569. return false;
  570. }
  571. return true;
  572. } catch (Exception e) {
  573. throw new BusinessCheckException("WeixinService.doRefund 微信退款失败:" + e.getMessage());
  574. }
  575. }
  576. /**
  577. * 获取支付配置
  578. * @param storeId
  579. * @param platform
  580. * */
  581. private WxPayApiConfig getApiConfig(Integer storeId, String platform) throws BusinessCheckException {
  582. WxPayApiConfig apiConfig;
  583. MtStore mtStore = storeService.queryStoreById(storeId);
  584. String mchId = wxPayBean.getMchId();
  585. String apiV2 = wxPayBean.getApiV2();
  586. if (mtStore != null && StringUtil.isNotEmpty(mtStore.getWxApiV2()) && StringUtil.isNotEmpty(mtStore.getWxMchId())) {
  587. mchId = mtStore.getWxMchId();
  588. apiV2 = mtStore.getWxApiV2();
  589. }
  590. apiConfig = WxPayApiConfig.builder()
  591. .appId(wxPayBean.getAppId())
  592. .mchId(mchId)
  593. .partnerKey(apiV2)
  594. .certPath(wxPayBean.getCertPath())
  595. .domain(wxPayBean.getDomain())
  596. .build();
  597. // 微信内h5公众号支付
  598. if (platform.equals(PlatformTypeEnum.H5.getCode())) {
  599. String wxAppId = env.getProperty("weixin.official.appId");
  600. String wxAppSecret = env.getProperty("weixin.official.appSecret");
  601. if (StringUtil.isNotEmpty(wxAppId) && StringUtil.isNotEmpty(wxAppSecret)) {
  602. apiConfig.setAppId(wxAppId);
  603. apiConfig.setApiKey(wxAppSecret);
  604. }
  605. }
  606. WxPayApiConfigKit.setThreadLocalWxPayApiConfig(apiConfig);
  607. return apiConfig;
  608. }
  609. }