API接入综述

1.引言

贷款超市用于连接用户和商户,借助于千万级的导流能力,满足商户扩大用户流量,提升品牌知名度的需求。另为加强用户体验,贷款超市打通商户账号体系,统一用户资料、授信、借款、还款流程。 本文档主要面对于需要接入贷款超市的商户系统开发人员。

2.简介

2.1 接口约定

签名机制: 贷款超市通过签名机制来保证通信安全。签名算法为 SHA256withRSA。RSA生成的密钥对请务必使用PKCS#8的格式。商户自行生成一份 RSA 密钥对,将公钥传给贷款超市,贷款超市也会给商户关于贷款超市的合作公钥。即商户有「商户私钥,贷款超市公钥」,贷款超市有「商户公钥,贷款超市私钥」。进行接口交互时,商户使用商户私钥对参数签名,贷款超市使用商户公钥来验证签名,反之亦然。具体语言实现见样例代码。

总体规范:

1. 申请APP_ID, 交换公私钥
2. 所有请求均使用 POST 方法
3. 所有请求均需要设置如下 header 头: `Content-type: application/json; charset=utf-8`
4. 所有请求参数顺序,请保持文档范例的顺序

接口涉及到的金额说明: 所有文档里面凡是金额字段,全部都是以最小单位做交互。例如人民币就是以为单位,印度币就是以派士为单位。

重要说明: 凡是商户自己数据库里面的还款计划表有任何变更,都必须回调贷款超市更新,调用接口3.1还款计划变更通知,建议使用mysql的触发器来做,或者消息队列。

请求接口:

请求接口如下:
    {
        "app_id": "",
        "params": "",
        "sign"  : "" 
    }
params 是由所有业务参数组成一个 string,再编码成 json 字符串,sign 是由appid加 对 params 参数进行json编码, 按照通过上诉签名算法生成的字符串,
对最终生成的签名进行base64编码, 最终将以上的得到的参数(sign)拼装到请求中,发起http请求。

响应请求

{
    "code": 0,
    "message": "success",
    "data": {}
}
code 用于表示错误编码(字典值详见4.1),message 表示用户友好的错误提示,data 才是真正的业务结果参数。

签名样例代码

