文档中心 云市场 商品接入 自动交付接入方案

自动交付接入方案

最近更新时间:2019-10-17 19:10:59

简介

本文档描述了 SaaS 应用接入到云市场所须实现的接口定义,服务商通过提供以下接口,即可获得商品购买成功的信息,并将成功信息返回给云市场展示给用户。

术语信息

文中涉及相关术语解释如下:

  • 发货 URL: 由服务商开发,用于接受云市场实例相关消息的地址。
  • Token:由服务商提供,用于校验发货 URL 有效性。
  • openId:当服务商的应用接入腾讯云开放平台后,可获得的腾讯云用户的唯一标识。

准备工作

在一个 SaaS 商品正式通过审核上架前,服务商需要进行以下准备工作:

  1. 云市场发货接口开发(必选)。
  2. 控制台的参数配置(必选)。
  3. 接入腾讯云 OAuth(可选)。

接口开发

接口要求

在进行接口开发前,请了解以下相关要求:

项目 说明
传输协议 采用 HTTP 传输协议(支持 HTTPS)
提交方式 均采用 POST 方法提交
数据格式 提交和响应均为 JSON 格式
字符编码 统一使用 UTF-8 字符编码
签名算法 SHA256
签名要求 云市场的通知会使用签名
请求超时时间 10s(为保证网络畅通,推荐接口层服务器使用腾讯云广州区域)

服务商在控制台配置发货接口 URL 和 Token 后,云市场会以 URL PARAMS(GET 参数)的方式添加到接口 URL 上,携带的参数如下:

参数 类型 说明
signature String 加密签名, signature 结合了服务商填写的 Token 参数和请求中的 timestamp 参数、eventId 等参数。
timestamp Integer UNIX 时间戳
eventId Integer 随机数

签名规则

服务商通过对 signature 进行校验(校验方式如下)。加密/校验流程如下:

  1. 判断 timestamp 是否已经超时(签名推荐超时为30s,免登校验推荐为120s)。
  2. 将 Token、timestamp、eventId 三个参数进行字典序排序。
  3. 将三个参数字符串,拼接成一个字符串进行 SHA256 加密。
  4. 服务商将加密后的字符串与 signature 对比即可。

检验 signature 的 PHP 示例代码:

function checkSignature($signature, $token, $timestamp, $eventId)
{
    $currentTimestamp = time();
    if ($currentTimestamp - $timestamp > 30) {
      return false;
    }
    $timestamp = (string)$timestamp;
    $eventId = (string)$eventId;
    $params = array($token, $timestamp, $eventId);
    sort($params, SORT_STRING);
    $str = implode('', $params);
    $requestSignature = hash('sha256', $str);
    return $signature === $requestSignature;
}

身份校验接口

  • 接口名:verifyInterface。
  • 接口说明:用户在 云市场服务商管理控制台 更改发货 URL 和 Token 时,后台会调用接口 URL 对 Token 进行实时校验,校验通过才可以设置成功。

请求参数说明

参数名 类型 是否必须 描述
action String verifyInterface
requestId String 接口请求的 ID
echoback String 随机字符串

响应参数说明

参数名 类型 是否必须 描述
echoback String 请求中的 echoback 参数的值

请求示例

curl -X POST -H 'Content-Type: application/json' 'http://isv/interface?signature=e3k9ierw&timestamp=1483944926&eventId=1780012140' --data '{"action":"verifyInterface","echoback":"Albert Einstein"}'

响应示例

{"echoback": "Albert Einstein"}

实例创建通知接口

  • 接口名:createInstance。
  • 接口说明:用户购买商品并支付后,云市场将通过实例创建通知接口发送信息至发货 URL。

请求参数说明

参数名 类型 是否必须 描述
action String createInstance
orderId String 订单 ID
accountId String 购买者账号 ID
openId String 用户在腾讯云开放平台的标识,此标识对于同一服务商是相同的,对于不同服务商是不同的,长度为32位。如果没有接入开放平台,此字段为空
productId Integer 云市场产品 ID
requestId String 接口请求的 ID
productInfo JSON 产品信息
productInfo.productName String 购买的产品名称
productInfo.isTrial Bool 是否为试用,true:是,false:否
productInfo.spec String 产品规格,是试用时为空
productInfo.timeSpan Integer 购买时长,是试用时为空
productInfo.timeUnit String 购买时长单位(y、m、d、h、t 分别代表年、月、日、天、时、次),是试用时为空
extendInfo JSON 订单扩展信息,通常情况下为空

