SSM框架——mmall

问题总结

错误如下:

### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: execute command denied to user '**********'@'%' for routine '********.COUNT'

 一般出现这种异常为Sql语句异常,我还定位了半天,不相信,开始以为权限问题呢。

后来发现,因为自己写代码养成习惯,习惯多敲空格,如下图所示:count和(1)之间多了空格,所以这个方法调用错误。还定位了半天。

 

 使用Eq方法,问题

忘记toString,所以不一样的对象,比较肯定false

数据库插入类型不一样报错:数据库是int,而我传入的却是double

试图从具有原始返回类型(int)的方法返回null

 

整合支付宝支付功能

支付宝测试Demo入口————》

上面测试通过之后,拷贝Demo的配置文件Lib下的jar包到我们项目中。

如图所示:

可以看到除了支付宝的jar包还有其它几个jar包,我们只需要拷贝支付宝jar包,其它jar包使用pom管理即可。为了依赖的顺利,其它jar包安装支付宝指定的版本号依赖。然后删除lib下面那些pom已经依赖的jar包

    <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>1.10</version>
    </dependency>
    <dependency>
      <groupId>commons-configuration</groupId>
      <artifactId>commons-configuration</artifactId>
      <version>1.10</version>
    </dependency>
    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>
      <version>2.6</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>com.google.zxing</groupId>
      <artifactId>core</artifactId>
      <version>2.1</version>
    </dependency>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.3.1</version>
    </dependency>
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-core</artifactId>
      <version>1.3</version>
    </dependency>

然后把Demo的两个类拷贝过来,并关联jar包。

然后运行Main方法,看下拷贝过来是否还好使。

对照当面付文档,https://docs.open.alipay.com/194/103296/,然后编写业务代码,对照文档进行校验

 

然后我们仿照逻辑来写业务:

首先新建一个Controller,订单OrderController:路径为:order

然后定义一个支付的方法:pay,路径为/pay.do

然后判断用户登录,如果用户登录了,则执行支付逻辑。

下面介绍支付逻辑的实现:

