在扣子官网文档里没有提供php版sdk,以下代码实现获取Oauth Access Token。

<?php

class CozeLogic
{
    public function get_access_token()
    {
        $apiUrl = "https://api.coze.cn/api/permission/oauth2/token";
        $postData = [
            'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', // 固定值
            'duration_seconds' => 86399, // 申请的 AccessToken 有效期,单位为秒,默认 900 秒,即 15 分钟。最大可设置为 86399 秒,即 24 小时。
        ];
        $headers = [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $this->get_jwt()
        ];
        $response = $this->httpRequest($apiUrl, 'POST', json_encode($postData, JSON_UNESCAPED_UNICODE), $headers, 300);
        $result = json_decode($response, true);
        if (!empty($result['access_token'])) {
            return [
                'code' => 1, 
                'access_token' => $result['access_token'],
            ];
        }

        return [
            'code' => 0, 
            'msg' => empty($result['error_message']) ? "获取访问令牌失败" : "获取访问令牌失败,错误码: {$result['error_code']},错误信息: {$result['error_message']}",
        ];
    }

    public function get_jwt()
    {
        // OAuth 应用的公钥指纹
        $public_key_id = 'OAuth 应用的公钥指纹';
        // OAuth 应用的 ID,必须是字符串类型
        $app_id = '应用的ID';
        // Header 参数
        $header = [
            'alg' => 'RS256', // 固定为RS256
            'typ' => 'JWT', // 固定为 JWT
            'kid' => strval($public_key_id) // OAuth 应用的公钥指纹
        ];
        /**
         * 访问令牌的会话标识。目前仅限在会话隔离场景下使用,即将 session_name 指定为用户在业务侧的 UID,以此区分不同业务侧用户的对话历史。
         * 若未指定 session_name,不同用户的对话历史可能会掺杂在一起。
         */
        $session_name = empty($_COOKIE['coze_session_name']) ? '' : $_COOKIE['coze_session_name'];
        if (empty($session_name)) {
            $session_name = 'coze_' . strval(session('users_id')); // 根据业务改成等于用户的uid
        }
        if (empty($session_name)) {
            $session_name = session_id();
        }
        setcookie('coze_session_name', $session_name);
        // Payload 参数
        $payload = [
            'iss' => strval($app_id), // OAuth 应用的 ID
            'aud' => 'api.coze.cn', //扣子 API 的Endpoint
            'iat' => time(), // JWT开始生效的时间,秒级时间戳
            'exp' => time() + 86400, // JWT过期时间,秒级时间戳
            'jti' => (string) $this->get_rand_str(48, 0, 0), // 随机字符串,防止重放攻击
            'session_name' => strval($session_name),  // 必须是字符串类型
        ];
        $encodedHeader = $this->base64url_encode(json_encode($header));
        $encodedPayload = $this->base64url_encode(json_encode($payload));
        $signature = '';
        $data = $encodedHeader . '.' . $encodedPayload;

        // 私钥,改成自己的私钥
        $private_key = "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCY7GRpdboUXUV3\ntmuGjvx0P8GeoMaU1ybW/ipHKA1QMmkIXOWjS+c4p6e/VCRQXCETsLgzzvkGPtMd\nfeJkFlegXf/OcgGiqZZwZENc2pgFhqAyJtuP6AVgEEwMzrStZy/L8/HiBN+qQSL/\ntd+wuPIk5kqCHTkpJB2Of4awEe7yZHLqSAiHVjSWH3RVos6ftq140XFDlSF32B0C\noSQzrizvxA2XTRgvoZJoofX29eCXtKAP/ERdsUfjpR2synEWpBu0Hq/XZ/WSBz6O\nBPXRxBuTFDdeNFYfmNIjWDCFj/V5IwvsGNGm9uIPqYlRWo14CO3jrI07EzkEJ2L4\nYJ35GlAJAgMBAAECggEAAXbZy/xp61emMNwU+dejZ76nCglTawz62Boceaxl+tYH\niHvkO1ATuK8Hrz0wSlOX6C/iYAufE1rCMhd4tbLhkXcCjXcs8eIcEmHHyj5lFrN0\n7ZObhiYBEpJuLTBRZx70cUhgRFGxE98jC8eehFTAnwIUZbXMLIsy6PPQ/51YvrOP\nLDoo/cr+PFhDDh+shpoSIxHaKvBlGsgbcvOkCmZukMRamD9gE/gtiBHrIrNzVbam\nF9DopX8BJdTMJt13R952LT9+bdsh57F1ufW3JCp0m0x9gL0NisPARqQr4MuYByWX\nBDWpCQuk4SjQIug3YE5kdqLMBZRsv5Zb+gIchX7lUQKBgQDXn7h1aMkVVJ7EfmBV\nkLpNMWrXV3Qd/1Afsa5xUVUcgdmGN3wokGZFxLXSysXpqKW5kFygsI5EUttUWHWs\nMqmyvpHYSzUcmM84PubLqZM3wNGUSs7mds8otFJ+/ZIy/e7o8DQbb6T/hb1hvlhK\nB76IQF08H/d+t/FQXSVO5UaT8QKBgQC1jwWrT0RcxQ7T2TS5oHZ9p8DTLpbfmdsl\n7zJc3wjId5OhWZtsNm/FlBqkQtL86jCa0UEHvBZ7at78iE6hkWwJWgprPIqStrdz\nW1RCNqJAJjfTWvgSfHTVBCkLd7R7aN6AjERRPuK1NCS4BZS+0II3nADVfE3iVU4v\n6Rq4yPs1mQKBgHYXhtnewsqI5s9s+2vBcRdX6lD4F6NaMlK5aHWterS7NRw6BSje\nNPUfEAnbfltDP0iLFP/40mH/Exp1vDTMJn67unrAbd00n8EJSjPesr9FPkM+WWSF\nS13arJfaXpjI1HEFJTSlh7NeVuNScV5SMXrD7YxBYbYoHeTKruUv0uCxAoGAGON6\nS3maTX1WAwSLjcbJd4A/lrOUSyK0QAcCSGjqs1BxFHVMMRFlURh0boAanEEeWJrG\n52CJVHHaD5Q/8zYUMVP+iaTK+kHB9DZY3prOsyyKCpc12pLlES540fGe9Cv0FHsm\nAl00htKXFgLt2RqMP0NLefiIVLaM+VRjLWGfzkECgYBhUsvJjmQFDN/7I9o162EF\ny2sXOpMiwiMJbqs564nbYPlY0v/T2dXaGJiLp42Dm07RiY13uy1tfLKWhd5d/7Sl\nZDtSqT6i5EsEkobfyDL8TWilo3SFkEMev2OemkgP10wq+qOA1W81AiuFZb8bNVyp\nyDLlkHBdofoPtXWzKtBZGQ==\n-----END PRIVATE KEY-----";

        openssl_sign($data, $signature, $private_key, 'sha256WithRSAEncryption');
        $encodeSignature = $this->base64url_encode($signature);

        return $encodedHeader . '.' . $encodedPayload . '.' . $encodeSignature;
    }