响应参数说明

参数名 类型 是否必须 描述
signId String 实例标识 ID,服务商提供的唯一标识。不可为空,长度最长为11位。当为"0"时,系统会认为是异步发货。
appInfo JSON 应用信息
appInfo.website String 可以为服务商的网站
appInfo.authUrl String 服务商提供给客户的免登地址
additionalInfo JSON 自定义数据,会显示在实例详情中。格式为[{"name":"","value":""}]

请求示例

curl -X POST -H 'Content-Type: application/json' 'http://isv/interface?signature=e3k9ierw&timestamp=1483944926&eventId=1780012140' --data '{"action":"createInstance","orderId":"20170109199524","accountId":"123545678"," openId ":"xz_D4XL_u7hKY5zt","productId":1024,"requestId":"fab8a029-22fa-41b1-ac08-5cdde878ed04","productInfo":{"productName":"云服务市场测试商品","isTrail":"false","spec":"普通版","timeSpan":2,"timeUnit":"m"}}'

响应示例

{"signId": "36441d902ba", "appInfo": {"website":"http://www.example.com", "authUrl": "http://www.example.com/oauth/login"},"additionalInfo":[{"name":"注意","value":"这是一条注意"},{"name":"说明","value":"这是说明"}]}

实例续费通知接口

接口名:renewInstance。
接口说明:用户续费商品后,云市场将通过实例续费通知接口发送消息至发货 URL。该接口需要服务商立即返回响应。

请求参数说明

参数名 类型 是否必须 描述
action String renewInstance
orderId String 订单 ID
accountId String 购买者账号 ID
openId String 用户在腾讯云开放平台的标识,此标识对于同一服务商是相同的,对于不同服务商是不同的,长度为32位;如果没有接入开放平台,此字段为空
productId Integer 云市场产品 ID
requestId String 接口请求的 ID
signId String 实例标识 ID
instanceExpireTime DateTime 新的实例到期时间(yyyy-MM-dd HH:mm:ss)

响应参数说明

参数名 类型 是否必须 描述
success String true/false

请求示例

curl -X POST -H 'Content-Type: application/json' 'http://isv/interface?signature=e3k9ierw&timestamp=1483944926&eventId=1780012140' --data '{"action":"renewInstance","orderId":"20170109199524","accountId":"123545678"," openId ":"xz_D4XL_u7hKY5zt","productId":1024,"requestId":"3c45e1f3-22b9-4346-9898-4467d3aea000","signId":"kjsadkjhdskjh3k","expiredTime":"2017-02-09 19:59:59"}'

响应示例

{"success":"true"}

实例配置变更通知接口

  • 接口名:modifyInstance。
  • 接口说明:用户将实例从试用版转为正式版时,云市场将通过实例配置变更通知接口发送消息至发货 URL。
    注意:

    如果用户仅仅是配置变更,则参数中只会包含实例的新规格,而不会包含实例价格参数。

请求参数说明

参数名 类型 是否必须 描述
action String modifyInstance
orderId String 订单 ID
accountId String 购买者账号 ID
openId String 用户在腾讯云开放平台的标识,此标识对于同一服务商是相同的,对于不同服务商是不同的,长度为32位。如果没有接入开放平台,此字段为空
productId Integer 云市场产品 ID
requestId String 接口请求的 ID
signId String 实例标识 ID
spec String 实例新规格
timeSpan Integer 购买时长,仅在试用版转为正式购买时传递
timeUnit String 购买时长单位(y、m、d、h、t 分别代表年、月、日、天、时、次),是试用时为空
instanceExpireTime Datetime 新的实例到期时间,仅在试用版转为正式购买时传递

响应参数说明

参数名 类型 是否必须 描述
success String true/false
appInfo JSON 新的应用信息
appInfo.authUrl String 新的免登地址

请求示例

curl -X POST -H 'Content-Type: application/json' 'http://isv/interface?signature=e3k9ierw&timestamp=1483944926&eventId=1780012140' --data '{"action":"modifyInstance","orderId":"20170109199524","accountId":"123545678"," openId ":"xz_D4XL_u7hKY5zt","productId":1024,"requestId":"1d8326b2-9a94-4bf3-91ce-c7a94add99d3","signId":"kjsadkjhdskjh3k","spec":"高级版","timeSpan":2,"timeUnit":"m"}'

响应示例

{"success":"true"}