public ServiceResponse pay(Long orderNo, Integer userId, String path) {
        Map<String, String> resultMap = Maps.newHashMap();
        Order order = orderMapper.selectByUserIdAndOrderNo(userId, orderNo);
        if (order == null) {
            return ServiceResponse.createByErrorMessage("用户没有该订单");
        }
        resultMap.put("orderNo", String.valueOf(order.getOrderNo()));

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

        // (必填) 订单标题,粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费”
        String subject = "哎呦,不错哦。快来支付吧。";

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

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

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

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

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

        // (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持
        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> orderItemList = orderItemMapper.getByOrderNoUserId(orderNo, userId);
        if (orderItemList == null) {
            return ServiceResponse.createByErrorMessage("用户订单没有商品信息");
        }
        for (OrderItem orderItem :
                orderItemList) {
            BigDecimalUtil.mul(orderItem.getCurrentUnitPrice().doubleValue(), new Double(100).doubleValue());

            //orderItem.getCurrentUnitPrice()
            // 创建一个商品信息,参数含义分别为商品id(使用国标)、名称、单价(单位为分)、数量,如果需要添加商品类别,详见GoodsDetail
            GoodsDetail goods = GoodsDetail.newInstance(orderItem.getProductId().toString(), orderItem.getProductName(),
                    BigDecimalUtil.mul(orderItem.getCurrentUnitPrice().doubleValue(), new Double(100).doubleValue()).longValue(), orderItem.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)
                //TODO:回调地址
                .setNotifyUrl("http://ssrs-wh.s1.natapp.cc/order/alipay_callback.do")//支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置
                .setGoodsDetailList(goodsDetailList);

        /** 一定要在创建AlipayTradeService之前调用Configs.init()设置默认参数
         *  Configs会读取classpath下的zfbinfo.properties文件配置信息,如果找不到该文件则确认该文件是否在classpath目录
         */
        Configs.init("zfbinfo.properties");

        /** 使用Configs提供的默认参数
         *  AlipayTradeService可以使用单例或者为静态成员对象,不需要反复new
         */
        AlipayTradeService tradeService = new AlipayTradeServiceImpl.ClientBuilder().build();


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

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

                File folder = new File(path);
                //目录是否存在
                if (!folder.exists()) {
                    folder.setWritable(true);
                    folder.mkdirs();
                }
                // 需要修改为运行机器上的路径
                // path是不带/,所以我们要追加/    二维码目录
                String qrPath = String.format(path + "/qr-%s.png",
                        response.getOutTradeNo());
                LOGGER.info("qrPath:" + qrPath);
                // FTP上传文件名称
                String ftpFileName = String.format("/qr-%s.png", response.getOutTradeNo());
                //生成二维码  二维码目录
                ZxingUtils.getQRCodeImge(response.getQrCode(), 256, qrPath);
                FtpUtils f = new FtpUtils("43.***.**.**", 21, "host805096504", "*******");
                try{
                    boolean b = false;
                    if(f.open()){
                        b =f.upload(qrPath, ftpFileName, Const.FTP_SERVER_HTTP_PREFIX);
                    }
                    LOGGER.info("上传文件:{}",b);
                }finally {
                    f.close();
                }
                LOGGER.info("qrUrl:", Const.FTP_SERVER_HTTP_PREFIX+ftpFileName);
                resultMap.put("qrUrl", PropertiesUtil.getProperty("ftp.server.http.prefix")+Const.FTP_SERVER_HTTP_PREFIX+ftpFileName);
                return ServiceResponse.createBySuccess(resultMap);
            case FAILED:
                LOGGER.error("支付宝预下单失败!!!");
                return ServiceResponse.createByErrorMessage("支付宝预下单失败!!!");

            case UNKNOWN:
                LOGGER.error("系统异常,预下单状态未知!!!");
                return ServiceResponse.createByErrorMessage("系统异常,预下单状态未知!!!");
            default:
                LOGGER.error("不支持的交易状态,交易返回异常!!!");
                return ServiceResponse.createByErrorMessage("不支持的交易状态,交易返回异常!!!");
        }

代码中一级注释很清楚了,说几个重要的,需要特别注意的;

第一、回调地址,因为支付宝会回调地址,如果本地测试建议使用一个域名,可以让支付宝调用,本人使用的是个内网穿透的软件:软件介绍入口——》

第二、仿照Demo中的main方法创建Serveice,加载拷贝过来的配置文件

第三、上传二维码到Ftp,本地测试的话,可不进行上传。FTPUtils工具类入口——>

 

支付接口需要注意的就那些。

下面在写一个接收支付宝回调的接口:/alipay_callback.do,就是刚才填写的支付宝回调

如果支付宝验证通过了之后,就要我们进行订单信息验证了。支付宝返回的字段里面有订单信息,可以自行对比。文档地址————》

这里需要注意的是支付宝检验回调的一个方法,支付宝文档上面数了需要注意的:

有的人会说,怎么只移除一个,另一个怎么不移除?我们可以看一下这个方法的源码:

然后再进getSignCheckContentV2,可以看到这个方法移除了sign,至此两个参数都被移除了。

然后我们找一下Base64解密。

可以看到下面调用的这个方法会根据加密类型进行方法的不同调用,我们可以看一下我们配置文件中是什么加密类型:

我们就找RSA2分支进入的方法:可看到该方法,经过一番处理后,再解密

此方法会返回一个boolean值

然后验证通过之后,执行充值成功的逻辑,如果验证失败,则不必例会。

支付宝集成功能测试

首先我要开启本地服务。

然后开启服务外网映射,让支付宝回调可以请求到本地

开启成功,让模拟一次访问,确认可以映射。

然后使用postMan或者其它模拟请求软件,请求服务:因为我有写验证订单信息,所以就从数据库拷贝一个订单:

可以看到模拟请求,成功给了响应,支付宝接口也生成了一个二维码:

 

 然后使用二维码生成器,把控制台打印的二维码链接,生成二维码,或者直接去ftp找这个二维码图片

下面上两个演示的例子,注:我两个表字段类型不一样,一个是元,一个是分,因为只是Demo就不改了。正常情况忌讳这个

注:我两个表字段类型不一样,一个是元,一个是分,因为只是Demo就不改了。正常情况忌讳这个

注:我两个表字段类型不一样,一个是元,一个是分,因为只是Demo就不改了。正常情况忌讳这个

posted @ 2018-08-04 01:49  苦心明  阅读(452)  评论(0)    收藏  举报