SpringBoot整合支付宝支付

前言

 最近一直在负责公司的支付模块,现在想要把SringBoot整合支付宝的当面付整理出来。
支付宝当面付链接

 感兴趣的小伙伴们可以参考一下SpringBoot开发微信Native支付

支付宝当面付配置

 支付宝的当面付和支付宝其它支付功能有些不一样的地方,当面付的官方demo中是有一个zfbinfo.properties的配置文件,支付宝的其它支付功能直接new支付宝整合的对象即可

支付宝zfbinfo.properties配置文件

# 支付宝网关名、partnerId和appId
# 请求支付宝的网关 
# https://openapi.alipay.com/gateway.do 正式环境的alipay不带dev
# https://openapi.alipaydev.com/gateway.do 沙箱环境的alipay带dev
open_api_domain = https://openapi.alipay.com/gateway.do
mcloud_api_domain = http://mcloudmonitor.com/gateway.do

# PID就是沙箱环境的商户UID
pid = 你的PID
# 你的APPID
appid = 你的APPID

# RSA私钥、公钥和支付宝公钥
private_key = 你的RSA2的私钥
public_key = 你的RSA2的公钥

#SHA1withRsa对应支付宝公钥
#alipay_public_key = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDI6d306Q8fIfCOaTXyiUeJHkrIvYISRcc73s3vF1ZT7XN8RNPwJxo8pWaJMmvyTn9N4HQ632qJBVHf8sxHi/fEsraprwCtzvzQETrNRwVxLO5jVmRGi60j8Ue1efIlzPXV9je9mkjzOmdssymZkh2QhUrCmZYI/FCEa3/cNMW0QIDAQAB

#SHA256withRsa对应支付宝公钥
alipay_public_key = 你的支付宝公钥

# 签名类型: RSA->SHA1withRsa,RSA2->SHA256withRsa
sign_type = RSA2
# 当面付最大查询次数和查询间隔(毫秒)
max_query_retry = 5
query_duration = 5000

# 当面付最大撤销次数和撤销间隔(毫秒)
max_cancel_retry = 3
cancel_duration = 2000

# 交易保障线程第一次调度延迟和调度间隔(秒)
heartbeat_delay = 5
heartbeat_duration = 900

项目引入支付宝当面付的jar包

项目中引入支付宝当面付jar包

pom文件引入系统的jar包

		<!-- 引入项目的支付宝支付maven依赖 -->
        <dependency>
            <groupId>com.yiyang.pay</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>3.3.0</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/alipay-sdk-java-3.3.0.jar</systemPath>
        </dependency>

        <dependency>
            <groupId>com.yiyang.pay</groupId>
            <artifactId>alipay-trade-sdk</artifactId>
            <version>20161215</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/alipay-trade-sdk-20161215.jar</systemPath>
        </dependency>
        <!-- 引入支付宝需要使用的commons -->
        <dependency>
            <groupId>commons-configuration</groupId>
            <artifactId>commons-configuration</artifactId>
            <version>1.10</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

发起支付宝当面付支付请求

Controller层

/**
     * 支付宝当面付生成二维码
     * @return
     */
    @PostMapping("/accrueQRCode")
    public R accrueQRCode(){
        return zfbPayService.accrueQRCode();
    }

Service层

/**
     * 支付宝当面付生成二维码
     * @return
     */
    R accrueQRCode();

实现类

