美文网首页
java后台当面付支付宝接入三

java后台当面付支付宝接入三

作者: 简单coder | 来源:发表于2018-04-20 22:55 被阅读484次

这一章开始正式接入支付宝支付功能.

接入前声明一下,一定要确保demo能够正常跑通.跑不通直接接就是给自己找罪受o(╯□╰)o

1.环境配置

1.配置需要的文件

properties文件和alipay所需jar包如下,复制即可


2.alipay所需的三方jar,我们用pom依赖进来,版本就焊死alipay需要的版本,如下

3.增加一个将jar包编译到package的插件

为了让线上的版本也有我们拖进来的jar包,我们这里需要在pom插件这里增加一个插件,目录指定为lib文件夹,如下



记得jar包需要添加到我们的工程中,如下


4.运行main函数

为了让自己的项目能很快地兼容alipay的环境,我们可以直接把demo中的main函数拖进来



编译运行main函数,如下



显示下单成功即可,说明环境配置是没有问题的

2.接入业务代码

环境配置完成,我们可以直接开始告我们的业务了.说干就干!

1.新建控制器

传orderNo是因为我们支付模块只对我们系统的订单模块负责


注意点:

支付宝回调的验签在sdk里面只remove了sign,我们需要在外面把sign_type移除,否则验签通不过

支付代码逻辑

1.控制器代码,主要功能就是判断一下登录权限,代码实现在impl

    /*
     * 支付
     * */
    @RequestMapping(value = "pay.do", method = RequestMethod.POST)
    @ResponseBody
    public ServerResponse pay(HttpSession session, Long orderNo, HttpServletRequest request) {

        User user = (User) session.getAttribute(Const.CURRENT_USER);
        //判断用户登录
        if (user == null) {
            return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),ResponseCode.NEED_LOGIN.getDesc());
        }
        //判断订单与用户是否一致
        String path = request.getSession().getServletContext().getRealPath("upload");
        return iOrderService.pay(user.getId(),orderNo,path);
    }

2.pay接口service层实现
这一层主要实现支付宝的调用,基本源码就是抄的当面付的demo,不过有些参数修改成我们的业务参数

public ServerResponse pay(Integer userId,Long orderNo,String path) {

        HashMap<String, String> resultMap = Maps.newHashMap();
        Order order = orderMapper.selectByUserIdAndOrderNo(userId, orderNo);
        if (order == null) {
            return ServerResponse.createByErrorMessage("用户没有该订单");
        }

        resultMap.put("orderNo",order.getOrderNo().toString());


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

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

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

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

        // 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)
        // 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID
        String sellerId = "";

        // 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"
        String body = "订单:"+order.getOrderNo()+"购买商品共"+order.getPayment();

        // 商户操作员编号,添加此参数可以为商户操作员做销售统计
        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>();

        List<OrderItem> orderItems = orderItemMapper.getByOrderNoAndUserId(orderNo, userId);
        for (OrderItem item :
                orderItems) {
            GoodsDetail goods = GoodsDetail.newInstance(item.getId().toString(), item.getProductName(), BigDecimalUtil.mul(item.getCurrentUnitPrice().doubleValue(),new Double(100).doubleValue()).longValue(), item.getQuantity());
            goodsDetailList.add(goods);
        }

        // 创建扫码支付请求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(PropertiesUtil.getProperty("alipay.callback.url"))//支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置
                .setGoodsDetailList(goodsDetailList);

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

                AlipayTradePrecreateResponse response = result.getResponse();
                dumpResponse(response);
                //path: 127.0.0.1:8080/upload
                //这里搞图片存储
                File folder = new File(path);
                if (!folder.exists()) {
                    folder.setWritable(true);
                    folder.mkdirs();
                }



                // 需要修改为运行机器上的路径,注意加/
                String qrPath = String.format(path+"/qr-%s.png",
                        response.getOutTradeNo());
                String qrFileName = String.format("qr-%s.png",response.getOutTradeNo());
                ZxingUtils.getQRCodeImge(response.getQrCode(),256,qrPath);
                log.info("filePath:" + qrPath);

                resultMap.put("qrUrl",qrPath);

                return ServerResponse.createBySuccess(resultMap);
                //                ZxingUtils.getQRCodeImge(response.getQrCode(), 256, filePath);
            case FAILED:
                log.error("支付宝预下单失败!!!");
                return ServerResponse.createByErrorMessage("支付宝预下单失败!!!");

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

            default:
                log.error("不支持的交易状态,交易返回异常!!!");
                return ServerResponse.createByErrorMessage("不支持的交易状态,交易返回异常!!!");
        }
    }

支付下单这块注意这点



我没有自己的图片服务器,所以把二维码图片保存到了服务器里面
下面简单讲一下支付的调试流程

1.先去数据库中拿一个未支付的数据



2.调用支付接口



3.支付宝下单成功后去查看图片

4.访问图片



5.支付成功

支付成功后支付宝通过我们的natapp成功调起我们的接口,这里可以看一下参数



这里的参数传来我们需要的所有参数

3.支付结果处理

1.controller

