"use client" import { useState } from "react" import { ChevronLeft, Copy, Check, Info } from "lucide-react" import { Button } from "@/components/ui/button" import { useRouter } from "next/navigation" import { useToast } from "@/components/ui/use-toast" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { getApiGuideForScenario } from "@/docs/api-guide" import { Badge } from "@/components/ui/badge" import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion" const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'https://yishi.com' export default function ApiDocPage({ params }: { params: { channel: string; id: string } }) { const router = useRouter() const { toast } = useToast() const [copiedExample, setCopiedExample] = useState(null) const apiGuide = getApiGuideForScenario(params.id, params.channel) // 假设 fullUrl 和 apiKey 可通过 props 或接口获取,这里用演示值 const [apiKey] = useState("naxf1-82h2f-vdwcm-rrhpm-q9hd1") const [fullUrl] = useState("/v1/api/scenarios") const testUrl = fullUrl.startsWith("http") ? fullUrl : `${API_BASE_URL}${fullUrl}` const copyToClipboard = (text: string, exampleId: string) => { navigator.clipboard.writeText(text) setCopiedExample(exampleId) toast({ title: "已复制代码", description: "代码示例已复制到剪贴板", }) setTimeout(() => { setCopiedExample(null) }, 2000) } return (

计划接口文档

接口说明 本接口用于将外部客户数据导入到存客宝计划。请使用 apiKey 进行身份认证,建议仅在服务端调用。

支持多种编程语言和平台集成。接口地址和参数请参考下方说明。

安全提示: 请妥善保管您的API密钥,不要在客户端代码中暴露它。建议在服务器端使用该接口。

签名规则
签名参数: sign
签名算法: 将所有请求参数(排除 sign 和 apiKey)按参数名升序排序,直接拼接参数值(不含等号和&),对该字符串进行 MD5 加密,得到中间串,再拼接 apiKey,最后再进行一次 MD5 加密,结果作为 sign 参数传递。
签名步骤:
  1. 去除 sign 和 apiKey 参数,将其余所有参数(如 name、phone、timestamp、source、remark、tags)按参数名升序排序
  2. 直接拼接参数值,如 value1value2value3...
  3. 对拼接后的字符串进行 MD5 加密,得到中间串
  4. 将中间串与 apiKey 直接拼接
  5. 对拼接后的字符串再进行一次 MD5 加密,结果即为 sign
示例:
{`参数:
  name=张三
  phone=18888888888
  timestamp=1700000000
  apiKey=naxf1-82h2f-vdwcm-rrhpm-q9hd1

排序后拼接(排除apiKey,直接拼接值):
  张三188888888881700000000

第一步MD5:
  md5(张三188888888881700000000) = 123456abcdef...

拼接apiKey:
  123456abcdef...naxf1-82h2f-vdwcm-rrhpm-q9hd1

第二步MD5:
  sign=md5(123456abcdef...naxf1-82h2f-vdwcm-rrhpm-q9hd1)`}
                
注意:所有参数均需参与签名(除 sign 和 apiKey),且参数值需为原始值(不可 URL 编码)。
接口地址
{testUrl}
必要参数: phone (电话), timestamp (时间戳), apiKey (接口密钥)
可选参数: name (姓名), source (来源), remark (备注), tags (标签)
请求方式: POSTGET
请求参数
phone (string, 必填): 客户电话
timestamp (int, 必填): 当前时间戳,精确到秒(如 1700000000,建议用 Math.floor(Date.now() / 1000) 获取)
apiKey (string, 必填): 接口密钥
name (string, 可选): 客户姓名
source (string, 可选): 来源
remark (string, 可选): 备注
tags (string, 可选): 标签
响应示例
{`{
  "code": 200,
  "msg": "导入成功",
  "data": {
    "customerId": "123456"
  }
}`}
            
