在前面我们业务功能篇98-99中,我们介绍了电商项目中的订单模块服务,那么最后就是需要进行支付动作,那么我们这里就通过订阅第三方平台支付宝的支付调用接口功能,来进一步完成订单提交后的支付动作,支付宝的接口使用可以登录官网开发指南详情去了解
SDK依赖引入
在我们对应需要进行支付调用的订单服务中的pom文件加入依赖,其他服务不涉及的不需要加
XML
<!-- 支付宝SDK -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.31.12.ALL</version>
</dependency>
配置支付宝的配置文件类
- 根据支付宝的接口使用方式,传递对应订单封装类对象,进行支付操作,并且还有同步回调地址,在支付完成之后自动跳转到我们指定页面
- 回调地址:public static String return_url = "http://order.msb.com/orderPay/returnUrl"; 指定到该接口,该接口会进行支付完成后需要的操作,比如修改订单的状态为已完成,发货状态为待发货等等.. 然后return指定的html订单详情页面list.html
java
/**
* 支付宝的配置文件
*/
//@ConfigurationProperties(prefix = "alipay")
@Component
@Data
public class AlipayTemplate {
// 商户appid 沙箱账号: tklalf8880@sandbox.com
public static String APPID = "2021000121601310";
// 私钥 pkcs8格式的
public static String RSA_PRIVATE_KEY = "xx";
// 服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://order.msb.com/payed/notify";
// 页面跳转同步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 商户可以自定义同步跳转地址
public static String return_url = "http://order.msb.com/orderPay/returnUrl";
// 请求网关地址
public static String URL = "https://openapi.alipaydev.com/gateway.do";
// 编码
public static String CHARSET = "UTF-8";
// 返回格式
public static String FORMAT = "json";
// 支付宝公钥
public static String ALIPAY_PUBLIC_KEY = "xx";
// 日志记录目录
public static String log_path = "/log";
// RSA2
public static String SIGNTYPE = "RSA2";
public String pay(PayVo payVo){
// SDK 公共请求类,包含公共请求参数,以及封装了签名与验签,开发者无需关注签名与验签
//调用RSA签名方式
AlipayClient client = new DefaultAlipayClient(URL,
APPID,
RSA_PRIVATE_KEY,
FORMAT,
CHARSET,
ALIPAY_PUBLIC_KEY,
SIGNTYPE);
AlipayTradeWapPayRequest alipay_request=new AlipayTradeWapPayRequest();
// 封装请求支付信息
AlipayTradeWapPayModel model=new AlipayTradeWapPayModel();
model.setOutTradeNo(payVo.getOut_trader_no());
model.setSubject(payVo.getSubject());
model.setTotalAmount(payVo.getTotal_amount());
model.setBody(payVo.getBody());
model.setTimeoutExpress("5000");
model.setProductCode("11111");
alipay_request.setBizModel(model);
// 设置异步通知地址
alipay_request.setNotifyUrl(notify_url);
// 设置同步地址
alipay_request.setReturnUrl(return_url);
// form表单生产
String form = "";
try {
// 调用SDK生成表单
form = client.pageExecute(alipay_request).getBody();
return form;
} catch (AlipayApiException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
支付接口
controller层
payOrder:在订单页面中,点击 支付宝发起支付请求的方法
orderPay:支付完成之后,会同步回调到订单详情的方法,同时执行支付完成后需要的动作,比如修改订单状态为 关闭, 发货状态为待发货等等..
java
@Controller
public class OrderWebController {
@Autowired
private OrderService orderService;
@Autowired
AlipayTemplate alipayTemplate;
/**
* 获取订单相关信息
* 然后跳转到支付页面 tklalf8880@sandbox.com
* @param orderSn
* @return
*/
@GetMapping(value = "/payOrder",produces = "text/html")
@ResponseBody
public String payOrder(@RequestParam("orderSn") String orderSn){
// 根据订单编号查询出相关的订单信息,封装到PayVO中
PayVo payVo = orderService.getOrderPay(orderSn);
String pay = alipayTemplate.pay(payVo);
//System.out.println(pay);
return pay;
}
@GetMapping("/orderPay/returnUrl")
public String orderPay(@RequestParam(value = "orderSn",required = false) String orderSn,
@RequestParam(value = "out_trade_no",required = false) String out_trade_no){
// TODO 完成相关的支付操作
System.out.println("orderSn = " + orderSn);
if(StringUtils.isNotBlank(orderSn)){
orderService.handleOrderComplete(orderSn);
}else{
//orderService.updateOrderStatus(out_trade_no,OrderConstant.OrderStateEnum.TO_SEND_GOODS.getCode());
orderService.handleOrderComplete(out_trade_no);
}
return "list";
}
service层
java
@Override
//封装订单号对应的信息 返回给支付宝配置类的方法
public PayVo getOrderPay(String orderSn) {
// 根据订单号查询相关的订单信息
OrderEntity orderEntity = this.getBaseMapper().getOrderByOrderSn(orderSn);
// 通过订单信息封装 PayVO对象
PayVo payVo = new PayVo();
payVo.setOut_trader_no(orderSn);
payVo.setTotal_amount(orderEntity.getTotalAmount().setScale(2, RoundingMode.UP).toString());
// 订单名称和订单描述
payVo.setSubject(orderEntity.getOrderSn());
payVo.setBody(orderEntity.getOrderSn());
return payVo;
}
@Override
//支付完成之后,修改订单状态
public void handleOrderComplete(String orderSn) {
// 1.更新订单状态
this.updateOrderStatus(orderSn,OrderConstant.OrderStateEnum.TO_SEND_GOODS.getCode());
// TODO
// 2.更新库存信息 库存数量递减
// 3.购物车中的已经支付的商品移除
// 4.更新会员积分 ....
}
前端核心请求跳转
html
<form action="/orderPay/returnUrl" method="get">
<input type="hidden" name="orderSn" th:value="${orderResponseVO.orderEntity.orderSn}">
<li>
<input type="submit" value="立即支付">
</li>
</form>
<li>
<img src="/static/order/pay/img\zhifubao.png" style="weight:auto;height:30px;" alt="">
<a th:href="'/payOrder?orderSn='+${orderResponseVO.orderEntity.orderSn}">
支付宝
</a>
</li>
支付宝支付完成之后的回调请求需要放过拦截器,否则会报错需要重新登录的,因为是不同的一个域名跳转,所以之前保存的用户登录session会话就不会带过来,所以需要放过这个请求接口
new AntPathMatcher().match("/orderPay/**", requestURI)
java
public class AuthInterceptor implements HandlerInterceptor {
public static ThreadLocal threadLocal = new ThreadLocal();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 支付宝的回调我们放过
String requestURI = request.getRequestURI();
boolean match = new AntPathMatcher().match("/orderPay/**", requestURI);
if(match){
return true;
}
// 通过HttpSession获取当前登录的用户信息
HttpSession session = request.getSession();
Object attribute = session.getAttribute(AuthConstant.AUTH_SESSION_REDIS);
if(attribute != null){
MemberVO memberVO = (MemberVO) attribute;
threadLocal.set(memberVO);
return true;
}
// 如果 attribute == null 说明没有登录,那么我们就需要重定向到登录页面
session.setAttribute(AuthConstant.AUTH_SESSION_MSG,"请先登录");
response.sendRedirect("http://auth.msb.com/login.html");
return false;
}
}