JAVA对接支付宝
我们在做开发时很多时候会涉及到支付功能,常见会对接支付宝和微信支付,本文将对JAVA对接支付宝进行详细的讲解。
在进行开发编码之前我们首选需要去申请开发所需要的支付宝一些配置,即支付宝沙盒环境的申请、
1. 支付宝沙盒环境申请
1.1 注册
登录支付宝开发者网站 https://developers.alipay.com/ ,使用个人的支付宝扫码登录后,填写个人信息并提交
1.2 沙盒环境设置
按如下图所示进入沙盒环境设置页面
打开页面 https://opendocs.alipay.com/open/291/105971 下载公私钥生成工具,安装后生成应用的公钥和私钥
生成公私钥如下图
将公私钥保存好,后面程序中要用到,同时将公钥复制到沙盒环境的设置中,如下图
保存会生成一个支付宝的公钥,这个公钥用于后续的支付宝支付后的回调验签使用
至此。沙盒环境设置完毕。
2. 开发部分
我们模拟一个简单的订单支付功能,只有一个简单的金额字段
2.1 数据库表结构
1 CREATE TABLE `user_order_t` ( 2 `order_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单Id', 3 `user_id` varchar(64) DEFAULT NULL COMMENT '用户Id', 4 `order_no` varchar(64) NOT NULL COMMENT '订单号', 5 `order_amount` decimal(16,4) NOT NULL COMMENT '订单金额', 6 `order_status` int(11) NOT NULL COMMENT '0 待付款 1 已付款 -1 已取消', 7 `create_time` varchar(32) NOT NULL COMMENT '创建时间', 8 `last_update_time` varchar(32) NOT NULL COMMENT '最后修改时间', 9 PRIMARY KEY (`order_id`) 10 ) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4;
2.2 项目结构
新建一个springboot项目,结构如图
2.3 pom依赖
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>org.springframework.boot</groupId> 7 <artifactId>spring-boot-starter-parent</artifactId> 8 <version>2.2.5.RELEASE</version> 9 <relativePath/> <!-- lookup parent from repository --> 10 </parent> 11 <groupId>com.devin</groupId> 12 <artifactId>alipay_demo</artifactId> 13 <version>0.0.1-SNAPSHOT</version> 14 <name>alipay_demo</name> 15 <description>Demo project for Spring Boot</description> 16 17 <properties> 18 <java.version>1.8</java.version> 19 </properties> 20 21 <dependencies> 22 23 <dependency> 24 <groupId>org.springframework.boot</groupId> 25 <artifactId>spring-boot-starter-web</artifactId> 26 </dependency> 27 28 29 <dependency> 30 <groupId>tk.mybatis</groupId> 31 <artifactId>mapper-spring-boot-starter</artifactId> 32 <version>2.0.4</version> 33 </dependency> 34 35 <dependency> 36 <groupId>org.springframework.boot</groupId> 37 <artifactId>spring-boot-starter-jdbc</artifactId> 38 <version>2.0.0.RELEASE</version> 39 </dependency> 40 41 <dependency> 42 <groupId>org.mybatis.spring.boot</groupId> 43 <artifactId>mybatis-spring-boot-starter</artifactId> 44 <version>2.0.1</version> 45 </dependency> 46 47 <dependency> 48 <groupId>mysql</groupId> 49 <artifactId>mysql-connector-java</artifactId> 50 <version>5.1.17</version> 51 </dependency> 52 53 <dependency> 54 <groupId>com.alibaba</groupId> 55 <artifactId>druid</artifactId> 56 <version>1.1.1</version> 57 </dependency> 58 59 <dependency> 60 <groupId>org.projectlombok</groupId> 61 <artifactId>lombok</artifactId> 62 <version>1.16.22</version> 63 </dependency> 64 65 66 <dependency> 67 <groupId>org.springframework.boot</groupId> 68 <artifactId>spring-boot-starter-amqp</artifactId> 69 <version>2.1.8.RELEASE</version> 70 </dependency> 71 72 <dependency> 73 <groupId>org.springframework.boot</groupId> 74 <artifactId>spring-boot-test</artifactId> 75 <version>2.2.6.RELEASE</version> 76 </dependency> 77 <dependency> 78 <groupId>junit</groupId> 79 <artifactId>junit</artifactId> 80 <version>4.12</version> 81 </dependency> 82 <dependency> 83 <groupId>org.springframework</groupId> 84 <artifactId>spring-test</artifactId> 85 <version>5.2.5.RELEASE</version> 86 </dependency> 87 88 89 <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> 90 <dependency> 91 <groupId>com.alibaba</groupId> 92 <artifactId>fastjson</artifactId> 93 <version>1.2.57</version> 94 </dependency> 95 96 97 <dependency> 98 <groupId>org.apache.commons</groupId> 99 <artifactId>commons-lang3</artifactId> 100 <version>3.5</version> 101 </dependency> 102 103 <dependency> 104 <groupId>commons-codec</groupId> 105 <artifactId>commons-codec</artifactId> 106 <version>1.10</version> 107 </dependency> 108 <dependency> 109 <groupId>com.alipay.sdk</groupId> 110 <artifactId>alipay-sdk-java</artifactId> 111 <version>3.4.27.ALL</version> 112 </dependency> 113 114 </dependencies> 115 116 <build> 117 <plugins> 118 <plugin> 119 <groupId>org.springframework.boot</groupId> 120 <artifactId>spring-boot-maven-plugin</artifactId> 121 </plugin> 122 </plugins> 123 </build> 124 125 <repositories> 126 <repository> 127 <id>maven-ali</id> 128 <url>http://maven.aliyun.com/nexus/content/groups/public//</url> 129 <releases> 130 <enabled>true</enabled> 131 </releases> 132 <snapshots> 133 <enabled>true</enabled> 134 <updatePolicy>always</updatePolicy> 135 <checksumPolicy>fail</checksumPolicy> 136 </snapshots> 137 </repository> 138 </repositories> 139 140 </project>
2.4 模型的定义
2.4.1 订单model
1 package com.devin.alipay_demo.entity; 2 3 import javax.persistence.GeneratedValue; 4 import javax.persistence.GenerationType; 5 import javax.persistence.Id; 6 import javax.persistence.Table; 7 import java.math.BigDecimal; 8 9 /** 10 * @author Devin Zhang 11 * @className UserOrder 12 * @description TODO 13 * @date 2020/5/12 10:49 14 */ 15 @Table(name = "user_order_t") 16 public class UserOrder { 17 18 @Id 19 @GeneratedValue(strategy= GenerationType.IDENTITY) 20 private Integer orderId; 21 private String orderNo; 22 private String userId; 23 private BigDecimal orderAmount; 24 private Integer orderStatus; 25 private String createTime; 26 private String lastUpdateTime; 27 28 public Integer getOrderId() { 29 return orderId; 30 } 31 32 public void setOrderId(Integer orderId) { 33 this.orderId = orderId; 34 } 35 36 public String getOrderNo() { 37 return orderNo; 38 } 39 40 public void setOrderNo(String orderNo) { 41 this.orderNo = orderNo; 42 } 43 44 public String getUserId() { 45 return userId; 46 } 47 48 public void setUserId(String userId) { 49 this.userId = userId; 50 } 51 52 public BigDecimal getOrderAmount() { 53 return orderAmount; 54 } 55 56 public void setOrderAmount(BigDecimal orderAmount) { 57 this.orderAmount = orderAmount; 58 } 59 60 public Integer getOrderStatus() { 61 return orderStatus; 62 } 63 64 public void setOrderStatus(Integer orderStatus) { 65 this.orderStatus = orderStatus; 66 } 67 68 public String getCreateTime() { 69 return createTime; 70 } 71 72 public void setCreateTime(String createTime) { 73 this.createTime = createTime; 74 } 75 76 public String getLastUpdateTime() { 77 return lastUpdateTime; 78 } 79 80 public void setLastUpdateTime(String lastUpdateTime) { 81 this.lastUpdateTime = lastUpdateTime; 82 } 83 }
2.4.2 支付宝调用参数封装model
1 package com.devin.alipay_demo.dto; 2 3 /** 4 * @author Devin Zhang 5 * @className AlipayBean 6 * @description TODO 7 * @date 2020/5/12 11:33 8 */ 9 10 public class AlipayBean { 11 /** 12 * 商户订单号,必填 13 */ 14 private String out_trade_no; 15 /** 16 * 订单名称,必填 17 */ 18 private String subject; 19 /** 20 * 付款金额,必填 21 * 根据支付宝接口协议,必须使用下划线 22 */ 23 private String total_amount; 24 /** 25 * 商品描述,可空 26 */ 27 private String body; 28 /** 29 * 超时时间参数 30 */ 31 private String timeout_express = "10m"; 32 /** 33 * 产品编号 34 */ 35 private String product_code = "FAST_INSTANT_TRADE_PAY"; 36 37 public String getOut_trade_no() { 38 return out_trade_no; 39 } 40 41 public void setOut_trade_no(String out_trade_no) { 42 this.out_trade_no = out_trade_no; 43 } 44 45 public String getSubject() { 46 return subject; 47 } 48 49 public void setSubject(String subject) { 50 this.subject = subject; 51 } 52 53 public String getTotal_amount() { 54 return total_amount; 55 } 56 57 public void setTotal_amount(String total_amount) { 58 this.total_amount = total_amount; 59 } 60 61 public String getBody() { 62 return body; 63 } 64 65 public void setBody(String body) { 66 this.body = body; 67 } 68 69 public String getTimeout_express() { 70 return timeout_express; 71 } 72 73 public void setTimeout_express(String timeout_express) { 74 this.timeout_express = timeout_express; 75 } 76 77 public String getProduct_code() { 78 return product_code; 79 } 80 81 public void setProduct_code(String product_code) { 82 this.product_code = product_code; 83 } 84 }
2.5 工具类
2.5.1 支付宝参数读取
package com.devin.alipay_demo.util; import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * @author Devin Zhang * @className AlipayProperties * @description 读取支付宝配置 * @date 2020/5/12 11:16 */ @Component public class AlipayProperties { private static final String APP_ID = "appId"; private static final String PRIVATE_KEY = "privateKey"; private static final String PUBLIC_KEY = "publicKey"; private static final String NOTIFY_URL = "notifyUrl"; private static final String RETURN_URL = "returnUrl"; private static final String SIGN_TYPE = "signType"; private static final String CHARSET = "charset"; private static final String GATEWAY_URL = "gatewayUrl"; private static final String LOG_PATH = "logPath"; /** * 保存加载配置参数 */ private static Map<String, String> propertiesMap = new HashMap<>(); /** * 加载属性 */ @PostConstruct public void loadProperties() throws Exception { // 获得PathMatchingResourcePatternResolver对象 PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { // 加载resource文件(也可以加载resources) Resource resources = resolver.getResource("classpath:config/alipay.properties"); PropertiesFactoryBean config = new PropertiesFactoryBean(); config.setLocation(resources); config.afterPropertiesSet(); Properties prop = config.getObject(); // 循环遍历所有得键值对并且存入集合 for (String key : prop.stringPropertyNames()) { propertiesMap.put(key, (String) prop.get(key)); } } catch (Exception e) { throw new Exception("配置文件加载失败"); } } public String getAppId() { return propertiesMap.get(APP_ID); } public String getPrivateKey() { return propertiesMap.get(PRIVATE_KEY); } public String getPublicKey() { return propertiesMap.get(PUBLIC_KEY); } public String getNotifyUrl() { return propertiesMap.get(NOTIFY_URL); } public String getReturnUrl() { return propertiesMap.get(RETURN_URL); } public String getSignType() { return propertiesMap.get(SIGN_TYPE); } public String getCharset() { return propertiesMap.get(CHARSET); } public String getGatewayUrl() { return propertiesMap.get(GATEWAY_URL); } public String getLogPath() { return propertiesMap.get(LOG_PATH); } }
2.5.2 支付宝工具类
1 package com.devin.alipay_demo.util; 2 3 import com.alibaba.fastjson.JSON; 4 import com.alipay.api.AlipayApiException; 5 import com.alipay.api.AlipayClient; 6 import com.alipay.api.DefaultAlipayClient; 7 import com.alipay.api.request.AlipayTradePagePayRequest; 8 import com.devin.alipay_demo.dto.AlipayBean; 9 import org.springframework.stereotype.Component; 10 11 import javax.annotation.Resource; 12 13 /** 14 * @author Devin Zhang 15 * @className AliPayUtil 16 * @description TODO 17 * @date 2020/5/12 10:47 18 */ 19 20 @Component 21 public class AliPayUtil { 22 23 @Resource 24 private AlipayProperties alipayProperties; 25 /** 26 * 支付接口 27 * 28 * @param alipayBean 封装的支付宝入参 29 * @return 返回支付结果 30 * @throws AlipayApiException 抛出异常 31 */ 32 public String pay(AlipayBean alipayBean) throws AlipayApiException { 33 // 1、获得初始化的AlipayClient 34 String serverUrl = alipayProperties.getGatewayUrl(); 35 String appId = alipayProperties.getAppId(); 36 String privateKey = alipayProperties.getPrivateKey(); 37 String format = "json"; 38 String charset = alipayProperties.getCharset(); 39 String alipayPublicKey = alipayProperties.getPublicKey(); 40 String signType = alipayProperties.getSignType(); 41 String returnUrl = alipayProperties.getReturnUrl(); 42 String notifyUrl = alipayProperties.getNotifyUrl(); 43 AlipayClient alipayClient = new DefaultAlipayClient(serverUrl, appId, privateKey, format, charset, alipayPublicKey, signType); 44 // 2、设置请求参数 45 AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); 46 // 页面跳转同步通知页面路径 47 alipayRequest.setReturnUrl(returnUrl); 48 // 服务器异步通知页面路径 49 alipayRequest.setNotifyUrl(notifyUrl); 50 // 封装参数 51 alipayRequest.setBizContent(JSON.toJSONString(alipayBean)); 52 // 3、请求支付宝进行付款,并获取支付结果 53 return alipayClient.pageExecute(alipayRequest).getBody(); 54 } 55 56 }
2.5.3 订单状态枚举
1 package com.devin.alipay_demo.util; 2 3 /** 4 * @author Devin Zhang 5 * @className OrderEnum 6 * @description TODO 7 * @date 2020/5/12 11:43 8 */ 9 10 public enum OrderEnum { 11 ORDER_STATUS_PAID(1, "已支付"), 12 ORDER_STATUS_NOT_PAY(0, "待支付"), 13 14 ORDER_STATUS_CANCEL(2, "已取消"); 15 16 17 private int status; 18 private String statusName; 19 20 OrderEnum(int status, String statusName) { 21 this.status = status; 22 this.statusName = statusName; 23 } 24 25 public int getStatus(){ 26 return this.status; 27 } 28 29 30 }
2.6 dao
2.6.1 订单操作dao
因为使用了通用mapper tkmybatis,所以dao只是实现了相应的父类
1 package com.devin.alipay_demo.dao; 2 3 import com.devin.alipay_demo.entity.UserOrder; 4 import tk.mybatis.mapper.common.Mapper; 5 import tk.mybatis.mapper.common.MySqlMapper; 6 7 /** 8 * @author Devin Zhang 9 * @className UserOrderMapper 10 * @description TODO 11 * @date 2020/5/12 10:54 12 */ 13 14 public interface UserOrderMapper extends Mapper<UserOrder>, MySqlMapper<UserOrder> { 15 }
2.6.2 对应的mapper xml文件
同样,xml文件中内容也为空
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > 3 <mapper namespace="com.devin.alipay_demo.dao.UserOrderMapper" > 4 5 </mapper>
2.7 service
service中定义的下单等方法,其中下单时回去调用支付宝
1 package com.devin.alipay_demo.service; 2 3 import com.alipay.api.AlipayApiException; 4 import com.devin.alipay_demo.dao.UserOrderMapper; 5 import com.devin.alipay_demo.dto.AlipayBean; 6 import com.devin.alipay_demo.entity.UserOrder; 7 import com.devin.alipay_demo.util.AliPayUtil; 8 import com.devin.alipay_demo.util.OrderEnum; 9 import org.apache.commons.lang3.time.DateFormatUtils; 10 import org.springframework.stereotype.Service; 11 import tk.mybatis.mapper.entity.Example; 12 13 import javax.annotation.Resource; 14 import java.math.BigDecimal; 15 import java.util.Date; 16 import java.util.UUID; 17 18 /** 19 * @author Devin Zhang 20 * @className OrderService 21 * @description TODO 22 * @date 2020/5/12 10:53 23 */ 24 25 @Service 26 public class OrderService { 27 28 29 @Resource 30 private UserOrderMapper userOrderMapper; 31 32 @Resource 33 private AliPayUtil aliPayUtil; 34 35 /** 36 * 下单 37 * 38 * @param orderAmount 订单金额 39 * @return 返回支付结果页面内容 40 * @throws AlipayApiException 41 */ 42 public String orderPay(BigDecimal orderAmount) throws AlipayApiException { 43 44 //1. 产生订单 45 UserOrder order = new UserOrder(); 46 order.setOrderNo(System.currentTimeMillis() + ""); 47 order.setUserId(UUID.randomUUID().toString()); 48 order.setOrderAmount(orderAmount); 49 order.setOrderStatus(OrderEnum.ORDER_STATUS_NOT_PAY.getStatus()); 50 String format = "yyyy-MM-dd HH:mm:ss"; 51 order.setCreateTime(DateFormatUtils.format(new Date(), format)); 52 order.setLastUpdateTime(DateFormatUtils.format(new Date(), format)); 53 54 userOrderMapper.insert(order); 55 56 //2. 调用支付宝 57 AlipayBean alipayBean = new AlipayBean(); 58 alipayBean.setOut_trade_no(order.getOrderNo()); 59 alipayBean.setSubject("充值:" + order.getOrderAmount()); 60 alipayBean.setTotal_amount(orderAmount.toString()); 61 String pay = aliPayUtil.pay(alipayBean); 62 System.out.println("pay:" + pay); 63 return pay; 64 } 65 66 /** 67 * 根据订单号查询订单 68 * 69 * @param orderNo 70 * @return 返回订单信息 71 */ 72 public UserOrder getOrderByOrderNo(String orderNo) { 73 Example example = new Example(UserOrder.class); 74 Example.Criteria criteria = example.createCriteria(); 75 criteria.andEqualTo("orderNo", orderNo); 76 return userOrderMapper.selectOneByExample(example); 77 } 78 79 /** 80 * 更新订单 81 * 82 * @param userOrder 订单对象 83 * @return 返回更新结果 84 */ 85 public int updateOrder(UserOrder userOrder) { 86 return userOrderMapper.updateByPrimaryKeySelective(userOrder); 87 } 88 }
2.8 Controller
1 package com.devin.alipay_demo.controller; 2 3 import com.alipay.api.AlipayApiException; 4 import com.alipay.api.internal.util.AlipaySignature; 5 import com.devin.alipay_demo.entity.UserOrder; 6 import com.devin.alipay_demo.service.OrderService; 7 import com.devin.alipay_demo.util.AlipayProperties; 8 import com.devin.alipay_demo.util.OrderEnum; 9 import org.apache.commons.lang3.time.DateFormatUtils; 10 import org.springframework.stereotype.Controller; 11 import org.springframework.web.bind.annotation.PostMapping; 12 import org.springframework.web.bind.annotation.RequestMapping; 13 import org.springframework.web.bind.annotation.ResponseBody; 14 15 import javax.annotation.Resource; 16 import javax.servlet.http.HttpServletRequest; 17 import javax.servlet.http.HttpServletResponse; 18 import java.math.BigDecimal; 19 import java.util.Date; 20 import java.util.HashMap; 21 import java.util.Map; 22 23 /** 24 * @author Devin Zhang 25 * @className OrderController 26 * @description TODO 27 * @date 2020/5/12 10:53 28 */ 29 30 @Controller 31 @RequestMapping("/order") 32 public class OrderController { 33 34 @Resource 35 private OrderService orderService; 36 @Resource 37 private AlipayProperties alipayProperties; 38 39 /** 40 * 跳转到下单页面 41 * 42 * @return 43 */ 44 @RequestMapping("/goPay") 45 public String goPay() { 46 return "pay"; 47 } 48 49 /** 50 * 下单,并调用支付宝 51 * 52 * @param orderAmount 53 * @return 54 * @throws AlipayApiException 55 */ 56 @PostMapping("/pay") 57 public void pay(BigDecimal orderAmount, HttpServletResponse httpResponse) throws Exception { 58 String payResult = orderService.orderPay(orderAmount); 59 httpResponse.setContentType("text/html;charset=" + alipayProperties.getCharset()); 60 httpResponse.getWriter().write(payResult); 61 httpResponse.getWriter().flush(); 62 httpResponse.getWriter().close(); 63 } 64 65 66 /** 67 * 支付成功的跳转页面 68 * 69 * @return 70 */ 71 @RequestMapping("/goPaySuccPage") 72 public String goPaySuccPage() { 73 return "pay_succ"; 74 } 75 76 /** 77 * 支付成功的回调接口 78 * 79 * @return 80 */ 81 @ResponseBody 82 @RequestMapping("/notifyPayResult") 83 public String notifyPayResult(HttpServletRequest request) { 84 System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<进入支付宝回调->>>>>>>>>>>>>>>>>>>>>>>>>"); 85 // 1.从支付宝回调的request域中取值放到map中 86 Map<String, String[]> requestParams = request.getParameterMap(); 87 88 Map<String, String> params = new HashMap(); 89 for (String name : requestParams.keySet()) { 90 String[] values = requestParams.get(name); 91 String valueStr = ""; 92 for (int i = 0; i < values.length; i++) { 93 valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; 94 } 95 params.put(name, valueStr); 96 } 97 //2.封装必须参数 98 // 商户订单号 99 String outTradeNo = params.get("out_trade_no"); 100 //交易状态 101 String tradeStatus = params.get("trade_status"); 102 103 System.out.println("outTradeNo:" + outTradeNo + " tradeStatus:" + tradeStatus); 104 105 //3.签名验证(对支付宝返回的数据验证,确定是支付宝返回的) 106 boolean signVerified = false; 107 try { 108 //3.1调用SDK验证签名 109 signVerified = AlipaySignature.rsaCheckV1(params, alipayProperties.getPublicKey(), alipayProperties.getCharset(), alipayProperties.getSignType()); 110 111 } catch (Exception e) { 112 e.printStackTrace(); 113 } 114 System.out.println("--------------->验签结果:" + signVerified); 115 116 //4.对验签进行处理 117 118 if (signVerified) { 119 //验签通过 120 //只处理支付成功的订单: 修改交易表状态,支付成功 121 if ("TRADE_FINISHED".equals(tradeStatus) || "TRADE_SUCCESS".equals(tradeStatus)) { 122 //根据订单号查找订单,防止多次回调的问题 123 UserOrder orderByOrder = orderService.getOrderByOrderNo(outTradeNo); 124 if (orderByOrder != null && orderByOrder.getOrderStatus() == OrderEnum.ORDER_STATUS_NOT_PAY.getStatus()) { 125 //修改订单状态 126 orderByOrder.setOrderStatus(OrderEnum.ORDER_STATUS_PAID.getStatus()); 127 orderByOrder.setLastUpdateTime(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss")); 128 orderService.updateOrder(orderByOrder); 129 } 130 return "success"; 131 } else { 132 return "failure"; 133 } 134 } else { 135 //验签不通过 136 System.err.println("-------------------->验签失败"); 137 return "failure"; 138 } 139 } 140 141 142 }
2.9 启动类
1 package com.devin.alipay_demo; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import tk.mybatis.spring.annotation.MapperScan; 6 7 @MapperScan("com.devin.alipay_demo.dao") 8 @SpringBootApplication 9 public class AlipayDemoApplication { 10 11 public static void main(String[] args) { 12 SpringApplication.run(AlipayDemoApplication.class, args); 13 } 14 15 }
2.10 支付宝的配置 alipay.properties
其中私钥是我们生成的私钥,支付宝公钥可以从开发者沙盒环境拿到
其中notifyUrl 和 returnUrl 我使用natapp ,将内网的映射到外网,以方便做测试
1 # 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号 2 appId:2016102400751401 3 # 商户私钥,您的PKCS8格式RSA2私钥 4 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 5 # 支付宝公钥,不是应用公钥 查看地址:https://openhome.com/platform/keyManage.htm 对应APPID下的支付宝公钥。 6 publicKey:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqOl0nOy/amFKACzBMpO2uvibXOJbehvdMFUMxJMomtYp1RfqvTddsPtGPtX6EX8LFzKB5HC4Ew8rMlh+qvCQctcJfLOsYgK1W8dReLqRxsvMDBtrvDPGVKJwJFmRCbvqWX5b4BOj2fhCP1q0GfS9wsYTP08Xvwq2bDx4OpqdMMi04MPNomIgjgIZmUKeUepsOb0K7rUsgB0OZ6h2RxwKX8BTh2I8AiCU1bvYu6dKpikXz0x72JUg5l69gQCWe6CYHlUSu+gakTz648/GZ6pJXiMzQeemdcytczHrbzA7b3GqtnC5Fj+mxe5E3Upi5MOm82Tl6H3GOyXJ1OuKXdJj1wIDAQAB 7 # 服务器异步通知页面路径需http://格式的完整路径,不能加?id=123这类自定义参数 8 notifyUrl:http://mf5s4k.natappfree.cc/order/notifyPayResult 9 # 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数 10 returnUrl:http://mf5s4k.natappfree.cc/order/goPaySuccPage 11 # 签名方式 12 signType:RSA2 13 # 字符编码格式 14 charset:utf-8 15 # 支付宝网关 16 gatewayUrl:https://openapi.alipaydev.com/gateway.do 17 # 日志路径 18 logPath:"d:\\data\\"
2.11 应用设置application.yml
1 # https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html spring\u914D\u7F6E\u7684\u6587\u6863 2 server: 3 port: 10086 4 5 spring: 6 servlet: 7 multipart: 8 max-request-size: 100MB #最大请求文件的大小 9 max-file-size: 20MB #设置单个文件最大长度 10 mvc: 11 view: 12 prefix: / 13 suffix: .html 14 datasource: 15 platform: mysql 16 type: com.alibaba.druid.pool.DruidDataSource 17 initialSize: 3 18 minIdle: 1 19 maxActive: 500 20 maxWait: 60000 21 timeBetweenEvictionRunsMillis: 60000 22 minEvictableIdleTimeMillis: 30000 23 validationQuery: select 1 24 testOnBorrow: true 25 poolPreparedStatements: true 26 maxPoolPreparedStatementPerConnectionSize: 20 27 driverClassName: com.mysql.jdbc.Driver 28 29 url: jdbc:mysql://localhost:3306/order_db?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8&useAffectedRows=true&rewriteBatchedStatements=true 30 31 username: root 32 password: root 33 mybatis: 34 # config-location: classpath:mybatis-config.xml 35 mapper-locations: classpath:mapper/*.xml 36 type-aliases-package: com.devin.alpay_demo.entity 37 # configuration: 38 # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 39 #配置分页插件pagehelper 40 pagehelper: 41 helperDialect: mysql 42 reasonable: true 43 supportMethodsArguments: true 44 params: count=countSql
2.12 下单页面
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>order pay</title> 6 <script type="text/javascript" src="/js/jquery-1.8.2.js"></script> 7 </head> 8 <body> 9 <form action="/order/pay" method="post"> 10 金额:<input id="orderAmount" name="orderAmount"><br> 11 <input type="submit"> 12 </form> 13 </body> 14 </html>
2.13 支付成功跳转页面
该页面即为上面支付宝配置的returnUrl
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 充值成功! </body> </html>
3. 测试
启动程序,访问 http://localhost:10086/order/goPay,输入金额后点击去支付,可以看到订单已经产生,并跳转到支付宝扫码支付页面
此时,我们可以去数据库里面看到,订单已经产生,并且此时订单的状态是待付款
下载支付宝沙盒app,登录测试账号,扫码付款后可以看到,可以付款成功,并且回调也调用成功,订单状态变为已支付。
沙盒支付宝下载: https://sandbox.alipaydev.com/user/downloadApp.htm
沙盒测试账号可以在开发者平台上看到
支付后结果
至此,支付宝的对接完成。
完整的代码git地址: https://github.com/devinzhang0209/alipay_demo.git