实例过期通知接口

  • 接口名:expireInstance。
  • 接口说明:实例到期后(用户最后操作后的 instanceExpireTime ),云市场将通过实例续费通知接口发送消息至发货 URL。

请求参数说明

参数名 类型 是否必须 描述
action String expireInstance
accountId String 购买者账号 ID
openId String 用户在腾讯云开放平台的标识,此标识对于同一服务商是相同的,对于不同服务商是不同的,长度为32位。如果没有接入开放平台,此字段为空
productId Integer 云市场产品 ID
requestId String 接口请求的 ID
signId String 实例标识 ID,服务商提供的唯一标识。长度最长为11位

响应参数规范

参数名 类型 是否必须 描述
success String true/false

请求示例

curl -X POST 'http://isv/interface?signature=e3k9ierw&timestamp=1483944926&eventId=1780012140' --data '{"action":"expireInstance","accountId":"123545678"," openId ":"xz_D4XL_u7hKY5zt","productId":1024,"requestId":"ea372177-809d-4722-91d0-d6df4edf7bc9","signId":"kjsadkjhdskjh3k"}'

响应示例

{"success":"true"}

实例销毁通知接口

  • 接口名:destroyInstance。
  • 接口说明:用户退款、实例到期后的七天内如果用户没有进行续费操作,云市场会销毁该实例并通过该接口发送消息至发货 URL。

请求参数说明

参数名 类型 是否必须 描述
action String destroyInstance
orderId String 客户退款的订单 ID
accountId String 购买者账号 ID
openId String 用户在腾讯云开放平台的标识,此标识对于同一服务商是相同的,对于不同服务商是不同的,长度为32位。如果没有接入开放平台,此字段为空
productId Integer 云市场产品 ID
requestId String 接口请求的 ID
signId String 实例标识 ID,服务商提供的唯一标识。长度最长为11位

响应参数说明

参数名 类型 是否必须 描述
success String true/false

请求示例

curl -X POST 'http://isv/interface?signature=e3k9ierw&timestamp=1483944926&eventId=1780012140' --data '{"action":"destroyInstance","orderId":"20170109199524","accountId":"123545678"," OpenID ":"xz_D4XL_u7hKY5zt","productId":1024,"requestId":"80b75030-6571-46a8-87ef-5b414f66dc39","signId":"kjsadkjhdskjh3k"}'

响应示例

{"success":"true"}

参数配置

在完成了相关开发后,服务商可登录 云市场服务商管理控制台,在【开发配置】> 【SaaS 接入设置】页面中,填写相关信息:

注意:

为保证数据的安全,发货 URL 必须为 HTTPS 协议,且发货 URL 不能包含查询字符串。如果配置保存失败,请检查 verifyInterface 接口是否正常。

在线接口调试

在接口开发过程中,您可以登录 云市场服务商管理控制台,在【开发配置】 > 【在线接口调试】中,可使用在线调试功能,用于模拟各种类型的发货行为。

注意:

此为调试环境,请勿使用正式环境的接口 URL 或数据库对接。

接入腾讯云 OAuth

为了提升用户使用 SaaS 产品的体验,建议服务商将应用接入腾讯云开放平台 OAuth ,实现用户在腾讯云控制台登录后,直接免登访问 SaaS 应用管理后台。

申请接入开放平台

由于腾讯云暂未开放申请腾讯云开放平台的入口,因此申请者需要先发邮件至:mqcloud@tencent.com 协助开通。邮件内容如下:

  • 腾讯云账户的 ID:您的账号 ID 可在 腾讯云控制台 总览页面右上角处查看。
  • 平台名称:平台名称会展示在授权页面。
  • 平台 Logo:建议尺寸:26 x 48,PNG 格式,Logo 将会展示在授权页面中。
  • 平台官网地址:您可以跳转到的第三方平台官网,即服务商的 SaaS 地址。
  • 平台回调地址:去掉 HTTP[s] 的域名部分,不能只是顶级域名,例如可以为api.example.com,而不允许为 example.com

申请成功后您将会得到如下的信息:

  • AppId:第三方平台唯一标识。
  • AppSecretId/AppSecretKey :用来请求腾讯云 API 时的密钥对。
  • EncryKey:用来校验回调地址中带入的 code 参数。

OAuth 接入说明

