# 企业对公打款认证API

# 业务介绍

企业对公打款认证API是通过企业的对公账户及企业的打款动作来核验企业信息的真实性,可搭配其他企业身份认证方式使用,以提高企业认证的安全性及可信度。

打款金额在0.01~1元之间

注意: 打款金额,概不退款!

# 获取 Token

根据传入的sdk的信息生成认证token

# 一、请求说明

# 二、请求参数

名称 类型 是否必须 描述
entName String 企业名称
entBankNumber String 企业对公账号
paymentPurpose String 打款附言(如果填写,打款时必须填写相同的附言)
notifyUrl String 服务器通知地址(点我查看详细使用)

请求示例:

https://api.spiderid.cn/api/router/rest?
entName=XXX
&entBankNumber=XXX
&<[公共请求参数]>

# 三、响应参数

data 结果信息 类型 描述
incorrect Integer 返回码(详情见字段解释)
token String 认证Token
message String 对返回码的描述
receivingPaymentAccountName String 收款户名
receivingPaymentBankName String 收款开户行名称
receivingPaymentBank String 收款银行
receivingPaymentUnionNo String 收款联行号
receivingPaymentEntName String 收款公司名称
receivingPaymentAccount String 收款银行账号
amount String 打款金额
purpose String 打款时附言
  • 字段解释

  • incorrect 返回码介绍

字段 状态介绍
100 成功

# 四、成功示例

JSON示例

{
    "code": 0,
    "requestId":"f1006...",
    "message": "success",
    "data": {
        "amount": "0.76",
        "incorrect": 100,
        "message": "成功",
        "receivingPaymentAccount": "3116**3140",
        "receivingPaymentAccountName": "收款户名",
        "receivingPaymentBank": "**银行",
        "receivingPaymentBankName": "**银行",
        "receivingPaymentEntName": "**公司",
        "receivingPaymentUnionNo": "**123",
        "token": "f1006..."
        "purpose": "123..."
    }
}

# 获取认证状态

根据token获取认证状态

# 一、请求说明

# 二、请求参数

名称 类型 是否必须 描述
token String 认证token

请求示例:

https://api.spiderid.cn/api/router/rest?
token=XXX
&<[公共请求参数]>

# 三、响应参数

data 结果信息 类型 描述
status String 认证状态
incorrect Integer 返回码(详情见字段解释)
message String 对返回码的描述
  • 字段解释

  • status 认证状态介绍

字段 状态介绍
ACTION 认证中
SUCCESS 认证成功
FAILURE 认证失败
INVALID 认证超时
  • incorrect 返回码介绍
字段 状态介绍
100 成功
101 认证记录不存在

# 四、成功示例

JSON示例

{
    "code": 0,
    "requestId":"f1006...",
    "message": "success",
    "data": {
        "status":"SUCCESS",
        "incorrect":100,
        "message":"成功"
    }
}

# 五、失败示例

JSON示例

{
    "code": 0,
    "requestId":"f1006...",
    "message": "success",
    "data": {
        "incorrect":101,
        "message":"认证记录不存在"
    }
}

# 获取认证结果

根据token获取认证结果。

# 二、请求参数

名称 类型 是否必须 描述
token String 认证token

请求示例:

https://api.spiderid.cn/api/router/rest?
token=XXX
&<[公共请求参数]>

# 三、响应参数

data 结果信息 类型 描述
amount String 打款金额
incorrect Integer 返回码(详情见字段解释)
message String 对返回码的描述
paymentTime String 打款日期
purpose String 打款附言
  • 字段解释

  • incorrect 返回码介绍

字段 状态介绍
100 成功
101 认证记录不存在
102 认证状态非法
103 认证失败
104 认证超时

# 四、成功示例

JSON示例

{
    "code": 0,
    "requestId":"f1006...",
    "message": "success",
    "data": {
        "amount":"0.32",
        "incorrect":100,
        "message":"成功",
        "paymentTime": "2021-10-13 10:31:58",
        "purpose": "123..."
    }
}

