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的配置文件与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就不改了。正常情况忌讳这个



浙公网安备 33010602011771号