工作需要调用支付宝支付接口,相比微信支付接口,支付宝真的是很简便了。
支付宝可以使用沙箱环境来进行调试,微信支付接口没有支付测试环境,要调用只能通过官方平台注册企业开发用户。
直接上正题:
本人开发环境: 使用IDEA、JDK-9
第一次调用准备工作: 下载密钥工具,生成公钥、密钥
下载完成后: 生成所需密钥和公钥
来到沙箱环境:
将生成的密钥上传到沙箱环境中:
在调用接口时需要用到的还有APPID,这个是你在程序中配置支付参数时需要填写的,可以在沙箱环境中看到。
上述配置完毕后,如果条件允许最好下载一下下面这个沙箱环境的支付宝APP,方便后面测试使用,当然不配置也是可以的
准备工作完成之后,正式开始在后台对支付宝接口进行调用,我这里用的不是官网给demo的jsp页面展示,而是根据说明文档用HTML来实现。
写一个公共参数类:AlipayConfig
public class AlipayConfig {
//商户APPID
public static String APPID = "商户APPID";
//商户私钥
public static String RSA_PRIVATE_KEY = "密钥";
//支付宝公钥
public static String ALIPAY_PUBLIC_KEY = "公钥";
//"http://localhost:8080/alipay.trade.page.pay-JAVA-UTF-8/notify_url.jsp";
//服务器异步通知页面路径 : http:// 或者 https:// 格式的完整路径,不能加自定义参数(?id=12),必须外网能正常访问
public static String notify_url = "http://zqqe3t.natappfree.cc/notify_url";
//"http://localhost:8080/alipay.trade.page.pay-JAVA-UTF-8/return_url.jsp" 同步地址是支付成功后跳转的地址
//页面跳转同步通知页面路径:http:// 或者 https:// 格式的完整路径,不能加自定义参数(?id=12),必须外网能正常访问
public static String return_url = "http://localhost:8888/return_url";
// 签名方式
public static String sign_type = "RSA2";
// 字符编码格式
public static String charset = "utf-8";
//返回格式
public static String format = "json";
// 支付宝网关
public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
// 日志记录路径
public static String log_path = "E:\\";
}
注意!!!!!!!
异步通知页面路径 public static String return_url 必须外网能访问才能收到异步通知信息
如果是个人电脑测试,外网无法访问可以通过内网穿透访问:使用natapp
这里推荐两个小工具,可以进行内网穿透,natapp 或者ngrock,这也是平时做微信开发或者支付宝开发模拟调试的时候使用的两个小工具,我这里使用的是natapp ,关于natapp 的用法我简单书哟一下,方便大家使用,进入natapp 官网,下载natapp 压缩包,解压到本地
还有要注意一点:下载完解压不一定有config.ini文件
可以新建一个文件,将以下内容拷贝过去:
#将本文件放置于natapp同级目录 程序将读取 [default] 段
#在命令行参数模式如 natapp -authtoken=xxx 等相同参数将会覆盖掉此配置
#命令行参数 -config= 可以指定任意config.ini文件
[default]
authtoken= #对应一条隧道的authtoken
clienttoken= #对应客户端的clienttoken,将会忽略authtoken,若无请留空,
log=none #log 日志文件,可指定本地文件, none=不做记录,stdout=直接屏幕输出 ,默认为none
loglevel=DEBUG #日志等级 DEBUG, INFO, WARNING, ERROR 默认为 DEBUG
http_proxy= #代理设置 如 http://10.123.10.10:3128 非代理上网用户请务必留空
然后我们双击natapp.exe,看到如下信息说明映射成功了
然后我们将这个地址填写到配置类中的return_url和notify_url中即可
public static String notify_url = “http://5c7dxs.natappfree.cc/notify_url”;
public static String return_url = “http://5c7dxs.natappfree.cc/return_url”;
注意:notify_url 和 return_url 是我们在控制器中定义的两个方法!!!
回调和异步通知时可以在里面处理事务。
控制器:AlipayController 里的方法
@CrossOrigin
@Controller
public class AlipayController {
@Autowired
private AlipayService payService;
@Autowired
private TchargeService chargeService;
/**【支付宝下单返回付款二维码】**/
@GetMapping("/pay")
@ResponseBody
public String alipayQrcode(@RequestParam(value="FormVal",defaultValue="") String FormVal){
System.out.println("阿里支付接收表单提交的数据:" + FormVal);
Tcharge chargeInfo = PayUtils.getPayInfo(FormVal); //订单信息
PayUtils.ListChargeInfo(chargeInfo);
chargeService.insertChargeData(chargeInfo);
String result = payService.create(chargeInfo);
return result;
}
/**【支付宝异步通知,支付宝支付后,会回调该接口】**/
@PostMapping(value = "/notify_url")
public String notifyUrl(HttpServletRequest request){
String orderNumber = payService.notifyUrl(request);
System.out.println("【异步通知返回的订单号】" + orderNumber);
if (!orderNumber.equals("fail")){
Tcharge chargeInfo = chargeService.selectDataByOrderNumber(orderNumber);
if (chargeInfo != null){
chargeInfo.setPayStatus("已支付");
chargeService.updateChargeData(chargeInfo);
System.out.println("【异步通知订单更新完成!】");
}else {
System.out.println("【异步通知订单更新失败!】");
}
}
return "recordTab";
}
/**【同步跳转,告诉你是否调用成功,不能拿来判断支付成功】**/
@GetMapping(value = "/return_url")
public String returnUrl(HttpServletRequest request) throws UnsupportedEncodingException {
payService.returnUrl(request);
return "Index";
}
/**【支付宝交易状态查询】**/
@RequestMapping(value = "/requestQuery")
@ResponseBody
public String requestQuery() throws AlipayApiException {
return payService.queryOrder("ON20200714170132531");
}
}
接口:AlipayService
public interface AlipayService {
/**【生成订单】**/
public String create(Tcharge chargeInfo);
/**【异步通知会返回一个request】**/
public String notifyUrl(HttpServletRequest request);
/**【同步跳转】**/
public void returnUrl(HttpServletRequest request) throws UnsupportedEncodingException;
/**【交易查询】**/
public String queryOrder(String orderNumber) throws AlipayApiException;
}
接口实现:AlipayServiceImpl
@Service
public class AlipayServiceImpl implements AlipayService {
/**【下单】**/
@Override
public String create(Tcharge chargeInfo) {
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl,AlipayConfig.APPID,AlipayConfig.RSA_PRIVATE_KEY,
AlipayConfig.format,AlipayConfig.charset,AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.sign_type);
//创建PC场景下单并支付请求对象
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();//创建API对应的request
//设置同步返回地址,HTTP/HTTPS开头字符串
alipayRequest.setReturnUrl(AlipayConfig.return_url);
//支付宝服务器主动通知商户服务器里指定的页面http/https路径。
alipayRequest.setNotifyUrl(AlipayConfig.notify_url);//在公共参数中设置回跳和通知地址
//商户订单号,不能为空
String out_trade_no = chargeInfo.getOrderNumber();
//付款金额,不能为空,单位:元
String total_amount = chargeInfo.getTotalAmount();
//订单名称,不能为空
String subject = "待支付订单车牌号:" + chargeInfo.getCarNo();
//商品描述,可空
String body = "支付订单:" + chargeInfo.getOrderNumber();
//该笔订单最晚付款时间,逾期关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)参数数值不接受小数点, 如 1.5h,可转换为 90m。
String timeout_express = "1c";
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"body\":\""+ body +"\","
+ "\"timeout_express\":\""+ timeout_express +"\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//填充业务参数
String form="";
try {
//调用SDK生成表单
//form = alipayClient.pageExecute(alipayRequest).getBody();
AlipayTradePagePayResponse responseData = alipayClient.pageExecute(alipayRequest);
form = responseData.getBody();
} catch (AlipayApiException e) {
e.printStackTrace();
}
return form;
}
/**【异步通知】**/
@Override
public String notifyUrl(HttpServletRequest request) {
String payStatus = "fail";
Map<String , String > parmas = new HashMap<>();
Map<String , String []> requestParmas = request.getParameterMap();
for (Iterator<String> iter = requestParmas.keySet().iterator(); iter.hasNext() ; ){
String name = iter.next();
String [] values = requestParmas.get(name);
String valStr = "";
for (int i = 0 ; i < values.length ; i ++){
valStr = (i == values.length - 1) ? valStr + values[i] : valStr + values[i] + ",";
}
parmas.put(name , valStr);
//System.out.println("【异步通知的值】" + name + "\t\t:" + valStr);
}
//签名验证
boolean signVerified = false;
try {
signVerified = AlipaySignature.rsaCheckV1(parmas,AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.charset,AlipayConfig.sign_type);
}catch (AlipayApiException e){
System.out.println("【异步签名异常】" + e.getErrMsg());
}
//签名验证通过
if (signVerified){
System.out.println("【异步通知签名验证】" + signVerified);
String trade_status = request.getParameter("trade_status");//交易状态
String out_trade_no = request.getParameter("out_trade_no");//商户订单号
//System.out.println("【异步通知商户订单号】" + out_trade_no + "\t\t【异步通知交易状态】" + trade_status);
if (trade_status.equals("TRADE_SUCCESS")){ //判断订单交易状态
payStatus = out_trade_no;
}
}
return payStatus;
}
/**【同步跳转】**/
@Override
public void returnUrl(HttpServletRequest request) throws UnsupportedEncodingException {
Map<String , String > params = new HashMap<>();
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] + ",";
}
//System.out.println("<--同步回调的值-->" + name + "\t\t" + valueStr);
//乱码解决,这段代码在出现乱码时使用。
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
//切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
//boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
boolean signVerified = false;
try {
signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.charset,AlipayConfig.sign_type);
}catch (Exception e){
System.out.println("报错:" + e.getMessage());
e.printStackTrace();
}
if(signVerified) {
System.out.println("<--同步回调签名验证-->" + 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");
request.setAttribute("out_trade_no", out_trade_no);
request.setAttribute("trade_no", trade_no);
request.setAttribute("total_amount", total_amount);
//System.out.println("<--同步回调系统订单号-->" + out_trade_no + "\t\t<--同步回调支付宝交易号-->" + trade_no);
//系统处理根据支付宝回调更改订单状态或者其他关联表的数据
}else{
request.setAttribute("reason", "验签失败");
}
request.setAttribute("signVerified", signVerified);
}
/**【交易查询】**/
@Override
public String queryOrder(String orderNumber) throws AlipayApiException {
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl,AlipayConfig.APPID,AlipayConfig.RSA_PRIVATE_KEY,
AlipayConfig.format,AlipayConfig.charset,AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.sign_type);
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent("{" +
"\"out_trade_no\":\"" + orderNumber + "\"," +
"\"trade_no\":\"" + "" + "\"," +
"\"org_pid\":\"" + "" + "\"," +
" \"query_options\":[" +
" \"TRADE_SETTLE_INFO\"" +
" ]" +
" }");
AlipayTradeQueryResponse response = alipayClient.execute(request);
System.out.println("【查询返回交易状态】" + response.getTradeStatus());
return response.getTradeStatus();
}
}
以上就是支付宝接口调用实现。
前端HTML实现下单生成二维码并用手机沙箱app扫码付款。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>收费登记</title>
<!-- 引入组件库 vue.min.js 要在 index.js 之前引入-->
<script src="js/vue.min.js"></script>
<script src="js/axios.min.js"></script>
<!-- 引入样式 -->
<link rel="stylesheet" type="text/css" href="css/index.css">
<!-- 引入组件库 -->
<script src="js/index.js"></script>
</head>
<body>
<div id="app"><!-- action="http://localhost:8888/pay" -->
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px"
class="demo-ruleForm">
<el-form-item required>
<el-col :span="10">
<el-form-item prop="carNo">
<el-input v-model="ruleForm.carNo" placeholder="请输入车牌号码">
<template slot="prepend" >车牌号码</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item prop="carType">
<el-input v-model="ruleForm.carType" placeholder="请选择车辆类型" :disabled="true">
<el-select v-model="ruleForm.carType" slot="append" style="width: 120px;">
<el-option label="小型汽车" value="小型汽车"></el-option>
<el-option label="中型汽车" value="中型汽车"></el-option>
<el-option label="大型汽车" value="大型汽车"></el-option>
<el-option label="挂车" value="挂车"></el-option>
</el-select>
<template slot="prepend" >车辆类型</template>
</el-input>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item>
<el-col :span="10">
<el-form-item prop="name">
<el-input v-model="ruleForm.name" placeholder="请输入车主姓名">
<template slot="prepend" >车主姓名</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item prop="phone">
<el-input v-model="ruleForm.phone" placeholder="请输入车主手机">
<template slot="prepend" >车主手机</template>
</el-input>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item>
<el-col :span="10">
<el-form-item prop="totalAmount">
<el-input type="number" v-model="ruleForm.totalAmount" placeholder="请输入收费金额">
<template slot="prepend" >收费金额</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="登记日期" required prop="selData">
<el-date-picker type="date" placeholder="选择日期" v-model="ruleForm.selData" style="width: 100%;"></el-date-picker>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item>
<el-col :span="20">
<el-form-item label="支付方式" prop="payType" style="width: 100%;">
<el-radio-group v-model="ruleForm.payType">
<el-radio border label="支付宝"></el-radio>
<el-radio border label="微信"></el-radio>
<el-radio border label="现金"></el-radio>
<el-radio border label="刷卡"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item>
<el-col :span="20">
<el-form-item label="备注信息" prop="remark">
<el-input type="textarea" v-model="ruleForm.remark" placeholder="备注信息">
</el-input>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item style="text-align: center;">
<el-button type="primary" @click="submitForm('ruleForm')">立即创建</el-button><!-- style="display:none" -->
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
<script>
var vues = new Vue({
el: '#app',
/**-----【数据初始化】-----**/
data() {
return {
ruleForm: {
name: '',
carNo: '',
selData: '',
payType: '',
phone: '',
totalAmount: '',
carType: '',
remark: '',
operater: sessionStorage.getItem("realName")
},
rules: {
name: [{
required: true,
message: '请输入车主姓名',
trigger: 'blur'
}],
carNo: [{
required: true,
message: '请输入车牌号码',
trigger: 'change'
}],
selData: [{
type: 'date',
required: true,
message: '请选择日期',
trigger: 'blur'
}],
payType: [{
required: true,
message: '请选择支付方式',
trigger: 'change'
}],
phone: [{
required: true,
message: '请输入车主手机号码',
trigger: 'blur'
}],
totalAmount: [{
required: true,
message: '请输入收费金额',
trigger: 'blur'
}],
carType: [{
required: true,
message: '请选择车辆类型',
trigger: 'blur'
}],
}
};
},
/**-----【方法启动执行】-----**/
mounted() {
},
/**-----【方法列表】-----**/
methods: {
/**【form表单数据提交】**/
submitForm(formName) {
let findStr = {
FormVal: vues.ruleForm
};
this.$refs[formName].validate((valid) => {
if (valid) {
console.log("【收费登记From表单数据】" + JSON.stringify(findStr));
axios({
method: "get",
url: "http://localhost:8888/pay",
params: findStr //请求参数
}).then(function(response) {
console.log("【支付宝返回】" + response.data);
document.querySelector('body').innerHTML = response;
const div = document.createElement('div');
div.innerHTML = response.data;
document.body.appendChild(div);
document.forms[0].setAttribute('target', '_blank');// 新建窗口页面
document.forms[0].submit();
})
} else {
this.$message.error('请输入合法数据!');
return false;
}
});
},
/**【form表单数据重置】**/
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
})
</script>
</body>
</html>
注意:支付宝下单接口返回的是form表单
留意上面的一段代码:是将支付宝返回的form表单提交,之后才返回二维码
document.querySelector('body').innerHTML = response;
const div = document.createElement('div');
div.innerHTML = response.data;
document.body.appendChild(div);
document.forms[0].setAttribute('target', '_blank');// 打开新窗口页面
document.forms[0].submit();
运行下单支付:
在浏览器控制器里可以看到支付宝下单返回的内容
然后我这里下单之后就创建了一条记录:订单编号是自己通过时间戳来生成的,也是支付宝接口需要请求的参数,可以通过自己的订单号来查询订单状态,判断订单是否支付成功等等信息。
上面查询接口已经写好,传入参数就可以调用。
以上就是支付宝接口调用的过程,记录一下以后用到直接看博客。