一步步教你如何在SpringBoot项目中引入支付功能 您所在的位置:网站首页 支付宝怎么设置图案密码支付功能 一步步教你如何在SpringBoot项目中引入支付功能

一步步教你如何在SpringBoot项目中引入支付功能

2024-07-10 21:00| 来源: 网络整理| 查看: 265

听说微信搜索《Java鱼仔》会变更强哦!

本文收录于JavaStarter ,里面有我完整的Java系列文章,学习或面试都可以看看哦

(一)引言

支付功能如今已经成为一个需要盈利的网站的基本功能了,如今的网站如果想要做支付功能,往往都是将支付宝或者微信的支付功能集成进来。尽管支付宝已经给出了许多文档和代码,但是这项工作并没有那么简单。今天我就一步步带大家去实现在SpringBoot项目中对支付宝的功能引入。

(二)功能介绍

我们要实现的功能很简单,当传入用户购买的信息之后,生成一个二维码供支付使用,同时提供一个查询接口查询该笔订单是否已支付,这种支付方式叫做当面付。学会这一种支付方式之后,支付宝的其他的功能也会很容易上手。

首先给出当面付的文档地址:https://opendocs.alipay.com/open/194

文档中很详细地描述了支付的整体逻辑。

(三)开发前准备

首先需要引入相关的依赖,我把这个项目中会用到的依赖一次性给出:

com.alipay.sdk alipay-sdk-java 4.10.218.ALL commons-logging commons-logging commons-lang commons-lang 2.6 commons-configuration commons-configuration 1.8 commons-logging commons-logging commons-codec commons-codec 1.11 com.google.zxing core 3.2.1 org.hamcrest hamcrest-core 1.3 test com.google.code.gson gson 2.8.6 org.projectlombok lombok

同时官方已经提供了Demo,我们也直接下载下来: https://opendocs.alipay.com/open/54/104506

在这里插入图片描述

另外支付功能还涉及到私钥公钥的加签,支付宝给我们提供了密钥加密工具,也需要下载: https://opendocs.alipay.com/open/291/105971

最后还要一个沙箱环境的地址,这个地址用于测试: https://opendocs.alipay.com/open/200/105311

(四)项目搭建

首先搭建一个SpringBoot项目,这一步就跳过了,打开我们上面下载的Demo文件,里面有一个TradePayDemo和TradePaySDK,TradePaySDK是支付过程中需要调用的一些类,因此需要把TradePaySDK中的代码引进来:

在这里插入图片描述 将上面这四个文件引入到我们的项目中:

在这里插入图片描述

TradePayDemo中提供了具体代码如何调用的示例,src目录下有一个文件叫做:zfbinfo.properties,把这个文件放入到resource目录下。关于zfbinfo.properties文件,里面有五个参数是需要我们自己去填写的: 注意,由于是测试环境,因此将open_api_domain修改成: https://openapi.alipaydev.com/gateway.do 在这里插入图片描述 pid是每个人自己账号的Id,登陆沙箱环境后,点击右上角的账号,选中账户中心,里面的账号ID就是pid。

在这里插入图片描述

在沙箱环境中,你还能看到自己的appid,将这个appid赋值到配置文件中的appid处:

在这里插入图片描述

接下来就是公钥和私钥以及支付宝的公钥,上面让大家下载了工具,打开后直接用默认的加密方式生成公私钥:

在这里插入图片描述 将上面的私钥和公钥分别放入对应的private_key和public_key中。

在沙箱环境中,将上面生成的公钥复制上去,可以得到支付宝的公钥,配置文件就齐全了

在这里插入图片描述

(五)业务开发

完成上面一长串工作后,就可以开始写业务了,Demo文件中还要一个包叫做TradePayDemo的,我们主要参考里面的Main方法。

5.1 二维码生成功能

新建一个Service叫做TradeService,再新建他的实体类TradeServiceImpl

