1.概述
PartnerShare 通过提供标准化的 中继授权登录接口,使接入 PartnerShare 的应用能够在用户增长和合作推广过程中,实现跨应用的无缝授权访问,避免用户重复登录,从而优化用户体验并降低身份验证的复杂度。
接入 PartnerShare 中继授权登录接口,SaaS 应用可实现高效的用户引流。该接口采用 灵活的 code 授权机制 和 严格的签名验证,确保授权信息在 SaaS 应用之间安全传输。此外,PartnerShare 还提供全面的数据统计与分析功能,包括 授权次数、访问频次、用户画像 等,帮助应用精准洞察用户行为。同时,结合 奖励分佣体系,促进合作伙伴互利共赢,进一步增强生态价值。
对于 授权方应用,PartnerShare 可帮助其快速变现端内流量并获得佣金收益;对于 被授权应用,PartnerShare 作为流量枢纽,能够精准匹配高价值用户,助力高效获客。PartnerShare 致力于成为 SaaS 应用间用户授权的桥梁,推动合作伙伴实现业务增长与生态互联。
2.接入前准备
1.接入前确保您方产品已入驻PartnerShare成为品牌主
2.若您方产品作为授权方还需要申请一个独立的推广者账号,用于推广数据统计以推广佣金管理
3.若您方产品作为授权方您的推广者账号还需要申请成为被授权方的推广者
3.接入流程图
4.数据约定
-
请求数据约定
• HTTP Method: POST
• Content-Type: application/json
• 数据通过 Raw JSON 格式传输。
-
答复数据约定
所有响应的接口数据均为 Json 格式
字段名 |
类型 |
描述 |
code |
int |
响应状态码 0 为成功 非 0 则失败 |
message |
string |
响应描述信息 |
data |
object|array |
响应数据 |
5.签名机制
签名的生成过程如下:
1. 提取请求参数:
• 包含所有 GET 和 POST 参数、RAW JSON 参数。
• 仅使用参数的键名,不包括参数值。
2. 处理键名:
• 将所有参数的键名转换为小写。
• 对键名按照自然顺序(SORT_NATURAL)进行排序。
3. 拼接签名字符串:
• 将排序后的键名使用 & 连接,形成键名字符串。
• 在键名字符串后拼接请求的时间戳和密钥 secret_key
(secret_key 由 PartnerShare 发放和 product_key 一一对应)。
4. 生成签名:
• 使用 SHA256 算法对拼接后的字符串进行哈希处理,生成最终签名。
5. 签名方法参考:
PHP
/**
* 生成请求签名
* @param string $timestamp 请求时间戳
* @param string $secretKey 密钥
* @return string 返回生成的签名
*/
private function generateSign( string $timestamp, string $secretKey ): string
{
// 合并 GET 和 POST 参数
$params = $this->getAllParams();
// 提取所有键名并转换为小写
$lowercaseKeys = array_map( 'strtolower', array_keys( $params ) );
// 按自然顺序对键名排序
sort( $lowercaseKeys, SORT_NATURAL );
// 拼接排序后的键名
$keyString = implode( '&', $lowercaseKeys );
// 拼接时间戳和密钥
$signString = $keyString . $timestamp . $secretKey;
// 生成签名 (使用 SHA256 算法)
$sign = hash( 'sha256', $signString );
return $sign;
}
GO
package main
import (
"crypto/sha256"
"encoding/hex"
"sort"
"strings"
)
// GenerateSign 生成请求签名
func GenerateSign(params map[string]string, timestamp, secretKey string) string {
// 提取所有键名并转换为小写
var keys []string
for key := range params {
keys = append(keys, strings.ToLower(key))
}
// 按自然顺序对键名排序
sort.Strings(keys)
// 拼接排序后的键名
keyString := strings.Join(keys, "&")
// 拼接时间戳和密钥
signString := keyString + timestamp + secretKey
// 使用 SHA256 算法生成签名
hash := sha256.New()
hash.Write([]byte(signString))
sign := hex.EncodeToString(hash.Sum(nil))
return sign
}
在请求接口的时候,所有的签名校验的参数不直接携带到请求参数中,均通过 Header 参数传输,分别包括如下三个 Header 参数
参数名称 |
参数格式 |
描述 |
x-Product-Key |
string |
PartnerShare 平台发放唯一应用标识 |
x-Timestamp |
string |
请求秒级时间戳 |
x-Sign |
string |
签名 |
Content-Type |
string |
固定值:application/json |
备注:对接前请联系PartnerShare技术生成密钥
6.接口列表
测试环境域名:https://testing-api.partnershare.net
正式环境域名:https://api.partnershare.net
-
授权应用生成授权码并自动跳转到授权地址
请求地址:/open/web/oauth/authorizeAndRedirect
请求Query参数
字段名 |
是否必填 |
类型 |
描述 |
示例 |
product_key |
是 |
string |
PartnerShare 平台发放唯一应用标识 |
K20xon3htdg |
x-timestamp |
是 |
string |
秒级时间戳 |
1738725269 |
data |
是 |
string |
数据加密后的字符串 |
xxxxx |
sign |
是 |
string |
签名 |
23ee1028ca0e7f60f2002a11de3c3380f86e64f12b404c20ae000646acb956a9
|
请求参数完整示例
{{host}}/open/web/oauth/authorizeAndRedirect?product_key=K20xon3htdg&x-timestamp=1737538469&data=%20XE9Y0Ua2MM9GSu5wVVTXs_E5q9PegnwqMAqXSdtEezYB2ssVnEG70DEsr7ULXSzR6-PFUCo9gsKr9Acl9fbYoCkueZGcAjDUjoOdhz7VWfwGmbp9iO6Op2POWSsT-ctyNmYUL3PpN2HKbDekEP6i61y-jx9Z55WmzsngjOofknvTTLy5cm1A_bpLlywEfAnNReIsXjXUcLavpddIED2VmH-varlBwQV9f2qshFrd8T8SU3hN-qjQ7-bsoJMohZeVqlKHQQVD3LGTEWEiFTe7Op5DD29AmtxFZJLJKkEsIyCpXaFJWzAqLmR-HkgJM5-qoSD6fU2OUpbiqGaelMWumwslPcUc5iSHc_VdElD5wQTW5xdE9iUzhg9YlfnVOyUB5EPBMVEmN3-5FUHEvRmBiw&sign=23ee1028ca0e7f60f2002a11de3c3380f86e64f12b404c20ae000646acb956a9
参数data要求为转为json字符串再通过加密方法加密成加密后的字符串
参数加密类示例:
class Encryptor
{
private static $instance = null; // 静态实例
private $secretKey;
private $cipherMethod = 'AES-256-CBC'; // 加密算法
private function __construct()
{
}
/**
* 配置密钥
* @param string $secretKey
* @return Encryptor
*/
public static function secret( string $secretKey ): Encryptor
{
if ( self::$instance === null ) {
self::$instance = new self();
}
self::$instance->secretKey = hash( 'sha256', $secretKey, true ); // 生成32字节密钥
return self::$instance;
}
/**
* 加密参数
* @param array $params 明文参数
* @return string 加密后的字符串
* @throws \Exception
*/
public function encrypt( array $params ): string
{
$iv = random_bytes( openssl_cipher_iv_length( $this->cipherMethod ) ); // 生成随机IV
$plaintext = json_encode( $params ); // 转换为JSON字符串
$ciphertext = openssl_encrypt( $plaintext, $this->cipherMethod, $this->secretKey, OPENSSL_RAW_DATA, $iv );
if ( $ciphertext === false ) {
throw new BaseException( BaseException::PARAMS_ERROR,'Encryption failed.' );
}
return rtrim( strtr( base64_encode( $iv . $ciphertext ), '+/', '-_' ), '=' );
}
/**
* 解密参数
* @param string $encrypted 加密字符串
* @return array 解密后的参数
* @throws \Exception
*/
public function decrypt( string $encrypted ): array
{
$data = base64_decode( strtr( $encrypted, '-_', '+/' ) ); // 解码字符串
$ivLength = openssl_cipher_iv_length( $this->cipherMethod );
$iv = substr( $data, 0, $ivLength ); // 提取IV
$ciphertext = substr( $data, $ivLength ); // 提取密文
$plaintext = openssl_decrypt( $ciphertext, $this->cipherMethod, $this->secretKey, OPENSSL_RAW_DATA, $iv );
if ( $plaintext === false ) {
throw new BaseException( BaseException::PARAMS_ERROR,'Decryption failed.' );
}
return json_decode( $plaintext, true ); // 转换为数组
}
}
2.授权应用生成用户授权码 (当应用选择端内跳转授权地址可以调该接口获取授权地址再根据自身逻辑跳转到授权地址)
接口地址:/open/api/oauth/getAuthorizationCode
请求参数
字段名 |
是否必填 |
类型 |
描述 |
示例 |
product_key |
是 |
string |
PartnerShare 平台发放唯一应用标识 |
K20xon3htdg22 |
target_product_key |
是 |
string |
授权目标产品Key |
jx30zoh0ooa11 |
user_id |
是 |
string |
授权应用唯一用户标识 |
9927356xxx |
extra |
否 |
object |
授权用户额外用户信息,用于扩展用户信息,可根据实际对接需要,通过 PartnerShare 授权给被授权应用使用 |
{"email":"xxx@partnershare.net","phone":"1552107xxxx"}
|
请求参数完整示例
{
"product_key":"K20xon3htdg",
"target_product_key":"jx30zoh0ooa",
"user_id":"9927356",
"extra":{
"user_name":"平台优质用户",
"user_logo":"dwadwaddwa",
"email":"xxx@partnershare.net",
"phone":"1552107xxxx",
"locale":"zh"
}
}
data 响应参数
字段名 |
是否必填 |
类型 |
描述 |
示例 |
code |
是 |
string |
用户授权码(5 分钟有效期) |
2fwth3pfj8 |
redirect_url |
是 |
string |
跳转链接 |
https://xxx.saaslink.net/login/jx30zoh0ooa?x_auth_code=2fwth3pfj8 |
响应参数完整示例
{
"code": 0,
"message": "success",
"data": {
"code": "2fwth3pfj8",
"redirect_url":"https://xxx.saaslink.net/login/jx30zoh0ooa?x_auth_code=2fwth3pfj8"
}
}
3.被授权应用通过授权码读取授权信息
若您方产品是作为被授权方,则您需要在品牌主后台设置中设置好默认的推广地址,默认的推广地址将是授权方推广的默认地址,授权方授权用户后通过PartnerShare的推广路由将直接重定向到您的推广地址并带上授权码,授权码参数名为x_auth_code
示例:例如 https://www.baidu.com/login 是您方的推广目标地址,则跳转您方的目标推广地址的时候除了会带上ref邀请码等参数外l,会带上x_auth_code 授权码,该授权码5分钟有效,识别到请求中存在授权码后通过该接口可请求到授权用户信息,在您端内完成用户注册或登录,注册的用户需要关联上ref,以便做后续的数据回传。
eg: https://www.baidu.com/login?ref=XPRUPY&snid=1736254248204743302_c2WD8F2q&custom_id=&x_auth_code=8fhgnbk0r3
接口地址:/open/api/oauth/getUserByAuthorizationCode
请求参数
字段名 |
是否必填 |
类型 |
描述 |
示例 |
code |
是 |
string |
用户授权码 |
2fwth3pfj8 |
请求参数完整示例
{
"code":"3hjm8fck1s"
}
data 响应参数
字段名 |
类型 |
描述 |
示例 |
user_key |
string |
授权用户唯一标识(被授权方请保留在本地关联本地用户) |
Q7vKriNbn1rbx3flecZkYsihBq6g |
user_name |
string |
PartnerShare 自动生成的用户名 |
来自 AdsPower 的授权用户 Q7vKriNb |
ref |
string |
来自授权方的产品名称 |
AdsPower |
ref_product_key |
string |
授权方在 PartnerShare 的产品唯一标识 |
jx30zoh0ooax |
locale |
string |
语言 |
zh |
user_id |
string |
关联被授权方的用户唯一标识 |
2861912 被授权方可根据user_id快速登录 |
响应参数完整示例
{
"code": 0,
"message": "success",
"data":{
"user_key": "iwj76O11p2ou1dOPu6Rnihcj5iBd",
"user_name": "来自AdsPower的授权用户iwj76O11",
"ref": "AdsPower",
"ref_product_key": "K20xon3htdg",
"locale": "zh",
"user_id": "2861912" //关联被授权方的用户唯一标识
"email":"xxxx@partershare.net",
"phone":"12345678910",
"extra":{
"sex":"男",
"age":18
}
}
}
4.被授权应用成功登录后回调通知 PartnerShare 完成授权数据统计打点记录 (只要成功完成授权登录后就需要调一次)
接口地址:/open/api/tracer/relavanceOauthUser
请求参数
字段名 |
是否必填 |
类型 |
描述 |
示例 |
user_key |
是 |
string |
授权方用户唯一标识 |
Q7vKriNbn1rbx3flecZkYsihBq6g |
user_id |
是 |
string |
被授权方应用内用户唯一标识 |
7762831 |
请求参数完整示例
{
"user_key":"iwj76O11p2ou1dOPu6Rnihcj5iBd",
"user_id":"8927125"
}
data 响应参数
字段名 |
类型 |
描述 |
示例 |
user_key |
string |
授权用户唯一标识 |
Q7vKriNbn1rbx3flecZkYsihBq6g |
target_user_key |
string |
被授权方用户生成的唯一标识 |
lz3KA0y4usdsvkB3mlzhej81tmYl |
响应参数完整示例
{
"code": 0,
"message": "success",
"data": {
"user_key": "Q7vKriNbn1rbx3flecZkYsihBq6g",
"target_user_key": "lz3KA0y4usdsvkB3mlzhej81tmYl"
}
}
5.获取调试授权码
接口地址:/open/api/oauth/getDebugAuthCode
请求参数
字段名 | 是否必填 | 类型 | 描述 | 示例 |
---|---|---|---|---|
source_product_key | 是 | string | 授权产品Key | vKriNbn1rb |
product_key | 是 | string | 被授权登录产品Key | iriNbn1r2 |
请求参数完整示例
{
"source_product_key": "vKriNbn1rb",
"product_key": "iriNbn1r2"
}
data 响应参数
字段名 | 是否必填 | 类型 | 描述 | 示例 |
---|---|---|---|---|
code | 是 | string | 用户授权码(5 分钟有效期) | 2fwth3pfj8 |
redirect_url | 是 | string | 跳转链接 | https://demo.saaslink.net/login/jx30zoh0ooa?x_auth_code=2fwth3pfj8 |
is_authorized | 是 | int | 该用户是否已经授权过给目标应用 1是 0否 | 1 |
响应参数完整示例
{
"code": 0,
"message": "success",
"data": {
"is_authorized": 0,
"code": "zv1oqz75ul",
"redirect_url": "https://demo.saaslink.net/login/jx30zoh0ooa?x_auth_code=2fwth3pfj8"
}
}