文档中心 > 服务商(ISV)接入

业务场景

在套件发生创建、开通授权等事件的时候,钉钉服务器端需要向ISV的服务器推送数据。钉钉向ISV推送的数据包,将使用套件申请时的加解密key来进行加密,服务商在收到推送后需要返回包含经过加密的字符串的json数据。

主要有以下三个场景需要使用ISV回调协议:

1.套件(微应用)被创建时。相关接口为:

    a.验证回调URL有效性事件

    b.接收套件Ticket

2.企业管理员在开通您的套件(微应用)时,通过钉钉进行授权与激活。与之关联的回调接口为:

    c.回调向ISV推送临时授权码

    d.回调向ISV推送授权变更消息

    e.套件信息更新事件

    f.解除企业授权事件

    g.校验序列号事件

3.企业下单购买您的收费商品后,你可以通过回调事件拿到用户购买的商品规格对应的规格参数及企业购买的服务时长。与之对应的回调接口为:

    h.用户购买下单事件

接收回调消息

准备配置

在使用回调接口之前您首先要拿到您创建套件时填写的“回调URL”,“Token”,“数据加密密钥(ENCODING_AES_KEY)”,获取方式如下图所示:



参数说明

参数 说明
回调URL ISV接收推送请求的地址
Token ISV在注册时任意填写的,用来生成signature,用来和回调参数中的signature比对,校验消息的合法性
数据加密密钥(ENCODING_AES_KEY) 用于消息体的加解密
suiteKey 套件key

回调地址说明

钉钉服务器会向ISV申请时填写的套件回调URL定时推送SuiteTicket,以及临时授权码和授权设置变更。

服务端demo java版本做示例,您提供的回调URL是:

http://您服务端部署的IP:您的端口/ding-isv-access/suite/callback/{suitekey} // 替换成自己的suiteKey

钉钉服务器每一次访问回调URL的时候将发出如下请求(下面链接中的signature,timestamp,nonce的值只是示例,并不代表真实值):

http://您服务端部署的IP:您的端口/ding-isv-access/suite/callback/{suitekey}?signature=111108bb8e6dbce3c9671d6fdb69d15066227608&timestamp=1783610513&nonce=380320111

包含的json数据为:

{
	"encrypt":"1ojQf0NSvw2WPvW7LijxS8UvISr8pdDP+rXpPbcLGOmIBNbWetRg7IP0vdhVgkVwSoZBJeQwY2zhROsJq/HJ+q6tp1qhl9L1+ccC9ZjKs1wV5bmA9NoAWQiZ+7MpzQVq+j74rJQljdVyBdI/dGOvsnBSCxCVW0ISWX0vn9lYTuuHSoaxwCGylH9xRhYHL9bRDskBc7bO0FseHQQasdfghjkl"
}

其中的encrypt字段是经过加密的消息体。encrypt经过一系列解密步骤后,才能产生下一小节所述的“POST数据解密后示例”,ISV可以直接使用钉钉提供的库(解密库下载)进行解密的处理。

除此之外,在接收到推送之后,需要返回相应的加密字符串(代表了你收到了推送),如果ISV未能成功返回,钉钉服务器将持续推送下去,达到100次后将不再推送。

参数 说明
示例代码地址 SuiteCallBackController
加解密库和demo下载 下载
本地调试地址 调试地址
加解密方案说明 加解密方案说明

验证回调URL有效性事件

此事件的推送会发生在创建套件,点击下图按钮之时。注意,若未能成功验证回调URL有效性,套件将不能被创建。

verifyurl

验证回调URL有效性在ISV服务端接收消息的处理代码逻辑:

