我們在做開發時很多時候會涉及到支付功能,常見會對接支付寶和微信支付,本文將對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