PHP版本


     $private = '-----BEGIN PRIVATE KEY-----
     MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKyDAsD34pUzg0JQ
     MSd5dRhmoirckyxNQtryVrt6AeLJBKUtuSRZRSQFh6YxFWSI/CFJTm4Y4/35ahsM
     hE+ruxAL7LU0oxo7jigmlir8vSxD2EQJeuQSLa+fcFIeOD+zC3dUycU70hJBXctJ
     TLZCKcAhXmvYfTLAdiFDpZog0bSJAgMBAAECgYBb2c2HjPRZWHoUvrnNOL/94/eI
     NrUtq11jUhCX9Vg3tawBfpzLG7TSfxdAh3XKd502498cg6gwu7GhAUMZYGhSPLzR
     X1/NRQwCzYpD4JpiEn5OPkfu/GkF3wUe2x7/NcBwjqttdNHLXzTuZwSi51cKxLG3
     L8VlBG+qWOLT4f0QGQJBAN7FmimamESNIiCiBDylf6MEZlO4+dazFMvb0etr4Ke5
     hD13yCqwVgmgdRyyXEVXr0fpFkS8yEAI6pfPWhJhNT8CQQDGPkJDPKacn6AMk/0d
     3aM7J9HSD3easSgvmheT/lDV0ywpNO9gDHj+SuPXrr9g1QssRO3MeSS3068h8i6W
     67w3AkA5nNcKzW11xR4/XEdGO/LvgPLJ0jvi17uYIUh/3w1hsyvNDc5Zo/MMWFAR
     VNtiH5Q4P8K/kicj0GqDSl/cAwHXAkAcr2eRKRaGtUZIa6WFi8uRxLpEpf5NgPLq
     qGZfAL6bohnSwJkekc90JEwBqoSAs3MRbcvm+WSJwPSnec6qAeRTAkEAl/3FMsj7
     E2tTF2gK4k78f0UXk6lFTnihL/bwxtC+EkUrDOAIDC5uDQokoilYN+NqYHK8dqwY
     zl0AauMwcchaCw==
     -----END PRIVATE KEY-----';

     $public = "-----BEGIN PUBLIC KEY-----
     MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsgwLA9+KVM4NCUDEneXUYZqIq
     3JMsTULa8la7egHiyQSlLbkkWUUkBYemMRVkiPwhSU5uGOP9+WobDIRPq7sQC+y1
     NKMaO44oJpYq/L0sQ9hECXrkEi2vn3BSHjg/swt3VMnFO9ISQV3LSUy2QinAIV5r
     2H0ywHYhQ6WaING0iQIDAQAB
     -----END PUBLIC KEY-----";

     // 获取公私钥
     $pi_key = openssl_pkey_get_private($private);
     $pub_key = openssl_pkey_get_public($public);


     $params = [
         "username" => "mmomomo",
         "orderNo"  => "SH20180913111929"
     ];

     // 处理请求参数
     $params = getParams($params);
     // 生成签名
     $sign = sign($params, $private);
     // 验签
     $result = (bool)openssl_verify($params, base64_decode($sign), $public, OPENSSL_ALGO_SHA256);

     if ($result) {
         echo "验签成功" . PHP_EOL;
     } else {
         echo "验签失败" . PHP_EOL;
     }


     /**
      * @param $params
      *
      * @return string
      */
     function getParams($params)
      {
          $appId = "12345678";
          $params = "app_id={$appId}&params=" . json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
          return $params;
      }

     function sign($str, $privateKey)
     {
         //返回16进制小写字母
         $pkeyid = openssl_pkey_get_private($privateKey);
         // compute signature
         openssl_sign(trim($str), $signature, $pkeyid, "sha256WithRSAEncryption");
         // free the key from memory
         openssl_free_key($pkeyid);

         return base64_encode(trim($signature));
     }

