支付宝支付环境搭建
# 支付宝测试环境开发的前期准备
# 1.下载Java支付的demo
demo下载地址:https://docs.open.alipay.com/270/106291/
1.下载解压导入idea
主要是拿到AlipayConfig.java文件
readme.txt请好好看一下,里边是关于一些下载demo到jsp页面实现的过程,供学习参考,解压的demo中只有一个Java配置类,其余都是JSP页面。
# 2.配置AlipayConfig
(1).免费注册蚂蚁金服开发者账号
注册地址:https://open.alipay.com (opens new window) ,用你的支付宝账号扫码或者账号登录,完善个人信息,选择服务类型(我选的是自研)。
(2).设置app_id和gatewayUrl(支付宝网关)
其中密钥需要自己生成,appID和支付宝网关是已经给好的,网关有dev字样,表明是用于开发测试。
(1).设置密匙
点击“生成方法”,打开界面如下图:
下载密钥生成工具,解压打开后,双击脚本文件 “RSA签名验签工具.bat” 即运行RSA签名验签工具,选择PKCS8(Java适用)和2048位生成密钥:
如果没有设置过,此时显示文本是“设置应用公钥”,我这里是已经设置过得,设置过得可以永久使用了。
设置方法,“打开密钥文件路径”:
复制应用公钥2048.txt中的内容到点击“设置应用公钥”的弹出框中,保存:
商户私钥(merchant_private_key)
复制 应用私钥2048.txt 中的内容到merchant_private_key中。
支付宝公钥(alipay_public_key)
点击如上图链接,复制弹出框里面的内容到alipay_public_key。
如果这个设置不对,结果是:支付成功,但是验签失败。
如果是正式环境,需要上传到对应的应用中
(4).服务器异步通知页面路径(notify_url)
如果没有改名,修改IP和端口号就可以了,我自己的如下:
http://localhost:8080/alipay/alipayNotifyNotice
(5).页面跳转同步通知页面路径(return_url)
http://localhost:8080/alipay/alipayReturnNotice
# 沙箱环境和正式环境的区别
沙箱环境是开放平台提供给开发者测试的虚拟环境,有专门的沙箱应用APPID和网关,无需创建应用,账号入驻开放平台即可看到,沙箱支持大部分产品,无需签约即可调用接口测试。
二、如何切换
1、将代码中APPID换成沙箱APPID
2、将沙箱网关切换成正式环境网关:https://openapi.alipay.com/gateway.do
3、如果沙箱环境和正式环境配置的密钥不是同一套,就需要更换成正式环境密钥,如果配置的是同一套密钥则代码中密钥不用更改
注意:即使是相同的密钥,支付宝公钥也是不一样的,需要修改。
# 下单代码
package com.zhu.pay_demo.controller;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.zhu.pay_demo.config.AlipayConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* @author ggBall
* @version 1.0.0
* @ClassName PayController.java
* @Description TODO
* @createTime 2021年09月12日 15:54:00
*/
@RestController
@Slf4j
public class AliPayController {
@RequestMapping("/pay")
public void payController(HttpServletRequest request, HttpServletResponse response) throws IOException {
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.GATEWAY_URL, AlipayConfig.APP_ID, AlipayConfig.MERCHANT_PRIVATE_KEY, "json", AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.SIGN_TYPE);
//设置请求参数
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(AlipayConfig.RETURN_URL);
alipayRequest.setNotifyUrl(AlipayConfig.NOTIFY_URL);
//商户订单号,商户网站订单系统中唯一订单号,必填
String out_trade_no = new String(request.getParameter("WIDout_trade_no").getBytes("ISO-8859-1"), "UTF-8");
//付款金额,必填
String total_amount = new String(request.getParameter("WIDtotal_amount").getBytes("ISO-8859-1"), "UTF-8");
//订单名称,必填
String subject = new String(request.getParameter("WIDsubject").getBytes("ISO-8859-1"), "UTF-8");
//商品描述,可空
String body = new String(request.getParameter("WIDbody").getBytes("ISO-8859-1"), "UTF-8");
alipayRequest.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\","
+ "\"total_amount\":\"" + total_amount + "\","
+ "\"subject\":\"" + subject + "\","
+ "\"body\":\"" + body + "\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//若想给BizContent增加其他可选请求参数,以增加自定义超时时间参数timeout_express来举例说明
//alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
// + "\"total_amount\":\""+ total_amount +"\","
// + "\"subject\":\""+ subject +"\","
// + "\"body\":\""+ body +"\","
// + "\"timeout_express\":\"10m\","
// + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//请求参数可查阅【电脑网站支付的API文档-alipay.trade.page.pay-请求参数】章节
//请求
String form = "";
try {
form = alipayClient.pageExecute(alipayRequest).getBody(); //调用SDK生成表单
} catch (AlipayApiException e) {
e.printStackTrace();
}
// 后台直接返回 from表单
response.setContentType("text/html;charset=" + AlipayConfig.CHARSET);
response.getWriter().write(form);//直接将完整的表单html输出到页面
response.getWriter().flush();
response.getWriter().close();
// 也可以将form表单信息返回给前端
}
/**
* @Description: 支付宝同步通知页面
*/
@RequestMapping(value = "/alipayReturnNotice")
public ModelAndView alipayReturnNotice(HttpServletRequest request, HttpServletRequest response) throws Exception {
log.info("支付成功, 进入同步通知接口...");
//获取支付宝GET过来反馈信息
Map<String,String> params = new HashMap<String,String>();
Map<String,String[]> requestParams = request.getParameterMap();
for (Iterator<String> 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] + ",";
}
//乱码解决,这段代码在出现乱码时使用
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
//调用SDK验证签名
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE);
ModelAndView mv = new ModelAndView("alipaySuccess");
//——请在这里编写您的程序(以下代码仅作参考)——
if(signVerified) {
//商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
//支付宝交易号
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
//付款金额
String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8");
// 修改订单状态为支付成功,已付款; 同时新增支付流水
// orderService.updateOrderStatus(out_trade_no, trade_no, total_amount);
//
// Order order = orderService.getOrderById(out_trade_no);
// Product product = productService.getProductById(order.getProductId());
log.info("********************** 支付成功(支付宝同步通知) **********************");
log.info("* 订单号: {}", out_trade_no);
log.info("* 支付宝交易号: {}", trade_no);
log.info("* 实付金额: {}", total_amount);
// log.info("* 购买产品: {}", product.getName());
log.info("***************************************************************");
mv.addObject("out_trade_no", out_trade_no);
mv.addObject("trade_no", trade_no);
mv.addObject("total_amount", total_amount);
// mv.addObject("productName", product.getName());
}else {
log.info("支付, 验签失败...");
}
return mv;
}
/**
* @Description: 支付宝异步 通知页面
*/
@RequestMapping(value = "/alipayNotifyNotice")
@ResponseBody
public String alipayNotifyNotice(HttpServletRequest request, HttpServletRequest response) throws Exception {
log.info("支付成功, 进入异步通知接口...");
//获取支付宝POST过来反馈信息
Map<String,String> params = new HashMap<String,String>();
Map<String,String[]> requestParams = request.getParameterMap();
for (Iterator<String> 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] + ",";
}
//乱码解决,这段代码在出现乱码时使用
/*valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");*/
params.put(name, valueStr);
}
//调用SDK验证签名
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE);
//——请在这里编写您的程序(以下代码仅作参考)——
/* 实际验证过程建议商户务必添加以下校验:
1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
4、验证app_id是否为该商户本身。
*/
//验证成功
if(signVerified) {
//商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
//支付宝交易号
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
//交易状态
String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");
//付款金额
String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8");
if(trade_status.equals("TRADE_FINISHED")){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程序
//注意: 尚自习的订单没有退款功能, 这个条件判断是进不来的, 所以此处不必写代码
//退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
}else if (trade_status.equals("TRADE_SUCCESS")){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程序
//注意:
//付款完成后,支付宝系统发送该交易状态通知
// 修改叮当状态,改为 支付成功,已付款; 同时新增支付流水
// orderService.updateOrderStatus(out_trade_no, trade_no, total_amount);
//
// Order order = orderService.getOrderById(out_trade_no);
// Product product = productService.getProductById(order.getProductId());
log.info("********************** 支付成功(支付宝异步通知) **********************");
log.info("* 订单号: {}", out_trade_no);
log.info("* 支付宝交易号: {}", trade_no);
log.info("* 实付金额: {}", total_amount);
// log.info("* 购买产品: {}", product.getName());
log.info("***************************************************************");
}
log.info("支付成功...");
}else {//验证失败
log.info("支付, 验签失败...");
}
return "success";
}
}
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