进入开发者中心–开发工具–沙箱–设置公钥
搜索电脑网上支付–查看Demo–查看配置类–查看业务逻辑
我们的基础配置类主要是初始化我们的alipay客户端
真正去付款的时候是提交了一个form表单达到一个真正的支付jsp,java代码首先初始化我买的Alipay客户端,拿到传过来的参数,通过aliPayClient去执行这个付款请求
实现:
新建module–导入依赖–配置jetty
jetty:run -Djetty.port=9100
测试,访问首页 localhost:9100/alipay-demo
,到达订单页面
将付款金额改成1000,点击付款之后,到达alipay.trade.page.pay.jsp页面,在这个页面获取了AliPay客户端,获取前台传过来的参数,设置订单信息,执行请求获取一个form表单格式的字符串,输出到页面中。
如果没有登录,让你登录,否则进行一个扫码付款
接下来我们用沙箱账号进行登录,进入我们的支付页面
输入支付密码,支付成功
这时刷新沙箱账号页面,发现商家信息多了1000块钱,买家少了1000块钱。支付成功。
用了一个result,一个form表单。
同步通知和异步通知
同步通知,付款成功之后会去同步通知的商户页面。
为什么需要一个同步通知呢?
因为我们之前有订单了,订单付款之后订单的状态需要一个变更,是不是真的付款了,因为有的用户到付款页面之后就把付款页面给关闭了,不去付款。所以我们需要付款成功后跳回商户页面,对订单状态进行变更。
为什么需要一个异步通知?
因为付款完毕之后支付宝还有几秒是需要跳转页面的,而有的客户是不想等去跳转页面的因此会直接关闭页面不跳转到商户页面,那么我们对订单的状态的变更也就没法实现了。这个时候就会使用异步通知。
在父项目中定义版本号,声明依赖
在订单系统中引入aplipay的依赖
<dependency>
<groupId>com.alipay.sdkgroupId>
<artifactId>alipay-sdk-javaartifactId>
dependency>
package com.alipay.config;
import java.io.FileWriter;
import java.io.IOException;
/* *
*类名:AlipayConfig
*功能:基础配置类
*详细:设置帐户有关信息及返回路径
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
public static String app_id = "2021000122689739";
// 商户私钥,您的PKCS8格式RSA2私钥
public static String merchant_private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDKQ2LXAcegUdGkzsIXCyJ/Ugff7NwFpV0kDiY8zLm93Nr4/6KHM846kqUsQV9kZt4h0ZqesylSDvwKMKGm9o8fgmBlBg76TCJ+j65NxxuqQWOIGbo3VmO7t8dbHukBjTB2nboMDKNb1NYPERsIH3zcltw8+vCchCE+wra8HCmTHG5DVZY17/WdRnD7lMg5FEdd7o0xwT1DrTJTG76UTsc16PNTHb0Hd6PXKqSYeIvADoj3cNhZBo+MSp8+sYqRB6lx84KxtTpqB5t29by750h/jV+OmKljfUJJBWTNBuy06JhE6HDgyI3/KdfmprZU6obO71aib3xbfQZliRJsZmlpAgMBAAECggEAF1T+qd7CsHO/w0s2iIH4UMedsO8Z0mzo+afIMWtMYeKAQAj9LquDNq6D7z77ShGzviL3uybftV+VI3fIrgHzMKdRh1XTlHwD3qWkbcRyJIm2eZS+LnQAvT7N+GzwZAVtecFoHMu4bHd42fQXMjCvrqKYdVBdQTDcxOihO8Hj6dxBJREmShT68X+7vtEaICjIcPPk7Eu4aAVTYwwWuz3zjboSMMsKO1EGruDI/mQXD8CpDSv+EG7V2zyy+6nS82c/BvqH4Hd8/ERepwVqbWeMKA36v9hHGfWCWGHq37sG1RhyhPMo5lA6fJcCla5OAoTY4qDYWhGvplXI0RHk1VHgIQKBgQDxTt7KRAw7MhBeo4SoyxbPQaPOWlQoMS11h1fS4Dx4dSlg4sHrPQOoXRf0ATpQhLdKUngwoq8/iQ70ML+AYn1OrM5vKnGsvjXpO0UucbN/KfXLkxHAY8VPrgaSSlImLphJwKRJ9ucOwMW3zp2MMzz5ykTU+Yvs9e0wtfZoW/ZJkwKBgQDWk/I2keFkDakGvpHaCWNOmqe8ZaBk3GVYR5WJY7oJAdg6B1fsqmBo7Gkcb8pfZvcMVJ1AnuC3bRF315pQtaUXYFFFixP2HHeoowkXRP0DDKXXPUDIakv74y5j7YL6iIFaQOQONjx9/ziJ21L+wn7XQy9qzjQ64Bhlkn6wUk9ukwKBgQCvayB2dYVWuww/YCud8RVB0XvdFLk9BpL/b0Ye3DfZcYKFFhLWiWJRUGuLTCEyS2hbNRx+bqVrUJ14Ur/D+tURDnp7QaWlaBkU1atzGeDcvooaVW2AKIdVZzMLwc96RkEazFoiEsThaq3Q8viA5Pact7fWAxr/RcAJux3Hu6KM1QKBgCIs8hjq5yEOY+tlEsnNUPnF/A0vinpF+AY1cIWvUoP4vT2Qydox0KUlciBLVcoYFiTqnRkbtOLCBlQ2DKfqJl8wwgD3g7hhFFXRkbMZE8sWiptbcCP7ehqmNk9IFuJcN23ug+QXk07mBVU/j1pwE8+pLbYvHXZ7Fqemy3K9yVTtAoGASL5QeKvu1Si/gsUCu0ka9e+V6iHmWTLFCN2z/JZUVACUQ8uAvLWhxQNVW2GJQ0//yCm04jn8gaVa593Aj5i6GmbyggspVrRUekRtRDGcFLARj6g1/ChGzJfj4P/o/rBKocPAtIsDBZnq/+AsGIaatl32O6t48wJfzqjVUaUA0cQ=";
// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
public static String alipay_public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAykNi1wHHoFHRpM7CFwsif1IH3+zcBaVdJA4mPMy5vdza+P+ihzPOOpKlLEFfZGbeIdGanrMpUg78CjChpvaPH4JgZQYO+kwifo+uTccbqkFjiBm6N1Zju7fHWx7pAY0wdp26DAyjW9TWDxEbCB983JbcPPrwnIQhPsK2vBwpkxxuQ1WWNe/1nUZw+5TI";
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://srgrcv.natappfree.cc/shop-portal/order/callback";
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String return_url = "http://localhost:9096/shop-order/order/myOrder";
// 签名方式
public static String sign_type = "RSA2";
// 字符编码格式
public static String charset = "utf-8";
// 支付宝网关
public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
// 支付宝网关
public static String log_path = "C:\\";
//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
/**
* 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
* @param sWord 要写入日志里的文本内容
*/
public static void logResult(String sWord) {
FileWriter writer = null;
try {
writer = new FileWriter(log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
writer.write(sWord);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
配置完之后,点击提交订单,传购物车列表和总金额到预订单页面,到预订单页面进行显示,然后再预订单页面点击确定提交订单后生成一个确定的订单,存入数据库中,传给确定订单页面订单参数等信息。在实际项目中这个确定订单走的是/order/submitOrder,注意存入数据库的同时,还需要将购物车中的信息清空。在确定订单页面中我们要点击去付款,付款的这个数据来自我们确定订单中的数据,因此我们在付款的时候需要查询数据库,防止出现bug,并且在付款页面我们需要传过去订单编号和总金额。也就是进入/order/payment进行真正的付款,获取支付宝阿里云客户端,根据前端传过来的参数去数据库中进行查询,设置alipayRequest的请求参数,客户端执行请求,返回一个form表单格式的字符串,跳转到一个页面中去显示这个字符串的信息。因此需要将该字符串放到request域中,用el表达式取出来。
/**
* 跳转到预订单页面
*
* @return
*/
@RequestMapping("/preOrder")
public String preOrder(Model model, HttpServletRequest request) {
Admin admin = (Admin) request.getSession().getAttribute("user");
model.addAttribute("cartResult", cartService.getCartList(admin));
return "order/preOrder";
}
/**
* 跳转到订单提交页面.这是一个确定的订单
* 在这里查询一个订单信息,可能查询出来的订单需要判断是否为空,因为根据订单编号查出来的订单是唯一的,因此还需要判断是否大小大于1,如果大于1,则返回空。
*
* @return
*/
@RequestMapping("/submitOrder")
public String submitOrder(Model model, HttpServletRequest request) {
Admin admin = (Admin) request.getSession().getAttribute("user");
CartResult cartResult = cartService.getCartList(admin);
//1.存入订单信息
BaseResult baseResult = orderService.orderSave(admin, cartResult);
//2.清除购物车信息
cartService.clearCart(admin);
//总价
model.addAttribute("totalPrice", cartResult.getTotalPrice());
//订单编号
model.addAttribute("orderSn", baseResult.getMessage());
//3.页面跳转
return "order/submitOrder";
}
/**
* 去付款,点击付款按钮去pay-ment,前台传过来订单编号,查询订单,执行付款请求,得到一个form表单格式的字符串,显示到个付款页面,因此会使用到Model。
* @param request
* @param model
* @return
*/
@RequestMapping("payment")
public String payment(HttpServletRequest request,Model model,String orderSn) {
try {
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, 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);
//从数据库中去查询订单
Order order = orderService.selectOrderByOrderSn(orderSn);
//商户订单号,商户网站订单系统中唯一订单号,必填
String out_trade_no = orderSn;
//付款金额,必填
String total_amount = String.valueOf(order.getTotalAmount());
//订单名称,必填
String subject = "用户为"+order.getUserId()+"的订单";
//商品描述,可空
String body = "";
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 result = alipayClient.pageExecute(alipayRequest).getBody();
//输出
System.out.println(result);
model.addAttribute("result",result);
return "order/payment";
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
异步通知:当用户将跳转页面关闭后,通过异步通知告诉我们用户的支付订单是成功,可以进行订单状态的更改。
我们的支付宝去调用我们的页面,我们的页面是运行在localhost上的,支付宝是不可能调用我们的这个localhost上面的页面的,只能是公网,支付宝才可以调用,怎么解决呢?
1、把项目运行在服务器上,购买域名,挂在公网上面,支付宝通过公网域名调用我们的页面。
2、通过内网穿透,将公网的地址映射到我们的地址,可以通过公网地址访问我们的页面。
首先进入官网
官网:https://natapp.cn/
点击下载选择适用于自己电脑的版本
购买隧道–配置隧道–解压文件–修改natapp.nin–运行natapp.exe–在前台页面中写Controller跳转页面,写一个页面展示“”success“”。
/**
* 跳转回调页面
* @param model
* @return
*/
@RequestMapping("callback")
public String callback(Model model){
model.addAttribute("result","success");
System.out.println("异步通知成功!!!!!");
return "order/callback";
}
DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title heretitle>
head>
<body>
${result}
body>
html>