接口鉴权

获取授权密钥

  1. 注册登录猪八戒网
  2. 访问八戒财税开放平台应用中心申请应用
  3. 等待平台联系审核,通过之后自动创建AppKey和AppSecrect

WARNING

请勿把AppKey和AppSecret存到前端本地,导致数据被抓包、资源被盗用

公共参数

对接口签名时,公共参数需要统一放到请求头部Header中, 如下

参数名称 类型 必选 描述
X-CS-Authorization String 签名校验方式,目前只支持HMAC-SHA256
X-CS-Key String 授权应用AppKey
X-CS-Nonce String 自行生成的随机唯一UUID,36个字符以内
X-CS-Timestamp String 当前UNIX时间戳(10位),精确到秒,注意:如果和服务器时间相差超过10分钟,签名失败
X-CS-Version String 当前接口版本号

拼接签名字符串

1、 对公共参数名按字母升序排序, 得到:

  • X-CS-Authorization: "HMAC-SHA256"
  • X-CS-Key: "5673AEFC6D24351826B5"
  • X-CS-Nonce: "080537a0-8266-4053-a82c-404b7909afeb"
  • X-CS-Timestamp: "1559831475"
  • X-CS-Version: "v2"

将上一步排序好的请求参数格式化成“参数名”=“参数值”的形式,然后将格式化后的各个参数用|拼接在一起,注意:末尾不需要|

2、 在字符串前面加上接口【大写】Method和分隔符,得到如下字符串

POST|X-CS-Authorization=HMAC-SHA256|X-CS-Key=5673AEFC6D24351826B5|X-CS-Nonce=080537a0-8266-4053-a82c-404b7909afeb|X-CS-Timestamp=1559831475|X-CS-Version=v2

计算签名

通过HMAC-SHA256 ,用应用密钥AppSecrect对上一步拼接的字符串进行计算,返回格式为Base64

crypto
    .createHmac("sha256", appSecret)
    .update('signString')
    .digest("base64");

得到ABNBrbrj2awewbTzDeZvsPJKiXv5ftYXaCIBbQPFhqM=

使用签名

将计算的签名结果放到请求的 Header 中,Key为:X-CS-Signature

POST /v2/invoice/query HTTP/1.1
Host: open.cs.zbj.com
Content-Type: application/json;charset=utf-8
X-CS-Authorization: HMAC-SHA256
X-CS-Key: 5673AEFC6D24351826B5
X-CS-Nonce: 080537a0-8266-4053-a82c-404b7909afeb
X-CS-Timestamp: 1559831475
X-CS-Version: v2
X-CS-Signature: ABNBrbrj2awewbTzDeZvsPJKiXv5ftYXaCIBbQPFhqM=

// Payload data
{"key1":"val1","key2":"val2"}

示例代码

提供了部分语言的示例实现,其他语言可以根据以上流程自行实现

Java

package com.zbj.cs;

import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.google.gson.Gson;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

/**
 * Hello csopen
 *
 */
public class App {