代码示例 以下是不同编程语言的接口调用示例 cURL Python Node.js PHP Java
{`curl -X POST 'http://yishi.com/v1/plan/api/scenariosz' \
  -d "phone=18888888888" \
  -d "timestamp=1700000000" \
  -d "name=张三" \
  -d "apiKey=naxf1-82h2f-vdwcm-rrhpm-q9hd1" \
  -d "sign=请用签名算法生成"`}
{`import hashlib
import time
import requests

def gen_sign(params, api_key):
    data = {k: v for k, v in params.items() if k not in ('sign', 'apiKey')}
    s = ''.join([str(data[k]) for k in sorted(data)])
    first = hashlib.md5(s.encode('utf-8')).hexdigest()
    return hashlib.md5((first + api_key).encode('utf-8')).hexdigest()

api_key = 'naxf1-82h2f-vdwcm-rrhpm-q9hd1'
params = {
    'phone': '18888888888',
    'timestamp': int(time.time()),
    'name': '张三',
}
params['apiKey'] = api_key
params['sign'] = gen_sign(params, api_key)
resp = requests.post('http://yishi.com/v1/plan/api/scenariosz', data=params)
print(resp.json())`}
{`const axios = require('axios');
const crypto = require('crypto');

function genSign(params, apiKey) {
  const data = {...params};
  delete data.sign;
  delete data.apiKey;
  const keys = Object.keys(data).sort();
  let str = '';
  keys.forEach(k => { str += data[k]; });
  const first = crypto.createHash('md5').update(str).digest('hex');
  return crypto.createHash('md5').update(first + apiKey).digest('hex');
}

const apiKey = 'naxf1-82h2f-vdwcm-rrhpm-q9hd1';
const params = {
  phone: '18888888888',
  timestamp: Math.floor(Date.now() / 1000),
  name: '张三',
};
params.apiKey = apiKey;
params.sign = genSign(params, apiKey);

axios.post('http://yishi.com/v1/plan/api/scenariosz', params)
  .then(res => console.log(res.data));`}
{` '18888888888',
    'timestamp' => time(),
    'name' => '张三',
    // 'source' => '', 'remark' => '', 'tags' => ''
];
$apiKey = 'naxf1-82h2f-vdwcm-rrhpm-q9hd1';
$params['apiKey'] = $apiKey;
$params['sign'] = md5_sign($params, $apiKey);

$url = '${API_BASE_URL}/v1/api/scenarios';

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>`}
{`import java.security.MessageDigest;
import java.util.*;
import java.net.*;
import java.io.*;

public class ApiSignDemo {
    public static String md5(String s) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(s.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte b : array) sb.append(String.format("%02x", b));
        return sb.toString();
    }
    public static void main(String[] args) throws Exception {
        Map params = new HashMap<>();
        params.put("phone", "18888888888");
        params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        params.put("name", "张三");
        // params.put("source", ""); params.put("remark", ""); params.put("tags", "");
        String apiKey = "naxf1-82h2f-vdwcm-rrhpm-q9hd1";
        // 排序并拼接
        List keys = new ArrayList<>(params.keySet());
        keys.remove("sign");
        keys.remove("apiKey");
        Collections.sort(keys);
        StringBuilder sb = new StringBuilder();
        for (String k : keys) {
            sb.append(params.get(k));
        }
        String first = md5(sb.toString());
        String sign = md5(first + apiKey);
        params.put("apiKey", apiKey);
        params.put("sign", sign);
        // 发送POST请求
        StringBuilder postData = new StringBuilder();
        for (Map.Entry entry : params.entrySet()) {
            if (postData.length() > 0) postData.append("&");
            postData.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
            postData.append("=");
            postData.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
        }
        URL url = new URL("${API_BASE_URL}/v1/api/scenarios");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        OutputStream os = conn.getOutputStream();
        os.write(postData.toString().getBytes("UTF-8"));
        os.flush(); os.close();
        BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String line; StringBuilder resp = new StringBuilder();
        while ((line = in.readLine()) != null) resp.append(line);
        in.close();
        System.out.println(resp.toString());
    }
}`}

集成指南

集简云平台集成
  1. 登录集简云平台
  2. 导航至"应用集成" > "外部接口"
  3. 选择"添加新接口",输入存客宝接口信息
  4. 配置回调参数,将"X-API-KEY"设置为您的API密钥
  5. 设置接口URL为: {testUrl}
  6. 映射必要字段(name, phone等)
  7. 保存并启用集成
问题排查

接口认证失败

请确保X-API-KEY正确无误,此密钥区分大小写。如需重置密钥,请联系管理员。

数据格式错误

确保所有必填字段已提供,并且字段类型正确。特别是电话号码格式需符合标准。

请求频率限制

单个API密钥每分钟最多可发送30个请求,超过限制将被暂时限制。对于大批量数据,请使用批量接口。

) }