    private function base64url_encode($data)
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }

    /**
     * 获取随机字符串
     * @param int $randLength 长度
     * @param int $addtime 是否加入当前时间戳
     * @param int $includenumber 是否包含数字
     * @return string
     */
    private function get_rand_str($randLength = 6, $addtime = 1, $includenumber = 0)
    {
        if (1 == $includenumber) {
            $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQEST123456789';
        } else if (2 == $includenumber) {
            $chars = '123456789';
        } else if (3 == $includenumber) {
            $chars = 'ABCDEFGHJKLMNPQEST123456789';
        } else {
            $chars = 'abcdefghijklmnopqrstuvwxyz';
        }
        $len     = strlen($chars);
        $randStr = '';
        for ($i = 0; $i < $randLength; $i++) {
            $randStr .= $chars[rand(0, $len - 1)];
        }
        $tokenvalue = $randStr;
        if ($addtime) {
            $tokenvalue = $randStr . time();
        }
        return $tokenvalue;
    }

    /**
     * CURL请求
     *
     * @param $url 请求url地址
     * @param $method 请求方法 get post
     * @param null $postfields post数据数组
     * @param array $headers 请求header信息
     * @param bool|false $debug 调试开启 默认false
     * @return mixed
     */
    private function httpRequest($url, $method = "GET", $postfields = null, $headers = array(), $timeout = 30, $debug = false)
    {
        $method = strtoupper($method);
        $ci     = curl_init();
        /* Curl settings */
        // curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); // 使用哪个版本
        curl_setopt($ci, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0");
        curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 60); /* 在发起连接前等待的时间,如果设置为0,则无限等待 */
        // curl_setopt($ci, CURLOPT_TIMEOUT, 7); /* 设置cURL允许执行的最长秒数 */
        curl_setopt($ci, CURLOPT_TIMEOUT, $timeout); /* 设置cURL允许执行的最长秒数 */
        curl_setopt($ci, CURLOPT_RETURNTRANSFER, true);
        switch ($method) {
            case "POST":
                curl_setopt($ci, CURLOPT_POST, true);
                if (!empty($postfields)) {
                    if (is_string($postfields) && preg_match('/^([\w\-]+=[\w\-]+(&[\w\-]+=[\w\-]+)*)$/', $postfields)) {
                        parse_str($postfields, $output);
                        $postfields = $output;
                    }
                    if (is_array($postfields)) {
                        $tmpdatastr = http_build_query($postfields);
                    } else {
                        $tmpdatastr = $postfields;
                    }
                    curl_setopt($ci, CURLOPT_POSTFIELDS, $tmpdatastr);
                }
                break;
            default:
                curl_setopt($ci, CURLOPT_CUSTOMREQUEST, $method); /* //设置请求方式 */
                break;
        }

        $ssl = preg_match('/^https:\/\//i', $url) ? TRUE : FALSE;
        curl_setopt($ci, CURLOPT_URL, $url);
        if ($ssl) {
            curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts
            curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, FALSE); // 不从证书中检查SSL加密算法是否存在
            // curl_setopt($ci, CURLOPT_SSLVERSION, 4);   //因为之前的POODLE 病毒爆发,许多网站禁用了sslv3(nginx默认是禁用的,ssl_protocols 默认值为TLSv1 TLSv1.1 TLSv1.2;),最新使用sslv4
        }
        //curl_setopt($ci, CURLOPT_HEADER, true); /*启用时会将头文件的信息作为数据流输出*/
        if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) {
            curl_setopt($ci, CURLOPT_FOLLOWLOCATION, 1);
        }
        curl_setopt($ci, CURLOPT_MAXREDIRS, 2);/*指定最多的HTTP重定向的数量,这个选项是和CURLOPT_FOLLOWLOCATION一起使用的*/
        curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ci, CURLINFO_HEADER_OUT, true);
        /*curl_setopt($ci, CURLOPT_COOKIE, $Cookiestr); * *COOKIE带过去** */
        $response    = curl_exec($ci);
        $requestinfo = curl_getinfo($ci);
        $http_code   = curl_getinfo($ci, CURLINFO_HTTP_CODE);
        if ($debug) {
            echo "=====post data======\r\n";
            var_dump($postfields);
            echo "=====info===== \r\n";
            print_r($requestinfo);
            echo "=====response=====\r\n";
            print_r($response);
        }
        curl_close($ci);
        return $response;
        //return array($http_code, $response,$requestinfo);
    }
}

