AlipayPagePayController.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. package com.example.controller;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.alipay.api.AlipayApiException;
  4. import com.alipay.api.AlipayClient;
  5. import com.alipay.api.domain.*;
  6. import com.alipay.api.internal.util.AlipaySignature;
  7. import com.alipay.api.request.*;
  8. import com.alipay.api.response.AlipayTradePayResponse;
  9. import com.alipay.api.response.AlipayTradePrecreateResponse;
  10. import com.alipay.api.response.AlipayTradeRefundResponse;
  11. import com.example.base.BaseController;
  12. import com.example.base.ResponseBase;
  13. import com.example.config.ali.AlipayProperties;
  14. import com.example.service.AlipayRefundService;
  15. import com.example.service.AlipayService;
  16. import com.example.util.SnowFlake;
  17. import io.swagger.annotations.ApiOperation;
  18. import io.swagger.annotations.ApiParam;
  19. import org.slf4j.Logger;
  20. import org.slf4j.LoggerFactory;
  21. import org.springframework.beans.factory.annotation.Autowired;
  22. import org.springframework.beans.factory.annotation.Value;
  23. import org.springframework.stereotype.Controller;
  24. import org.springframework.web.bind.annotation.*;
  25. import springfox.documentation.annotations.ApiIgnore;
  26. import javax.servlet.http.HttpServletRequest;
  27. import javax.servlet.http.HttpServletResponse;
  28. import java.io.IOException;
  29. import java.io.UnsupportedEncodingException;
  30. import java.util.HashMap;
  31. import java.util.Map;
  32. import java.util.Objects;
  33. @Controller
  34. @RequestMapping("/alipay")
  35. public class AlipayPagePayController extends BaseController {
  36. private static final Logger logger = LoggerFactory.getLogger(AlipayPagePayController.class);
  37. @Autowired
  38. private AlipayProperties alipayProperties;
  39. @Autowired
  40. private AlipayClient alipayClient;
  41. /**
  42. * 商品定价
  43. */
  44. @Value("${pay.alipay.totalAmount}")
  45. private String totalAmount;
  46. /**
  47. * 商品名
  48. */
  49. @Value("${pay.subjectName}")
  50. public String subjectName;
  51. @GetMapping("/test")
  52. @ResponseBody
  53. public String test() {
  54. return "suucess";
  55. }
  56. /**
  57. * 扫码付款成功后跳转的url
  58. */
  59. @Value("${pay.alipay.returnUrlForScan}")
  60. private String returnUrlForScan;
  61. @GetMapping("/getPayimg")
  62. @ResponseBody
  63. public ResponseBase getPayimg(String outTradeNo) {
  64. AlipayTradePrecreateRequest req = new AlipayTradePrecreateRequest();
  65. AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
  66. model.setOutTradeNo(outTradeNo);
  67. model.setSubject("测试二维码");
  68. String amount = String.valueOf(0.01);
  69. model.setTotalAmount(amount);
  70. req.setBizModel(model);
  71. req.setNotifyUrl(alipayProperties.getNotifyUrl());
  72. logger.info("发起AliPay下单请求");
  73. AlipayTradePrecreateResponse execute = null;
  74. String img = null;
  75. try {
  76. execute = alipayClient.execute(req);
  77. String result = execute.getBody();
  78. JSONObject res = JSONObject.parseObject(result);
  79. res = res.getJSONObject("alipay_trade_precreate_response");
  80. String code = res.getString("code");
  81. String qr_code = res.getString("qr_code");
  82. if (code.equals("10000") && qr_code != null) {
  83. img = qr_code;
  84. }
  85. } catch (Exception e) {
  86. return responseSuccess(error("构建二维码失败!"));
  87. }
  88. return responseSuccess(success(img));
  89. }
  90. /**
  91. * 条码支付
  92. *
  93. * @return
  94. * @throws AlipayApiException
  95. */
  96. @GetMapping("/barcode")
  97. @ResponseBody
  98. public ResponseBase barcode(String authCode) throws AlipayApiException {
  99. SnowFlake snowFlake = new SnowFlake(2, 3);
  100. String outTradeNo = snowFlake.nextId() + "";
  101. AlipayTradePayRequest request = new AlipayTradePayRequest();
  102. AlipayTradePayModel model = new AlipayTradePayModel();
  103. model.setOutTradeNo(outTradeNo);
  104. model.setScene("bar_code");
  105. //扫码枪的二维码
  106. model.setAuthCode(authCode);
  107. model.setSubject("条码支付测试");
  108. String amount = String.valueOf(0.01);
  109. model.setStoreId("NJ_001");
  110. model.setTotalAmount(amount);
  111. model.setTimeoutExpress("2m");
  112. request.setBizModel(model);
  113. request.setNotifyUrl(alipayProperties.getNotifyUrl());
  114. logger.info("发起AliPay下单请求");
  115. AlipayTradePayResponse response = null;
  116. try {
  117. response = alipayClient.execute(request);
  118. String result = response.getBody();
  119. JSONObject res = JSONObject.parseObject(result);
  120. res = res.getJSONObject("alipay_trade_pay_response");
  121. String code = res.getString("code");
  122. if (code.equals("10000")) {
  123. return responseSuccess(success("成功"));
  124. } else {
  125. return responseSuccess(error("条幅支付失败"));
  126. }
  127. } catch (Exception e) {
  128. return responseSuccess(error("条幅支付失败!"));
  129. }
  130. }
  131. /**
  132. * h5 页面支付
  133. *
  134. * @param outTradeNo
  135. * @return
  136. * @throws AlipayApiException
  137. */
  138. @GetMapping("/alipage")
  139. @ResponseBody
  140. public ResponseBase gotoPayPage(@RequestParam String outTradeNo) throws AlipayApiException {
  141. logger.info("支付订单号:{}", outTradeNo);
  142. // 订单模型
  143. String productCode = "QUICK_WAP_WAY";
  144. AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
  145. //防止传空参数
  146. if (outTradeNo == null || outTradeNo == "" || outTradeNo.length() < 4) {
  147. outTradeNo = String.valueOf(System.nanoTime());
  148. }
  149. // String outTradeNo = UUID.randomUUID().toString();
  150. model.setOutTradeNo(outTradeNo);
  151. model.setSubject(subjectName);
  152. model.setTotalAmount(totalAmount);
  153. model.setBody(subjectName + ",共0.01元");
  154. model.setTimeoutExpress("2m");
  155. model.setProductCode(productCode);
  156. AlipayTradeWapPayRequest wapPayRequest = new AlipayTradeWapPayRequest();
  157. wapPayRequest.setReturnUrl(alipayProperties.getReturnUrl());
  158. wapPayRequest.setNotifyUrl(alipayProperties.getNotifyUrl());
  159. wapPayRequest.setBizModel(model);
  160. // 调用SDK生成表单, 并直接将完整的表单html输出到页面
  161. String form = alipayClient.pageExecute(wapPayRequest).getBody();
  162. return responseSuccess(success(form));
  163. }
  164. @ApiOperation(value = "网页扫码支付", notes = "跳转到支付宝扫码支付页面,用户可通过扫码进行支付,成功则跳转到成功页面")
  165. @GetMapping("/gotoPayPage")
  166. public void gotoPayPage(HttpServletResponse response) throws AlipayApiException, IOException {
  167. // 订单模型
  168. String productCode = "FAST_INSTANT_TRADE_PAY";
  169. AlipayTradePagePayModel model = new AlipayTradePagePayModel();
  170. SnowFlake snowFlake = new SnowFlake(2, 3);
  171. model.setOutTradeNo(snowFlake.nextId() + "");
  172. model.setSubject(subjectName);
  173. model.setTotalAmount(totalAmount);
  174. model.setBody(subjectName + ",共0.01元");
  175. model.setProductCode(productCode);
  176. AlipayTradePagePayRequest pagePayRequest = new AlipayTradePagePayRequest();
  177. pagePayRequest.setReturnUrl(returnUrlForScan);
  178. pagePayRequest.setNotifyUrl(alipayProperties.getNotifyUrl());
  179. pagePayRequest.setBizModel(model);
  180. // 调用SDK生成表单, 并直接将完整的表单html输出到页面
  181. String form = alipayClient.pageExecute(pagePayRequest).getBody();
  182. response.setContentType("text/html;charset=" + alipayProperties.getCharset());
  183. response.getWriter().write(form);
  184. response.getWriter().flush();
  185. response.getWriter().close();
  186. }
  187. /**
  188. * 支付宝页面跳转同步通知页面
  189. */
  190. @ApiIgnore
  191. @GetMapping("/returnUrl")
  192. public String returnUrl(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException, AlipayApiException {
  193. response.setContentType("text/html;charset=" + alipayProperties.getCharset());
  194. //获取支付宝GET过来反馈信息
  195. Map<String, String> params = new HashMap<>();
  196. Map requestParams = request.getParameterMap();
  197. for (Object iter : requestParams.keySet()) {
  198. String name = (String) iter;
  199. String[] values = (String[]) requestParams.get(name);
  200. String valueStr = "";
  201. for (int i = 0; i < values.length; i++) {
  202. valueStr = (i == values.length - 1) ? valueStr + values[i]
  203. : valueStr + values[i] + ",";
  204. }
  205. //乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
  206. valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
  207. params.put(name, valueStr);
  208. }
  209. logger.info("params is " + params.toString());
  210. //验签
  211. boolean verifyResult = AlipaySignature.rsaCheckV1(params, alipayProperties.getAlipayPublicKey(),
  212. alipayProperties.getCharset(), "RSA2");
  213. if (verifyResult) {
  214. //验证成功
  215. //请在这里加上商户的业务逻辑程序代码,如保存支付宝交易号
  216. //商户订单号
  217. String outTradeNo = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
  218. //支付宝交易号
  219. String tradeNo = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
  220. return "success";
  221. } else {
  222. return "error";
  223. }
  224. }
  225. @Autowired
  226. private AlipayService alipayService;
  227. @Autowired
  228. private AlipayRefundService alipayRefundService;
  229. static boolean rsaCheckV1(HttpServletRequest request) {
  230. return true;
  231. }
  232. /**
  233. * 支付异步通知
  234. * 接收到异步通知并验签通过后,一定要检查通知内容,包括通知中的app_id、out_trade_no、total_amount是否与请求中的一致,
  235. * 并根据trade_status进行后续业务处理
  236. * https://docs.open.alipay.com/194/103296
  237. */
  238. @ApiIgnore
  239. @RequestMapping("/notify")
  240. @ResponseBody
  241. public String notify(HttpServletRequest request) throws AlipayApiException, UnsupportedEncodingException {
  242. // 一定要验签,防止黑客篡改参数
  243. Map<String, String[]> parameterMap = request.getParameterMap();
  244. StringBuilder notifyBuild = new StringBuilder("/****************************** alipay notify **********" +
  245. "********************/\n");
  246. parameterMap.forEach((key, value) -> notifyBuild.append(key + "=" + value[0] + "\n"));
  247. //订单信息
  248. logger.info("notifyBuild" + notifyBuild.toString());
  249. //校验签名
  250. boolean flag = this.rsaCheckV1(request);
  251. if (flag) {
  252. /**
  253. * 需要严格按照如下描述校验通知数据的正确性
  254. * 商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
  255. * 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
  256. * 同时需要校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
  257. * 上述有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。
  258. * 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。
  259. * 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
  260. */
  261. //交易状态
  262. String tradeStatus = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");
  263. //支付宝交易号
  264. String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
  265. String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
  266. //商户id
  267. String app_id = new String(request.getParameter("app_id").getBytes("ISO-8859-1"), "UTF-8");
  268. //退款费用 用于判断是否是退款
  269. String refund_fee = null;
  270. try {
  271. refund_fee = new String(request.getParameter("refund_fee").getBytes("ISO-8859-1"), "UTF-8");
  272. } catch (Exception e) {
  273. refund_fee = "";
  274. }
  275. /*判断是否是支付异步通知*/
  276. if (refund_fee == null || refund_fee == "") {
  277. //付款金额
  278. String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");
  279. //判断订单号是否已处理
  280. if (alipayService.containsByOutTradeNo(trade_no)) {
  281. logger.warn("订单已处理,支付宝重复调用");
  282. return "success";
  283. }
  284. //判断订单金额是否一致 防止被篡改
  285. if (!total_amount.equals(totalAmount)) {
  286. logger.warn("订单金额不一致,请防止黑客恶意篡改信息");
  287. return "fail";
  288. }
  289. //判断商户id是否一致,防止被篡改
  290. if (!app_id.equals(alipayProperties.getAppid())) {
  291. logger.warn("操作的商户id不一致,请防止黑客恶意篡改信息");
  292. return "fail";
  293. }
  294. // TRADE_FINISHED(表示交易已经成功结束,并不能再对该交易做后续操作);
  295. // TRADE_SUCCESS(表示交易已经成功结束,可以对该交易做后续操作,如:分润、退款等);
  296. if ("TRADE_FINISHED".equals(tradeStatus)) {
  297. //判断该笔订单是否在商户网站中已经做过处理
  298. //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,
  299. // 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),并执行商户的业务程序
  300. //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
  301. //如果有做过处理,不执行商户的业务程序
  302. //注意:
  303. //如果签约的是可退款协议,退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
  304. //如果没有签约可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
  305. } else if ("TRADE_SUCCESS".equals(tradeStatus)) {
  306. //判断该笔订单是否在商户网站中已经做过处理
  307. //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,
  308. // 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),并执行商户的业务程序
  309. //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
  310. //如果有做过处理,不执行商户的业务程序
  311. logger.info("签约可退款协议");
  312. alipayService.saveToDb(parameterMap);
  313. //注意:
  314. //如果签约的是可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
  315. }
  316. }
  317. /*判断是否是退款异步通知*/
  318. else {
  319. if (alipayRefundService.containsByOutTradeNo(trade_no)) {
  320. logger.warn("退款已处理,用户可能重复提交");
  321. return "have refunded";
  322. }
  323. //判断订单金额是否一致 防止被篡改
  324. if (!refund_fee.equals(totalAmount)) {
  325. logger.warn("订单金额不一致,请防止黑客恶意篡改信息");
  326. return "fail";
  327. }
  328. //判断商户id是否一致,防止被篡改
  329. if (!app_id.equals(alipayProperties.getAppid())) {
  330. logger.warn("操作的商户id不一致,请防止黑客恶意篡改信息");
  331. return "fail";
  332. }
  333. if ("TRADE_FINISHED".equals(tradeStatus)) {
  334. //判断该笔订单是否在商户网站中已经做过处理
  335. //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,
  336. // 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),并执行商户的业务程序
  337. //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
  338. //如果有做过处理,不执行商户的业务程序
  339. //注意:
  340. //如果签约的是可退款协议,退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
  341. //如果没有签约可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
  342. } else if ("TRADE_SUCCESS".equals(tradeStatus)) {
  343. //判断该笔订单是否在商户网站中已经做过处理
  344. //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,
  345. // 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),并执行商户的业务程序
  346. //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
  347. //如果有做过处理,不执行商户的业务程序
  348. alipayRefundService.saveToDb(parameterMap);
  349. //注意:
  350. //如果签约的是可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
  351. }
  352. }
  353. return "success";
  354. }
  355. return "fail";
  356. }
  357. @ApiOperation(value = "退款", notes = "通过商户订单号退款")
  358. @ApiParam(name = "out_trade_no", value = "商户订单号", required = true)
  359. @GetMapping("/refund")
  360. @ResponseBody
  361. public ResponseBase refund(@RequestParam String outTradeNo) {
  362. //若订单已处理,直接返回
  363. if (alipayRefundService.containsByOutTradeNo(outTradeNo)) {
  364. logger.warn("退款已处理,用户可能重复提交");
  365. responseSuccess(error("退款已处理,用户可能重复提交"));
  366. }
  367. AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
  368. AlipayTradeRefundModel refundModel = new AlipayTradeRefundModel();
  369. //refundModel.setTradeNo(outTradeNo);
  370. refundModel.setOutTradeNo(outTradeNo);
  371. refundModel.setRefundAmount(totalAmount);
  372. refundModel.setRefundReason("用户归还胶片,退还费用");
  373. request.setBizModel(refundModel);
  374. AlipayTradeRefundResponse response = null;
  375. try {
  376. response = alipayClient.execute(request);
  377. logger.info(response.getBody());
  378. } catch (Exception e) {
  379. logger.error("申请退款出错");
  380. e.printStackTrace();
  381. responseSuccess(error("申请退款出错"));
  382. }
  383. if (Objects.requireNonNull(response).isSuccess()) {
  384. logger.info("申请退款成功");
  385. Map<String, String> responseMap = new HashMap<>(2);
  386. responseMap.put("return_code", "200");
  387. responseMap.put("return_msg", "refund success");
  388. responseSuccess(error("申请退款成功"));
  389. } else {
  390. logger.info("申请退款失败");
  391. Map<String, String> responseMap = new HashMap<>(2);
  392. responseMap.put("return_code", "200");
  393. responseMap.put("return_msg", "refund error");
  394. return responseSuccess(error("申请退款失败"));
  395. }
  396. return responseSuccess(success("申请退款成功"));
  397. }
  398. }