支付宝接口对接指南(三、支付宝开放接口对接:老版SDK)【保姆级】

导航

导读

  • 再接上文,框架已经搭建好,本篇对接口对接进行详细说明,直进主题。

gitee代码地址

  • https://gitee.com/zhaifengxi/zhai-docking-alipay-open

案例:支付宝图片上传

  • 目前对接支付宝接口,问题比较多的就是支付宝图片上传接口,针对这个接口进行具体详细说明,如果这个接口搞定了,其他的都不是问题。
支付宝开放接口文档解读
一、图片上传文档地址
  • https://opendocs.alipay.com/apis/01ea4t
二、公共请求参数
  • 作为支付宝的公共参数,里面的参数基本都是固定的,把因为环境不同的参数提取到配置文件中,剩余可以定义为常量。

提炼字段关键信息:图片二进制字节流,最小50K,最大为5M,支持png/bmp/gif/jpg/jpeg格式

三、请求参数
  • 具体业务请求,根据业务传参。
四、公共响应参数
  • 作为支付宝的公共响应参数,被区分为网关和业务两种返回码,作为对接方不区分网关还是业务可以通过封装进行合并。
五、响应参数
  • 具体业务响应结果,根据业务处理。
六、请求示例
  • 非常好的示例,直接粘贴下来按要求传好参数即可调用,支付宝的SDK封装的非常奶思,很值得其他开放平台借鉴学习。
七、响应示例
  • 接口调用之后的成功情况下的返回结果。
八、异常示例
  • 接口调用之后的失败情况下的返回结果。这里需要特别注意是支付宝网关失败还是业务失败,如果是网关失败,业务失败没有值。
九、业务错误码
  • 统一的业务错误码,不能完全描述清楚具体场景的问题,仅供参考,具体问题还得去找支付宝的技术支持沟通确认。
十、SDK对接文档
  • 地址:https://opendocs.alipay.com/open/54/cyz7do
  • 服务端对接两种方式:1.服务端 SDK(新版) 2.服务端 SDK(老版)
  • 服务端 SDK(新版):1.链式编程 2.代码简洁 3.参考资料少 4.部分接口找不到
  • 服务端 SDK(老版):1.相对新版代码臃肿 2.接口都可以找到,故选用此方式进行对接。
核心代码
一、支付宝基础服务层
@Slf4j
@Service
public class BaseAlipayApiImageSerImpl implements BaseAlipayApiImageSer {
    @Autowired
    AlipayApiProperties alipayApiProp;
    @Autowired
    BizLogSer bizLogSer;

    @SneakyThrows
    @Override
    public BaseAlipayImageUploadOut upload(BaseAlipayImageUploadIn in) {
        /** 操作:调用支付宝接口 */
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConstant.COMMON_SERVER_URL, alipayApiProp.getAppid(), alipayApiProp.getPrivateKey(), AlipayConstant.COMMON_FORMAT, AlipayConstant.COMMON_CHARSET, alipayApiProp.getPublicKey(), AlipayConstant.COMMON_SIGN_TYPE);
        AlipayOpenSpImageUploadRequest request = new AlipayOpenSpImageUploadRequest();
        request.setImageContent(new FileItem(in.getImageFile()));
        AlipayOpenSpImageUploadResponse response = alipayClient.execute(request);
        /** 操作:记录业务日志 */
        bizLogSer.save(new BizLogSave(new BizLogDataTemp(in, response), AlipayConstant.IMAGE_UPLOAD_LOG));
        /** 操作:结果转换 */
        BaseAlipayImageUploadOut result = ObjectUtil.copy(response, BaseAlipayImageUploadOut.class);
        BaseAlipayCommonOut commonOut = result.commonOut(response);
        ObjectUtil.copy(commonOut, result);
        return result;
    }
  1. 通过Base接口层进行和具体业务的解耦。
  2. 通过封装,简化代码。
  3. 通过灵活配置,方便不同环境的切换。
  4. 配合Slf4j + mongodb关键日志存储,方便快速捕获定位线上问题。

BaseAlipayApiImageSerImpl.java 详情:https://gitee.com/zhaifengxi/zhai-docking-alipay-open/blob/master/src/main/java/zhai/docking/alipay/service/base/alipay/api/BaseAlipayApiImageSerImpl.java

二、业务层处理
  • api层(controller层),也就是restful接口,对系统提供后端接口支持。api层对入参做基本校验,校验根据支付宝接口文档要求编写。
@RestController
@RequestMapping("/api/alipay/img")
public class AlipayImgCtr {
    @Autowired
    AlipayImgSer alipayImgSer;