扣子chat sdk 在网页前端的代码实现:

<!DOCTYPE html>
<html>
<head>
    <title>扣子智能客服</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
    <script src="https://lf-cdn.coze.cn/obj/unpkg/flow-platform/chat-app-sdk/1.1.0-beta.3/libs/cn/index.js"></script>
    <script>
        var botId = "Agent ID";
        var access_token = "access_token值";
        const cozeWebSDK = new CozeWebSDK.WebChatClient({
            config: {
                // Agent ID
                botId: botId,
            },
            // componentProps: {
            //     title: '智能客服',
            // },
            auth:  {
                // Authentication methods, the default type is 'unauth', which means no authentication is required; it is recommended to set it to 'token', indicating authentication through PAT (Personal Access Token) or OAuth
                type: 'token',    
                // When the type is set to 'token', it is necessary to configure a PAT (Personal Access Token) or OAuth access token for authentication.
                token: access_token,
                // When the access token expires, use a new token and set it as needed.
                onRefreshToken: async () => {
                    $.ajax({
                        type : 'get',
                        url : "这里填写获取access token的url",
                        data : {_ajax:1},
                        dataType : 'json',
                        success : function(res){
                            if (res.code == 1) {
                                return res.data.access_token;
                            }else{
                                console.log(res.msg);
                            }
                        },
                        error: function(e){
                            console.log(e.responseText);
                        }
                    });
                },
            },
            // userInfo: {
            //     url: '用户头像',
            //     nickname: "用户名或昵称",
            //     id: "用户id",
            // },
            ui: {
                base: {
                    // icon: '自定义图标',
                    // layout: 'pc',
                    // lang: 'zh-CN',
                    zIndex: 1000,
                },
                header: {
                    isShow: true,
                    isNeedClose: true,
                },
                asstBtn: {
                    isNeed: true, // 悬浮球的显示与隐藏
                },
                footer: {
                    isShow: true,
                    expressionText: 'Powered by © {{name}} {{name1}} 版权所有',
                    linkvars: {
                        name: {
                            text: '2016-2024',
                        },
                        name1: {
                            text: 'coze',
                            link: 'https://www.coze.cn'
                        }
                    }
                },
                chatBot: {
                    title: "智能客服",
                    uploadable: true,
                    width: 460, // PC 端窗口的宽度,单位为 px,默认为 460。
                    el: undefined,
                    // el: document.getElementById('position_demo'),
                    onHide: () => { // 当聊天框隐藏的时候,会回调该方法。
                        console.log('onHide')
                        // todo...
                    },
                    onShow: () => { // 当聊天框显示的时候,会回调该方法。
                        console.log('onShow')
                        // todo...
                    },
                    onBeforeShow: () => { // 聊天框显示前调用,如果返回了 false,则不会显示。支持异步函数。
                        // return false;
                        console.log('onBeforeShow')
                        // todo...
                    },
                    onBeforeHide: () => { // 聊天框隐藏前调用,如果返回了 false,则不会隐藏。支持异步函数。
                        // return false;
                        console.log('onBeforeHide')
                        // todo...
                    },
                },
            },
        });
    </script>
</body>
</html>

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