try {
		 // 创建加解密类
		 // 第一个参数为注册套件的之时填写的token
		 // 第二个参数为注册套件的之时生成的数据加密密钥(ENCODING_AES_KEY)
		 // 第三个参数,ISV开发传入套件的suiteKey
		 // 具体参数值请查看开发者后台(//open-dev.dingtalk.com)
		 // 注:其中,对于第三个参数,在第一次接受『验证回调URL有效性事件的时候』
		 // 传入Env.CREATE_SUITE_KEY,对于这种情况,已在异常中catch做了处理。
		 //  
		dingTalkEncryptor = new DingTalkEncryptor(Env.TOKEN, Env.ENCODING_AES_KEY, Env.SUITE_KEY);
		 // 
		 // 获取从encrypt解密出来的明文
		 // 
		plainText = dingTalkEncryptor.getDecryptMsg(msgSignature, timeStamp, nonce, encrypt);
	} catch (DingTalkEncryptException e) {
		// TODO Auto-generated catch block
		dingTalkEncryptException = e;
		e.printStackTrace();
	} finally {

	}

	// 
	//  对从encrypt解密出来的明文进行处理
	//  不同的eventType的明文数据格式不同
	// 

	JSONObject plainTextJson = JSONObject.parseObject(plainText);
	String eventType = plainTextJson.getString("EventType");
	// res是需要返回给钉钉服务器的字符串,一般为success
	// "check_create_suite_url"和"check_update_suite_url"事件为random字段
	// 具体请查看文档或者对应eventType的处理步骤
	// 
	String res = "success";

	switch (eventType) {
		case "check_create_suite_url":
			//此事件需要返回的"Random"字段
			res = plainTextJson.getString("Random");
			String testSuiteKey = plainTextJson.getString("TestSuiteKey");
			break;
	}

	//  对返回信息进行加密
	long timeStampLong = Long.parseLong(timeStamp);
	Map<String, String> jsonMap = null;
	try {
		// 
		// jsonMap是需要返回给钉钉服务器的加密数据包
		// 
		jsonMap = dingTalkEncryptor.getEncryptedMap(res, timeStampLong, nonce);
	} catch (DingTalkEncryptException e) {
		System.out.println(e.getMessage());
		e.printStackTrace();
	}
	JSONObject json = new JSONObject();
	json.putAll(jsonMap);
	response.getWriter().append(json.toString());

DEMO代码地址

同时,Demo的配置文件(Env.java)中还需要根据套件创建时填写的TOKEN 和 ENCODING_AES_KEY做相应的更改。

verifyurl

待成功处理『验证回调URL有效性事件』事件,套件创建成功之后,就不能再使用默认的SUITE_KEY了,需要使用套件本身的SUITE_KEY,所以需要在Env.java中配置SUITE_KEY,同时将新创建套件中的SUITE_SECRET配置到Env.java中,然后重新部署代码,此时将用下面的语句进行加解密。因为推送ticket是二十分钟推送一次,再次部署也需要等待二十分钟服务端才会推送,后续会优化该逻辑,保证即时推送,提高推送效率。

dingTalkEncryptor = new DingTalkEncryptor(Env.TOKEN, Env.ENCODING_AES_KEY, Env.SUITE_KEY);//套件创建成功后,使用套件本身的SUITE_KEY进行加解密

POST数据解密后示例

{

  "EventType":"check_create_suite_url",
  "Random":"brdkKLMW",
  "TestSuiteKey":"suite4xxxxxxxxxxxxxxx"

}
参数 说明
Random 随机字符串
EventType check_create_suite_url
TestSuiteKey 校验的SuiteKey

返回说明

服务提供商在收到此事件推送后务必返回包含经过加密后“Random”字段的json数据,比如对于上面的示例,需要返回的即是加密后的“brdkKLMW”字符串。

只有返回了对应的json数据,钉钉才会判断此事件推送成功,套件才能创建成功。

{
  "msg_signature":"111108bb8e6dbce3c9671d6fdb69d15066227608",
  "timeStamp":"1783610513",
  "nonce":"123456",
 "encrypt":"1ojQf0NSvw2WPvW7LijxS8UvISr8pdDP+rXpPbcLGOmIBNbWetRg7IP0vdhVgkVwSoZBJeQwY2zhROsJq/HJ+q6tp1qhl9L1+ccC9ZjKs1wV5bmA9NoAWQiZ+7MpzQVq+j74rJQljdVyBdI/dGOvsnBSCxCVW0ISWX0vn9lYTuuHSoaxwCGylH9xRhYHL9bRDskBc7bO0FseHQQasdfghjkl" // "Random"字段的加密数据
}
参数 说明
msg_signature 消息体签名
timeStamp 时间戳
nonce 随机字符串
encrypt “Random”字段的加密字符串

