2.8 Controller
package com.devin.alipay_demo.controller; import com.alipay.api.AlipayApiException; import com.alipay.api.internal.util.AlipaySignature; import com.devin.alipay_demo.entity.UserOrder; import com.devin.alipay_demo.service.OrderService; import com.devin.alipay_demo.util.AlipayProperties; import com.devin.alipay_demo.util.OrderEnum; import org.apache.commons.lang3.time.DateFormatUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.math.BigDecimal; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * @author Devin Zhang * @className OrderController * @description TODO * @date 2020/5/12 10:53 */ @Controller @RequestMapping("/order") public class OrderController { @Resource private OrderService orderService; @Resource private AlipayProperties alipayProperties; /** * 跳转到下单页面 * * @return */ @RequestMapping("/goPay") public String goPay() { return "pay"; } /** * 下单,并调用支付宝 * * @param orderAmount * @return * @throws AlipayApiException */ @PostMapping("/pay") public void pay(BigDecimal orderAmount, HttpServletResponse httpResponse) throws Exception { String payResult = orderService.orderPay(orderAmount); httpResponse.setContentType("text/html;charset=" + alipayProperties.getCharset()); httpResponse.getWriter().write(payResult); httpResponse.getWriter().flush(); httpResponse.getWriter().close(); } /** * 支付成功的跳转页面 * * @return */ @RequestMapping("/goPaySuccPage") public String goPaySuccPage() { return "pay_succ"; } /** * 支付成功的回调接口 * * @return */ @ResponseBody @RequestMapping("/notifyPayResult") public String notifyPayResult(HttpServletRequest request) { System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<进入支付宝回调->>>>>>>>>>>>>>>>>>>>>>>>>"); // 1.从支付宝回调的request域中取值放到map中 Map<String, String[]> requestParams = request.getParameterMap(); Map<String, String> params = new HashMap(); for (String name : requestParams.keySet()) { String[] values = requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } params.put(name, valueStr); } //2.封装必须参数 // 商户订单号 String outTradeNo = params.get("out_trade_no"); //交易状态 String tradeStatus = params.get("trade_status"); System.out.println("outTradeNo:" + outTradeNo + " tradeStatus:" + tradeStatus); //3.签名验证(对支付宝返回的数据验证,确定是支付宝返回的) boolean signVerified = false; try { //3.1调用SDK验证签名 signVerified = AlipaySignature.rsaCheckV1(params, alipayProperties.getPublicKey(), alipayProperties.getCharset(), alipayProperties.getSignType()); } catch (Exception e) { e.printStackTrace(); } System.out.println("--------------->验签结果:" + signVerified); //4.对验签进行处理 if (signVerified) { //验签通过 //只处理支付成功的订单: 修改交易表状态,支付成功 if ("TRADE_FINISHED".equals(tradeStatus) || "TRADE_SUCCESS".equals(tradeStatus)) { //根据订单号查找订单,防止多次回调的问题 UserOrder orderByOrder = orderService.getOrderByOrderNo(outTradeNo); if (orderByOrder != null && orderByOrder.getOrderStatus() == OrderEnum.ORDER_STATUS_NOT_PAY.getStatus()) { //修改订单状态 orderByOrder.setOrderStatus(OrderEnum.ORDER_STATUS_PAID.getStatus()); orderByOrder.setLastUpdateTime(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss")); orderService.updateOrder(orderByOrder); } return "success"; } else { return "failure"; } } else { //验签不通过 System.err.println("-------------------->验签失败"); return "failure"; } } }
2.9 启动类
package com.devin.alipay_demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import tk.mybatis.spring.annotation.MapperScan; @MapperScan("com.devin.alipay_demo.dao") @SpringBootApplication public class AlipayDemoApplication { public static void main(String[] args) { SpringApplication.run(AlipayDemoApplication.class, args); } }
2.10 支付宝的配置 alipay.properties
其中私钥是我们生成的私钥,支付宝公钥可以从开发者沙盒环境拿到 其中notifyUrl 和 returnUrl 我使用natapp ,将内网的映射到外网,以方便做测试
# 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号 appId:2016102400751401 # 商户私钥,您的PKCS8格式RSA2私钥 privateKey:MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDncMvgR0vP05DtLHSIoboaSd7mCzVMzS3VSxl0RdHf/HPFWcqzZqAgtDOPGd9fWcr3M65HcGEY4F69Rn5SKuBHTuq+QJFkq7i2Px/EtbCjBWK1bMyk+fYwYwgFQ7YB4bYp2ssDAf6fDB0sDdylnAZY4Pr3OyRZFLRsbZvLAMpc5pymtTMa3knbIruxsKkj4Fc0QVAkOZCgnV2tD+KucZAoO88K1MBSnZyiO06cFX6vclpv1W7rb+pLs+2x6bxNNRMy4wf/LWrqkZlFnZrxRyF30FoJGaQY4rSJZ5FOHoputO/H0TAKT3hUjLOlauo9awo7oh/VJq55xMy8TQJU2tE/AgMBAAECggEBALRrEPPAgI/9nH/XZOzSLnKp3XI1kJJTbIRWq/whJY/XjCRvb/3UZcW41Gycva3mILv+dMDKEVdEKXahin3hYL8V/Rbc3Lq+xxqDMO+2t4EOBLnrm8iL92gW+zynYS4sV0ZfglzQ5D32QpOCJtyPOb91ew7Z8ubiphfUhluFPTuXK0MCt5n12tBI8kelGQsf3GP0xCEM1um6Ic3tDKOWwQjH0/CvssIZo5dzdc4WBv1Jgh1YconqT6oLOjfitLs6Py9K8jIrFLE1RWzt0/8L5fEyPqquqrmhML5Dce5qztdxeGDWVqVm/KFZJe8k5l8HtUlQYX7SaJ80G2UZEROQJ2kCgYEA9rszfwhF5rFQJNBKlrOl023IuSVhKmt0o8B/hR1d34y88I7cHGJ6a4+MMookpvKIEAOvdf0r/d0Z6MrQ1ptwkimm2Qa0jbFRSiG7GDJk3gVfpaNOvO3KdGWnbeJ4np51i1I80HGt9HeavyxGSeLI144A8CgK66vPkKF1FBX/vTUCgYEA8CKLzBdg26yG9SRRINA+ATaQM1myitggjk8p1dv7erFeYpKzzIWjvKZvH+kiFtAo0HLhTyF7a8V14m6znVU2SJ22GbBauXmFTjjhCbQvqUwgUjM57oH9EJMCxE9brIPV3VHdEtibQFhS36lg47NM6A5tGCEPb+Bt6dXhtLpJhyMCgYAeCQfpzO4FeUxSTvDli5UCOfkXYM+FRHN8g7CCWeLVleJiPmHZKrvQYDcm594yXI/nsysm59z1GHdQ+W+W0HFRubRP8xsDrLRCm/yUo33X8TuFhG3PXfspVD6fh9Q7KvsQLMCud0g/3FeAMjmUQQFGDElc8uLxcYbhCmagPVVWiQKBgHFNO3y+gxrjGoJL8mNzHe5gmkVASzerpiC/RVP8iXloesozwdX8MDdwp/n8e/MboEZKDfjSKXO+JVMDPIg9jnFQyHzycrwUlEtGFxgHBn3wx0dBmFHqz0aktqd9chnB0oSsfYzI2ufPRLr3JhoJnX3YYK0D3E7DK9kq62Xkh5DVAoGBAPW3Ltan5MT9Ry5D6W6ROPoPfl4Sy7C13FjFlujC8LcwsGoIt3+cKntDv7CJuiLoBDf+JtF9v8OSMlhYzy1ozLpVrO7Xn2k1fO76sotlXxsI3cqwnUlkcOuF7TpnHWGVnFGj7/9bPdIOEfnikdDTmxvqUSD9QdvhVtWfFECr2hlw # 支付宝公钥,不是应用公钥 查看地址:https://openhome.com/platform/keyManage.htm 对应APPID下的支付宝公钥。 publicKey:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqOl0nOy/amFKACzBMpO2uvibXOJbehvdMFUMxJMomtYp1RfqvTddsPtGPtX6EX8LFzKB5HC4Ew8rMlh+qvCQctcJfLOsYgK1W8dReLqRxsvMDBtrvDPGVKJwJFmRCbvqWX5b4BOj2fhCP1q0GfS9wsYTP08Xvwq2bDx4OpqdMMi04MPNomIgjgIZmUKeUepsOb0K7rUsgB0OZ6h2RxwKX8BTh2I8AiCU1bvYu6dKpikXz0x72JUg5l69gQCWe6CYHlUSu+gakTz648/GZ6pJXiMzQeemdcytczHrbzA7b3GqtnC5Fj+mxe5E3Upi5MOm82Tl6H3GOyXJ1OuKXdJj1wIDAQAB # 服务器异步通知页面路径需http://格式的完整路径,不能加?id=123这类自定义参数 notifyUrl:http://mf5s4k.natappfree.cc/order/notifyPayResult # 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数 returnUrl:http://mf5s4k.natappfree.cc/order/goPaySuccPage # 签名方式 signType:RSA2 # 字符编码格式 charset:utf-8 # 支付宝网关 gatewayUrl:https://openapi.alipaydev.com/gateway.do # 日志路径 logPath:"d:\\data\\"
2.11 应用设置application.yml
# https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html spring\u914D\u7F6E\u7684\u6587\u6863 server: port: 10086 spring: servlet: multipart: max-request-size: 100MB #最大请求文件的大小 max-file-size: 20MB #设置单个文件最大长度 mvc: view: prefix: / suffix: .html datasource: platform: mysql type: com.alibaba.druid.pool.DruidDataSource initialSize: 3 minIdle: 1 maxActive: 500 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 30000 validationQuery: select 1 testOnBorrow: true poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20 driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/order_db?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8&useAffectedRows=true&rewriteBatchedStatements=true username: root password: root mybatis: # config-location: classpath:mybatis-config.xml mapper-locations: classpath:mapper/*.xml type-aliases-package: com.devin.alpay_demo.entity # configuration: # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #配置分页插件pagehelper pagehelper: helperDialect: mysql reasonable: true supportMethodsArguments: true params: count=countSql
2.12 下单页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>order pay</title> <script type="text/javascript" src="/js/jquery-1.8.2.js"></script> </head> <body> <form action="/order/pay" method="post"> 金额:<input id="orderAmount" name="orderAmount"><br> <input type="submit"> </form> </body> </html>
2.13 支付成功跳转页面
该页面即为上面支付宝配置的returnUrl
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 充值成功! </body> </html>
- 测试
启动程序,访问 http://localhost:10086/order/goPay,输入金额后点击去支付,可以看到订单已经产生,并跳转到支付宝扫码支付页面
下载支付宝沙盒app,登录测试账号,扫码付款后可以看到,可以付款成功,并且回调也调用成功,订单状态变为已支付。
沙盒支付宝下载: https://sandbox.alipaydev.com/user/downloadApp.htm 沙盒测试账号可以在开发者平台上看到
至此,支付宝的对接完成。
完整的代码git地址: https://github.com/devinzhang0209/alipay_demo.git
结语
🔥一个人可以掌握知识,但只有与他人交流才能形成智慧。
🔥One person can acquire knowledge, but wisdom is formed only in the exchange with others.
🏆 我坚信人与人之间的差距是表面上是财富的差距,本质上是大脑中认知的差距,
我们下期再见。