WxpayController.java 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. package com.uas.service.donate.controller;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.github.binarywang.utils.qrcode.QrcodeUtils;
  4. import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
  5. import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
  6. import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
  7. import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
  8. import com.github.binarywang.wxpay.constant.WxPayConstants;
  9. import com.github.binarywang.wxpay.exception.WxPayException;
  10. import com.github.binarywang.wxpay.util.SignUtils;
  11. import com.uas.dfs.service.FileClient;
  12. import com.uas.platform.core.util.HttpUtil;
  13. import com.uas.service.donate.api.WxPayApi;
  14. import com.uas.service.donate.config.WxConfig;
  15. import com.uas.service.donate.core.support.SystemSession;
  16. import com.uas.service.donate.model.Project;
  17. import com.uas.service.donate.model.ProjectRecode;
  18. import com.uas.service.donate.model.User;
  19. import com.uas.service.donate.model.WechatOrder;
  20. import com.uas.service.donate.service.ProjectRecodeService;
  21. import com.uas.service.donate.service.ProjectService;
  22. import com.uas.service.donate.service.UserService;
  23. import com.uas.service.donate.service.WechatOrderService;
  24. import com.uas.service.donate.util.IpUtils;
  25. import com.uas.service.donate.util.StringUtils;
  26. import com.uas.service.donate.util.WechatConnector;
  27. import com.uas.service.donate.util.WxCheckoutUtil;
  28. import org.apache.commons.io.IOUtils;
  29. import org.slf4j.Logger;
  30. import org.slf4j.LoggerFactory;
  31. import org.springframework.beans.factory.annotation.Autowired;
  32. import org.springframework.stereotype.Controller;
  33. import org.springframework.ui.ModelMap;
  34. import org.springframework.web.bind.annotation.RequestMapping;
  35. import org.springframework.web.bind.annotation.RequestMethod;
  36. import org.springframework.web.bind.annotation.RequestParam;
  37. import org.springframework.web.bind.annotation.ResponseBody;
  38. import javax.servlet.http.HttpServletRequest;
  39. import javax.servlet.http.HttpServletResponse;
  40. import java.io.IOException;
  41. import java.io.PrintWriter;
  42. import java.net.URLEncoder;
  43. import java.util.Date;
  44. import java.util.HashMap;
  45. import java.util.Map;
  46. /**
  47. * Created by 黄诚天 on 2017-10-27.
  48. */
  49. @Controller
  50. @RequestMapping("/wxpay")
  51. public class WxpayController {
  52. @Autowired
  53. private FileClient fileClient;
  54. @Autowired
  55. protected WxPayApi wxPayApi;
  56. @Autowired
  57. protected WechatOrderService wechatOrderService;
  58. @Autowired
  59. private ProjectRecodeService projectRecodeService;
  60. @Autowired
  61. private ProjectService projectService;
  62. @Autowired
  63. private UserService userService;
  64. private Logger logger = LoggerFactory.getLogger(WxpayController.class);
  65. private static String redirectUrl = "http://lj.ubtob.com/wxpay/redirectUrl";
  66. //private String orderNumber = NumberGenerator.generateId();
  67. /**
  68. * 扫码支付 @RequestParam ProjectRecode projectRecode
  69. * 使用模式一 模式一,先扫码,再生成订单。模式二,先生成订单,再扫码。
  70. * @param request
  71. * @param response
  72. * @throws Exception
  73. */
  74. @ResponseBody
  75. @RequestMapping(value = "/pcPay", method = RequestMethod.POST)
  76. public ModelMap pcPay(@RequestParam("jsonStr") String jsonStr, HttpServletRequest request, HttpServletResponse response) throws Exception {
  77. ModelMap modelMap = new ModelMap();
  78. ProjectRecode projectRecode= null;
  79. try {
  80. projectRecode = this.createProjectRecode(jsonStr);
  81. } catch (Exception e) {
  82. logger.error("生成商户订单错误,原因:{}", e.getMessage());
  83. modelMap.put("error", "参数错误");
  84. return modelMap;
  85. }
  86. WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = createWxPayUnifiedOrderRequest(request, projectRecode, WxPayConstants.TradeType.NATIVE);
  87. Map map = unifiedOrder(wxPayUnifiedOrderRequest);
  88. if ("FAIL".equals(map.get("status"))) {
  89. modelMap.put("error", map.get("message"));
  90. } else {
  91. modelMap.put("qrcodeUrl", map.get("qrcodeUrl"));
  92. modelMap.put("outTradeNo", projectRecode.getId());
  93. }
  94. return modelMap;
  95. }
  96. /**
  97. * 公众号支付 网页支付JSAPI即公众号支付是用户在微信中打开商户的H5页面
  98. * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
  99. * @param request
  100. * @param response
  101. * @throws Exception
  102. */
  103. @ResponseBody
  104. @RequestMapping(value = "/webPay", method = RequestMethod.POST)
  105. public ModelMap webPay(@RequestParam("jsonStr") String jsonStr, HttpServletRequest request, HttpServletResponse response) throws Exception {
  106. ModelMap modelMap = new ModelMap();
  107. ProjectRecode projectRecode= null;
  108. try {
  109. projectRecode = this.createProjectRecode(jsonStr);
  110. } catch (Exception e) {
  111. logger.error("生成商户订单错误,原因:{}", e.getMessage());
  112. modelMap.put("error", "参数错误");
  113. return modelMap;
  114. }
  115. WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = createWxPayUnifiedOrderRequest(request, projectRecode, WxPayConstants.TradeType.JSAPI);
  116. String openId = null;
  117. Object openIdObj = request.getSession().getAttribute("openId");
  118. /*User user =SystemSession.getUser();
  119. if (user != null && user.getWxOpenId() != null) {
  120. logger.info("当前用户信息:" + user.toString());
  121. openId = user.getWxOpenId();
  122. } else {
  123. logger.info("公众号支付缺少openId:--需要用户授权");
  124. String url = WechatConnector.createAuthorizeUrl(WxConfig.oauthUserScope, wxPayApi.getConfig().getAppId(), URLEncoder.encode("http://lj.ubtob.com", "UTF-8"));
  125. response.sendRedirect(url);
  126. }*/
  127. if (openIdObj != null) {
  128. openId = (String) openIdObj;
  129. logger.info("当前微信用户信息:" + openId);
  130. } else {
  131. logger.info("公众号支付缺少openId:--需要用户授权");
  132. String url = WechatConnector.createAuthorizeUrl(WxConfig.oauthUserScope, wxPayApi.getConfig().getAppId(), URLEncoder.encode("http://lj.ubtob.com", "UTF-8"));
  133. response.sendRedirect(url);
  134. return null;
  135. }
  136. logger.info("公众号支付处理:openId=" + openId);
  137. if (StringUtils.isEmpty(wxPayApi.getConfig().getSubAppId())) {
  138. wxPayUnifiedOrderRequest.setOpenid(openId);//否是 trade_type=JSAPI,此参数必传,用户在主商户appid下的唯一标识。openid和sub_openid可以选传其中之一,如果选择传sub_openid,则必须传sub_appid。
  139. } else {
  140. wxPayUnifiedOrderRequest.setSubOpenid(openId);//否是 trade_type=JSAPI,此参数必传,用户在子商户appid下的唯一标识。openid和sub_openid可以选传其中之一,如果选择传sub_openid,则必须传sub_appid。
  141. }
  142. Map map = unifiedOrder(wxPayUnifiedOrderRequest);
  143. if ("FAIL".equals(map.get("status"))) {
  144. modelMap.put("error", map.get("message"));
  145. } else {
  146. //modelMap.put("outTradeNo", projectRecode.getId());
  147. //将prepayId返回给js
  148. String nonceStr = String.valueOf(System.currentTimeMillis());
  149. String timeStamp = String.valueOf(Long.parseLong(nonceStr) / 1000);
  150. String appId = wxPayApi.getConfig().getAppId();
  151. String packagePrepayId = "prepay_id=" + map.get("prepayId");
  152. modelMap.put("appId", appId);
  153. modelMap.put("timeStamp", timeStamp);
  154. modelMap.put("nonceStr", nonceStr);
  155. modelMap.put("package", packagePrepayId);
  156. modelMap.put("signType", "MD5");
  157. logger.info("参数--" + appId + "," + timeStamp + "," + nonceStr + "," + packagePrepayId);
  158. Map<String, String> signMap = new HashMap<>();
  159. signMap.put("appId", appId);
  160. signMap.put("timeStamp", timeStamp);
  161. signMap.put("nonceStr", nonceStr);
  162. signMap.put("package", packagePrepayId);
  163. signMap.put("signType", "MD5");
  164. //预付订单再次签名
  165. String packageSign = SignUtils.createSign(signMap, "MD5", wxPayApi.getConfig().getMchKey(), false);
  166. logger.info("预付订单再次签名验签--" + SignUtils.checkSign(signMap, "MD5", wxPayApi.getConfig().getMchKey()));
  167. modelMap.put("paySign", packageSign);
  168. }
  169. return modelMap;
  170. }
  171. /**
  172. * H5支付 商户在微信客户端外的移动端网页
  173. * https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4 应让用户去点击按钮触发查单操作
  174. * @param request
  175. * @param response
  176. * @throws Exception
  177. */
  178. @ResponseBody
  179. @RequestMapping(value = "/wapPay", method = RequestMethod.POST)
  180. public ModelMap wapPay(@RequestParam("jsonStr") String jsonStr, HttpServletRequest request, HttpServletResponse response) throws Exception {
  181. ModelMap modelMap = new ModelMap();
  182. ProjectRecode projectRecode= null;
  183. try {
  184. projectRecode = this.createProjectRecode(jsonStr);
  185. } catch (Exception e) {
  186. logger.error("生成商户订单错误,原因:{}", e.getMessage());
  187. modelMap.put("error", "参数错误");
  188. return modelMap;
  189. }
  190. WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = createWxPayUnifiedOrderRequest(request, projectRecode, WxPayConstants.TradeType.MWEB);
  191. Map map = unifiedOrder(wxPayUnifiedOrderRequest);
  192. if ("FAIL".equals(map.get("status"))) {
  193. modelMap.put("error", map.get("message"));
  194. } else {
  195. modelMap.put("mwebUrl", map.get("mwebUrl"));
  196. }
  197. return modelMap;
  198. }
  199. //通过统一下单接口发起请求,获得prepay_id(预支付交易会话标识),这个标示是微信提交支付的关键数据
  200. public Map unifiedOrder(WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest) throws Exception {
  201. Map map = new HashMap();
  202. WxPayUnifiedOrderResult wxPayUnifiedOrderResult = null;
  203. try {
  204. wxPayUnifiedOrderResult = wxPayApi.unifiedOrder(wxPayUnifiedOrderRequest);
  205. //map.put("status", "SUCCESS");
  206. if (WxPayConstants.TradeType.MWEB.equals(wxPayUnifiedOrderRequest.getTradeType())) {
  207. /*H5支付开发步骤:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4*/
  208. String mwebUrl = wxPayUnifiedOrderResult.getMwebUrl() + "&redirect_url=" + URLEncoder.encode(redirectUrl,"UTF-8");
  209. map.put("mwebUrl", mwebUrl);
  210. } else if (WxPayConstants.TradeType.NATIVE.equals(wxPayUnifiedOrderRequest.getTradeType())) {
  211. //扫码支付
  212. //QrcodeUtils.encode(wxPayUnifiedOrderResult.getCodeURL(),response.getOutputStream());
  213. byte[] bytes = QrcodeUtils.createQrcode(wxPayUnifiedOrderResult.getCodeURL(), 400, null);
  214. String qrcodeUrl = fileClient.upload(bytes, bytes.length,"jpg",null);
  215. //保存图片地址
  216. String outTradeNo = wxPayUnifiedOrderRequest.getOutTradeNo();
  217. ProjectRecode projectRecode = projectRecodeService.findOne(Long.parseLong(outTradeNo));
  218. projectRecode.setQrcodeUrl(qrcodeUrl);
  219. projectRecodeService.update(projectRecode);
  220. map.put("qrcodeUrl", qrcodeUrl);
  221. } else if (WxPayConstants.TradeType.JSAPI.equals(wxPayUnifiedOrderRequest.getTradeType())) {
  222. //公众号支付
  223. String prepayId = wxPayUnifiedOrderResult.getPrepayId();
  224. map.put("prepayId", prepayId);
  225. }
  226. //byte[] arr = wxPayApi.createScanPayQrcodeMode2(wxPayUnifiedOrderResult.getCodeURL(),null,null);
  227. } catch (WxPayException e) {
  228. logger.error("微信支付失败!订单号:{},原因:{}", wxPayUnifiedOrderRequest.getOutTradeNo(), e.getMessage());
  229. e.printStackTrace();
  230. map.put("status", e.getReturnCode());
  231. map.put("message", e.getReturnMsg());
  232. }
  233. return map;
  234. }
  235. /**
  236. * 查询订单支付状态 参数二选一
  237. * @param transactionId
  238. * @param outTradeNo
  239. */
  240. @ResponseBody
  241. @RequestMapping(value = "/queryTradeState", method = RequestMethod.POST)
  242. public String queryTradeState(@RequestParam(required = false) String transactionId, @RequestParam(required = false) String outTradeNo){
  243. try {
  244. WxPayOrderQueryResult wxPayOrderQueryResult =wxPayApi.queryOrder(transactionId, outTradeNo);
  245. return wxPayOrderQueryResult.getTradeState();
  246. } catch (WxPayException e) {
  247. return "ERROR";
  248. }
  249. }
  250. /**
  251. * 推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,
  252. * 如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,
  253. * 要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
  254. * 特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,
  255. * 防止数据泄漏导致出现“假通知”,造成资金损失。
  256. * @param request
  257. * @param response
  258. * @return
  259. */
  260. @ResponseBody
  261. @RequestMapping("/notifyUrl")
  262. public synchronized void payNotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
  263. ModelMap map = new ModelMap();
  264. String returnResult = null;
  265. try {
  266. String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
  267. WxPayOrderNotifyResult result = wxPayApi.parseOrderNotifyResult(xmlResult);
  268. //商户系统订单默认支付状态
  269. short projectRecodePayStatus = 1;
  270. if ("SUCCESS".equals(result.getReturnCode())) {
  271. if ("SUCCESS".equals(result.getResultCode())) {
  272. returnResult = setXml("SUCCESS", "OK");
  273. WechatOrder wechatOrder = wechatOrderService.getByTransactionId(result.getTransactionId());
  274. WxPayOrderQueryResult OrderQueryResult = null;
  275. if (wechatOrder != null) {
  276. OrderQueryResult = wxPayApi.queryOrder(result.getTransactionId(),null);//二选一填
  277. }
  278. ProjectRecode projectRecode = projectRecodeService.findOne(Long.parseLong(result.getOutTradeNo()));
  279. //微信在数据库中订单存在且状态已处理
  280. if (wechatOrder != null && wechatOrder.getStatus() == 1 && projectRecode != null && projectRecode.getStatus() != 1 && wechatOrder.getTradeState().equals(OrderQueryResult.getTradeState())) {
  281. logger.info("系统已对该笔交易进行处理,交易订单号" + wechatOrder.getOutTradeNo() + "微信交易号" + wechatOrder.getTransactionId());
  282. returnResult = setXml("SUCCESS", "OK");
  283. } else {
  284. // 自己处理订单的业务逻辑
  285. //校验返回的订单金额是否与商户侧的订单金额一致
  286. if (!(projectRecode != null && ((int) (projectRecode.getAmount()*100)) == result.getTotalFee().intValue())) {
  287. logger.warn("防止数据泄漏导致出现“假通知”,以免造成资金损失");
  288. map.put("warning", "防止数据泄漏导致出现“假通知”,以免造成资金损失");
  289. returnResult = setXml("FAIL", "商户侧的订单金额与返回的订单金额不一致");
  290. projectRecodePayStatus = 3;
  291. projectRecode.setStatus(projectRecodePayStatus);
  292. projectRecode.setExceptionMsg("{'AmountMsg' : '支付宝返回金额(单位分)与订单金额不相等', 'notifyTotalFee' : " + result.getTotalFee().intValue() + "}");
  293. } else {
  294. WxPayOrderQueryResult wxPayOrderQueryResult = wxPayApi.queryOrder(result.getTransactionId(),null);//二选一填
  295. if (wxPayOrderQueryResult != null) {
  296. map.put("tradeState", wxPayOrderQueryResult.getTradeState());//微信订单状态
  297. map.put("tradeStateDesc", wxPayOrderQueryResult.getTradeStateDesc());//微信订单状态详情
  298. }
  299. if ("SUCCESS".equals(wxPayOrderQueryResult.getTradeState())) {
  300. logger.info("异步通知支付成功处理开始:商户订单状态:" + projectRecode.getStatus());
  301. projectRecodePayStatus = 2;
  302. if (projectRecode.getStatus() == 1) {
  303. //如果当前数据库中商户订单还是待支付状态 更新项目参数人数和金额
  304. Project project = projectService.findOne(projectRecode.getProId());
  305. logger.info("查看项目,查看项目id为" + project.getId() + ",项目名称为"
  306. + project.getName() + ",当前参与人数为" + project.getJoinAmount() + ",当前已筹集的金额" + project.getTotalAmount());
  307. project.setJoinAmount(project.getJoinAmount() + 1);
  308. project.setTotalAmount(project.getTotalAmount() + projectRecode.getAmount());
  309. logger.info("此次用户捐款金额为" + projectRecode.getAmount() + "元");
  310. projectService.save(project);
  311. logger.info("更新项目,更新了项目id为" + project.getId() + ",项目名称为"
  312. + project.getName()+ "的参数人数和金额" + ",当前参与人数为" + project.getJoinAmount() + ",当前已筹集的金额" + project.getTotalAmount());
  313. }
  314. projectRecode.setStatus(projectRecodePayStatus);
  315. logger.info("异步通知支付成功处理结束:商户订单状态:" + projectRecode.getStatus());
  316. }
  317. wechatOrderService.save(WechatOrder.WxPayOrderNotifyResultToConvert(result, (short) 1, wxPayOrderQueryResult));
  318. returnResult = setXml("SUCCESS", "OK");
  319. }
  320. //更新订单状态
  321. projectRecodeService.update(projectRecode);
  322. }
  323. //String totalFee = WxPayBaseResult.feeToYuan(result.getTotalFee());
  324. } else {
  325. logger.info("微信支付业务处理失败:" + result.getErrCodeDes());
  326. returnResult = setXml("FAIL", "微信支付业务处理失败");
  327. }
  328. } else {
  329. logger.info("微信支付返回错误状态码(通信错误),错误信息:" + result.getReturnMsg());
  330. returnResult = setXml("FAIL", "微信支付返回错误状态码(通信错误)");
  331. }
  332. } catch (WxPayException e) {
  333. logger.error("微信回调结果异常,异常原因{}", e.getMessage());
  334. map.put("error", e.getMessage());
  335. returnResult = setXml("FAIL", "签名失败");
  336. } finally {
  337. //TODO map
  338. response.getWriter().write(returnResult);
  339. }
  340. }
  341. @ResponseBody
  342. @RequestMapping("/redirectUrl")
  343. public String redirectUrl(HttpServletRequest request) {
  344. /*由于设置redirect_url后,回跳指定页面的操作可能发生在:1,微信支付中间页调起微信收银台后超过5秒
  345. 2,用户点击“取消支付“或支付完成后点“完成”按钮。因此无法保证页面回跳时,支付流程已结束,
  346. 所以商户设置的redirect_url地址不能自动执行查单操作,应让用户去点击按钮触发查单操作。*/
  347. //效果图可以参考: https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4 最下面
  348. return "h5支付回调";
  349. }
  350. /**
  351. * 判断是否是微信浏览器发起的请求,如果是返回true,反正返回false
  352. * @param req
  353. * @return
  354. */
  355. @ResponseBody
  356. @RequestMapping("/isWechat")
  357. public boolean isWechatBrowser(HttpServletRequest req){
  358. String ua = req.getHeader("user-agent").toLowerCase();
  359. if (ua.indexOf("micromessenger") >= 0) {// 是微信浏览器
  360. return true;
  361. }
  362. return false;
  363. }
  364. /**
  365. * 获取token 基础支持token 一日上限2000次 与用户网页授权无关 与支付无关
  366. * @return
  367. * @throws Exception
  368. */
  369. public String getAccessToken() throws Exception {
  370. String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + WxConfig.APPID + "&secret=" + WxConfig.APPSECRET;
  371. HttpUtil.Response response = HttpUtil.sendGetRequest(url,null);
  372. if (response.getStatusCode() == 200) {
  373. return response.getResponseText();
  374. }
  375. return null;
  376. }
  377. @ResponseBody
  378. @RequestMapping(value = "/userAdvanced", method = RequestMethod.GET)
  379. public void userAdvanced(HttpServletRequest request, HttpServletResponse response) throws Exception {
  380. //配置网页授权url
  381. String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + wxPayApi.getConfig().getAppId() + "&redirect_uri=" + URLEncoder.encode("http://lj.ubtob.com/wxpay/getOpenId", "UTF-8") + "&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect";
  382. response.sendRedirect(url);
  383. }
  384. /**
  385. * 获取用户基本信息 静默获取 无需用户同意 只有公众号模式需要
  386. * https://mp.weixin.qq.com/advanced/advanced?action=table&token=719542712&lang=zh_CN
  387. * 网页授权添加url 需icp备案
  388. * @param request
  389. * @return
  390. */
  391. @ResponseBody
  392. @RequestMapping(value = "/getOpenId", method = RequestMethod.GET)
  393. public void getOpenId(HttpServletRequest request) throws IOException {
  394. try {
  395. //TODO 授权方式 是否需要电话号码 名字等信息
  396. //TODO 获取code
  397. String code = request.getParameter("code");
  398. String state = request.getParameter("state");
  399. logger.info("公众号获取:authCode=" + code + ",state=" + state);
  400. //String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
  401. String openId = wxPayApi.authcode2Openid(code);
  402. } catch (WxPayException e) {
  403. e.printStackTrace();
  404. }
  405. }
  406. /**
  407. * 验证token
  408. * @param request
  409. * @param response
  410. */
  411. @ResponseBody
  412. @RequestMapping("/checkToken")
  413. public void checkToken(HttpServletRequest request, HttpServletResponse response) {
  414. boolean isGet = request.getMethod().toLowerCase().equals("get");
  415. boolean isPost = request.getMethod().toLowerCase().equals("post");
  416. PrintWriter print;
  417. if (isGet) {
  418. // 微信加密签名
  419. String signature = request.getParameter("signature");
  420. // 时间戳
  421. String timestamp = request.getParameter("timestamp");
  422. // 随机数
  423. String nonce = request.getParameter("nonce");
  424. // 随机字符串
  425. String echostr = request.getParameter("echostr");
  426. // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
  427. if (signature != null && WxCheckoutUtil.checkSignature(signature, timestamp, nonce)) {
  428. try {
  429. print = response.getWriter();
  430. print.write(echostr);
  431. print.flush();
  432. } catch (IOException e) {
  433. e.printStackTrace();
  434. }
  435. }
  436. } else if (isPost) {
  437. //核心逻辑
  438. }
  439. }
  440. //
  441. private WxPayUnifiedOrderRequest createWxPayUnifiedOrderRequest(HttpServletRequest request, ProjectRecode projectRecode, String tradeType) {
  442. //Project project = projectRecode.getProject();
  443. WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = new WxPayUnifiedOrderRequest();
  444. wxPayUnifiedOrderRequest.setDeviceInfo("WEB");//否 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB
  445. wxPayUnifiedOrderRequest.setNonceStr(String.valueOf(System.currentTimeMillis()));//是 随机字符串,不长于32位。
  446. //wxPayUnifiedOrderRequest.setSign("xixihahxixhahaha");//是 签名
  447. //wxPayUnifiedOrderRequest.setSignType(null);//否 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
  448. //商品描述交易字段格式根据不同的应用场景按照以下格式:(1)PC网站——传入浏览器打开的网站主页title名-实际商品名称,例如:腾讯充值中心-QQ会员充值;(2) 公众号——传入公众号名称-实际商品名称,例如:腾讯形象店- image-QQ公仔;(3) H5——应用在浏览器网页上的场景,传入浏览器打开的移动网页的主页title名-实际商品名称,例如:腾讯充值中心-QQ会员充值;(4) 线下门店——门店品牌名-城市分店名-实际商品名称,例如: image形象店-深圳腾大- QQ公仔)(5) APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。
  449. if (WxPayConstants.TradeType.MWEB.equals(tradeType)) {
  450. //H5——应用在浏览器网页上的场景,传入浏览器打开的移动网页的主页title名-实际商品名称,例如:腾讯充值中心-QQ会员充值;
  451. wxPayUnifiedOrderRequest.setBody("一元捐-" + projectService.findOne(projectRecode.getProId()).getName());
  452. } else if (WxPayConstants.TradeType.JSAPI.equals(tradeType)) {
  453. if (StringUtils.isEmpty(wxPayApi.getConfig().getSubAppId())) {
  454. wxPayUnifiedOrderRequest.setOpenid(null);//TODO 否是 trade_type=JSAPI,此参数必传,用户在主商户appid下的唯一标识。openid和sub_openid可以选传其中之一,如果选择传sub_openid,则必须传sub_appid。
  455. } else {
  456. wxPayUnifiedOrderRequest.setSubOpenid(null);//否是 trade_type=JSAPI,此参数必传,用户在子商户appid下的唯一标识。openid和sub_openid可以选传其中之一,如果选择传sub_openid,则必须传sub_appid。
  457. }
  458. //公众号——传入公众号名称-实际商品名称,例如:腾讯形象店- image-QQ公仔;
  459. wxPayUnifiedOrderRequest.setBody("优软科技-" + projectService.findOne(projectRecode.getProId()).getName());
  460. } else if (WxPayConstants.TradeType.NATIVE.equals(tradeType)) {
  461. //PC网站——传入浏览器打开的网站主页title名-实际商品名称,例如:腾讯充值中心-QQ会员充值;
  462. wxPayUnifiedOrderRequest.setBody("一元捐-" + projectService.findOne(projectRecode.getProId()).getName());
  463. }
  464. //wxPayUnifiedOrderRequest.setDetail("");//否 商品详细描述,对于使用单品优惠的商户,改字段必须按照规范上传,详见“单品优惠参数说明”
  465. wxPayUnifiedOrderRequest.setAttach("这是附加数据");//否 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
  466. wxPayUnifiedOrderRequest.setOutTradeNo(projectRecode.getId().toString());//是 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。
  467. //wxPayUnifiedOrderRequest.setFeeType("CNY");//否 符合ISO 4217标准的三位字母代码,默认人民币:CNY
  468. wxPayUnifiedOrderRequest.setTotalFee((int)(projectRecode.getAmount() * 100));//是 订单总金额,只能为整数
  469. String spbillCreateIp = IpUtils.getIpFromRequest(request);
  470. if ("0:0:0:0:0:0:0:1".equals(spbillCreateIp)) {
  471. spbillCreateIp = "127.0.0.1";
  472. }
  473. wxPayUnifiedOrderRequest.setSpbillCreateIp(spbillCreateIp);//是 APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
  474. //wxPayUnifiedOrderRequest.setSpbillCreateIp(getIp(request));
  475. //wxPayUnifiedOrderRequest.setSpbillCreateIp("10.10.100.200");
  476. //wxPayUnifiedOrderRequest.setTimeStart(null);//否 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。
  477. //wxPayUnifiedOrderRequest.setTimeExpire(null);//否 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。
  478. //wxPayUnifiedOrderRequest.setGoodsTag(null);//否 订单优惠标记,代金券或立减优惠功能的参数
  479. //wxPayUnifiedOrderRequest.setNotifyURL();//是 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
  480. wxPayUnifiedOrderRequest.setTradeType(tradeType); //是 取值如下:JSAPI,NATIVE,APP
  481. wxPayUnifiedOrderRequest.setProductId(projectRecode.getProId().toString());//否是 trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义。
  482. //wxPayUnifiedOrderRequest.setLimitPay(null);//否 指定支付方式 如:no_credit--指定不能使用信用卡支付
  483. //wxPayUnifiedOrderRequest.setSceneInfo(null);//否 该字段用于上报场景信息,目前支持上报实际门店信息。该字段为JSON对象数据,对象格式为{"store_info":{"id": "门店ID","name": "名称","area_code": "编码","address": "地址" }}
  484. return wxPayUnifiedOrderRequest;
  485. }
  486. //通过xml 返回给给微信消息
  487. public String setXml(String return_code, String return_msg) {
  488. return "<xml><return_code><![CDATA[" + return_code + "]]>" +
  489. "</return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
  490. }
  491. private ProjectRecode createProjectRecode(String jsonStr) throws Exception {
  492. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  493. String imid = jsonObject.getString("imid");
  494. logger.info("移动端imid=" + imid);
  495. Long uuid = null;
  496. User user = SystemSession.getUser();
  497. if (user != null) {
  498. uuid = user.getUserUU();
  499. }
  500. if (!StringUtils.isEmpty(imid)) {
  501. jsonObject.remove("imid");
  502. //TODO 通过imid获取uuid
  503. Object uuidObj = userService.getUserByImId(Long.parseLong(imid));
  504. if (uuidObj !=null) {
  505. uuid = (Long)uuidObj;
  506. logger.info("移动端imid转为uuid,uuid=" + uuid);
  507. }
  508. }
  509. ProjectRecode projectRecode= JSONObject.parseObject(jsonObject.toJSONString(),ProjectRecode.class);
  510. if (uuid != null) {
  511. projectRecode.setUuid(uuid);
  512. }
  513. projectRecode.setTime(new Date());
  514. ProjectRecode newProjectRecode = projectRecodeService.join(projectRecode);
  515. return newProjectRecode;
  516. }
  517. public String getIp(HttpServletRequest request) {
  518. String ip = request.getHeader("X-Forwarded-For");
  519. if(ip!=null && !"unKnown".equalsIgnoreCase(ip)){
  520. int index = ip.indexOf(",");
  521. if(index != -1){
  522. return ip.substring(0,index);
  523. }else{
  524. return ip;
  525. }
  526. }
  527. ip = request.getHeader("X-Real-IP");
  528. if(ip!=null && !"unKnown".equalsIgnoreCase(ip)){
  529. return ip;
  530. }
  531. return request.getRemoteAddr();
  532. }
  533. }