    public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException {
        try {
            final String host = "https://open.cs.zbj.com";

            String appKey = "应用中心申请";
            String appSecret = "应用中心申请";
            String method = "post";

            String path = "/v2/invoice/query";
            String url = host + path;

            HashMap<String,String> payload = new HashMap<String,String>();
            payload.put("fpdm", "051001900111");
            payload.put("fphm", "51096586");
            payload.put("kprq", "20190608");
            payload.put("checkCode", "769875");
            payload.put("noTaxAmount", "110.72");

            doRequest(appKey, appSecret, method, url, payload);

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

    private static void doRequest(String appKey, String appSecret, String method, String url, HashMap<String,String> payload)
            throws Exception {

        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Content-Type", "application/json;charset=UTF-8");

        List<NameValuePair> params = getParams(appKey);

        final String splitor = "|";
        StringBuilder builder = new StringBuilder(method.toUpperCase() + splitor);
        for (int i = 0; i < params.size(); i++) {
            NameValuePair pair = params.get(i);
            String key = pair.getName();
            String value = pair.getValue();
            httpPost.addHeader(key, value);
            builder.append(key + "=" + value + splitor);
        }

        String signString = builder.substring(0, builder.length() - 1);
        System.out.println(signString);
        String signature = getSignature(appSecret, signString);
        System.out.println(signature);
        httpPost.addHeader("X-CS-Signature", signature);

        String json = new Gson().toJson(payload);
        httpPost.setEntity(new StringEntity(json, "UTF-8"));

        CloseableHttpResponse response = httpclient.execute(httpPost);
        HttpEntity entity = response.getEntity();
        System.out.println("------------------response------------------");
        System.out.println(EntityUtils.toString(entity));
    }

    /**
     * 获取公共参数
     *
     * @param appKey 授权应用的appKey
     * @return 签名字符串
     */
    private static List<NameValuePair> getParams(String appKey) {
        if (appKey == null) {
            throw new NullPointerException();
        }

        List<NameValuePair> params = new ArrayList<NameValuePair>();

        params.add(new BasicNameValuePair("X-CS-Authorization", "HMAC-SHA256"));
        params.add(new BasicNameValuePair("X-CS-Key", appKey));
        params.add(new BasicNameValuePair("X-CS-Nonce", UUID.randomUUID().toString()));
        params.add(new BasicNameValuePair("X-CS-Timestamp", "" + System.currentTimeMillis() / 1000L));
        params.add(new BasicNameValuePair("X-CS-Version", "v2"));
        return params;
    }

    /**
     * 获取签名字符串 跟据公共参数的集合,按名称升序排列后用‘|’拼接
     *
     * @param appKey    授权应用的appKey
     * @param 请求的Http方法
     * @return 签名字符串
     */
    public static String getSignature(final String key, final String data)
            throws NoSuchAlgorithmException, InvalidKeyException {
        if (key == null || data == null) {
            throw new NullPointerException();
        }
        final String HMAC_SHA256 = "HmacSHA256";
        final Mac hMacSHA256 = Mac.getInstance(HMAC_SHA256);
        byte[] hmacKeyBytes = key.getBytes(StandardCharsets.UTF_8);
        final SecretKeySpec secretKey = new SecretKeySpec(hmacKeyBytes, HMAC_SHA256);
        hMacSHA256.init(secretKey);
        byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
        byte[] res = hMacSHA256.doFinal(dataBytes);
        return Base64.getEncoder().encodeToString(res);
    }
}

PHP

<?php

function guid(){
    if (function_exists('com_create_guid')){
        return com_create_guid();
    }else{
        mt_srand((double)microtime()*10000);
		$charid = strtoupper(md5(uniqid(rand(), true)));
        $hyphen = chr(45);
        $uuid = substr($charid, 0, 8).$hyphen
                .substr($charid, 8, 4).$hyphen
                .substr($charid,12, 4).$hyphen
                .substr($charid,16, 4).$hyphen
                .substr($charid,20,12);
        return $uuid;
    }
}

function getSignaure($secret, $message) {
	$s = hash_hmac('sha256', $message, $secret, true);
	return base64_encode($s);
}

function getParams($appKey) {
	$params = array(
		"X-CS-Authorization" => "HMAC-SHA256",
		"X-CS-Key" => $appKey,
		"X-CS-Nonce" => guid(),
		"X-CS-Timestamp" => time(),
		"X-CS-Version" => "v2"
	);

	return $params;
}

function doRequest($appKey, $appSecret, $url, $method, $payload) {
	$curl = curl_init();

	$headers = array(
		"content-type:application/json;charset=UTF-8"
	);

	$splitor = '|';
	$builder = strtoupper($method).$splitor;
	$params = getParams($appKey);

	foreach ($params as $key => $value) {
		array_push($headers, $key.":".$value);
		$builder.=$key.'='.$value.$splitor;
	}

	$signString = substr($builder, 0 , strlen($builder) - 1);
	echo $signString."\n";
	$signature = getSignaure($appSecret, $signString);
	echo $signature."\n";

	array_push($headers, "X-CS-Signature:".$signature);

	curl_setopt($curl, CURLOPT_URL, $url);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

	curl_setopt($curl, CURLOPT_POST, true);
	curl_setopt($curl, CURLOPT_HEADER, true);
	curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
	curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);

	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);

	curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
	curl_setopt($curl, CURLOPT_TIMEOUT, 30);

	$resp = curl_exec($curl);
	curl_close($curl);

	echo $resp;
}

try {
	$baseUri = "https://open.cs.zbj.com";

	$appKey = "应用中心申请";
	$appSecret = "应用中心申请";
	$method = "post";

	// 基础查询
	$path = "/v2/invoice/query";
	$url = $baseUri.$path;
	$payload = array(
		"fpdm" => "051001900111",
		"fphm" => "51096586",
		"kprq" => "20190608",
		"checkCode" => "769875",
		"noTaxAmount" => "110.72"
	);
	$payloadJson = json_encode($payload);
	// echo $payloadJson;

	doRequest($appKey, $appSecret, $url, $method, $payloadJson);
} catch (Exception $e) {
    echo 'Caught exception: ',  $e->getMessage(), "\n";
}

Node.js

const crypto = require("crypto");
const uuid = require('uuid/v4');
const axios = require('axios');
const url = "https://open.cs.zbj.com/v2/invoice/query";
const AppKey = "应用中心申请";
const AppSecret = "应用中心申请";

const headers = {
  "X-CS-Authorization": "HMAC-SHA256",
  "X-CS-Key": AppKey,
  "X-CS-Nonce":  uuid(),
  "X-CS-Timestamp": Math.ceil(Date.now() / 1000),
  "X-CS-Version": "v2"
}