@RequestMapping("alipay_callback.do")
    @ResponseBody
    public Object alipayCallback(HttpServletRequest request){
        Map<String,String> params = Maps.newHashMap();

        Map requestParams = request.getParameterMap();
        for(Iterator iter = requestParams.keySet().iterator(); iter.hasNext();){
            String name = (String)iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for(int i = 0 ; i <values.length;i++){

                valueStr = (i == values.length -1)?valueStr + values[i]:valueStr + values[i]+",";
            }
            params.put(name,valueStr);
        }
        logger.info("支付宝回调,sign:{},trade_status:{},参数:{}",params.get("sign"),params.get("trade_status"),params.toString());

        //非常重要,验证回调的正确性,是不是支付宝发的.并且呢还要避免重复通知.

        params.remove("sign_type");
        try {
            boolean alipayRSACheckedV2 = AlipaySignature.rsaCheckV2(params, Configs.getAlipayPublicKey(),"utf-8",Configs.getSignType());

            if(!alipayRSACheckedV2){
                return ServerResponse.createByErrorMessage("非法请求,验证不通过,再恶意请求我就报警找网警了");
            }
        } catch (AlipayApiException e) {
            logger.error("支付宝验证回调异常",e);
        }

        //todo 验证各种数据


        //
        ServerResponse serverResponse = iOrderService.aliCallback(params);
        if(serverResponse.isSuccess()){
            return Const.AlipayCallback.RESPONSE_SUCCESS;
        }
        return Const.AlipayCallback.RESPONSE_FAILED;
    }

2.service

public ServerResponse aliCallback(Map<String,String> params){
        Long orderNo = Long.parseLong(params.get("out_trade_no"));
        String tradeNo = params.get("trade_no");
        String tradeStatus = params.get("trade_status");
        Order order = orderMapper.selectByOrderNo(orderNo);
        if(order == null){
            return ServerResponse.createByErrorMessage("非快乐慕商城的订单,回调忽略");
        }
        if(order.getStatus() >= Const.OrderStatusEnum.PAID.getCode()){
            return ServerResponse.createBySuccess("支付宝重复调用");
        }
        if(Const.AlipayCallback.TRADE_STATUS_TRADE_SUCCESS.equals(tradeStatus)){
            order.setPaymentTime(DateTimeUtil.strToDate(params.get("gmt_payment")));
            order.setStatus(Const.OrderStatusEnum.PAID.getCode());
            orderMapper.updateByPrimaryKeySelective(order);
        }

        PayInfo payInfo = new PayInfo();
        payInfo.setUserId(order.getUserId());
        payInfo.setOrderNo(order.getOrderNo());
        payInfo.setPayPlatform(Const.PayPlatformEnum.ALIPAY.getCode());
        payInfo.setPlatformNumber(tradeNo);
        payInfo.setPlatformStatus(tradeStatus);

        payInfoMapper.insert(payInfo);

        return ServerResponse.createBySuccess();
    }

注意验签的时候去掉sign_type
这里的处理其实只是后台数据库什么的状态更新,不过我们最好做些防误操作处理,比如订单错误,多次调用等等
因为这个接口有可能是会被多次调用的,如果我们已经支付成功后,就不需要再去处理支付成功的状态

4.账单结果显示



支付账单成功,数据显示成功
交易忌讳单边账,所以预下单下单是同步的结果,callback会多次的回调,我们自己也要注意这点


总结

支付宝接入流程终于结束,也是听不容易的,查了很多很多资料,也自己给自己埋了不少的坑,不过最终成功调通支付宝,确实十分开心.
这样一来,基本上基础电商项目,只剩下一个下单自动取消这种功能没有完成,后续会补上这一块,把整个基础电商后台原起来.

相关文章

  • java后台当面付支付宝接入三

    这一章开始正式接入支付宝支付功能. 接入前声明一下,一定要确保demo能够正常跑通.跑不通直接接就是给自己找罪受o...

  • java后台当面付支付宝接入一

    对于一个电商网站来说,支付宝的接入算是一个不小的功能,毕竟想要做好一个整的支付系统,当面付呀,前端调,原生调会设计...

  • java后台当面付支付宝接入二

    这里重新理一下调用的顺序 1.支付宝处理下单请求: 这里是在调用支付宝的接口后生成的json数据,其内容都是我们自...

  • 三、支付宝扫码支付接入指引

    支付宝官方文档:https://docs.open.alipay.com/194/106078/当面付快速接入:h...

  • 支付宝云支付商家代签约进件开码教程

    前言 支付宝云支付服务商助手代签约为未签约过支付宝当面付的账号签约流程,已经签约过当面付的支付宝账号,只能在...

  • 代理价格表

    支付宝点亮13星————————————18元 口碑当面付———————————————16元 同名营业执照———...

  • iOS接入支付宝支付,hex编码,ALI38173问题

    iOS端今天接入了支付宝支付,接入过程很简单,代码量也很少,说一下与后台交互踩的坑: 后台拼接完订单信息直接将返回...

  • 签约流程

    找到商户并准备代签约资料代签约支付宝当面付商户资质要求代签约微信当面付商户资质要求 代签约作业拓展员录入商户基础信...

  • 三方支付接入(PHP)

    微信、微信直付通、支付宝、支付宝全球购、支付宝分账、银联、农行 现在三方支付的接入越来越多,作为一名PHP研发一直...

  • 支付宝微信支付智付等第三方支付将要变国企,是真的吗?

    目前,支付宝、微信支付(财付通旗下)、智付等20余家第三方支付机构和银行已经完成网联接入工作。根据央行要求,所有银...

网友评论

      本文标题:java后台当面付支付宝接入三

      本文链接:https://www.haomeiwen.com/subject/beozkftx.html