财资云开放平台(开发版)
  1. 开发指南
财资云开放平台(开发版)
  • 接入指南V3
    • 创建应用
    • APIFOX脚本及外部程序使用教程
    • 规则说明
      • 基本原则
      • 错误码
    • 密钥与签名
      • 接口加签方式
      • 签名规则
      • 验签规则
      • 请求内容加密
    • 服务端SDK
      • SDK使用帮助
      • Java
    • 应用开发配置
      • IP白名单
        • 服务器IP白名单
    • 常见问题
  • 接入指南
    • 接口调用方式说明
    • 创建应用
    • APIFOX脚本及外部程序使用教程
    • 应用开发配置
      • 接口加签方式
        • 接口加签方式说明
        • 设置密钥加签方式
        • 开发指南
          • 如何使用密钥
          • 密钥格式说明
          • 自行实现签名
          • 自行实现验签
        • 常见问题
          • 常见问题
        • SAP加签验签指南
      • IP白名单
        • 服务器IP白名单
      • 接口内容加密方式
        • 接口内容加密方式说明
      • 错误码
        • 公共错误码
    • 开发工具包
      • 开发工具包简介
      • 服务端SDK
        • 概述
        • Java
        • PHP
        • .Net
        • Python
        • NodeJS
    • 密钥工具
      • 密钥工具简介
      • 生成密钥
      • 密钥匹配
      • 格式转换
      • 同步验签
      • 签名
      • 异步验签
  • 产品能力
    • 示例产品
      • 示例功能
        • 产品介绍
        • 接入准备
        • 接入指南
        • 常见问题
        • 更新日志
        • API列表
          • API接口列表示例
    • 账户产品
      • 产品介绍
      • 接入准备
      • 接入指南
      • 常见问题
      • 更新日志
      • 账户管理
        • 实体户接口
          • 新增实体账户信息
          • 变更实体账户状态
          • 修改实体账户信息
          • 查询实体账户信息
        • 三方户接口
          • 新增三方户(商户号)信息
          • 变更三方户(商户号)状态
          • 修改三方户(商户号)信息
          • 查询三方户(商户号)信息
        • 内部户接口
          • 新增内部账户信息
          • 变更内部账户状态
          • 修改内部户信息
          • 查询内部账户信息
        • 现金户接口
          • 新增现金户信息
          • 变更现金账户状态
          • 变更现金户信息
          • 查询现金账户信息
        • 虚拟户接口
          • 新增虚拟子账户信息
          • 变更虚拟子账户信息
          • 销户虚拟子账户
          • 查询虚拟子账户信息
      • 账户余额
        • 查询账户余额
        • 同步账户余额(含RPA)--先不处理
      • 账户明细
        • 查询三方账户明细
        • 同步实体账户明细(含RPA)---先不处理
      • 回单(作废)
        • 同步电子回单(含RPA)--先不处理
      • 关联关系(作废)
        • 根据明细查回单
      • 流水
        • 流水
      • 实物资产
        • 实物资产查询接口
      • 合作银行
        • 合作银行查询接口
      • 交易对手
        • 交易对手查询接口
    • 结算产品
      • 应付
        • 产品介绍
        • 接入准备
        • 接入指南
        • 常见问题
        • 更新日志
        • API列表
          • 创建应付款单
          • 应付单查询接口
      • 国内付款
        • 产品介绍
        • 接入准备
        • 接入指南
        • 常见问题
        • 更新日志
        • API列表
          • 创建国内付款
        • 国内支付填写要求
      • 费用报销
        • 产品介绍
        • 接入准备
        • 接入指南
        • 常见问题
        • 更新日志
        • API列表
          • 创建批量报销
      • 工资发放
        • 产品介绍
        • 接入指南
        • 接入准备
        • 常见问题
        • 更新日志
        • API列表
          • 创建工资代发
          • 创建工资批次总额信息
      • 全球付款
        • 产品介绍
        • 接入准备
        • 接入指南
        • 常见问题
        • 更新日志
        • API列表
          • 创建全球付款
      • 撤销付款
        • API列表
          • 撤销付款
      • 付款结果
        • 支付结果通知
        • 支付结果查询
        • 应付结果通知
        • 应付结果查询
      • 单据查询
    • 其他通用产品
      • 系统
        • 组织部门
          • 资源介绍
          • API列表
            • 创建组织部门
            • 修改组织部门
            • 删除组织部门
            • 获取单个组织部门信息
            • 组织部门批量查询
          • 事件列表
            • 组织创建
            • 组织创建
            • 组织变更
            • 删除组织
        • 开户行
          • 开户行查询
        • 系统字典
          • 资源介绍
          • API列表
            • 系统字典查询
            • 银行查询
        • 业务字典
          • 资源介绍
          • API列表
            • 新增业务字典
            • 修改业务字典
            • 重置字典
            • 删除单个业务字典
            • 删除指定类型字典
            • 业务字典批量查询
            • 单个业务字典查询
          • 事件列表
            • 新增业务字典
            • 修改业务字典
            • 删除单个业务字典
            • 删除指定类型字典
            • 重置字典
        • 汇率
          • 新增汇率
          • 修改汇率
          • 删除汇率
          • 查询单条汇率
          • 批量查询汇率
        • 会计科目
          • 新增会计科目
          • 修改会计科目
          • 删除会计科目
          • 查询单个会计科目
          • 批量查询会计科目
        • 交易对手
          • 交易对手信息
        • 通用文件上传
          • 上传文件
    • 票证产品
      • 票据
        • 查询
          • 附件查询
          • 票据正背面查询
        • 操作
          • 签收/承兑 /付款
          • 质押申请
          • 背书申请
          • 贴现申请
          • 撤回操作
          • 撤票操作
          • 提示付款申请
          • 票据同步
          • 票据异常处理
        • 出票申请
        • 票据查询(拆开)
        • 交易状态查询
        • 异步通知(同步 票据当前操作 状态)
        • 票据详情查询
        • 对方是否签收状态查询
      • 新版票据
        • 外部对接
          • 票据贴现对外接口
          • 票据出票对外接口
        • 资产池对接
          • 入池校验/跨行调拨操作
          • 资产池通知操作
      • 信用证
        • 信用证信息查询
        • 【老版本停止维护】信用证信息-推送(全字段版,部分字段改造前暂无)
        • 信用证信息-推送(新版信用证)
      • 贴现运营
        • 查询票据贴现运营列表信息
        • 贴现运营配票进度缓存查询接口
        • 贴现运营-配票接口
    • 对账产品
      • 认领接口
    • 投融资
      • 融资
        • 授信&发行申报
          • 【上报】查询授信&发行申报合同
          • 查询授信合同
          • 授信新增
          • 授信查询
        • 发行申报
          • 查询发行申报
        • 融资合同&内部往来&债券登记
          • 查询融资合同
          • 【上报】查询融资&往来&债券合同
        • 债券登记
          • 查询债券合同
        • 往来借款
          • 内部往来新增
          • 查询往来借款合同
        • 提款&放款
          • 【上报】查询提款&放款列表
        • 贷后
          • 【上报】查询贷后列表
          • 【上报】查询还款记录列表
        • 保函
          • 【上报】查询保函列表
        • 资产台账
          • 【上报】查询资产台账列表
        • 担保
          • 【上报】查询担保列表
        • 融资新增
        • 融资查询
        • 内部往来查询
        • 提款
        • 放款
        • 按期还款
        • 提前还款
      • 投资
        • 存款
          • 存款新增
          • 存款详情查询
          • 存款支取
          • 存款申购
          • 转让
          • 存款结息
          • 存款作废
        • 理财
          • 理财详情查询
          • 理财新增
          • 理财作废
    • 预算和计划
      • API列表
        • 预算控制
      • 资金预算
        • 预算编制查询
        • 预算编制更新接口
    • 身份认证
      • 获取登录预授权码
    • 结算中心
      • 结算中心计息单查询接口
    • 事件中心
      • webhook
        • 明细
          • 实体户明细删除
          • 三方明细删除
        • 对账
          • 应收单
          • 应收单实体账户流水
          • 应收单虚拟户流水
          • 应收单票据流水
          • 实体账户流水票据流水
        • 账户
          • 账户变更销
    • 组织架构
      • 员工
        • 资源介绍
        • API列表
          • 新增员工
          • 员工转用户
          • 修改员工
          • 删除员工信息
          • 查询单个员工信息
          • 批量查询员工信息
        • 事件列表
          • 新增员工
          • 修改员工
          • 删除员工
          • 员工转用户
      • 组织
        • 通过企业名称查询详细信息
        • API列表
          • 通过企业名称查询详细信息
  • 更新日志
    • 产品更新日志
    • 工具更新日志
  • 枚举值
    • 预算跨度
    • 预算颗粒度
    • 滚动颗粒度
    • 滚动期数
    • 参考值取数
    • 预算类型
    • 编制状态
    • 审批状态
    • 编制总状态
    • 催办状态
    • 执行状态
    • 预算旬
    • 预算周
    • 科目分类
    • 收支方向
    • 收支类型
    • 余额模板类型
    • 币种
    • 交易对手种类
    • 账户性质
    • 支付方式
    • 应付来源
  1. 开发指南