const signStr = "POST|" + Object.entries(headers).map(([key, val]) => `${key}=${val}`).join("|");

const signature = crypto.createHmac("sha256", AppSecret).update(signStr).digest("base64")

axios.post(url, {
  kprq: "20190608",
  fpdm: "051001900111",
  fphm: "51096586",
  checkCode: "769875"
}, {
  headers: {...headers, 'X-CS-Signature': signature}
}).then(data => {
  console.log(data)
}).catch(e => {
  console.error(e)
})

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;

namespace csopen_csharp
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            try
            {
                const string host = "https://open.cs.zbj.com";

                const string appKey = "应用中心申请";
                const string appSecret = "应用中心申请";
                const string method = "post";

                const string path = "/v2/invoice/query";
                const string url = host + path;

                var payload = new
                {
                    fpdm= "051001900111",
                    fphm = "51096586",
                    kprq = "20190608",
                    checkCode= "769875",
                    noTaxAmount= "110.72"
                };

                DoRequest(appKey, appSecret, method, url, payload);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.StackTrace);
            }

            Console.ReadKey(true);
        }

        private static async void DoRequest(string appKey, string appSecret, string method, string url,
            Object payload)
        {
            using (var client = new HttpClient())
            {
                
                HttpContent content = new StringContent(JsonConvert.SerializeObject(payload));
                content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

                var parameters = BuildBasicParams(appKey);
                const string splitor = "|";
                var builder = new StringBuilder(method.ToUpper() + splitor);
                foreach (var parameter in parameters)
                {
                    content.Headers.Add(parameter.Key, parameter.Value);
                    builder.AppendFormat("{0}={1}{2}", parameter.Key, parameter.Value, splitor);
                }

                var signString = builder.ToString(0, builder.Length - 1);
                Console.WriteLine(signString);
                var signature = GetSignature(appSecret, signString);
                Console.WriteLine(signature);
                content.Headers.Add("X-CS-Signature", signature);

                var response = await client.PostAsync(url, content);
                var responseText = await response.Content.ReadAsStringAsync();
                Console.WriteLine("------------------response------------------");
                Console.WriteLine(responseText);
            }
        }

        private static IList<KeyValuePair<string, string>> BuildBasicParams(string appKey)
        {
            if (string.IsNullOrEmpty(appKey))
            {
                throw new ArgumentNullException(nameof(appKey));
            }

            IList<KeyValuePair<string, string>> parameters = new List<KeyValuePair<string, string>>
            {
                new KeyValuePair<string, string>("X-CS-Authorization", "HMAC-SHA256"),
                new KeyValuePair<string, string>("X-CS-Key", appKey),
                new KeyValuePair<string, string>("X-CS-Nonce", Guid.NewGuid().ToString()),
                new KeyValuePair<string, string>("X-CS-Timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString()),
                new KeyValuePair<string, string>("X-CS-Version", "v2")
            };

            parameters.OrderBy(x => x.Key);

            return parameters;
        }

        private static string GetSignature(string key, string data)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException(nameof(key));
            }

            if (string.IsNullOrEmpty(data))
            {
                throw new ArgumentNullException(nameof(data));
            }

            var encoding = new ASCIIEncoding();
            var hash = new HMACSHA256(encoding.GetBytes(key));
            var hashMessage = hash.ComputeHash(encoding.GetBytes(data));
            return Convert.ToBase64String(hashMessage);
        }
    }
}

Python

#!/usr/bin/python3
import time
import uuid
import hmac
import base64
import hashlib
from urllib import request, parse, error

AppKey = "应用中心申请"
AppSecret = "应用中心申请"

url = "https://open.cs.zbj.com/v2/invoice/query"
headers = {
  "X-CS-Authorization": "HMAC-SHA256",
  "X-CS-Key": AppKey,
  "X-CS-Nonce":  str(uuid.uuid4()),
  "X-CS-Timestamp": str(int(time.time())),
  "X-CS-Version": "v2"
}

signList = ['POST']
for key,val in  headers.items():
  signList.append(key + '=' + val)

signStr = '|'.join(signList)
signature = base64.b64encode(hmac.new(bytes(AppSecret, 'utf-8'), bytes(signStr, 'utf-8'), hashlib.sha256).digest())

headers['X-CS-Signature'] = str(signature, 'utf-8')

parms = {
  "kprq": "20190608",
  "fpdm": "051001900111",
  "fphm": "51096586",
  "checkCode": "769875"
}
req = request.Request(url, parse.urlencode(parms).encode(encoding='utf8'), headers)
try:
  res = request.urlopen(req)
  resText = res.read()
  print(resText.decode())
except error.HTTPError as err:
  error_message = err.read()
  print (error_message.decode())