接收套件Ticket

钉钉开方平台会向服务提供商申请时填写的套件事件接收 URL不定期(约二十分钟)推送ticket,注意:开发者需要持久化TICKET,也不要设置20分钟失效的缓存时间,新的ticket推送会失效掉之前的ticket。

服务提供商在收到ticket推送后务必返回经过加密的字符串“success”的json数据,如果该套件下没有任何的微应用,定时推送的ticket也不会成功,需要保证至少有一个ticket。

如果不返回,钉钉服务器将连续推送,直到推送次数超过100次,就不再推送。倘若您希望钉钉服务器重新推送,需要进入开发者后台,进入套件管理页面,点击『重新推送』按钮,即可重新推送。

repush

demo示例

服务端demo java版本做示例,每二十分钟服务端会向服务商提供的接口进行一次回调。可以在IsvReceiveServlet.java文件中,打印并获取Env.suiteTicket,在debug平台获取套件的SuiteAccessToken。

POST数据解密后示例

{
  "SuiteKey": "suitexxxxxx",
  "EventType": "suite_ticket ",
  "TimeStamp": 1234456,
  "SuiteTicket": "adsadsad"
}
参数 说明
SuiteKey 应用套件的SuiteKey
EventType suite_ticket
TimeStamp 时间戳
SuiteTicket Ticket内容

返回说明

服务提供商在收到此事件推送后务必返回包含经过加密的字符串“success”的json数据,只有返回了对应的json数据,钉钉服务端才会判断此事件推送成功,套件才能创建成功。

{
  "msg_signature":"111108bb8e6dbce3c9671d6fdb69d15066227608",
  "timestamp":"1783610513",
  "nonce":"123456",
  "encrypt":"1ojQf0NSvw2WPvW7LijxS8UvISr8pdDP+rXpPbcLGOmIBNbWetRg7IP0vdhVgkVwSoZBJeQwY2zhROsJq/HJ+q6tp1qhl9L1+ccC9ZjKs1wV5bmA9NoAWQiZ+7MpzQVq+j74rJQljdVyBdI/dGOvsnBSCxCVW0ISWX0vn9lYTuuHSoaxwCGylH9xRhYHL9bRDskBc7bO0FseHQQasdfghjkl" // "Random"字段的加密数据
}
参数 说明
msg_signature 消息体签名
timestamp 时间戳
nonce 随机字符串
encrypt “success”加密字符串

接收授权成功事件

当企业管理员在应用市场应用的授权页面点击了确认授权(授权成功),钉钉服务器会向服务提供商的套件事件接收 URL(创建套件时填写)推送临时授权码,比如在钉钉开发者后台中,模拟测试企业发起授权,钉钉服务器就会向回调url推送测试企业的临时授权码

shouqun

服务端demo java版本做示例,可以在IsvReceiveServlet.java文件中,打印并获取Env.authCode。在debug平台获取套件的永久授权码,请将永久授权码保存下来,目前永久授权码丢失之后没法再次获取,只能让企业管理员解除授权,再重新授权,后续会优化该逻辑,解决临时授权码只能获取一次永久授权码的问题。

POST数据解密后示例

{
  "SuiteKey": "suitexxxxxx",
  "EventType": " tmp_auth_code",
  "TimeStamp": 1234456,
  "AuthCode": "adads"
}

字段说明

参数 说明
SuiteKey 应用套件的SuiteKey
EventType tmp_auth_code
TimeStamp 时间戳
AuthCode 临时授权码

返回说明

服务提供商在收到此事件推送后务必返回包含经过加密的字符串“success”的json数据,只有返回了对应的json数据,钉钉才会判断此事件推送成功,套件才能创建成功。

{
  "msg_signature":"111108bb8e6dbce3c9671d6fdb69d15066227608",
  "timestamp":"1783610513",
  "nonce":"123456",
  "encrypt":"1ojQf0NSvw2WPvW7LijxS8UvISr8pdDP+rXpPbcLGOmIBNbWetRg7IP0vdhVgkVwSoZBJeQwY2zhROsJq/HJ+q6tp1qhl9L1+ccC9ZjKs1wV5bmA9NoAWQiZ+7MpzQVq+j74rJQljdVyBdI/dGOvsnBSCxCVW0ISWX0vn9lYTuuHSoaxwCGylH9xRhYHL9bRDskBc7bO0FseHQQasdfghjkl" // "Random"字段的加密数据
}
参数 说明
msg_signature 消息体签名
timestamp 时间戳
nonce 随机字符串
encrypt “success”加密字符串

