支付宝支付整合到spring+springmvc+mybatis环境

支付宝支付整合到spring+springmvc+mybatis环境

十月 19, 2018 本文总阅读量

ssm-alipay

阿里支付宝支付,包括:阿里沙箱环境支付宝测试demo,支付宝支付整合到spring+springmvc+mybatis环境,功能非常齐全,只需要修改对应的配置文件即可,帮助文档齐全!
源码地址

环境准备

一、支付宝测试环境代码测试

1.下载电脑网站的官方demo:

下载地址:

2.下载解压导入eclipse

3.修改相关配置

1.进入支付宝沙箱环境:

https://openhome.alipay.com/platform/appDaily.htm

2.下载对应版本生成RSA密钥工具:

https://docs.open.alipay.com/291/105971

3.打开工具生成相关的公钥和私钥:

4. 将对应的公钥和私钥填入demo的相关配置文件:

5. 将对应的公钥填入沙箱环境生成相关支付宝公钥以及其他配置填入配置文件:

4.运行demo的main方法可以发现支付宝预下单成功:satisfied::satisfied::satisfied::satisfied::satisfied:

1.日志打印出的qr_code就是我们需要的二维码,用相关的二维码生成工具可以生成相对应的二维码

2.下载相对应的android版本的沙箱支付宝app扫描二维码可以进行支付:satisfied::satisfied::satisfied::satisfied::satisfied:

对应商家和买家账号 https://openhome.alipay.com/platform/appDaily.htm?tab=account
沙箱支付宝app https://openhome.alipay.com/platform/appDaily.htm?tab=tool

3.spring+springmvc+mybatis整合支付宝

1.建立相关数据表

用户表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DROP TABLE IF EXISTS `mmall_user`;
CREATE TABLE `mmall_user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户表id',
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(50) NOT NULL COMMENT '用户密码,MD5加密',
`email` varchar(50) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`question` varchar(100) DEFAULT NULL COMMENT '找回密码问题',
`answer` varchar(100) DEFAULT NULL COMMENT '找回密码答案',
`role` int(4) NOT NULL COMMENT '角色0-管理员,1-普通用户',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '最后一次更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `user_name_unique` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;

订单表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
DROP TABLE IF EXISTS `mmall_order`;
CREATE TABLE `mmall_order` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单id',
`order_no` bigint(20) DEFAULT NULL COMMENT '订单号',
`user_id` int(11) DEFAULT NULL COMMENT '用户id',
`shipping_id` int(11) DEFAULT NULL,
`payment` decimal(20,2) DEFAULT NULL COMMENT '实际付款金额,单位是元,保留两位小数',
`payment_type` int(4) DEFAULT NULL COMMENT '支付类型,1-在线支付',
`postage` int(10) DEFAULT NULL COMMENT '运费,单位是元',
`status` int(10) DEFAULT NULL COMMENT '订单状态:0-已取消-10-未付款,20-已付款,40-已发货,50-交易成功,60-交易关闭',
`payment_time` datetime DEFAULT NULL COMMENT '支付时间',
`send_time` datetime DEFAULT NULL COMMENT '发货时间',
`end_time` datetime DEFAULT NULL COMMENT '交易完成时间',
`close_time` datetime DEFAULT NULL COMMENT '交易关闭时间',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `order_no_index` (`order_no`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=118 DEFAULT CHARSET=utf8;

支付状态表

1
2
3
4
5
6
7
8
9
10
11
12
13
DROP TABLE IF EXISTS `mmall_pay_info`;
CREATE TABLE `mmall_pay_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL COMMENT '用户id',
`order_no` bigint(20) DEFAULT NULL COMMENT '订单号',
`pay_platform` int(10) DEFAULT NULL COMMENT '支付平台:1-支付宝,2-微信',
`platform_number` varchar(200) DEFAULT NULL COMMENT '支付宝支付流水号',
`platform_status` varchar(20) DEFAULT NULL COMMENT '支付宝支付状态',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=61 DEFAULT CHARSET=utf8;