账号打通
云市场在发货通知中加入用户在腾讯云的标识 OpenID (对于不同服务商用户 OpenID 不同),服务商在获取到 OpenID 之后需要首先要查询该用户是否已经跟本地系统某个账户绑定,如果已经绑定,则直接处理发货逻辑;若未绑定,则应该在本地系统生成一个账号并与 OpenID 绑定。

免登校验
服务商对于发货的响应数据中应该返回一个authUrl,该 URL 最终会展示给用户。用户单击该 URL 后,服务商应该让用户登录到控制台以进行资源相关操作。

授权流程说明
服务商授权过程如下:

  1. 用户单击authUrl(假设为:http://example.com/qcloud/auth) 后,若未检测到用户登录,则跳转(302跳转)到:https://www.qcloud.com/open/authorize?scope=login&app_id=123456789012&redirect_url=https%3A%2F%2Fexample.com%2Fapi%2Foauth%2Fqcloud%2Fcallback&state=1234。 其中app_id为申请 OAuth 后获得的第三方平台唯一标识redirect_url为提交腾讯云开放平台的平台回调地址域名
  2. 用户在腾讯云开放平台页面进行登录并且授权;
  3. 页面跳转回第三方网站地址redirect_url,并且带上参数 code 和 signature ,类似:https://example.com/api/oauth/qcloud/callback?code=04f82b0d6fcfc0c2d967d808e6010bd8&signature=eafc9653bd5c17c6adea55bb516ba8b9&state=123, 其中 signature = md5(code+EncryKey)
    说明:

    • 为了防止暴力破解,signature 参数对 code 的合法性做了一个校验。
    • 该code一次性有效,并且有效期为6分钟。
  4. 获取 code 并且校验合法之后,调用 code 校验接口,获取用户的以下信息:
    • userOpenId:用户在该第三方平台下的身份唯一标识。userOpenId即为用户在腾讯云的OpenID(不同第三方平台获取的标识不同)。
    • userUnionId:如果同一个腾讯云账户有多个第三方平台,用户在这些第三方平台的 userUnionId 一致。
    • userAccessToken:用户访问 Token。
    • expiresAt:用户访问 Token 过期时间(时间戳,当前时间 + 2小时)。
    • userRefreshToken:刷新 Token ,有效期为60天。

code 校验接口

接口 URL

[https://open.api.qcloud.com/v2/index.php?Action=GetUserAccessToken&SecretId=AKIDQirtkVkUjoiQwHBRI0Xt55voZx9AQyd9&Nonce=56636&Timestamp=1492137022&userAuthCode=735bd6a208f9d70762c1bc03ad67540b&Signature=qvpUUyYIFfvlUz2DGkccH9rkyEs%3D]

请求参数说明

参数名 类型 是否必须 描述
Action String 接口名 GetUserAccessToken
SecretId String 第三方平台分配时获得加密 ID
userAuthCode String 业务参数, URL 跳转带入的 code
Signature String 请求签名,请参见 签名方法
Nonce Integer 随机正整数,与 Timestamp 联合起来,用于防止重放攻击
Timestamp Integer UNIX 时间戳,记录 API 发起时间

响应参数说明

参数名 类型 是否必须 描述
code Integer 错误码,为0时表示请求返回正确。可在 错误码 查看更新错误详情
message String 错误码 code 为0,错误信息 message 为空
data Array 错误码 code 为0,则会返回 data 字段
data.appId String -
data.userOpenId String - 用户在该第三方平台下的身份唯一标识
data.userUnionId String - 如果同一个腾讯云账户有多个第三方平台,用户在这些第三方平台的 userUnionId 一致。
data.userAccessToken String - 用户访问 Token
data.expiresAt Integer - 用户访问 Token 过期时间
data.userRefreshToken String - 刷新 Token,有效期为60天
data.scope String - -

请求示例

curl -X GET 'https://open.api.qcloud.com/v2/index.php?Action=GetUserAccessToken&SecretId=AKIDQirtkVkUjoiQwHBRI0Xt55voZx9AQyd9&Nonce=56636&Timestamp=1492137022&userAuthCode=735bd6a208f9d70762c1bc03ad67540b&Signature=qvpUUyYIFfvlUz2DGkccH9rkyEs%3D'

成功响应示例

{"code":0,"message":"ok","data":{"appId":"abc","userOpenId":"abc","userUnionId":"abc","userAccessToken":"abc","expiresAt":1231232141241,"userRefreshToken":"abvc","scope":"login"}}

失败响应示例

{"code":4000,"message":"(100004)请求参数非法"}