JAVA版本

    import com.alibaba.fastjson.JSON;
    import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

    import java.io.IOException;
    import java.security.*;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.X509EncodedKeySpec;
    import java.util.*;

    public class Demo {
        //RSA签名
        //text:待签名的数据,privateKeyData:第三方的私钥
        private static byte[] sign(final byte[] text, final byte[] privateKeyData) throws GeneralSecurityException {
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyData);
            KeyFactory keyFactory       = KeyFactory.getInstance("RSA");
            PrivateKey privateKey       = keyFactory.generatePrivate(keySpec);
            Signature signatureChecker  = Signature.getInstance("SHA256WITHRSA");
            signatureChecker.initSign(privateKey);
            signatureChecker.update(text);
            return signatureChecker.sign();
        }

        //RSA验签名检查
        //text:待签名数据,signedText:签名值,publicKeyData:开发商公钥
        private static boolean verify(final byte[] text, final byte[] signedText, final byte[] publicKeyData) throws GeneralSecurityException {
            X509EncodedKeySpec keySpec  = new X509EncodedKeySpec(publicKeyData);
            KeyFactory keyFactory       = KeyFactory.getInstance("RSA");
            PublicKey publicKey         = keyFactory.generatePublic(keySpec);
            Signature signatureChecker  = Signature.getInstance("SHA256WITHRSA");
            signatureChecker.initVerify(publicKey);
            signatureChecker.update(text);
            return signatureChecker.verify(signedText);
        }

        public static String buildSignStr(Map<String, String> requestData) {
            return "app_id=" + requestData.get("app_id") + "&params=" + requestData.get("params");
        }

        public static void main(String args[]) throws GeneralSecurityException, RuntimeException, IOException {
            String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKyDAsD34pUzg0JQMSd5dRhmoirckyxNQtryVrt6AeLJBKUtuSRZRSQFh6YxFWSI/CFJTm4Y4/35ahsMhE+ruxAL7LU0oxo7jigmlir8vSxD2EQJeuQSLa+fcFIeOD+zC3dUycU70hJBXctJTLZCKcAhXmvYfTLAdiFDpZog0bSJAgMBAAECgYBb2c2HjPRZWHoUvrnNOL/94/eINrUtq11jUhCX9Vg3tawBfpzLG7TSfxdAh3XKd502498cg6gwu7GhAUMZYGhSPLzRX1/NRQwCzYpD4JpiEn5OPkfu/GkF3wUe2x7/NcBwjqttdNHLXzTuZwSi51cKxLG3L8VlBG+qWOLT4f0QGQJBAN7FmimamESNIiCiBDylf6MEZlO4+dazFMvb0etr4Ke5hD13yCqwVgmgdRyyXEVXr0fpFkS8yEAI6pfPWhJhNT8CQQDGPkJDPKacn6AMk/0d3aM7J9HSD3easSgvmheT/lDV0ywpNO9gDHj+SuPXrr9g1QssRO3MeSS3068h8i6W67w3AkA5nNcKzW11xR4/XEdGO/LvgPLJ0jvi17uYIUh/3w1hsyvNDc5Zo/MMWFARVNtiH5Q4P8K/kicj0GqDSl/cAwHXAkAcr2eRKRaGtUZIa6WFi8uRxLpEpf5NgPLqqGZfAL6bohnSwJkekc90JEwBqoSAs3MRbcvm+WSJwPSnec6qAeRTAkEAl/3FMsj7E2tTF2gK4k78f0UXk6lFTnihL/bwxtC+EkUrDOAIDC5uDQokoilYN+NqYHK8dqwYzl0AauMwcchaCw==";
            String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsgwLA9+KVM4NCUDEneXUYZqIq3JMsTULa8la7egHiyQSlLbkkWUUkBYemMRVkiPwhSU5uGOP9+WobDIRPq7sQC+y1NKMaO44oJpYq/L0sQ9hECXrkEi2vn3BSHjg/swt3VMnFO9ISQV3LSUy2QinAIV5r2H0ywHYhQ6WaING0iQIDAQAB";

            final Map<String, String> params = new HashMap<String, String>();
            params.put("username", "mmomomo");
            params.put("orderNo", "SH20180913111929");

            Map<String, String> requestData = new HashMap<String, String>() {
                {
                    put("app_id", "12345678");
                    put("params", JSON.toJSONString(params));
                }
            };

            byte[] requestDataByJson = buildSignStr(requestData).getBytes();

            byte[] base64PrivateKey = Base64.decode(privateKey);
            byte[] base64PublicKey  = Base64.decode(publicKey);

            byte[] signBytes = sign(requestDataByJson, base64PrivateKey);
            String strSignature = new String(Base64.encode(signBytes));
            System.out.println("签名:  " + strSignature);
            boolean bolVerify = verify(requestDataByJson, signBytes, base64PublicKey);
            System.out.println("验签:  " + bolVerify);
        }
    }

GO版本

package main

import (
    "bytes"
    "crypto"
    "crypto/rand"
    "crypto/rsa"
    "crypto/sha256"
    "crypto/x509"
    "encoding/base64"
    "encoding/json"
    "encoding/pem"
    "fmt"
)

func main() {
    signature := Sign(GetParams())
    if Check(GetParams(), signature) {
        fmt.Println("验签成功")
    } else {
        fmt.Println("验签失败")
    }
}

func GetParams() string {
    params := map[string]interface{}{
        "username":"momo",
        "orderNo":"SH20180913111929",
    }
    paramJson, _ := json.Marshal(params)
    appId := "12345678"
    str := "app_id=" + appId+"&params=" + string(paramJson)
    return str
}