@Slf4j @Service public class TradeServiceImpl implements TradeService { // 支付宝当面付2.0服务 private static AlipayTradeService tradeService; @PostConstruct private void init(){ /** 一定要在创建AlipayTradeService之前调用Configs.init()设置默认参数 * Configs会读取classpath下的zfbinfo.properties文件配置信息,如果找不到该文件则确认该文件是否在classpath目录 */ Configs.init("zfbinfo.properties"); /** 使用Configs提供的默认参数 * AlipayTradeService可以使用单例或者为静态成员对象,不需要反复new */ tradeService = new AlipayTradeServiceImpl.ClientBuilder().build(); } @Override public String tradeQrCode(OrderDetail orderDetail){ //支付二维码的访问路径 String qrCodePath=null; // (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线, // 需保证商户系统端不能重复,建议通过数据库sequence生成, String outTradeNo = "tradeprecreate" + System.currentTimeMillis() + (long) (Math.random() * 10000000L); // (必填) 订单标题,粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费” String subject = orderDetail.getSubject(); // (必填) 订单总金额,单位为元,不能超过1亿元 // 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】 String totalAmount = orderDetail.getTotalAmount(); // (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段 // 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】 String undiscountableAmount = "0"; // 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号) // 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID String sellerId = ""; // 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元" String body = String.format("购买商品%s件共%s元",orderDetail.getGoodsDetail().size(),totalAmount); // 商户操作员编号,添加此参数可以为商户操作员做销售统计 String operatorId = "javayz"; // (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持 String storeId = "javayz001"; // 业务扩展参数,目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法),详情请咨询支付宝技术支持 ExtendParams extendParams = new ExtendParams(); extendParams.setSysServiceProviderId("2088100200300400500"); // 支付超时,定义为120分钟 String timeoutExpress = "120m"; // 商品明细列表,需填写购买商品详细信息, List goodsDetailList = new ArrayList(); orderDetail.getGoodsDetail().stream().forEach((item)->{ // 创建一个商品信息,参数含义分别为商品id(使用国标)、名称、单价(单位为分)、数量,如果需要添加商品类别,详见GoodsDetail GoodsDetail goods1 = GoodsDetail.newInstance(item.getGoodsId(), item.getGoodsName(), Long.valueOf(item.getPrice())*100, Math.toIntExact(item.getQuantity())); // 创建好一个商品后添加至商品明细列表 goodsDetailList.add(goods1); }); // 创建扫码支付请求builder,设置请求参数 AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder() .setSubject(subject).setTotalAmount(totalAmount).setOutTradeNo(outTradeNo) .setUndiscountableAmount(undiscountableAmount).setSellerId(sellerId).setBody(body) .setOperatorId(operatorId).setStoreId(storeId).setExtendParams(extendParams) .setTimeoutExpress(timeoutExpress) // .setNotifyUrl("http://www.test-notify-url.com")//支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置 .setGoodsDetailList(goodsDetailList); AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder); switch (result.getTradeStatus()) { case SUCCESS: log.info("支付宝预下单成功: )"); AlipayTradePrecreateResponse response = result.getResponse(); dumpResponse(response); // 需要修改为运行机器上的路径 String filePath = String.format("F:/qrcode/static/qrcode/qr-%s.png", response.getOutTradeNo()); log.info("filePath:" + filePath); //创建二维码 ZxingUtils.getQRCodeImge(response.getQrCode(), 256, filePath); qrCodePath=filePath; break; case FAILED: log.error("支付宝预下单失败!!!"); break; case UNKNOWN: log.error("系统异常,预下单状态未知!!!"); break; default: log.error("不支持的交易状态,交易返回异常!!!"); break; } return qrCodePath; } // 简单打印应答 private void dumpResponse(AlipayResponse response) { if (response != null) { log.info(String.format("code:%s, msg:%s", response.getCode(), response.getMsg())); if (StringUtils.isNotEmpty(response.getSubCode())) { log.info(String.format("subCode:%s, subMsg:%s", response.getSubCode(), response.getSubMsg())); } log.info("body:" + response.getBody()); } } }

代码注释已经解释的很清楚了,就是设置参数,生成二维码。

新建一个类OrderController,创建一个Post请求的接口:

@RestController @RequestMapping("/order") public class OrderController extends BaseController { @Autowired private TradeService tradeService; //创建支付二维码 @PostMapping("/qrcode") public CommonResult getQrCode(@RequestBody OrderDetail orderDetail){ String path = tradeService.tradeQrCode(orderDetail); if (StringUtils.isNotEmpty(path)){ return CommonResult.success(path); } return CommonResult.fail(); } }

访问http://localhost:8190/order/qrcode,参数可参考如下:

在这里插入图片描述

输出二维码的地址: 在这里插入图片描述 验证支付功能务必要使用沙盒地址中的钱包:

在这里插入图片描述

5.2 验证订单是否被支付

一样的逻辑,从Demo中找到验证订单的代码,放入TradeServiceImpl中

@Override public String alipayTradeQuery(String orderSn){ //返回信息 String responseResult=""; // (必填) 商户订单号,通过此商户订单号查询当面付的交易状态 String outTradeNo = orderSn; // 创建查询请求builder,设置请求参数 AlipayTradeQueryRequestBuilder builder = new AlipayTradeQueryRequestBuilder() .setOutTradeNo(outTradeNo); AlipayF2FQueryResult result = tradeService.queryTradeResult(builder); switch (result.getTradeStatus()) { case SUCCESS: responseResult="查询返回该订单支付成功"; log.info("查询返回该订单支付成功: )"); AlipayTradeQueryResponse response = result.getResponse(); dumpResponse(response); log.info(response.getTradeStatus()); if (Utils.isListNotEmpty(response.getFundBillList())) { for (TradeFundBill bill : response.getFundBillList()) { log.info(bill.getFundChannel() + ":" + bill.getAmount()); } } break; case FAILED: responseResult="查询返回该订单支付失败或被关闭"; log.error("查询返回该订单支付失败或被关闭!!!"); break; case UNKNOWN: responseResult="系统异常,订单支付状态未知"; log.error("系统异常,订单支付状态未知!!!"); break; default: responseResult="不支持的交易状态,交易返回异常"; log.error("不支持的交易状态,交易返回异常!!!"); break; } return responseResult; }

在Controller中加一个接口

//查询订单情况 @PostMapping("/queryOrderStatus") public CommonResult queryOrderStatus(@RequestParam("orderSn") String orderSn){ String result = tradeService.alipayTradeQuery(orderSn); if (StringUtils.isEmpty(result)){ return CommonResult.fail(); }else { return CommonResult.success(result); } } 5.3 设置一个回调的接口

可以通过5.2中的方法定时轮询订单是否被支付,也可以写一个回调接口给支付宝调用,但是这个接口必须确保能被支付宝外网访问到,这里我给出代码示例:

@PostMapping("payCallback") public void payCallback(){ Map map=new HashMap(); Enumeration parameterNames = getRequest().getParameterNames(); while (parameterNames.hasMoreElements()){ String parameter = parameterNames.nextElement(); if (!parameter.toLowerCase().equals("sign_type")){ map.put(parameter,getRequest().getParameter(parameter)); } } try { boolean result = AlipaySignature.rsaCertCheckV2(map, Configs.getPublicKey(), "utf-8", Configs.getSignType()); PrintWriter writer = getResponse().getWriter(); if (result){ writer.print("success"); }else { writer.print("unSuccess"); } } catch (AlipayApiException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }

然后在生成二维码的代码中增加回调接口

在这里插入图片描述

(六)总结

到这里,当面付的功能我们就开发好了,说简单也不简单,各种步骤都比较繁琐,但是只要把整体逻辑理清楚了,后续其他支付功能的开发就会很简单了。我是鱼仔,我们下期再见!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有