# 五、失败示例

JSON示例

{
    "code": 0,
    "requestId":"f1006...",
    "message": "success",
    "data": {
        "incorrect":103,
        "message":"认证失败"
    }
}

# SDK 请求示例

# 获取token

        //提供的url
        String url = "http://api.spiderid.cn/api/router/rest";
        //您的appKey
        String appkey = "XXX";
        //您的appSecret
        String secretKey = "XXX";
        //1.原客户端
        ApiClient apiClient = new DefaultApiClient(url, appkey, secretKey);
        //2.调用出错,自动重试客户端
        EntAuthBankpayGetTokenRequest req = new EntAuthBankpayGetTokenRequest();
        
        req.setEntName("xxx公司");
        
        req.setEntBankNumber("123...");
        //打款附言 (选填)
        //req.setPaymentPurpose();
        //平台方服务器主动通知客户服务器指定地址,向服务器主动推送验证结果(选填)
        //req.setNotifyUrl();

        try {
            EntAuthBankpayGetTokenResponse response = apiClient.execute(req);
            //后续业务处理

        } catch (ApiException e) {
            e.printStackTrace();
        }
    }

# 获取认证状态

        //提供的url
        String url = "http://api.spiderid.cn/api/router/rest";
        //您的appKey
        String appkey = "XXX";
        //您的appSecret
        String secretKey = "XXX";
       //1.原客户端
        ApiClient apiClient = new DefaultApiClient(url, appkey, secretKey);
        //2.调用出错,自动重试客户端
        EntAuthBankpayGetStatusRequest req = new EntAuthBankpayGetStatusRequest();
        //认证token
        req.setToken("XXX");

        try {
            EntAuthBankpayGetStatusResponse response = apiClient.execute(req);
            //后续业务处理
            
        } catch (ApiException e) {
            e.printStackTrace();
        }
    }

# 获取认证结果

        //提供的url
        String url = "http://api.spiderid.cn/api/router/rest";
        //您的appKey
        String appkey = "XXX";
        //您的appSecret
        String secretKey = "XXX";
        //1.原客户端
        ApiClient apiClient = new DefaultApiClient(url, appkey, secretKey);
        //2.调用出错,自动重试客户端
        EntAuthBankpayGetResultRequest req = new EntAuthBankpayGetResultRequest();
        //认证token
        req.setToken("xxx");


        try {
            EntAuthBankpayGetResultResponse response = apiClient.execute(req);
            //后续业务处理

        } catch (ApiException e) {
            e.printStackTrace();
        }

# 回调通知接口示例

当企业打款认证流程结束后,平台服务器会向开发者提供的notifyUrl参数地址向开发者发送 HTTP 通知。

开发者在接收到参数时,需要对参数进行验签,在确认无误的情况下,返回"success"字符串。

    /**
     * 回调通知示例
     *
     * @param request
     */
    public String notify(HttpServletRequest request) {
        Map<String, String> paranMap = Maps.newHashMap();

        //公共参数
        paranMap.put("appKey", request.getParameter("appKey"));
        paranMap.put("signType", request.getParameter("signType"));
        paranMap.put("notifyTime", request.getParameter("notifyTime"));
        paranMap.put("notifyId", request.getParameter("notifyId"));
        paranMap.put("version", request.getParameter("version"));
        paranMap.put("requestId", request.getParameter("requestId"));

        paranMap.put("sign", request.getParameter("sign"));

        String appSecret = "应用密钥";

        //验签
        boolean res = SignUtils.signCheck(paranMap, appSecret, SignUtils.CHARSET, request.getParameter("signType"));
        log.info("验签结果为: {}", res ? "验签成功" : "验签失败");
        if (res) {
            //验签成功
            // 可以获取认证结果了
            //返回通知成功
            return "success";
        }
        // 验签失败 可以给一个异常
        return "fail";
    }

# 验签工具类

import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * 验签工具类
 */
