PHP版 百度智能小程序第三方平台(TP)接入授权流程
百度官方授权接入文档地址:授权流程
下图是授权流程:
前期准备:在百度第三方平台申请资质,填写相关信息,包括安全域名、IP白名单,相关授权及事件接收URL。
第一步:接收ticket
并保存,第三方平台(TP)申请通过后,百度小程序官方会每十分钟向指定URL推送加密ticket
,我们需要做好接收、解密、保存工作
解密库参见:第三方平台消息加解密。
define('LOGPATH', dirname(__FILE__));
require './aes.class.php'; // 加密解密库
require './config.class.php'; // 配置读取库
require './functions.php'; // 公用方法
// 接收推送的加密数据流
$encryptMsg = file_get_contents('php://input');
// 立即返回字符串success
echo 'success';
$data = json_decode($encryptMsg, true); // 返回数组数据
// 解密获取ticket
$tp_config = Config::get('tp_config', true);
$dataCoder = new AesEncryptUtil($tp_config['client_id'], $tp_config['aes_key']);
$decrypt_data = $dataCoder->decrypt($data['Encrypt']);
$decrypt_data = json_decode($decrypt_data, true);
$ticket = $decrypt_data['Ticket'];
logResult('/msgmsg.log', 'Ticket: ' . $ticket);
// 保存ticket到config文件
if (array_key_exists('Ticket', $decrypt_data)) :
$file_handle = './config.php';
if ($fp = file_get_contents($file_handle)) {
$fp = preg_replace("/'ticket' =>.*,/", "'ticket' => '$ticket',", $fp);
if (!file_put_contents($file_handle, $fp)) {
logResult('/error.log', '保存ticket到config.php文件失败');
}
} else {
logResult('/error.log', 'config.php文件打开失败');
}
endif;
第二步:通过ticket获取第三方平台自己的access_token
,由于存在有效期(避免请求次数过多,但目前暂无次数限制),所以同样需要保存,便于后期重复使用。
if (!defined(LOGPATH)) define('LOGPATH', dirname(__FILE__));
// 由于tp的access_token会多次用到,我把获取过程写入函数(在functions.php文件中)
function get_tp_access_token() {
// 读取(或重新请求)第三方平台 access_token
$cur_time = time();
$tp_token = Config::get("tp_token", true);
if ((int)$tp_token['created_time'] + (int)$tp_token['expires_in'] > $cur_time) {
// 在有效期
$tp_access_token = $tp_token['access_token'];
} else {
// 已过期,重新获取
$ticket = Config::get('ticket');
$tp_config = Config::get('tp_config', true);
$get_token_url = "https://openapi.baidu.com/public/2.0/smartapp/auth/tp/token?client_id={$tp_config['client_key']}&ticket={$ticket}";
$tptoken_result = sendRequest($get_token_url);
$tptoken_result = json_decode($tptoken_result, true);
if ($tptoken_result['errno'] == 0) {
$tp_access_token = $tptoken_result['data']['access_token'];
// 保存tp access_token到config文件
$file_handle = './config.php';
if ($fp = file_get_contents($file_handle)) {
$fp = preg_replace(
array("/'access_token' =>.*,/", "/'expires_in' =>.*,/", "/'created_time' =>.*'/"),
array("'access_token' => '{$tp_access_token}',", "'expires_in' => '{$tptoken_result['data']['expires_in']}',", "'created_time' => '{$cur_time}'"),
$fp);
if (!file_put_contents($file_handle, $fp)) {
logResult('/error.log', '保存tp access_token到config.php文件失败');
}
} else {
logResult('/error.log', 'config.php文件打开失败');
}
} else {
logResult('/error.log', $tptoken_result['msg']);
}
}
return $tp_access_token;
}
// 该文件中另外两个函数是sendRequest和logResult,如下
// 日志保存
function logResult($path, $data){
file_put_contents(LOGPATH . $path, '[' . date('Y-m-d H:i:s',time()) . '] ' . $data . "\n", FILE_APPEND);
}
// 发送http(s)请求
function sendRequest($url, $data = null){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
if (!empty($data)) {
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($curl);
curl_close($curl);
return $output;
}
第三步和第四步:获取预授权码pre_auth_code
,并引导小程序管理员对第三方平台进行授权
// 实际使用中,直接访问该URL,获取到授权链接,小程序管理员点击后即可看到授权二维码,用手百APP扫码后完成授权过程,并返回回调页面
define('LOGPATH', dirname(__FILE__));
require './config.class.php';
require './functions.php';
// 读取(或重新请求)第三方平台 access_token
$tp_config = Config::get('tp_config', true);
$tp_access_token = get_tp_access_token();
// 获取预授权码,并生成授权链接
$get_pre_auth_code_url = "https://openapi.baidu.com/rest/2.0/smartapp/tp/createpreauthcode?access_token={$tp_access_token}";
$res_pre_auth_code = sendRequest($get_pre_auth_code_url);
$res_pre_auth_code = json_decode($res_pre_auth_code, true);
if ($res_pre_auth_code['errno'] == 0) {
$pre_auth_code = $res_pre_auth_code['data']['pre_auth_code'];
} else {
logResult('/error.log', $res_pre_auth_code['msg']);
}
echo '<a href="https://smartprogram.baidu.com/developer/tpservice.html?client_id=' . $tp_config['client_key'] . '&redirect_uri=https://miniapi.eiewz.cn/smartapp/granted.php&pre_auth_code=' . $pre_auth_code . '">点击进行授权</a>';
授权界面:
第五步和第六步,通过回调页面获取的authorization_code
和第三方平台access_token
获取小程序的接口调用凭据和授权信息
此处我通过首次获取的小程序access_token
,请求到小程序相关信息并保存到数据库表smartapp中
// 由于小程序的access_token在授权之后经常会用到,所以我选择保存到数据库(SQLite),字段包括:id, app_id, app_name, expires_in, create_time, refresh_token,其中,refresh_token可用来重新获取小程序access_token
define('LOGPATH', dirname(__FILE__));
require './functions.php';
require './config.class.php';
require './tp.config.php';
// 数据库信息
include_once "./ezSQL/shared/ez_sql_core.php";
include_once "./ezSQL/sqlite/ez_sql_sqlite3.php";
$db = new ezSQL_sqlite3('./','smartapp.db');
$authorization_code = $_GET['authorization_code'];
$expires_in = $_GET['expires_in'];
$tp_access_token = get_tp_access_token();
$get_access_token_url = "https://openapi.baidu.com/rest/2.0/oauth/token?access_token=$tp_access_token&code=$authorization_code&grant_type=app_to_tp_authorization_code";
$app_token_res = sendRequest($get_access_token_url);
$app_token_res = json_decode($app_token_res, true);
$cur_time = time();
$app_access_token = $app_token_res['access_token'];
$app_info_url = "https://openapi.baidu.com/rest/2.0/smartapp/app/info?access_token=$app_access_token";
$app_info = sendRequest($app_info_url);
$app_info = json_decode($app_info, true);
if ($app_info['errno'] == 0) {
$sql = "insert into smartapp (app_id,app_name,access_token,expires_in,create_time,refresh_token) values ({$app_info['data']['app_id']},'{$app_info['data']['app_name']}','{$app_access_token}','{$app_token_res['expires_in']}','{$cur_time}','{$app_token_res['refresh_token']}')";
//echo $sql;
$db->query($sql);
echo '授权成功';
} else {
echo '授权失败:';
logResult('/error.log', $app_info['msg']);
}
至此,整个授权过程完成。官方流程图第七步即通过refresh_token
重新获取小程序access_token
,第八步通过小程序access_token
获取小程序相关信息。
ps:保存配置的文件config.php
return array(
// ticket
'ticket' => 'xxxxxxxxxxxxxxxxxxx',
// tp令牌
'tp_token' => array(
'access_token' => 'xxxxxxxxxxx',
'expires_in' => '2592000',
'created_time' => '1551428362'
),
// tp配置
'tp_config' => array(
'aes_key' => 'xxxxxxxxxxxxxxxxxxxxxxx',
'token' => 'xxxxxxxxxx',
'client_id' => 'xxxxxxx', // 第三方平台ID
'client_key' => 'xxxxxxxxxxxxxxxxxxx', // 第三方平台key
'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxx' // 第三方平台密钥
)
);
配置读取库config.class.php
:
// 读取配置
class Config
{
// 存储配置信息
protected static $configs;
// 直接获取配置参数
public static function get($item = null, $array = false)
{
// 自动载入配置文件
if (! isset(self::$configs)) {
self::$configs = self::loadConfig();
}
// 返回全部配置
if ($item === null) {
return self::$configs;
}
$items = explode('.', $item);
if (isset(self::$configs[$items[0]])) {
$value = self::$configs[$items[0]];
} else {
return null;
}
$items_len = count($items);
for ($i = 1; $i < $items_len; $i ++) {
if (isset($value[$items[$i]])) {
$value = $value[$items[$i]];
} else {
return null;
}
}
// 强制返回数据为数组形式
if ($array && ! is_array($value)) {
if ($value) {
$value = explode(',', $value);
$value = array_map('trim', $value); // 去空格
} else {
$value = array();
}
}
return $value;
}
// 载入配置文件
private static function loadConfig()
{
// 载入用户主配置文件
if (file_exists('config.php')) {
$config = require 'config.php';
$configs = self::mult_array_merge($configs, $config);
}
// 清理缓冲区,避免配置文件出现Bom时影响显示
@ob_clean();
return $configs;
}
// 多维数组合并
private static function mult_array_merge($array1, $array2)
{
if (is_array($array2)) {
foreach ($array2 as $key => $value) {
if (is_array($value)) {
if (array_key_exists($key, $array1)) {
$array1[$key] = self::mult_array_merge($array1[$key], $value);
} else {
$array1[$key] = $value;
}
} else {
$array1[$key] = $value;
}
}
}
return $array1;
}
}