商户接入指南 商户接入指南
  • V2 (opens new window)
  • V3 (opens new window)
  • V4 (opens new window)
  • V4-English (opens new window)
  • V2 (opens new window)
  • V3 (opens new window)
  • V4 (opens new window)
  • V4-English (opens new window)
  • 接入指南

    • 起步
    • 名词约定
    • 对接流程(必读)
    • 沙箱环境测试资源
    • 对接验收
    • 签名规约
  • 接入方案

    • 收银台模式

      • 内嵌JS-SDK
      • 跳转接入
    • 端到端模式

      • 快速开始
      • 商户收银台规范
    • 交易状态

      • 交易状态
      • 风险订单REVIEW
      • 异步通知
    • 交易场景

      • 交易模式-DEBIT和AUTH
      • Tokenization
      • NetworkToken
      • 本地支付
      • 3D集成指南
      • recurring集成指南
      • 退款集成指南
      • dispute API集成指南
      • 一键支付集成指南
    • 开源建站工具插件支持

      • Magento235
      • Woocommerce
      • OpenCart
      • Prestashop
      • ZenCart
  • APIs

    • 交易下单

    • 交易查询

    • 快捷支付

    • 一键支付

    • 拒付管理

    • 风控管理

    • 物流信息

    • 交易账单

    • 币种汇率

    • 商户信息

    • KYB方案

      • KYB概述
      • KYB服务密钥获取流程
      • 签名
        • 签名类型
        • 请求签名
        • 异步通知签名
        • 签名串组装
      • 子商户注册
      • 批量新增法人股东受益人
      • 新增法人股东受益人
      • 删除法人股东受益人
      • 新增公司信息
      • 查询子商户详情
      • 保存店铺
      • 店铺列表
      • 店铺详情
      • 异步通知
      • 上传文件
  • 工具

  • 附录

  • v3

KYB-API签名

# KYB-API签名

# 签名类型

推荐使用SHA256签名方式,安全度高于MD5

签名类型 描述
MD5 表示选择 MD5 算法,商户使用 Salt 对报文进行摘要签名和验签
SHA256 表示选择 SHA256算法,商户使用 Salt 对报文进行摘要签名和验签

# 请求签名

商户需要使用自身的私钥对消息体中关键数据的组合进行签名。没有携带签名或者签名验证不通过的请求,都不会被执行,并返回错误。

以下为PingPong KYB API 与调用方约定的请求加签参数列表

参数名 描述
institutionId 机构ID
subClientId 子商户号
bizType 业务类型
bizId 业务id
signType 签名类型

KYB-API采用部分签名的方式,若请求参数在以上列表中,参与签名,否则不参与签名。

# 异步通知签名

异步通知签名只支持MD5的加签形式,加签参数同上述列表。

# 签名串组装

字典序:按首字母进行排序;

queryString:用'=' 进行参数名和参数值(trim 后的值)的拼接,用'&'进行多 个参数之间的拼接,即 key1=val1&key2=val2&key3=val3

  • 对参数名按字典序排序后,按照queryString方式组装。

  • 签名秘钥(salt)放入签名串的位置为: 签名串的开头 , 即{salt}key1=val2&key2=val2&key3=val3

    import com.google.common.collect.Maps;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.codec.digest.DigestUtils;
    import org.apache.commons.lang3.StringUtils;
    
    import java.security.MessageDigest;
    import java.util.Map;
    import java.util.TreeMap;
    
    @Slf4j
    public class Sign {
    
        /**
         * 部分参数签名,参与签名的字段
         */
        private static final String[] includeFields = {"institutionId", "subClientId", "bizType", "bizId", "signType"};
    
        /**
         * 签名秘钥
         */
        private String salt = null;
    
    
        public Sign(String salt) {
            this.salt = salt;
        }
    
        /**
         * 执行签名
         *
         * @param signType 签名类型
         * @param signMap  待签名串
         */
        public String signature(String signType, TreeMap<String, Object> signMap) {
    
            String  signContent = getPartSignParams(signMap);
            log.debug("signContent:{}", signContent);
    
            if (StringUtils.equalsIgnoreCase("MD5",signType)) {
                return md5Sign(salt, signContent);
            } else if (StringUtils.equalsIgnoreCase("SHA256",signType)) {
                return sha256(signContent, salt);
            }
            return null;
        }
    
    
        /**
         * 获取待签名串(部分字段签名)
         */
        private static String getPartSignParams(TreeMap<String, Object> signMap) {
            //添加需要签名的字段
            TreeMap<String, Object> resultMap = Maps.newTreeMap();
            for (String param : includeFields) {
                String value = (String) signMap.get(param);
                if (StringUtils.isNotBlank(value)) {
                    resultMap.put(param, value);
                }
            }
            return getSignParams(resultMap);
        }
    
        /**
         * 获取待签名串
         */
        private static String getSignParams(TreeMap<String, Object> resultMap) {
            StringBuilder stringBuilder = new StringBuilder();
            int paramNum = 0;
            for (Map.Entry<String, Object> signEntry : resultMap.entrySet()) {
                paramNum++;
                stringBuilder.append(signEntry.getKey());
                stringBuilder.append("=");
                stringBuilder.append(signEntry.getValue());
                if (paramNum < resultMap.size()) {
                    stringBuilder.append("&");
                }
            }
            log.debug("content:【{}】", stringBuilder);
            return stringBuilder.toString();
        }
    
    
        private static String md5Sign(String salt, String content) {
            try {
                MessageDigest md = MessageDigest.getInstance("MD5");
                md.update(salt.getBytes());
                md.update(content.getBytes());
                byte[] digest = md.digest();
                return byteToHexString(digest);
            } catch (Exception e) {
                log.error("md5签名失败", e);
            }
            return null;
        }
    
    
        private static String sha256(String content, String salt) {
            try {
                if (StringUtils.isBlank(salt)) {
                    throw new RuntimeException("salt is null");
                }
                String contentStr = salt.concat(content);
                return DigestUtils.sha256Hex(contentStr.getBytes("UTF-8")).toUpperCase();
            } catch (Exception e) {
                log.error("sha256", e);
            }
    
            return null;
        }
    
    
        public static String byteToHexString(byte[] b) {
            StringBuilder hexString = new StringBuilder();
            for (int i = 0; i < b.length; i++) {
                String hex = Integer.toHexString(b[i] & 0xFF);
                if (hex.length() == 1) {
                    hex = '0' + hex;
                }
                hexString.append(hex.toUpperCase());
            }
            return hexString.toString();
        }
    }
        
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    // Make sure to add code blocks to your code group
    上次更新: 2022/07/24, 18:48:08
    KYB服务密钥获取流程
    子商户注册

    ← KYB服务密钥获取流程 子商户注册→

    杭州乒乓智能技术有限公司 | Copyright © 2015-2024 checkout.pingpongx.com.All Rights Reserved.
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式