public class SignUtils {

    public final static String SIGN_TYPE_HMAC_SHA_1 = "HmacSHA1";

    public final static String SIGN_TYPE_HMAC_SHA_256 = "HmacSHA256";

    public final static String REQUEST_PUBLIC_PARAMS_SIGN = "sign";

    public final static String CHARSET = "UTF-8";

    private static final Object LOCK = new Object();

    /**
     * Prototype of the Mac instance.
     */
    private static Mac macInstance;

    /**
     * 验签
     *
     * @param paranMap
     * @param secretKey
     * @param charset
     * @param signType
     * @return
     */
    public static boolean signCheck(Map<String, String> paranMap, String secretKey, String charset, String signType) {
        // 字典排序
        String sign = paranMap.get(REQUEST_PUBLIC_PARAMS_SIGN);
        String content = getSignCheckContentV1(paranMap);

        if (SIGN_TYPE_HMAC_SHA_1.equals(signType)) {
            return sign.equals(computeSignature(secretKey, content, charset, SIGN_TYPE_HMAC_SHA_1));
        } else {
            return sign.equals(computeSignature(secretKey, content, charset, SIGN_TYPE_HMAC_SHA_256));
        }
    }


    public static String getSignCheckContentV1(Map<String, String> params) {
        if (params == null) {
            return null;
        }
        params.remove(REQUEST_PUBLIC_PARAMS_SIGN);
        return getSignatureContent(params);
    }


    public static String getSignatureContent(Map<String, String> paranMap) {
        return getSignContent(getSortedMap(paranMap));
    }

    public static Map<String, String> getSortedMap(Map<String, String> paranMap) {
        Map<String, String> sortedParams = Maps.newTreeMap();

        paranMap.keySet().stream()
                .sorted()
                .forEach(sortKey -> sortedParams.put(sortKey, paranMap.get(sortKey)));

        return sortedParams;
    }

    /**
     * @param sortedParams
     * @return
     */
    public static String getSignContent(Map<String, String> sortedParams) {
        StringBuffer content = new StringBuffer();
        List<String> keys = new ArrayList<String>(sortedParams.keySet());
        Collections.sort(keys);

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = sortedParams.get(key);
            if (StringUtils.isNotEmpty(key) && StringUtils.isNotEmpty(value)) {
                content.append(key).append(value);
            }
        }
        return content.toString();
    }


    public static String computeSignature(String key, String data, String charset, String algorithm) {
        try {
            byte[] signData = sign(key.getBytes(charset), data.getBytes(charset), algorithm);
            return byte2hex(signData);
        } catch (UnsupportedEncodingException ex) {
            throw new RuntimeException("Unsupported algorithm: " + charset, ex);
        }
    }


    private static byte[] sign(byte[] key, byte[] data, String algorithm) {
        try {
            // Because Mac.getInstance(String) calls a synchronized method, it could block on
            // invoked concurrently, so use prototype pattern to improve perf.
            if (macInstance == null) {
                synchronized (LOCK) {
                    if (macInstance == null) {
                        macInstance = Mac.getInstance(algorithm);
                    }
                }
            }

            Mac mac = null;
            try {
                mac = (Mac) macInstance.clone();
            } catch (CloneNotSupportedException e) {
                // If it is not clonable, create a new one.
                mac = Mac.getInstance(algorithm);
            }
            mac.init(new SecretKeySpec(key, algorithm));
            return mac.doFinal(data);
        } catch (NoSuchAlgorithmException ex) {
            throw new RuntimeException("Unsupported algorithm: " + algorithm, ex);
        } catch (InvalidKeyException ex) {
            throw new RuntimeException("Invalid key: " + key, ex);
        }
    }


    /**
     * 把字节流转换为十六进制表示方式。
     */
    protected static String byte2hex(byte[] bytes) {
        StringBuilder sign = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }
        return sign.toString();
    }


}
最后更新于: 10/20/2021, 10:07:30 AM