/**
     * 支付宝当面付生成二维码
     * @return
     */
    @Override
    public R accrueQRCode() {

        // (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线,
        // 需保证商户系统端不能重复,建议通过数据库sequence生成,
        String outTradeNo = createOrderNo().toString();

        // (必填) 订单标题,粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费”
        String subject = "某某商城扫码消费";

        // (必填) 订单总金额,单位为元,不能超过1亿元
        // 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
        String totalAmount = "10";

        // (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段
        // 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】
        String undiscountableAmount = "0";

        // 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)
        // 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID
        String sellerId = "";
        
        // 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"
        String body = "购买2件商品共5元";

        // 商户操作员编号,添加此参数可以为商户操作员做销售统计
        String operatorId = "test_operator_id";

        // (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持
        String storeId = "test_store_id";

        // 业务扩展参数,目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法),详情请咨询支付宝技术支持
        ExtendParams extendParams = new ExtendParams();
        extendParams.setSysServiceProviderId("2088100200300400500");

        // 支付超时,定义为120分钟
        String timeoutExpress = "120m";

        // 商品明细列表,需填写购买商品详细信息,
        List<GoodsDetail> goodsDetailList = new ArrayList<GoodsDetail>();

        //orderNos为前端传来的订单编号 根据需求修改
        for (String orderNo : orderNos) {

            //根据订单编号和用户id查询商品 防止横向越权
            List<OrderAndGoodsListVO> orderAndGoodsListVOList = orderDao.zfbPayGetOrderAndGoodInfo(orderNo, userId);
            if (orderAndGoodsListVOList.size() > 0){
                for (OrderAndGoodsListVO orderAndGoodsListVO : orderAndGoodsListVOList) {
                    //根据订单id修改使用支付宝支付的支付总订单编号
                    TOrder order = new TOrder();
                    order.setOrderId(orderAndGoodsListVO.getOrderId());
                    order.setPayTotalOrderNo(outTradeNo);
                    //设置订单的支付总订单编号
                    orderDao.updatePayTotalOrderNo(order);
                    log.info("购买的商品为:" + orderAndGoodsListVO);
                    // 创建一个商品信息,参数含义分别为商品id(使用国标)、名称、单价(单位为分)、数量,如果需要添加商品类别,详见GoodsDetail
                    GoodsDetail goodsDetail = GoodsDetail.newInstance(orderAndGoodsListVO.getGoodsId().toString(),orderAndGoodsListVO.getGoodsName(),
                            orderAndGoodsListVO.getSalePrice().longValue(),orderAndGoodsListVO.getCount());
                    goodsDetailList.add(goodsDetail);
                }
            }
        }

        // 创建扫码支付请求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)
                //支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置
                //设置支付宝的回调地址(必须外网能够访问)
				.setNotifyUrl("http://8fzxg9.natappfree.cc/zfbPayController/zfbCallBack")
                .setGoodsDetailList(goodsDetailList);

        AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);
        switch (result.getTradeStatus()) {
            case SUCCESS:
                log.info("支付宝预下单成功: )");

                AlipayTradePrecreateResponse response = result.getResponse();
                dumpResponse(response);

                //这里不需要修改商品的信息 在支付宝回调的地方修改商品的支付信息

                // 需要修改为运行机器上的路径
                String filePath = String.format("/Users/sudo/Desktop/qr-%s.png",
                        response.getOutTradeNo());
                log.info("filePath:" + filePath);
                //                ZxingUtils.getQRCodeImge(response.getQrCode(), 256, filePath);
                break;

            case FAILED:
                log.error("支付宝预下单失败!!!");
                return R.error(-1,"支付宝预下单失败");

            case UNKNOWN:
                log.error("系统异常,预下单状态未知!!!");
                return R.error(-1,"系统异常,预下单状态未知");

            default:
                log.error("不支持的交易状态,交易返回异常!!!");
                return R.error(-1,"不支持的交易状态,交易返回异常");
        }
        return R.ok(1,"支付宝预下单成功",result.getResponse().getQrCode());
    }

支付宝回调

 支付宝回调主要是用来我们发给支付宝的请求之后,支付宝那边给我们返回的一些基本信息,包括APPID、订单的信息等,我们拿到支付宝回调之后处理一下这些信息,具体的逻辑根据自己公司的需求来做即可。
支付宝回调参数链接

支付宝回调Controller

 支付宝回调给我们的是二进制流,我们将支付宝回调的参数转为Map集合

