1.RSA、加密加签、密钥
1.对称加密
- 不安全,一旦一方拥有密钥即可自由通信
2.非对称加密
- 通过4把钥匙来保证整个通信的安全性,使用RSA加密算法实现
- 公钥、私钥
- 相对概念,公私性是相对于生成者来说的,保存在生产者手里的就是私钥,生成者发布出去的就是公钥
- 加密和签名
- 我们使用一对公私钥中的一个密钥来对数据进行加密,而使用另一个密钥来进行解密的技术 公钥和私钥都可以用来加密,也都可以用来解密。 但这个加解密必须是一对密钥之间的互相加解密,否则不能成功。
- 加密的目的是:为了确保数据传输过程中的不可读性,就是不想让别人看到。
- 签名和验签
支付宝为了验证请求的数据是否商户本人发的, 商户为了验证响应的数据是否支付宝发的
2.集成支付宝
1.沙箱环境配置公私钥
3.支付流程图
4.代码实现
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.34.71.ALL</version>
</dependency>
// alipayConfig
@Configuration
@ConfigurationProperties(prefix = "alipay.config")
public class AliPayClientConfig {
@Setter
private String url;
@Setter
private String appId;
@Setter
private String alipayPublicKey;
@Setter
private String privateKey;
@Bean
public AlipayClient alipayClient() throws AlipayApiException {
AlipayConfig alipayConfig = new AlipayConfig();
// 支付宝网关
alipayConfig.setServerUrl(url);
// APPID
alipayConfig.setAppId(appId);
// 开发者私钥
alipayConfig.setPrivateKey(privateKey);
// 参数返回格式,只支持 JSON(固定)
alipayConfig.setFormat(AlipayConstants.FORMAT_JSON);
// 编码集,支持 GBK/UTF-8
alipayConfig.setCharset(AlipayConstants.CHARSET_UTF8);
// 支付宝公钥,由支付宝生成
alipayConfig.setAlipayPublicKey(alipayPublicKey);
// 生成签名字符串所使用的签名算法类型,目前支持 RSA2 算法
alipayConfig.setSignType(AlipayConstants.SIGN_TYPE_RSA2);
//构造client
return new DefaultAlipayClient(alipayConfig);
}
}
# 支付配置
alipay:
config:
url: https://openapi.alipaydev.com/gateway.do
appId: ***
alipayPublicKey: ***
privateKey: ***
returnUrl: http://order.dreammall.com/listWithItem.html
notifyUrl: https://k24p635902.imdo.co/payed/notify
- controller
@Controller
@Slf4j
public class OrderPayWebController {
@Autowired
private OrderService orderService;
@Value("${alipay.config.alipayPublicKey}")
@Setter
@Getter
public String alipayPublicKey;
/**
* 跳转到支付宝收银页面,等待付款
*
* @param orderSn
* @return
*/
@ResponseBody
@GetMapping(value = "/aliPayOrder", produces = "text/html")
public String aliPayOrder(@RequestParam("orderSn") String orderSn) throws AlipayApiException {
log.info("订单等待付款:orderSn={}", orderSn);
AlipayTradePagePayResponse response = orderService.aliPayOrder(orderSn);
return response.getBody();
}
/**
* 付款成功,跳转回会员信息页
*
* @param pageNum
* @param pageSize
* @param model
* @return
*/
@GetMapping("/listWithItem.html")
public String listWithItem(@RequestParam(value = "pageNum", required = false, defaultValue = "0") Integer pageNum,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize,
Model model) {
PageUtils orderInfo = orderService.listWithItem(pageNum, pageSize);
model.addAttribute("orders", orderInfo);
return "list";
}
/**
* 付款成功,异步通知外网地址,修改订单状态
* 1.购物车redis删除
* 2.库存数没减
*/
@PostMapping("/payed/notify")
@ResponseBody
public String handleAliPayed(@RequestParam Map<String, String> prams) {
// todo 坑 用requestBody接收实体失败 内网穿透无效 后面有时间再弄
log.info("支付成功,接收到异步通知,prams={}", prams);
String result = "failure";
try {
boolean signVerified = AlipaySignature.rsaCheckV1(prams, alipayPublicKey, AlipayConstants.CHARSET_UTF8, AlipayConstants.SIGN_TYPE_RSA2); //调用SDK验证签名
// 验签失败
if (!signVerified) {
return result;
}
PayAsyncVo asyncVo = JSONObject.parseObject(JSONObject.toJSONString(prams), PayAsyncVo.class);
return orderService.handleAliPayed(asyncVo);
} catch (AlipayApiException e) {
log.error("调用支付宝时出现异常", e);
return result;
}
}
}
- service
/**
* 跳转到支付宝支付页面
*
* @param orderSn
* @return
*/
@Override
public AlipayTradePagePayResponse aliPayOrder(String orderSn) throws AlipayApiException {
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
AlipayTradePagePayResponse response = new AlipayTradePagePayResponse();
// 异步通知地址 修改订单状态为已支付
request.setNotifyUrl(notifyUrl);
// 支付成功跳转地址
request.setReturnUrl(returnUrl);
JSONObject bizContent = new JSONObject();
// 商户订单号
Order order = getOne(Wrappers.<Order>lambdaQuery().eq(Order::getOrderSn, orderSn));
// 订单标题
List<OrderItem> orderItems = orderItemService.list(Wrappers.<OrderItem>lambdaQuery().eq(OrderItem::getOrderSn, orderSn));
OrderItem orderItem = orderItems.get(0);
bizContent.put("out_trade_no", orderSn);
// 交易金额 保留2位小数(支付宝要求)
bizContent.put("total_amount", order.getPayAmount().setScale(2, BigDecimal.ROUND_UP).toString());
// 订单标题
bizContent.put("subject", orderItem.getSkuName());
// 销售产品码
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
// 关单时间 超过多少分钟
bizContent.put("timeout_express", "2m");
request.setBizContent(bizContent.toString());
response = alipayClient.pageExecute(request);
return response;
}
/**
* 支付成功后显示用户订单信息
*
* @param pageNum
* @return pageSize
*/
@Override
public PageUtils listWithItem(Integer pageNum, Integer pageSize) {
ThreadLocal<MemberResponseVo> user = LoginUserInterceptor.loginUser;
Page<Order> orderPage = baseMapper.selectPage(new Page<>(pageNum, pageSize), Wrappers.<Order>lambdaQuery().eq(Order::getMemberId, user.get().getId()));
List<Order> orderList = orderPage.getRecords().stream().map(order -> {
List<OrderItem> orderItems = orderItemService.list(Wrappers.<OrderItem>lambdaQuery().eq(OrderItem::getOrderSn, order.getOrderSn()));
order.setOrderItemEntityList(orderItems);
return order;
}).collect(Collectors.toList());
orderPage.setRecords(orderList);
return new PageUtils(orderPage);
}
/**
* 支付成功异步回调修改订单状态真正扣减库存数
* @param payAsyncVo
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String handleAliPayed(PayAsyncVo payAsyncVo) {
MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();
//保存交易流水信息
PaymentInfo paymentInfo = new PaymentInfo();
paymentInfo.setOrderSn(payAsyncVo.getOutTradeNo());
paymentInfo.setAlipayTradeNo(payAsyncVo.getTradeNo());
paymentInfo.setTotalAmount(new BigDecimal(payAsyncVo.getBuyerPayAmount()));
paymentInfo.setSubject(payAsyncVo.getBody());
paymentInfo.setPaymentStatus(payAsyncVo.getTradeStatus());
paymentInfo.setCreateTime(new Date());
paymentInfo.setCallbackTime(payAsyncVo.getNotifyTime());
//添加到数据库中
this.paymentInfoService.save(paymentInfo);
//修改订单状态
String tradeStatus = payAsyncVo.getTradeStatus();
if (tradeStatus.equals("TRADE_SUCCESS") || tradeStatus.equals("TRADE_FINISHED")) {
//支付成功状态
String orderSn = payAsyncVo.getOutTradeNo(); //获取订单号
update(Wrappers.<Order>lambdaUpdate().eq(Order::getOrderSn, orderSn).set(Order::getStatus, OrderStatusEnum.PAYED.getCode()));
// task_detail修改状态
// 扣减库存数
wareFeignService.paySucceedUnStock(orderSn);
}
return "success";
}
@Configuration
public class LoginUserInterceptor implements HandlerInterceptor {
public static ThreadLocal<MemberResponseVo> loginUser = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object attribute = request.getSession().getAttribute(AuthConstant.LOGIN_USER);
String uri = request.getRequestURI();
AntPathMatcher antPathMatcher = new AntPathMatcher();
boolean match = antPathMatcher.match("/order/order/**", uri);
// 拦截器需要将异步回调路径放开
if (antPathMatcher.match("/payed/notify", uri)){
return true;
}
if (match) {
return true;
}
if (Objects.nonNull(attribute) && attribute instanceof MemberResponseVo) {
MemberResponseVo vo = (MemberResponseVo) attribute;
loginUser.set(vo);
return true;
}
request.getSession().setAttribute("msg", "请先进行登录");
response.sendRedirect("http://auth.dreammall.com/login.html");
return false;
}
}
- 内网穿透 配合nginx实现