自行实现签名

如果未使用财资云开放平台 SDK,需要自行实现加签过程。

签名算法说明#

财资云开放平台密钥生成工具生成的密钥主要使用 RSA2 、SM2 签名算法。签名主要包含三个过程:筛选并排序、拼接和非对称加密。
首先,获取所有请求参数,不包括字节类型参数,如文件、字节流,剔除sign参数。将筛选的参数按照第一个字符的键值ASCII码递增排序(字母升序排序),如果遇到相同字符则按照第二个字符的键值ASCII码递增排序,以此类推。
第三步,将排序后的参数与其对应值,组合成“key=value”的格式,并且把这些参数用&字符连接起来,此时生成的字符串为待签名字符串。
最后通过签名者的私钥对待签名字符串进行非对称加密即可得到签名结果。
说明:应用支持 RSA2、SM2 签名算法,推荐使用 RSA2 方式。
签名算法标准签名算法描述
RSA2SHA256WithRSA强制要求密钥的长度至少为 2048。
注意:
协议参数:开放平台的报文标准中,signType 参数用于指定签名算法,若使用 SHA256WithRSA 签名算法则在报文中需要指定 signType=RSA2,若开发者使用开放平台 SDK,详情可查看 接口加签方式说明。
避免公私钥混用:不同签名算法的签名密钥是隔离的。由于同时提供了两套签名算法,若选择了特定的签名算法,请保证使用对应的私钥签名,同时使用对应的公钥进行验签。