/**
     * 支付宝回调接口
     * @param request
     * @return
     */
    @PostMapping("/zfbCallBack")
    public R zfbCallBack(HttpServletRequest request){
        System.out.println("进入支付宝回调接口");
        //new一个HashMap集合
        Map<String,String> map = new HashMap<>();
        //获取支付宝返回给我们的请求参数 异步通知参数就在这个请求中
        Map requestParams = request.getParameterMap();
        System.out.println("支付宝回调的参数:" + requestParams);

        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();){
            //获取下一个数据
            String name = (String) iter.next();
            //获取数据
            //因为支付宝返回给我们的异步通知参数有很多参数 所以我们需要创建一个String类型的数组来进行接收
            //values=后面的是因为异步通知参数有些不是String类型的 所以需要我们转换一下
            String[] values = (String[])requestParams.get(name);
            //打印一下获取的key和value 也就是异步通知参数的key(键) 也就是参数(name)和value(值) 也就是类型 都将类型转换为String类型的
            System.out.println("迭代===>" + name + "===>" + Arrays.toString(values));
            //定义一个String类型的变量来存储value值
            String valueStr = "";
            //循环遍历获取key对应的值
            for (int i = 0;i < values.length;i++){
                //获取支付宝回调给我们的参数
                valueStr = (i == values.length-1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            //将校验数据存放到map集合中
            //为什么不用写双引号 是因为我们的name和valueStr都是String字符串类型的 所以就不需要写双引号了
            map.put(name,valueStr);
        }
        //这里我们将支付宝回调中的sign(签名)和trade_status(交易状态)进行一下输出
        log.info("支付宝回调:sign:{},trade_status:{},参数:{}",map.get("sign"),map.get("trade_status"),map.toString());

        //将支付宝实现的默认的签名方式移除掉 因为默认实现的RSA1方式 我们移除之后 会使用 zfbinfo.properties文件中的sign_type配置
        //移除签名方式sign_type(签名类型)
        map.remove("sign_type");

        try {
            //确定是否为支付宝的回调
            boolean rsaCheckV2 = AlipaySignature.rsaCheckV2(map, Configs.getAlipayPublicKey(), "utf-8", Configs.getSignType());
            //如果这个为false 返回错误信息
            if (!rsaCheckV2){
                return R.error(-1,"非法请求 验证不通过");
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return zfbPayService.zfbCallBack(map);
    }

支付宝回调Service

/**
     * 支付宝回调接口
     * @param map:支付宝回调参数map集合
     * @return
     */
    R zfbCallBack(Map<String, String> map);

支付宝回调实现类

 获取支付宝的回调信息,根据自己的需求处理信息

/**
     * 支付宝回调接口
     * @param map:支付宝回调参数map集合
     * @return
     */
    @Override
    public R zfbCallBack(Map<String, String> map) {
    	//获取支付宝回调回来的信息
        //获取商户订单号(商品的订单编号)
        String orderNos = map.get("out_trade_no");
        //获取支付的状态
        String tradeStatus = map.get("trade_status");
        //订单的付款时间
        String payTime = map.get("gmt_payment");
        //用户的付款金额
        String buyerPayAmount = map.get("buyer_pay_amount");
        //获取支付宝买家账号
        String buyerId = map.get("buyer_id");
        log.info("支付宝回调时获取的支付宝买家账号:{}" + buyerId);
        log.info("支付宝回调的付款金额为:" + buyerPayAmount);
        //用户请求支付时的商品信息
        log.info("支付宝的回调状态:" + tradeStatus);
        //根据逗号分割订单编号
        String[] split = orderNos.split(",");
        log.info("支付宝回调时 回调的订单编号的大小:" + orderNos.length());
        //判断是否支付成功 支付成功后开始自己的代码逻辑
        if (tradeStatus.equals(EnumFile.AlipayEnum.TRADE_SUCCESS.getStatus())){
            //new一个订单集合
            List<TOrder> orderList = new ArrayList<>();
            for (String orderNo : split) {
                log.info("支付宝回调时 回调的订单编号为:" + orderNo);
                //根据订单编号查询订单
                List<TOrder> tOrderList = orderDao.findByOrderNo(orderNo);
                if (tOrderList.size() < 1){
                    log.info("支付宝回调失败???");
                    return R.error(-1,"支付失败 没有查到相关订单信息");
                }
                //遍历查到的订单
                for (TOrder tOrder : tOrderList) {
                    //也可以在这里修改这个订单和商品的库存什么的 但是先不在这修改了
                    //将查到的订单添加到
                    orderList.add(tOrder);
                }
            }
            //遍历订单集合
            log.info("支付宝支付成功 修改订单的信息");
            for (TOrder tOrder : orderList) {
                //修改该订单的信息
                //设置订单的支付状态
                tOrder.setOrderStatus(EnumFile.order.ORDER_STATUS_01.getCode());
                tOrder.setPayTime(DateUtils.strDateTime(payTime));
                tOrder.setPaymentMethod(EnumFile.pay.PAY.getCode());
                tOrder.setAlipay(buyerPayAmount);
                tOrder.setBuyerId(buyerId);
                //修改该订单的信息
                Integer updateStatus = orderDao.updateZfbPayOrder(tOrder);
                //添加用户的历史账单
                addUserHistoryBill(tOrder);
            }
            //支付成功后,将支付成功的信息存入Redis
            ValueOperations valueOperations = redisTemplate.opsForValue();
            //存入Redis中支付成功信息的key为 用户id:付款金额
            valueOperations.set(orderList.get(0).getUserId().toString(),"支付成功:" + buyerPayAmount);
            redisTemplate.expire(orderList.get(0).getUserId().toString(),TIME, TimeUnit.MINUTES);
            log.info("支付宝回调 支付成功后存入Redis中的数据" + valueOperations.get(orderList.get(0).getUserId().toString()).toString());
        }else if (tradeStatus.equals(EnumFile.AlipayEnum.TRADE_CLOSED.getStatus())){
            log.info("支付宝 未付款交易超时关闭");
            return R.error(-1,"未付款交易超时关闭。");
        }else if (tradeStatus.equals(EnumFile.AlipayEnum.TRADE_FINISHED.getStatus())){
            log.info("支付宝 交易结束,不可退款。");
            return R.error(-1,"交易结束,不可退款。");
        }else if (tradeStatus.equals(EnumFile.AlipayEnum.WAIT_BUYER_PAY.getStatus())){
            log.info("支付宝 交易创建,等待买家付款。");
            return R.error(-1,"交易创建,等待买家付款。");
        }
        return R.error(-1,"支付宝 交易支付成功");
    }

总结

 SpringBoot整合支付宝支付相对来说简单一些,我个人感觉比微信支付简单太多了,过几天会出SpringBoot整合微信的文章(已出)。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中使用支付宝沙箱环境进行支付的步骤如下: 1. 首先,在项目的pom.xml文件中添加支付宝支付的依赖。例如,可以添加以下依赖: ``` <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.16.2.ALL</version> </dependency> ``` 这样就可以在项目中使用支付宝的SDK。 2. 创建支付的Controller类。在Controller类中,可以编写处理支付宝支付的逻辑。例如,可以创建一个PayController类,并在其中编写相应的代码。 3. 下载沙箱支付宝APP。由于沙箱环境是虚拟的,所以无法使用真实的支付宝进行扫码支付。需要下载沙箱支付宝APP,并使用其中的账号密码进行登录。同时,在支付时也需要使用沙箱环境的密码。 4. 编写支付完成后的同步回调方法。在支付完成后,支付宝会自动调用之前设置好的RETURN_URL,所以需要在Controller中编写相应的方法来处理回调。这个地址可以是私网地址。 5. 在前端页面点击支付后,会跳转到Controller的alipay()方法。在alipay()方法中,可以接收前端传递的参数,并调用支付宝提供的接口来完成支付。在sendRequestToAlipay()方法中,需要提供支付宝严格规定的参数。 以上是使用Spring Boot进行支付宝沙箱环境支付的基本步骤。通过这些步骤,可以在Spring Boot项目中实现支付宝支付功能。 #### 引用[.reference_title] - *1* *2* *3* [Springboot支付宝沙箱支付---完整详细步骤](https://blog.csdn.net/hhb442/article/details/123304287)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值