package com.example.controller; import com.alibaba.fastjson.JSONObject; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.domain.*; import com.alipay.api.internal.util.AlipaySignature; import com.alipay.api.request.*; import com.alipay.api.response.AlipayTradePayResponse; import com.alipay.api.response.AlipayTradePrecreateResponse; import com.alipay.api.response.AlipayTradeRefundResponse; import com.example.base.BaseController; import com.example.base.ResponseBase; import com.example.config.ali.AlipayProperties; import com.example.service.AlipayRefundService; import com.example.service.AlipayService; import com.example.util.SnowFlake; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import springfox.documentation.annotations.ApiIgnore; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; import java.util.Objects; @Controller @RequestMapping("/alipay") public class AlipayPagePayController extends BaseController { private static final Logger logger = LoggerFactory.getLogger(AlipayPagePayController.class); @Autowired private AlipayProperties alipayProperties; @Autowired private AlipayClient alipayClient; /** * 商品定价 */ @Value("${pay.alipay.totalAmount}") private String totalAmount; /** * 商品名 */ @Value("${pay.subjectName}") public String subjectName; @GetMapping("/test") @ResponseBody public String test() { return "suucess"; } /** * 扫码付款成功后跳转的url */ @Value("${pay.alipay.returnUrlForScan}") private String returnUrlForScan; @GetMapping("/getPayimg") @ResponseBody public ResponseBase getPayimg(String outTradeNo) { AlipayTradePrecreateRequest req = new AlipayTradePrecreateRequest(); AlipayTradePrecreateModel model = new AlipayTradePrecreateModel(); model.setOutTradeNo(outTradeNo); model.setSubject("测试二维码"); String amount = String.valueOf(0.01); model.setTotalAmount(amount); req.setBizModel(model); req.setNotifyUrl(alipayProperties.getNotifyUrl()); logger.info("发起AliPay下单请求"); AlipayTradePrecreateResponse execute = null; String img = null; try { execute = alipayClient.execute(req); String result = execute.getBody(); JSONObject res = JSONObject.parseObject(result); res = res.getJSONObject("alipay_trade_precreate_response"); String code = res.getString("code"); String qr_code = res.getString("qr_code"); if (code.equals("10000") && qr_code != null) { img = qr_code; } } catch (Exception e) { return responseSuccess(error("构建二维码失败!")); } return responseSuccess(success(img)); } /** * 条码支付 * * @return * @throws AlipayApiException */ @GetMapping("/barcode") @ResponseBody public ResponseBase barcode(String authCode) throws AlipayApiException { SnowFlake snowFlake = new SnowFlake(2, 3); String outTradeNo = snowFlake.nextId() + ""; AlipayTradePayRequest request = new AlipayTradePayRequest(); AlipayTradePayModel model = new AlipayTradePayModel(); model.setOutTradeNo(outTradeNo); model.setScene("bar_code"); //扫码枪的二维码 model.setAuthCode(authCode); model.setSubject("条码支付测试"); String amount = String.valueOf(0.01); model.setStoreId("NJ_001"); model.setTotalAmount(amount); model.setTimeoutExpress("2m"); request.setBizModel(model); request.setNotifyUrl(alipayProperties.getNotifyUrl()); logger.info("发起AliPay下单请求"); AlipayTradePayResponse response = null; try { response = alipayClient.execute(request); String result = response.getBody(); JSONObject res = JSONObject.parseObject(result); res = res.getJSONObject("alipay_trade_pay_response"); String code = res.getString("code"); if (code.equals("10000")) { return responseSuccess(success("成功")); } else { return responseSuccess(error("条幅支付失败")); } } catch (Exception e) { return responseSuccess(error("条幅支付失败!")); } } /** * h5 页面支付 * * @param outTradeNo * @return * @throws AlipayApiException */ @GetMapping("/alipage") @ResponseBody public ResponseBase gotoPayPage(@RequestParam String outTradeNo) throws AlipayApiException { logger.info("支付订单号:{}", outTradeNo); // 订单模型 String productCode = "QUICK_WAP_WAY"; AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); //防止传空参数 if (outTradeNo == null || outTradeNo == "" || outTradeNo.length() < 4) { outTradeNo = String.valueOf(System.nanoTime()); } // String outTradeNo = UUID.randomUUID().toString(); model.setOutTradeNo(outTradeNo); model.setSubject(subjectName); model.setTotalAmount(totalAmount); model.setBody(subjectName + ",共0.01元"); model.setTimeoutExpress("2m"); model.setProductCode(productCode); AlipayTradeWapPayRequest wapPayRequest = new AlipayTradeWapPayRequest(); wapPayRequest.setReturnUrl(alipayProperties.getReturnUrl()); wapPayRequest.setNotifyUrl(alipayProperties.getNotifyUrl()); wapPayRequest.setBizModel(model); // 调用SDK生成表单, 并直接将完整的表单html输出到页面 String form = alipayClient.pageExecute(wapPayRequest).getBody(); return responseSuccess(success(form)); } @ApiOperation(value = "网页扫码支付", notes = "跳转到支付宝扫码支付页面,用户可通过扫码进行支付,成功则跳转到成功页面") @GetMapping("/gotoPayPage") public void gotoPayPage(HttpServletResponse response) throws AlipayApiException, IOException { // 订单模型 String productCode = "FAST_INSTANT_TRADE_PAY"; AlipayTradePagePayModel model = new AlipayTradePagePayModel(); SnowFlake snowFlake = new SnowFlake(2, 3); model.setOutTradeNo(snowFlake.nextId() + ""); model.setSubject(subjectName); model.setTotalAmount(totalAmount); model.setBody(subjectName + ",共0.01元"); model.setProductCode(productCode); AlipayTradePagePayRequest pagePayRequest = new AlipayTradePagePayRequest(); pagePayRequest.setReturnUrl(returnUrlForScan); pagePayRequest.setNotifyUrl(alipayProperties.getNotifyUrl()); pagePayRequest.setBizModel(model); // 调用SDK生成表单, 并直接将完整的表单html输出到页面 String form = alipayClient.pageExecute(pagePayRequest).getBody(); response.setContentType("text/html;charset=" + alipayProperties.getCharset()); response.getWriter().write(form); response.getWriter().flush(); response.getWriter().close(); } /** * 支付宝页面跳转同步通知页面 */ @ApiIgnore @GetMapping("/returnUrl") public String returnUrl(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException, AlipayApiException { response.setContentType("text/html;charset=" + alipayProperties.getCharset()); //获取支付宝GET过来反馈信息 Map params = new HashMap<>(); Map requestParams = request.getParameterMap(); for (Object iter : requestParams.keySet()) { String name = (String) iter; String[] values = (String[]) requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } //乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化 valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8"); params.put(name, valueStr); } logger.info("params is " + params.toString()); //验签 boolean verifyResult = AlipaySignature.rsaCheckV1(params, alipayProperties.getAlipayPublicKey(), alipayProperties.getCharset(), "RSA2"); if (verifyResult) { //验证成功 //请在这里加上商户的业务逻辑程序代码,如保存支付宝交易号 //商户订单号 String outTradeNo = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8"); //支付宝交易号 String tradeNo = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8"); return "success"; } else { return "error"; } } @Autowired private AlipayService alipayService; @Autowired private AlipayRefundService alipayRefundService; static boolean rsaCheckV1(HttpServletRequest request) { return true; } /** * 支付异步通知 * 接收到异步通知并验签通过后,一定要检查通知内容,包括通知中的app_id、out_trade_no、total_amount是否与请求中的一致, * 并根据trade_status进行后续业务处理 * https://docs.open.alipay.com/194/103296 */ @ApiIgnore @RequestMapping("/notify") @ResponseBody public String notify(HttpServletRequest request) throws AlipayApiException, UnsupportedEncodingException { // 一定要验签,防止黑客篡改参数 Map parameterMap = request.getParameterMap(); StringBuilder notifyBuild = new StringBuilder("/****************************** alipay notify **********" + "********************/\n"); parameterMap.forEach((key, value) -> notifyBuild.append(key + "=" + value[0] + "\n")); //订单信息 logger.info("notifyBuild" + notifyBuild.toString()); //校验签名 boolean flag = this.rsaCheckV1(request); if (flag) { /** * 需要严格按照如下描述校验通知数据的正确性 * 商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号, * 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额), * 同时需要校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email), * 上述有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。 * 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。 * 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。 */ //交易状态 String tradeStatus = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8"); //支付宝交易号 String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8"); String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8"); //商户id String app_id = new String(request.getParameter("app_id").getBytes("ISO-8859-1"), "UTF-8"); //退款费用 用于判断是否是退款 String refund_fee = null; try { refund_fee = new String(request.getParameter("refund_fee").getBytes("ISO-8859-1"), "UTF-8"); } catch (Exception e) { refund_fee = ""; } /*判断是否是支付异步通知*/ if (refund_fee == null || refund_fee == "") { //付款金额 String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8"); //判断订单号是否已处理 if (alipayService.containsByOutTradeNo(trade_no)) { logger.warn("订单已处理,支付宝重复调用"); return "success"; } //判断订单金额是否一致 防止被篡改 if (!total_amount.equals(totalAmount)) { logger.warn("订单金额不一致,请防止黑客恶意篡改信息"); return "fail"; } //判断商户id是否一致,防止被篡改 if (!app_id.equals(alipayProperties.getAppid())) { logger.warn("操作的商户id不一致,请防止黑客恶意篡改信息"); return "fail"; } // TRADE_FINISHED(表示交易已经成功结束,并不能再对该交易做后续操作); // TRADE_SUCCESS(表示交易已经成功结束,可以对该交易做后续操作,如:分润、退款等); if ("TRADE_FINISHED".equals(tradeStatus)) { //判断该笔订单是否在商户网站中已经做过处理 //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细, // 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),并执行商户的业务程序 //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的 //如果有做过处理,不执行商户的业务程序 //注意: //如果签约的是可退款协议,退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知 //如果没有签约可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。 } else if ("TRADE_SUCCESS".equals(tradeStatus)) { //判断该笔订单是否在商户网站中已经做过处理 //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细, // 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),并执行商户的业务程序 //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的 //如果有做过处理,不执行商户的业务程序 logger.info("签约可退款协议"); alipayService.saveToDb(parameterMap); //注意: //如果签约的是可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。 } } /*判断是否是退款异步通知*/ else { if (alipayRefundService.containsByOutTradeNo(trade_no)) { logger.warn("退款已处理,用户可能重复提交"); return "have refunded"; } //判断订单金额是否一致 防止被篡改 if (!refund_fee.equals(totalAmount)) { logger.warn("订单金额不一致,请防止黑客恶意篡改信息"); return "fail"; } //判断商户id是否一致,防止被篡改 if (!app_id.equals(alipayProperties.getAppid())) { logger.warn("操作的商户id不一致,请防止黑客恶意篡改信息"); return "fail"; } if ("TRADE_FINISHED".equals(tradeStatus)) { //判断该笔订单是否在商户网站中已经做过处理 //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细, // 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),并执行商户的业务程序 //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的 //如果有做过处理,不执行商户的业务程序 //注意: //如果签约的是可退款协议,退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知 //如果没有签约可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。 } else if ("TRADE_SUCCESS".equals(tradeStatus)) { //判断该笔订单是否在商户网站中已经做过处理 //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细, // 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),并执行商户的业务程序 //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的 //如果有做过处理,不执行商户的业务程序 alipayRefundService.saveToDb(parameterMap); //注意: //如果签约的是可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。 } } return "success"; } return "fail"; } @ApiOperation(value = "退款", notes = "通过商户订单号退款") @ApiParam(name = "out_trade_no", value = "商户订单号", required = true) @GetMapping("/refund") @ResponseBody public ResponseBase refund(@RequestParam String outTradeNo) { //若订单已处理,直接返回 if (alipayRefundService.containsByOutTradeNo(outTradeNo)) { logger.warn("退款已处理,用户可能重复提交"); responseSuccess(error("退款已处理,用户可能重复提交")); } AlipayTradeRefundRequest request = new AlipayTradeRefundRequest(); AlipayTradeRefundModel refundModel = new AlipayTradeRefundModel(); //refundModel.setTradeNo(outTradeNo); refundModel.setOutTradeNo(outTradeNo); refundModel.setRefundAmount(totalAmount); refundModel.setRefundReason("用户归还胶片,退还费用"); request.setBizModel(refundModel); AlipayTradeRefundResponse response = null; try { response = alipayClient.execute(request); logger.info(response.getBody()); } catch (Exception e) { logger.error("申请退款出错"); e.printStackTrace(); responseSuccess(error("申请退款出错")); } if (Objects.requireNonNull(response).isSuccess()) { logger.info("申请退款成功"); Map responseMap = new HashMap<>(2); responseMap.put("return_code", "200"); responseMap.put("return_msg", "refund success"); responseSuccess(error("申请退款成功")); } else { logger.info("申请退款失败"); Map responseMap = new HashMap<>(2); responseMap.put("return_code", "200"); responseMap.put("return_msg", "refund error"); return responseSuccess(error("申请退款失败")); } return responseSuccess(success("申请退款成功")); } }