接收授权变更事件

当授权方(即授权企业)在微应用管理端中,修改了对套件的授权企业通讯录范围后,钉钉服务器会向服务提供商的套件事件接收 URL(创建套件时填写)推送授权变更消息。注意:推送的授权变更信息并不包括企业用户具体做了什么修改,所以收到推送之后,**ISV需要通过调用获取通讯录权限接口查询新的授权范围。再根据具体状态做具体操作,比如状态为2,就需要ISV为企业进行激活授权套件的操作。

POST数据解密后示例

{
  "SuiteKey": "suitexxxxxx",
  "EventType": " change_auth",
  "TimeStamp": 1234456,
  "AuthCorpId": "xxxxx"
}

字段说明

参数 说明
SuiteKey 应用套件的SuiteKey
EventType change_auth
TimeStamp 时间戳
AuthCorpId 授权方企业的corpid

返回说明

服务提供商在收到此事件推送后务必返回包含经过加密的字符串“success”的json数据,只有返回了对应的json数据,钉钉才会判断此事件推送成功,套件才能创建成功。

{
  "msg_signature":"111108bb8e6dbce3c9671d6fdb69d15066227608",
  "timestamp":"1783610513",
  "nonce":"123456",
  "encrypt":"1ojQf0NSvw2WPvW7LijxS8UvISr8pdDP+rXpPbcLGOmIBNbWetRg7IP0vdhVgkVwSoZBJeQwY2zhROsJq/HJ+q6tp1qhl9L1+ccC9ZjKs1wV5bmA9NoAWQiZ+7MpzQVq+j74rJQljdVyBdI/dGOvsnBSCxCVW0ISWX0vn9lYTuuHSoaxwCGylH9xRhYHL9bRDskBc7bO0FseHQQasdfghjkl" // "Random"字段的加密数据
}
参数 说明
msg_signature 消息体签名
timeStamp 时间戳
nonce 随机字符串
encrypt “success”加密字符串

套件信息更新事件

此事件的推送会发生在更新套件信息的时候。

POST数据解密后示例

{
  "EventType":"check_update_suite_url",
  "Random":"Aedr5LMW",
  "TestSuiteKey":"suited6db0pze8yao1b1y"

}

服务提供商在收到“套件信息更新”事件推送后务必返回经过加密后“Random”字段,比如对于右边的示例,需要返回的即是加密后的“Aedr5LMW”字符串。

参数 说明
Random 随机字符串
EventType check_update_suite_url
TestSuiteKey 校验的SuiteKey(此处为套件的SuiteKey)

返回说明

服务提供商在收到此事件推送后务必返回包含经过加密后“Random”字段的json数据,比如对于上面的示例,需要返回的即是加密后的“Aedr5LMW”字符串。只有返回了对应的json数据,钉钉才会判断此事件推送成功,套件才能创建成功。

{
  "msg_signature":"111108bb8e6dbce3c9671d6fdb69d15066227608",
  "timestamp":"1783610513",
  "nonce":"123456",
  "encrypt":"1ojQf0NSvw2WPvW7LijxS8UvISr8pdDP+rXpPbcLGOmIBNbWetRg7IP0vdhVgkVwSoZBJeQwY2zhROsJq/HJ+q6tp1qhl9L1+ccC9ZjKs1wV5bmA9NoAWQiZ+7MpzQVq+j74rJQljdVyBdI/dGOvsnBSCxCVW0ISWX0vn9lYTuuHSoaxwCGylH9xRhYHL9bRDskBc7bO0FseHQQasdfghjkl" // "Random"字段的加密数据
}
参数 说明
msg_signature 消息体签名
timestamp 时间戳
nonce 随机字符串
encrypt “Random”字段的加密字符串

解除授权事件