自行实现签名#

如果未使用财资云开放平台 SDK,开发者需要自行实现签名过程。
公共参数
参数类型是否必填最大长度描述示例值
appIdString必填32资金平台分配给企业的应用编号1633440541561720832
systemCodeString否16资金平台分配给企业对接系统的编码,部分非查询类接口需要系统来源kingdee
commandString必填128接口指令码yocyl.basic.organization.create
formatString必填10仅支持JSONJSON
charsetString必填10仅支持UTF-8UTF-8
timestampString必填14发送请求的时间,格式"yyyyMMddHHmmss",请求时间10分钟内有效20210604120100
versionString必填5调用的接口版本,固定为:1.0.01.0.0
signTypeString必填5生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2RSA2
signString必填512请求参数的签名串,详见签名
notifyUrlString可选255主动通知企业业务系统服务器里指定的页面http/https路径。http://cfs-test.yocyl.com/receive-notify
encryptTypeString可选3加密类型,仅支持AESAES
bizContentString可选请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各业务接口接入文档
签名过程:
生成签名方首先将所有参数和值放入一个map 中,并按照 key 值升序排列(调用Collections.sort 方法)。
然后将所有参数拼接起来,去掉 key 或 value 为空的参数,并用 & 连接,组成签名原文。
最后使用 RSA 的私钥对签名原文进行签名。
注意:若接口中需携带图片/视频等文件上传请求,文件流不参与签名,请自行将文件转换成文件流形式,且以文件流格式请求。
文中代码部分以 Java 语言调用 yocyl.account.banlance.query(账户余额查询)为例,介绍如何使用公钥方式自行实现签名及如何发起请求。

第一步:筛选并排序#

获取所有请求参数(包括公共请求参数),不包括字节类型参数,如文件、字节流,剔除sign参数。将筛选的参数按照第一个字符的键值ASCII码递增排序(字母升序排序),如果遇到相同字符则按照第二个字符的键值ASCII码递增排序,以此类推。

第二步:拼接#

将排序后的参数与其对应值,组合成 参数=参数值 的格式,并且把这些参数用 & 字符连接起来,此时生成的字符串为待签名字符串。