2.将支付宝sdk相关jar包拷贝到lib目录下、将支付宝相关java文件以及配置文件都拷贝进项目。

)

3.编写相关controller和service

controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
* 支付pay
* @param session
* @param orderNo
* @param request
* @return
*/
@RequestMapping("pay.do")
@ResponseBody
public ServerResponse pay(HttpSession session, Long orderNo, HttpServletRequest request){
User user = (User)session.getAttribute(Const.CURRENT_USER);
if(user ==null){
return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),ResponseCode.NEED_LOGIN.getDesc());
}
String path = request.getSession().getServletContext().getRealPath("upload");
logger.info("打印一下路径"+path);
return iOrderService.pay(orderNo,user.getId(),path);
}
@RequestMapping("alipay_callback.do")
@ResponseBody
public Object alipayCallback(HttpServletRequest request){
Map<String,String> params = Maps.newHashMap();
Map requestParams = request.getParameterMap();
for(Iterator iter = requestParams.keySet().iterator();iter.hasNext();){
String name = (String)iter.next();
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]+",";
}
params.put(name,valueStr);
}
logger.info("支付宝回调,sign:{},trade_status:{},参数:{}",params.get("sign"),params.get("trade_status"),params.toString());
//非常重要,验证回调的正确性,是不是支付宝发的.并且呢还要避免重复通知.
params.remove("sign_type");
try {
boolean alipayRSACheckedV2 = AlipaySignature.rsaCheckV2(params, Configs.getAlipayPublicKey(),"utf-8",Configs.getSignType());
if(!alipayRSACheckedV2){
return ServerResponse.createByErrorMessage("非法请求,验证不通过,再恶意请求我就报警找网警了");
}
} catch (AlipayApiException e) {
logger.error("支付宝验证回调异常",e);
}
//todo 验证各种数据
//
ServerResponse serverResponse = iOrderService.aliCallback(params);
if(serverResponse.isSuccess()){
return Const.AlipayCallback.RESPONSE_SUCCESS;
}
return Const.AlipayCallback.RESPONSE_FAILED;
}
/**
* 轮询 查询 支付状态
* @param session
* @param orderNo
* @return
*/
@RequestMapping("query_order_pay_status.do")
@ResponseBody
public ServerResponse<Boolean> queryOrderPayStatus(HttpSession session, Long orderNo){
User user = (User)session.getAttribute(Const.CURRENT_USER);
if(user ==null){
return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),ResponseCode.NEED_LOGIN.getDesc());
}
ServerResponse serverResponse = iOrderService.queryOrderPayStatus(user.getId(),orderNo);
if(serverResponse.isSuccess()){
return ServerResponse.createBySuccess(true);
}
return ServerResponse.createBySuccess(false);
}