var privateKey = []byte(`
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOXcXDlUfioHYK8x
+PTip8W9bwh9fWmTT9gXQGVo+91UQ0V/uZe1eR/PZrsW3GgZEqA3qavj2xSg/q+i
o8isZ2zm7qrzY0zvuCJ/ZTnqi1aGzlB2oH4CIq0nUtck/LeWS3Z5OQVQLB0FglRq
atZ/feqdBsqgqqwfggMVZI6A6w2PAgMBAAECgYACT7Pxs6LNI2PafyMkuz66o0wB
htmC/UM5RgqUuniF4joG8eDY4K9GHZzH1tQCi/X+/HBNVS43x+Knu3fwJaOolMzc
WagAW8csDXrT9M1XxfXiw/X/rKoit6Z60z+2OPO/XozR2MuP9Z6MYrLHRvD3RySD
rPK7ccogj3IJZ/4iUQJBAPYSb6Th2sb24flS8pxTgHMaBUeoRklnUMFW3UzpjsQV
uVGfr+P3RcqpWGh+9IqhFdMoVDmnUnr4Om712GIOJvcCQQDvInxeS9TqZNzSpVaZ
du7SL4tXEsZrgCpehtzQfEBCfReNTdEa25aseKSBqdpLyO8pgz5IjeFnU6ahifY0
y7ApAkEA2A6R9nzUglQtT6QUH0x2ARo8vpEyvaq4Tjn971U3JFZKtC942bm4jtwo
IwAtqTcNGa1UXpbapdwkOl8kEdyJOwJAQ0cKgmUHQ+KYldLaaFajnhKuOxMXK8tl
IC8FFMrAMXSMGb8Y41uAKonOjoRA3C1ty9oWvcbc8XsBWFU+JWBg8QJBAJ1xeRPh
SdYwi7GHNPQ4JruaYvrnBRK7XqYqHRBSMb8IFpDjWEoKJhr/rDoE/Z9x+pwgLsSM
OsfW8fVMcwILSqg=
-----END PRIVATE KEY-----
`)

var publicKey = []byte(`
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDl3Fw5VH4qB2CvMfj04qfFvW8I
fX1pk0/YF0BlaPvdVENFf7mXtXkfz2a7FtxoGRKgN6mr49sUoP6voqPIrGds5u6q
82NM77gif2U56otWhs5QdqB+AiKtJ1LXJPy3lkt2eTkFUCwdBYJUamrWf33qnQbK
oKqsH4IDFWSOgOsNjwIDAQAB
-----END PUBLIC KEY-----
`)

// 返回 signature
func  Sign(params string) string {
    messageBytes := bytes.NewBufferString(params)
    hash := sha256.New()
    hash.Write(messageBytes.Bytes())
    digest := hash.Sum(nil)
    //pem解码
    block, _ := pem.Decode(privateKey)
    var rsaPrivateKey interface{}

    //x509解码
    rsaPrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
    if err != nil {
        return ""
    }
    signature, err := rsa.SignPKCS1v15(rand.Reader, rsaPrivateKey.(*rsa.PrivateKey), crypto.SHA256, digest)
    if err != nil {
        return ""
    }
    return base64.StdEncoding.EncodeToString(signature)
}

func  Check(params, signature string) bool {
    messageBytes := bytes.NewBufferString(params)
    hash := sha256.New()
    hash.Write(messageBytes.Bytes())
    digest := hash.Sum(nil)
    //pem解码
    block, _ := pem.Decode(publicKey)
    //x509解码
    publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
    if err != nil {
        return false
    }
    sig, err := base64.StdEncoding.DecodeString(signature)
    if err != nil {
        return false
    }
    //类型断言
    publicKey := publicKeyInterface.(*rsa.PublicKey)
    err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, digest, sig)
    if err != nil {
        return false
    }
    return true
}

results matching ""

    No results matching ""