第三步:调用签名函数#

使用各自语言对应的 SHA256WithRSA(对应 signType 为 RSA2)签名函数利用租户私钥对签名字符串进行签名,并进行 Base64 编码。把生成的签名 encode 后赋值给 sign 参数,拼接到请求参数中。

示例代码#

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;

import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;
import java.util.stream.Stream;

/**
 * 加签工具DEMO
 *
 * @author Stewart
 * @version SignUtil v1.0 2023/6/2
 */
public class SignUtil {

    /**
     * 在财资云系统配置的私钥字符串,可以使用密钥工具生成
     */
    private static final String privateKey = "私钥字符串";

    /**
     * 字符集编码,默认UTF-8
     */
    private static final String charset = "UTF-8";



    /**
     * 加签Demo
     *
     * @param requestParams                 请求参数
     * @return                              加签后的字符串
     */
    public static String signDemo(Map<String,Object> requestParams) throws Exception {
        Map<String, String> filtedMap = filtering(requestParams);
        String preSignedStr = getContent(filtedMap);
        return signing(preSignedStr);
    }

    /**
     * 筛选
     * 将参数转换成String, 并筛选出不为空的键值
     *
     * @param requestParams                 请求参数
     * @return                              筛选结果
     */
    private static Map<String, String> filtering(Map<String,Object> requestParams){
        Map<String, String> dataMap = new TreeMap<>();
        requestParams.forEach((k,v) -> {
            String value = Optional.ofNullable(v).map(e -> {
                if(!(e instanceof String)){
                    //可以替换成任何JSON工具类
                    return JSONObject.toJSONString(e);
                }
                return e.toString();
            }).orElse(null);
            if(isNoneBlank(new CharSequence[]{k, value})){
                dataMap.put(k,value);
            }
        });
        return dataMap;
    }

    /**
     * 获取待加签字符串
     * 将参数Key排序后放入有序集合
     * 通过遍历Key的有序集合,按顺序拿到排序后的Key
     * 从参数Map中按Key的顺序取值,并拼接字符串
     *
     * @param filtedMap                     筛选后的请求参数
     * @return                              待加签字符串
     */
    private static String getContent(Map<String, String> filtedMap) {
        if (filtedMap == null) {
            return null;
        } else {
            filtedMap.remove("sign");
            List<String> keys = new ArrayList(filtedMap.keySet());
            Collections.sort(keys);
            StringJoiner joiner = new StringJoiner("&");
            keys.forEach((key) -> {
                String value = filtedMap.get(key);
                joiner.add(key.concat("=").concat(value));
            });
            return joiner.toString();
        }
    }

    /**
     * 加密
     * 使用RSA2算法对待加密字符串进行加密
     *
     * @param preSignedStr                  待加密字符串
     * @return                              加密后字符串
     * @throws Exception                    加密过程中出现异常
     */
    private static String signing(String preSignedStr) throws Exception {
        PrivateKey priKey = getPrivateKey("RSA", privateKey);
        Signature signature = Signature.getInstance("SHA256WithRSA");
        return rsaSign(priKey, signature, preSignedStr, charset);
    }

    /**
     * 获取私钥对象
     *
     * @param algorithm                     算法
     * @param privateKey                    私钥字符串
     * @return                              私钥对象
     * @throws InvalidKeySpecException      密钥不合法异常
     * @throws NoSuchAlgorithmException     算法不存在异常
     */
    public static PrivateKey getPrivateKey(String algorithm, String privateKey) throws InvalidKeySpecException, NoSuchAlgorithmException {
        if (!isEmpty(privateKey) && !isEmpty(algorithm)) {
            KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
            byte[] encodedKey = Base64.decodeBase64(privateKey);
            return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
        } else {
            return null;
        }
    }

    /**
     * RSA加密
     *
     * @param priKey                        私钥对象
     * @param signature                     签名对象
     * @param content                       待加密内容
     * @param charset                       字符集
     * @return                              加密后字符串
     * @throws InvalidKeyException          密钥不合法异常
     * @throws SignatureException           加签异常
     * @throws UnsupportedEncodingException 不支持的字符集异常
     */
    private static String rsaSign(PrivateKey priKey, Signature signature, String content, String charset) throws InvalidKeyException, SignatureException, UnsupportedEncodingException {
        signature.initSign(priKey);
        signature.update(content.getBytes(charset));
        byte[] signed = signature.sign();
        return Base64.encodeBase64String(signed);
    }