service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
public ServerResponse pay(Long orderNo,Integer userId,String path){
Map<String ,String> resultMap = Maps.newHashMap();
Order order = orderMapper.selectByUserIdAndOrderNo(userId,orderNo);
if(order == null){
return ServerResponse.createByErrorMessage("用户没有该订单");
}
resultMap.put("orderNo",String.valueOf(order.getOrderNo()));
// (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线,
// 需保证商户系统端不能重复,建议通过数据库sequence生成,
String outTradeNo = order.getOrderNo().toString();
// (必填) 订单标题,粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费”
String subject = new StringBuilder().append("happymmall扫码支付,订单号:").append(outTradeNo).toString();
// (必填) 订单总金额,单位为元,不能超过1亿元
// 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
String totalAmount = order.getPayment().toString();
// (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段
// 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】
String undiscountableAmount = "0";
// 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)
// 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID
String sellerId = "";
// 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"
String body = new StringBuilder().append("订单").append(outTradeNo).append("购买商品共").append(totalAmount).append("元").toString();
// 商户操作员编号,添加此参数可以为商户操作员做销售统计
String operatorId = "test_operator_id";
// (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持
String storeId = "test_store_id";
// 业务扩展参数,目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法),详情请咨询支付宝技术支持
ExtendParams extendParams = new ExtendParams();
extendParams.setSysServiceProviderId("2088100200300400500");
// 支付超时,定义为120分钟
String timeoutExpress = "120m";
// 商品明细列表,需填写购买商品详细信息,
List<GoodsDetail> goodsDetailList = new ArrayList<GoodsDetail>();
List<OrderItem> orderItemList = orderItemMapper.getByOrderNoUserId(orderNo,userId);
for(OrderItem orderItem : orderItemList){
GoodsDetail goods = GoodsDetail.newInstance(orderItem.getProductId().toString(), orderItem.getProductName(),
BigDecimalUtil.mul(orderItem.getCurrentUnitPrice().doubleValue(),new Double(100).doubleValue()).longValue(),
orderItem.getQuantity());
goodsDetailList.add(goods);
}
// 创建扫码支付请求builder,设置请求参数
AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder()
.setSubject(subject).setTotalAmount(totalAmount).setOutTradeNo(outTradeNo)
.setUndiscountableAmount(undiscountableAmount).setSellerId(sellerId).setBody(body)
.setOperatorId(operatorId).setStoreId(storeId).setExtendParams(extendParams)
.setTimeoutExpress(timeoutExpress)
.setNotifyUrl(PropertiesUtil.getProperty("alipay.callback.url"))//支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置
.setGoodsDetailList(goodsDetailList);
AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);
switch (result.getTradeStatus()) {
case SUCCESS:
logger.info("支付宝预下单成功: )");
AlipayTradePrecreateResponse response = result.getResponse();
dumpResponse(response);
/* 上传至ftp服务器
File folder = new File(path);
if(!folder.exists()){
folder.setWritable(true);
folder.mkdirs();
}
// 需要修改为运行机器上的路径
//细节细节细节
String qrPath = String.format(path+"/qr-%s.png",response.getOutTradeNo());
String qrFileName = String.format("qr-%s.png",response.getOutTradeNo());
ZxingUtils.getQRCodeImge(response.getQrCode(), 256, qrPath);
File targetFile = new File(path,qrFileName);
try {
FTPUtil.uploadFile(Lists.newArrayList(targetFile));
} catch (IOException e) {
logger.error("上传二维码异常",e);
}
logger.info("qrPath:" + qrPath);
String qrUrl = PropertiesUtil.getProperty("ftp.server.http.prefix")+targetFile.getName();
resultMap.put("qrUrl",qrUrl);
return ServerResponse.createBySuccess(resultMap);
*/
// 需要修改为运行机器上的路径 上传到本地
File folder = new File(path);
if(!folder.exists()){
folder.setWritable(true);
folder.mkdirs();
}
String qrPath = String.format(path+"qr-%s.png",response.getOutTradeNo());
String qrFileName = String.format("qr-%s.png",response.getOutTradeNo());
ZxingUtils.getQRCodeImge(response.getQrCode(), 256, qrPath);
logger.info("filePath:" + qrPath);
String qrUrl =qrFileName;
resultMap.put("qrUrl",qrUrl);
return ServerResponse.createBySuccess(resultMap);
case FAILED:
logger.error("支付宝预下单失败!!!");
return ServerResponse.createByErrorMessage("支付宝预下单失败!!!");
case UNKNOWN:
logger.error("系统异常,预下单状态未知!!!");
return ServerResponse.createByErrorMessage("系统异常,预下单状态未知!!!");
default:
logger.error("不支持的交易状态,交易返回异常!!!");
return ServerResponse.createByErrorMessage("不支持的交易状态,交易返回异常!!!");
}
}
// 简单打印应答
private void dumpResponse(AlipayResponse response) {
if (response != null) {
logger.info(String.format("code:%s, msg:%s", response.getCode(), response.getMsg()));
if (StringUtils.isNotEmpty(response.getSubCode())) {
logger.info(String.format("subCode:%s, subMsg:%s", response.getSubCode(),
response.getSubMsg()));
}
logger.info("body:" + response.getBody());
}
}
public ServerResponse aliCallback(Map<String,String> params){
Long orderNo = Long.parseLong(params.get("out_trade_no"));
String tradeNo = params.get("trade_no");
String tradeStatus = params.get("trade_status");
Order order = orderMapper.selectByOrderNo(orderNo);
if(order == null){
return ServerResponse.createByErrorMessage("非快乐慕商城的订单,回调忽略");
}
if(order.getStatus() >= Const.OrderStatusEnum.PAID.getCode()){
return ServerResponse.createBySuccess("支付宝重复调用");
}
//判断是否 等待买家付款 交易成功 交易失败
if(Const.AlipayCallback.TRADE_STATUS_TRADE_SUCCESS.equals(tradeStatus)){
order.setPaymentTime(DateTimeUtil.strToDate(params.get("gmt_payment")));
order.setStatus(Const.OrderStatusEnum.PAID.getCode());
orderMapper.updateByPrimaryKeySelective(order); //交易成功 改成 已付款
}
PayInfo payInfo = new PayInfo();
payInfo.setUserId(order.getUserId());
payInfo.setOrderNo(order.getOrderNo());
payInfo.setPayPlatform(Const.PayPlatformEnum.ALIPAY.getCode()); //交易平台
payInfo.setPlatformNumber(tradeNo);
payInfo.setPlatformStatus(tradeStatus);
payInfoMapper.insert(payInfo);
return ServerResponse.createBySuccess();
}
public ServerResponse queryOrderPayStatus(Integer userId,Long orderNo){
Order order = orderMapper.selectByUserIdAndOrderNo(userId,orderNo);
if(order == null){
return ServerResponse.createByErrorMessage("用户没有该订单");
}
if(order.getStatus() >= Const.OrderStatusEnum.PAID.getCode()){
return ServerResponse.createBySuccess();
}
return ServerResponse.createByError();
}
public ServerResponse<PageInfo> manageList(int pageNum,int pageSize){
PageHelper.startPage(pageNum,pageSize);
List<Order> orderList = orderMapper.selectAllOrder();
List<OrderVo> orderVoList = this.assembleOrderVoList(orderList,null);
PageInfo pageResult = new PageInfo(orderList);
pageResult.setList(orderVoList);
return ServerResponse.createBySuccess(pageResult);
}
public ServerResponse<OrderVo> manageDetail(Long orderNo){
Order order = orderMapper.selectByOrderNo(orderNo);
if(order != null){
List<OrderItem> orderItemList = orderItemMapper.getByOrderNo(orderNo);
OrderVo orderVo = assembleOrderVo(order,orderItemList);
return ServerResponse.createBySuccess(orderVo);
}
return ServerResponse.createByErrorMessage("订单不存在");
}
public ServerResponse<PageInfo> manageSearch(Long orderNo,int pageNum,int pageSize){
PageHelper.startPage(pageNum,pageSize);
Order order = orderMapper.selectByOrderNo(orderNo);
if(order != null){
List<OrderItem> orderItemList = orderItemMapper.getByOrderNo(orderNo);
OrderVo orderVo = assembleOrderVo(order,orderItemList);
PageInfo pageResult = new PageInfo(Lists.newArrayList(order));
pageResult.setList(Lists.newArrayList(orderVo));
return ServerResponse.createBySuccess(pageResult);
}
return ServerResponse.createByErrorMessage("订单不存在");
}
public ServerResponse<String> manageSendGoods(Long orderNo){
Order order= orderMapper.selectByOrderNo(orderNo);
if(order != null){
if(order.getStatus() == Const.OrderStatusEnum.PAID.getCode()){
order.setStatus(Const.OrderStatusEnum.SHIPPED.getCode());
order.setSendTime(new Date());
orderMapper.updateByPrimaryKeySelective(order);
return ServerResponse.createBySuccess("发货成功");
}
}
return ServerResponse.createByErrorMessage("订单不存在");
}

源码地址