    @RequestMapping(path = "/upload", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity upload(@RequestParam(name = "file") MultipartFile file) {
        /** 校验:是否为空 */
        if (file.isEmpty()) {
            return ResponseEntity.ok(Result.failure(ResultConstant.FAIL_SYS_NO_IN_ERROR_CODE, ResultConstant.FAIL_SYS_NO_IN_ERROR_MSG));
        }
        /** 校验:文件类型 */
        String fileType = FileUtil.getFileType(FileUtil.getFileName(file));
        if (StringUtil.isBlank(fileType)) {
            return ResponseEntity.ok(Result.failure(ResultConstant.FAIL_SYS_DEF_CODE, "该文件没有文件类型"));
        }
        if (!FileUtil.isFileType(fileType, FileUtil.FILE_TYPE_JPG, FileUtil.FILE_TYPE_JPEG)) {
            return ResponseEntity.ok(Result.failure(ResultConstant.FAIL_BIZ_DEF_CODE, StringUtil.join("不支持上传该文件格式:", fileType)));
        }
        /** 校验:文件大小 */
        boolean minSize = FileUtil.checkMinSize(file.getSize(), 50, FileUtil.UNIT_KB);
        if (!minSize) {
            return ResponseEntity.ok(Result.failure(ResultConstant.FAIL_BIZ_DEF_CODE, "文件大小不能小于50KB"));
        }
        boolean maxSize = FileUtil.checkMaxSize(file.getSize(), 5, FileUtil.UNIT_MB);
        if (!maxSize) {
            return ResponseEntity.ok(Result.failure(ResultConstant.FAIL_BIZ_DEF_CODE, "文件大小不能大于5MB"));
        }
        Result result = alipayImgSer.upload(file);
        return ResponseEntity.ok().body(result);
    }
}

AlipayImgCtr.java 详情:https://gitee.com/zhaifengxi/zhai-docking-alipay-open/blob/master/src/main/java/zhai/docking/alipay/api/AlipayImgCtr.java

  • service层提供业务服务支持。
@Service
public class AlipayImgSerImpl implements AlipayImgSer {
    @Autowired
    BaseAlipayApiImageSer baseAlipayApiImageSer;
    @Autowired
    SnowflakeUtil snowflakeUtil;

    @SneakyThrows
    @Override
    public Result upload(MultipartFile in) {
        /** 操作:重命名文件名 */
        String imageName = StringUtil.join(snowflakeUtil.nextId(), StringConstant.POINT, FileUtil.getFileType(FileUtil.getFileName(in)));
        File file = new File(imageName);
        FileUtil.copyInputStreamToFile(in.getInputStream(), file);
        BaseAlipayImageUploadOut upload = null;
        try {
            /** 操作:上送-支付宝图片接口 */
            BaseAlipayImageUploadIn uploadIn = new BaseAlipayImageUploadIn();
            uploadIn.setImageFile(file);
            upload = baseAlipayApiImageSer.upload(uploadIn);
        } finally {
            if (file.exists()) {
                file.delete(); // 删除临时文件
            }
        }
        /** 结果:转换 */
        if (!upload.isSuccess()) {
            log.info(StringUtil.join(BUSINESS_NAME, LogConstant.SERVICE, METHOD_UPLOAD, LogConstant.RESULT, LogConstant.FAIL, upload.getCode(), upload.getMsg()));
            return Result.failure(upload.getCode(), upload.getMsg());
        }
        AlipayImageUploadOut result = new AlipayImageUploadOut();
        result.setImgCode(upload.getImageId());
        return Result.success(result);
    }
}

AlipayImgSerImpl.java 详情:https://gitee.com/zhaifengxi/zhai-docking-alipay-open/blob/master/src/main/java/zhai/docking/alipay/service/biz/AlipayImgSerImpl.java

  • 以上核心代码就是完整的一套从后端接口到调用具体支付宝接口的核心代码。

避坑指南