    /**
     * 判断CharSequence是否为空
     *
     * @param css                           字符序列
     * @return                              是否为空的判断结果
     */
    public static boolean isNoneBlank(final CharSequence... css) {
        if(css == null){
            return false;
        }
        return Stream.of(css).allMatch(SignUtil::hasText);
    }

    /**
     * 判断字符串是否有内容
     *
     * @param str                           字符串
     * @return                              是否有内容
     */
    public static boolean hasText(CharSequence str) {
        return isEmpty(str) && containsText(str);
    }

    /**
     * 是否包含除空格外的内容
     *
     * @param str                           字符串
     * @return                              是否有内容
     */
    private static boolean containsText(CharSequence str) {
        long count = str.chars().filter(chr -> !Character.isWhitespace(chr)).count();
        if(count>0){
            return true;
        }
        return false;
    }

    /**
     * 判断字符串是不是空字符串
     *
     * @param str                           字符串
     * @return                              是否为空
     */
    public static boolean isEmpty(CharSequence str) {
        return str == null || "".contentEquals(str);
    }
}


信息
示例代码中的工具类可自行替换成功能一致的其他工具类。

第四步:拼接完整请求#

1. 拼接原始数据#

完成 第三步:调用签名函数 后需将生成的签名作为 sign 的 value 拼接到请求数据中。
appId=2014072300XXXXXX&bizContent={"id":"2022XXXX","totalAmount":88.88,"subject":"SOMETHING","scene":"xxx_code","authCode":"287634438256xxxxx"}&charset=UTF-8&command=xxx.xxx.xxx&signType=RSA2&timestamp=2014-07-24 03:07:50&version=1.0&sign=GhT5Q6YxBtvf4q855TiNWEF/DIeLMyEE97+OnJDHpe6joNfr9F3BAE37dczcogU7uLfpNo+dh3cgly05bQN5nTj9TJ3SrBZWa40cltlgkI+6+0FuJoFSA225+MCrD4fNNqi2Zd5Adi9qC9NdN8nBVsjvzqtn5NepOt8dG1qdv7KvVwG74emFea1j2JuMBVBMp2PgIEwCv5c8OJSXa2P66+l3YCqEEMjBN22Dz0chlJ4hKMW3rA2oISH6ryzIvNIiy4/djKEdmxFeBorp1vrbmAtBqE++afggzzGt5w4arkbQ4fWLXJwkC9+1y4irCgmX65CEb5ct+fRgG2/9/B0rYg==

2. Encode 请求数据#

将所有一级 key 的 value 值进行 encode 操作。
说明:将接口业务参数 bizContent 的 value 值作为一个整体进行 encode 操作。
appId=2014072300XXXXXX&bizContent=%7B%22id%22%3A%222022XXXX%22%2C%22totalAmount%22%3A88.88%2C%22subject%22%3A%22SOMETHING%22%2C%22scene%22%3A%22xxx_code%22%2C%22authCode%22%3A%22287634438256xxxxx%22%7D&charset=UTF-8&command=xxx.xxx.xxx&signType=RSA2&timestamp=2014-07-24%2003%3A07%3A50&version=1.0&sign=GhT5Q6YxBtvf4q855TiNWEF%2FDIeLMyEE97%2BOnJDHpe6joNfr9F3BAE37dczcogU7uLfpNo%2Bdh3cgly05bQN5nTj9TJ3SrBZWa40cltlgkI%2B6%2B0FuJoFSA225%2BMCrD4fNNqi2Zd5Adi9qC9NdN8nBVsjvzqtn5NepOt8dG1qdv7KvVwG74emFea1j2JuMBVBMp2PgIEwCv5c8OJSXa2P66%2Bl3YCqEEMjBN22Dz0chlJ4hKMW3rA2oISH6ryzIvNIiy4%2FdjKEdmxFeBorp1vrbmAtBqE%2B%2BafggzzGt5w4arkbQ4fWLXJwkC9%2B1y4irCgmX65CEb5ct%2BfRgG2%2F9%2FB0rYg%3D%3D

3. 拼接请求#

将编码后的请求数据发送至财资云网关地址。
上一页
密钥格式说明
下一页
自行实现验签
Built with