此事件的推送会发生在企业解除套件授权的时候,发生了“解除授权”事件之后,原来的永久授权码将立即失效,假设企业用户又重新发起授权,ISV将收到临时授权码以换取新的永久授权码。

POST数据解密后示例

{
  "EventType":"suite_relieve",
  "SuiteKey":"suited6db0pze8yao1b1y",
  "TimeStamp":"12351458245",
  "AuthCorpId":"ding4583267d28sd61"
}

服务提供商在收到“解除授权”事件推送后务必返回包含经过加密的字符串“success”的json数据。

参数 说明
SuiteKey 应用套件的SuiteKey
EventType suite_relieve
TimeStamp 时间戳
AuthCorpId 授权方企业的corpid

返回说明

服务提供商在收到“解除授权”事件推送后务必返回包含经过加密的字符串“success”的json数据。只有返回了对应的json数据,钉钉才会判断此事件推送成功。

{
  "msg_signature":"111108bb8e6dbce3c9671d6fdb69d15066227608",
  "timestamp":"1783610513",
  "nonce":"123456",
  "encrypt":"1ojQf0NSvw2WPvW7LijxS8UvISr8pdDP+rXpPbcLGOmIBNbWetRg7IP0vdhVgkVwSoZBJeQwY2zhROsJq/HJ+q6tp1qhl9L1+ccC9ZjKs1wV5bmA9NoAWQiZ+7MpzQVq+j74rJQljdVyBdI/dGOvsnBSCxCVW0ISWX0vn9lYTuuHSoaxwCGylH9xRhYHL9bRDskBc7bO0FseHQQasdfghjkl" // "Random"字段的加密数据
}
参数 说明
msg_signature 消息体签名
timestamp 时间戳
nonce 随机字符串
encrypt “success”字段的加密字符串

校验序列号事件

此事件的推送会发生在企业开通套件输入序列号的时候,要求请求立即返回。

POST数据解密后示例

{
  "EventType":"check_suite_license_code",
  "TimeStamp":15481221,
  "SuiteKey":"suited6db0pze8yao1b1y"
  "AuthCorpId":"dingxxxxxxxx"
  "LicenseCode":"xxxxxxxx"
}

服务提供商在收到“校验序列号”事件推送后,判断该LicenseCode是否合法,如果合法则返回加密“success”。返回其它值或者不返回视为LicenseCode不合法

参数 说明
TimeStamp 时间戳
EventType check_suite_license_code
SuiteKey 校验的SuiteKey(此处为套件的SuiteKey)
AuthCorpId 使用该套件企业的corpid
LicenseCode 企业使用套件输入的序列号

返回说明

服务提供商在收到“校验序列号”事件推送后,判断该LicenseCode是否合法,如果合法返回加密“success”。返回其它值或者不返回作为不合法处理

{
  "msg_signature":"111108bb8e6dbce3c9671d6fdb69d15066227608",
  "timestamp":"1783610513",
  "nonce":"123456",
  "encrypt":"1ojQf0NSvw2WPvW7LijxS8UvISr8pdDP+rXpPbcLGOmIBNbWetRg7IP0vdhVgkVwSoZBJeQwY2zhROsJq/HJ+q6tp1qhl9L1+ccC9ZjKs1wV5bmA9NoAWQiZ+7MpzQVq+j74rJQljdVyBdI/dGOvsnBSCxCVW0ISWX0vn9lYTuuHSoaxwCGylH9xRhYHL9bRDskBc7bO0FseHQQasdfghjkl" // "Random"字段的加密数据
}
参数 说明
msg_signature 消息体签名
timestamp 时间戳
nonce 随机字符串
encrypt 加密字符串

用户购买下单事件

此事件的推送会发生在企业在钉钉下单购买套件后,要求请求立即返回。

POST数据解密后示例