  • 上面的一整套代码已经躲避了支付宝开放接口的坑,下面讲解下这些坑。
一、未知的错误码:FILE_SIZE_MIN_LIMIT (文件大小低于范围,请检查文件类型的参数大小)
  • 首先支付宝接口文档要求:图片二进制字节流,最小50K,最大为5M,支持png/bmp/gif/jpg/jpeg格式,代码也是完全按照要求编写的。
  • 我们测试了1M的图片依然不行,然后联系了技术支持,问题依然没解决,最后上升的支付宝研发,期间花费了非常多的时间。
  • 支付宝研发回复:50K-5M,JPEG格式,不会报错。
  • 原来是接口文档给的格式除了jpg和jpeg两个格式以外,其他格式容易报该错误。所以做了用户图片上传类型的限制为 jpg和jpeg。关键代码如下:
if (!FileUtil.isFileType(fileType, FileUtil.FILE_TYPE_JPG, FileUtil.FILE_TYPE_JPEG)) {
            return ResponseEntity.ok(Result.failure(ResultConstant.FAIL_BIZ_DEF_CODE, StringUtil.join("不支持上传该文件格式:", fileType)));
}
二、一大长串html报错
  • 截选报错日志:
ERROR sdk.biz.err - 2021-01-19 14:34:04^_^<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>支付宝 - 网上支付 安全快速!</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<meta http-equiv="x-ua-compatible" content="ie=7" />
<meta name="description" content="中国最大的第三方电子支付服务提供商" />
...
<li>支付宝版权所有 2004-2021 ALIPAY.COM</li>
  </ul>
  <div id="ServerNum">openapi-49-14424</div>
</div>
<!--footer ending-->
  • 支付宝研发回复:大概率就是网络问题。
  • 最后发现原因是调用支付宝图片上传入参:“imageFile”:“210119143401000217bmp”
  • 原因:例如图片名 2020.01.01.bmp,但是重命名文件名的时候变成210119143401000217bmp,少了个点,推送给支付宝就报一长串html。关键代码如下
 		/** 操作:重命名文件名 */
        String imageName = StringUtil.join(snowflakeUtil.nextId(), StringConstant.POINT, FileUtil.getFileType(FileUtil.getFileName(in)));

支付宝报错特点:ERROR sdk.biz.err

三、文件名,目录名或卷标语法不正确
  • 现象:上传文件名包含中文的图片就会报错
  • 原因:MultipartFile 转 File 时文件名???,所以报如上错误。
 /** MultipartFile 转 File */
File file = new File(imageName);
图片文件名:你好_12313.jpg
后端接收:???_12313.jpg
  • 解决办法:重命名文件名,利用雪花算法生成不重复的随机ID。
 		/** 操作:重命名文件名 */
        String imageName = StringUtil.join(snowflakeUtil.nextId(), StringConstant.POINT, FileUtil.getFileType(FileUtil.getFileName(in)));
四、The field file exceeds its maximum permitted size of 1048576 bytes.
  • 具体报错
org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException: The field file exceeds its maximum permitted size of 1048576 bytes. 
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:111) 
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:85) 
	at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:79) 
  • 原因:上传的图片超过默认文件大小了。
  • 解决办法:修改配置图片大小。
  servlet: # 文件上传配置
    multipart:
      max-file-size: 10MB
      max-request-size: 10MB

最后

  • 本篇主要通过 “案例:支付宝图片上传”,来完整串联如何开发对接支付宝开放接口,通过对具体文档、核心代码、避坑指南来进行详细分析,希望能给你一些参考。下篇文章针对支付宝开放接口回调通知展开细致详细的描述。原创不易,希望大家多多支持。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对接支付宝接口涉及到前后端的交互,具体步骤如下: 1. 在支付宝开放平台注册开发者账号,并创建应用获取应用的AppID、公钥、私钥等信息。 2. 后端(Spring Boot)部分: - 引入支付宝SDK,可以使用官方提供的SDK或者第方封装的SDK。 - 在应用配置文件中配置支付宝相关参数,如AppID、公钥、私钥等。 - 编写接口用于生成支付宝订单信息,并将订单信息返回给前端。 - 编写接口用于接收支付宝异步通知,验证支付结果并处理业务逻辑。 - 编写接口用于查询订单状态等其他操作。 3. 前端(Vue)部分: - 使用支付宝提供的前端组件,如扫码支付组件、H5支付组件等。 - 在前端页面中引入支付宝提供的组件,并配置相关参数,如AppID、订单信息等。 - 编写逻辑处理用户支付成功或失败的回调方法,并提示用户支付结果。 4. 后端与前端通信: - 后端提供生成订单信息的接口,前端调用该接口获取订单信息。 - 前端将订单信息传递给支付宝前端组件,并完成支付操作。 - 支付宝将支付结果以异步通知的形式发送给后端,后端接收并验证支付结果。 - 后端处理支付结果,更新订单状态等业务逻辑。 需要注意的是,在实际操作中还需要考虑安全性、异常处理、日志记录等方面的内容,以确保支付流程的稳定和安全。同时,支付宝也提供了详细的开发文档和示例代码,可以参考官方文档进行具体操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值