{
    "EventType": "market_buy",
    "SuiteKey": "suited6db0pze8yao1b1y",
    "buyCorpId": "dingxxxxxxxx",
    "goodsCode": "FW_GOODS-xxxxxxxx",
    "itemCode": "1c5f70cf04c437fb9aa1b20xxxxxxxx",
    "itemName": "按照范围收费规格0-300",
    "subQuantity": 1(订购的具体人数),           
    "maxOfPeople": 300,
    "minOfPeople": 0,
    "orderId": 308356401xxxxxxxx,
    "paidtime": 1474535702000,
    "serviceStopTime": 1477065600000,
    "payFee":147600,
    "orderCreateSource":"DRP",
    "nominalPayFee":147600,
    "discountFee":600,
    "discount":0.06,
    "distributorCorpId":"ding9f50b15bccd16741",
   "distributorCorpName":"测试企业"
}

服务提供商在收到“用户下单购买”事件推送后务必返回包含经过加密的字符串“success”的json数据。

参数 说明
EventType market_buy
SuiteKey 用户购买套件的SuiteKey
buyCorpId 购买该套件企业的corpid
goodsCode 购买的商品码
itemCode 购买的商品规格码
itemName 购买的商品规格名称
maxOfPeople 购买的商品规格能服务的最多企业人数
minOfPeople 购买的商品规格能服务的最少企业人数
orderId 订单id
paidtime 下单时间
serviceStopTime 该订单的服务到期时间
payFee 订单支付费用,以分为单位
orderCreateSource 订单创建来源,如果来自钉钉分销系统,则值为“DRP”,
nominalPayFee 钉钉分销系统提单价,以分为单位
discountFee 折扣减免费用
discount 订单折扣
distributorCorpId 钉钉分销系统提单的代理商的企业corpId
distributorCorpName 钉钉分销系统提单的代理商的企业名称

返回说明

服务提供商在收到“用户下单购买”事件推送后务必返回包含经过加密的字符串“success”的json数据。

{
  "msg_signature":"111108bb8e6dbce3c9671d6fdb69d15066227608",
  "timestamp":"1783610513",
  "nonce":"123456",
  "encrypt":"1ojQf0NSvw2WPvW7LijxS8UvISr8pdDP+rXpPbcLGOmIBNbWetRg7IP0vdhVgkVwSoZBJeQwY2zhROsJq/HJ+q6tp1qhl9L1+ccC9ZjKs1wV5bmA9NoAWQiZ+7MpzQVq+j74rJQljdVyBdI/dGOvsnBSCxCVW0ISWX0vn9lYTuuHSoaxwCGylH9xRhYHL9bRDskBc7bO0FseHQQasdfghjkl" // "Random"字段的加密数据
}
参数 说明
msg_signature 消息体签名
timestamp 时间戳
nonce 随机字符串
encrypt 加密字符串

企业逻辑停用微应用事件

服务提供商在收到“企业逻辑停用微应用”事件推送后务必返回包含经过加密的字符串“success”的json数据。

POST数据解密后示例

{
    "AgentId": 54146891,
    "AppId": 1949,
    "AuthCorpId": "ding9f50b15bccd1xxxxx",
    "EventType": "org_micro_app_stop",
    "SuiteKey": "suitexdhgv7mnxxxxx",
    "TimeStamp":"1481173967075"
}

企业物理删除微应用事件

注意:在“解除授权”事件中,企业会物理删除该套件下的全部应用,但是不会发出“企业物理删除微应用”,服务提供商在收到“企业物理删除微应用”事件推送后务必返回包含经过加密的字符串“success”的json数据。

POST数据解密后示例

{
    "AgentId": 54146891,
    "AppId": 1949,
    "AuthCorpId": "ding9f50b15bccd1xxxxx",
    "EventType": "org_micro_app_remove",
    "SuiteKey": "suitexdhgv7mnxxxxx",
    "TimeStamp":"1481173967075"
}

企业逻辑启用微应用事件

服务提供商在收到“企业逻辑启用微应用”事件推送后务必返回包含经过加密的字符串“success”的json数据。

POST数据解密后示例

{
    "AgentId": 54146891,
    "AppId": 1949,
    "AuthCorpId": "ding9f50b15bccd1xxxxx",
    "EventType": "org_micro_app_restore",
    "SuiteKey": "suitexdhgv7mnxxxxx",
    "TimeStamp":"1481173967075"
}

消息体签名

消息体的加密解密方案和加密解密过程,请前往消息体加密解密查看。

FAQ

关于此文档暂时还没有FAQ
返回
顶部