符合GDPR要求
《通用数据保护条例》 (GDPR) 是欧盟法律中关于欧盟公民数据保护和隐私的一项条例。该条例指出控制个人数据的品牌必须要在一个月内满足访问数据、转移数据、更正数据和删除数据的要求。
关于移动归因,由于不可能事先知道哪些用户是欧洲公民,GDPR 适用于所有移动用户,无论是否为欧洲用户。
GDPR规定
为了按GDPR要求处理和管理数据对象请求,AppsFlyer与mParticle、Amplitude、Braze一同发起了OpenGDPR协议。
开放GDPR是一个统一的开源框架, 促进了技术公司之间的合作, 并公正透明地使用消费者数据。该框架使得供应商能够轻松地在多个系统中执行数据隐私操作, 以处理和存储客户数据。
可以点击这里阅读更多关于这个倡议的内容。
GDPR实体
数据对象 | 正在被收集数据的移动应用终端用户 |
数据控制人 | 广告主决定处理个人数据的目的和方式。 |
数据处理人 | AppsFlyer及其合作伙伴代表数据控制人处理个人数据 |
GDPR要求
GDPR详细说明了数据对象的强制性权利,广告主必须遵守这些权利。
权利 |
GDPR定义 |
AppsFlyer如何帮助控制人 |
---|---|---|
访问权利 |
|
发送“访问”请求时,数据控制人会收到一份经过处理的数据对象个人数据的可导出副本。 |
转移数据的权利 |
数据对象需要以结构化、常用和机器可读的格式 (如 CSV 文件) 接收其所有个人数据。 |
当发出“数据转移”请求时,数据控制人会收到一份经过处理的数据对象个人数据副本。 |
修改的权利 |
允许数据对象更正不准确或不真实的个人数据。然后,数据控制人必须要删除或更改不准确或不完整的数据。 |
当数据控制人提交 "更正" 请求时, AppsFlyer 会删除数据对象到当时为止的历史数据, 但之后收到的数据将由 AppsFlyer 进行更新。 |
删除数据的权利 |
删除数据的权利要求数据控制人在一个月内删除个人数据。 |
广告主可以在数据对象提出删除请求后删除所搜集的数据。 |
AppsFlyer的GDPR请求API
AppsFlyer通过GDPR 请求 API 支持上述要求 (从2018年5月25日开始-在此日期之前收到的请求将不予处理):
- GDPR 请求- 执行上述请求类型之一:"访问"、"转移"、"删除" 或 "更正"。
- 状态请求- 查询 GDPR 请求的当前状态
- 发现请求- 查询支持的 API 版本和数据格式
- 取消- 取消待审核阶段的GDPR 请求
GUI变更是由数据控制人实施的,以便其终端用户可以递交这些请求。请注意,GDPR请求只能每次一个用户。
1. GDPR 请求
GDPR 请求流程
所有 GDPR 请求类型 "访问"、"转移"、"删除" 和 "更正" 都是一样的共同流程:
- 数据对象(终端用户)递交请求。
- 数据控制人 (广告主) 编辑GDPR 请求 (见下文) 并将其发送给AppsFlyer。
- 数据处理人(AppsFlyer) 接收请求, 并以 "201 OK" 响应有效的请求。
- 在接下来的48小时,这些请求将处于待定阶段,并且可以取消。
- 之后, 请求状态会变成正在进行, AppsFlyer 会发送 "状态变更"的回传。到这步之后,请求不可取消。
- a) AppsFlyer 会在接下来的28天内完成请求。
在 "删除"/"更正" 情况下, 终端用户的数据会被删除。
在 "数据转移"/"访问" 情况下-可以在 AppsFlyer 控制面板中的GDPR 部分或通过请求 API (见下文)访问终端用户数据。
b) 该请求会把状态变成已完成, 然后AppsFlyer 会发送 "状态变更" 回传。
GDPR 请求格式
GDPR 请求 API 可以通过 HTTP POST 提交到以下端点-
https://hq1.appsflyer.com/gdpr/opengdpr_requests?api_token=[api token]
提示
该API证书和Pull API 证书是同一个API证书。您可以在控制面板的API权限下检索。
参数
参数名称 | 必填/可选 | description |
---|---|---|
subject_request_id | 必填 | UUID v4 字符串。该字符串必须由控制人在给处理人递交请求时生成。之后,可以用它来查询请求状态、更新或取消请求。 |
subject_request_type | 必填 | 表示 GDPR 请求类型的字符串值。支持的值: "删除"、"转移"、"访问" 和 "更正" |
subject_identities | 必填 | 标识对象数组,包括类型、值和设备 ID 的格式 (见下文)。每个请求只能有一个设备 ID。 |
submitted_time | 必填 | RFC 3339 日期字符串表示数据对象初次请求的时间。请注意 所有时间戳都按 UTC发送 |
property_id | 必填 | 代表此请求所属移动应用的字符串, 例如, com.之类的代表Android应用, id123456789之类的代表iOS应用 |
api_version | 可选 | 表示所需GDPR 请求 API 版本的字符串版本 |
status_callback_urls | 可选 | 下列请求状态变更后接收状态回调的端点数组。请注意, 仅支持https端点。 |
GDPR 请求必须包含一个或多个用于满足该请求的标识对象。
-
identity_type - REQUIRED string value representing the form of identity. The supported Identity Types by AppsFlyer are
ios_advertising_id
,android_advertising_id
,fire_advertising_id
andmicrosoft_advertising_id
. - 标识 _ 值- 必填 代表标识的字符串值。
-
identity_format - REQUIRED string value representing the encoding of the identity. Currently
raw
is the only supported Identity Format by AppsFlyer.
GDPR 数据删除请求示例
HTTP POST
https://hq1.appsflyer.com/gdpr/opengdpr_requests?api_token={api token]
HTTP/1.1Host: example.processo.com
Accept: application/json
Content-Type: application/json
{
"subject_request_id":"a7551968-d5d6-44b2-9831-815ac9017798",
"subject_request_type":"erasure",
"submitted_time":"2018-10-02T15:00:00Z",
"subject_identities":[
{
"identity_type":"android_advertising_id",
"identity_value":"a55684fd-j661-46df-9149-f7bfd652egge",
"identity_format":"raw"
}
],
"api_version":"0.1",
"property_id":"com.example",
"status_callback_urls":[
"https://examplecontroller.com/opengdpr_callbacks"
]
}
GDPR 数据删除请求代码示例
/*
using the okhttp package install from maven
https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
*/
import okhttp3.*;
import java.io.IOException;
public class GdprSendRequest {
public static void main(String[] args){
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(null, "" +
"{\r\n\"subject_request_id\": \"<SUBJECT_ID>\"," +
"\r\n\"subject_request_type\": \"erasure\"," +
"\r\n\"submitted_time\": \"2018-11-02T15:00:00Z\"," +
"\r\n\"subject_identities\": [\r\n" +
"{\r\n\"identity_type\": \"android_advertising_id\"," +
"\r\n\"identity_value\": \"<ADVERTISING_ID>\"," +
"\r\n\"identity_format\": \"raw\"\r\n}" +
"\r\n]," +
"\r\n\"property_id\": \"com.example.application\"}");
Request request = new Request.Builder()
.url("https://hq1.appsflyer.com/gdpr/opengdpr_requests?api_token=<API_TOKEN>")
.post(body)
.addHeader("Content-Type", "application/json")
.addHeader("Accept", "application/json")
.build();
try {
Response response = client.newCall(request).execute();
System.out.println(response.code());
System.out.println(response.body().string());
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
}
""" using the requests python package,
install using pip install requests """
import requests
import json
headers = {
"Content-Type": "application/json",
"Accept": "application/json"
}
params = {
"api_token": ""
}
body = {
"subject_request_id": "",
"subject_request_type": "erasure",
"submitted_time": "2018-10-02T15:00:00Z",
"subject_identities": [
{ "identity_type": "android_advertising_id",
"identity_value": "",
"identity_format": "raw" }
],
"property_id": "com.example.application"
}
body = json.dumps(body)
res = requests.request("POST",
"https://hq1.appsflyer.com/gdpr/opengdpr_requests",
headers=headers, data=body, params=params)
print(res.text)
/* using the request npm package,
install using npm install request */
var request = require("request");
var options = { method: 'POST',
url: 'https://hq1.appsflyer.com/gdpr/opengdpr_requests',
qs: { api_token: '' },
headers:
{ Accept: 'application/json',
'Content-Type': 'application/json' },
body:
{ subject_request_id: '<SUBJECT_ID>',
subject_request_type: 'erasure',
submitted_time: '2018-10-02T15:00:00Z',
subject_identities:
[ { identity_type: 'android_advertising_id',
identity_value: '<ADVERTISING_ID>',
identity_format: 'raw' } ],
property_id: 'com.example.application'
},
json: true };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
using System;
using RestSharp;
namespace CS
{
class Gdpr
{
static void Main(string[] args)
{
var url = "https://hq1.appsflyer.com/gdpr/opengdpr_requests";
var apiToken = "?api_token=<API_TOKEN>";
var client = new RestClient(url + apiToken);
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
Request.AddHeader("Accept", "application/json");
var body = "{\r\n\"subject_request_id\": \"<SUBJECT_ID>\"," +
"\r\n \"subject_request_type\": \"erasure\"," +
"\r\n \"submitted_time\": \"2018-11-02T15:00:00Z\"," +
"\r\n \"subject_identities\": [\r\n" +
"{ \r\n \"identity_type\": \"android_advertising_id\"," +
"\r\n \"identity_value\": \"<ADVERTISING_ID>\"," +
"\r\n \"identity_format\": \"raw\"\r\n }" +
"\r\n ]," +
"\r\n \"property_id\": \"com.example.application\"}";
request.AddParameter("undefined", body,ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
// handle response by reading response.StatusCode
Console.WriteLine(response.Content);
}
}
}
2. 状态请求
每个已提交的GDPR请求都可以通过subject_request_id
来查询其后续状态。支持的状态类型有四种:
- 待审核-已收到正确的请求, 并且正处于待审核队列中
- 正在进行中-正在被执行的请求
- 已完成-请求已完成
- 已取消-请求已被取消
状态请求格式
状态请求可以通过 HTTP GET 提交到以下端点:
https://hq1.appsflyer.com/gdpr/opengdpr_requests/[subject_request_id]?api_token=[api token]
状态响应示例
HTTP/1.1 200 OK
Content-Type: application/json
X-OpenGDPR-Processor Domain: example processor.com
X-OpenGDPR-Signature:
kiGlog3PdQx+FQmB8wYwFC1fekbJG7Dm9WdqgmXc9uKkFRSM4uPzylLi7j083461xLZ+mUloo3tpsmyIZpt5eMfgo7ejXPh6lqB4ZgCnN6+1b6Q3NoNcn/+11UOrvmDj772wvg6uIAFzsSVSjMQxRs8LAmHqFO4cF2pbuoPuK2diHOixxLj6+t97q0nZM7u3wmgkwF9EHIo3C6G1SI04/odvyY/VdMZgj3H1fLnz+X5rc42/wU4974u3iBrKgUnv0fcB4YB+L6Q3GsMbmYzuAbe0HpVA17ud/bVoyQZAkrW2yoSy1x4Ts6XKba6pLifIHf446Bubsf5r7x1kg6Eo7B8zur666NyWOYrglkOzU4IYO8ifJFRZZXazOgk7ggn9obEd78GBc3kjKKZdwaCrLx7WV5y9TMDCf+2FILOJM/MwTUy1dLZiaFHhGdzld2AjbjK1CfVzyPssch0iQYYtbR49GhumvkYl11S4oDfu0c3t/xUCZWg0hoR3XL3B7NjcrlrQinB1KbyTNZccKR0F4Lk9fDgwTVkrAg152UqPyzXxpdzXjfkDkSEgAevXQwVJWBNf18bMIEgdH2usF/XauQoyrne7rcMIWBISPgtBPj3mhcrwscjGVsxqJva8KCVCKD/4Axmo9DISib5/7A6uczJxQG2Bcrdj++vQqK2succ=
{
"controller_id":"example_controller_id",
"expected_completion_time":"2018-11-01T15:00:01Z",
"subject_request_id":"a7551968-d5d6-44b2-9831-815ac9017798",
"request_status":"pending",
"api_version":"0.1"
}
状态回传
如上GDPR请求流程所述,当GDPR请求状态改变时,从待定到进行中到完成,AppsFlyer会以status_callback_urls形式给GDPR请求端点回传状态信息。
状态回传示例:
POST /opengdpr_callbacks HTTP/1.1
Host: examplecontroller.com
Content-Type: application/json
X-OpenGDPR-Processor Domain: appsflyer.com
X-OpenGDPR-Signature: kiGlog3PdQx+FQmB8wYwFC1fekbJG7Dm9WdqgmXc9uKkFRSM4uPzylLi7j083461xLZ+mUloo3tpsmyIZpt5eMfgo7ejXPh6lqB4ZgCnN6+1b6Q3NoNcn/+11UOrvmDj772wvg6uIAFzsSVSjMQxRs8LAmHqFO4cF2pbuoPuK2diHOixxLj6+t97q0nZM7u3wmgkwF9EHIo3C6G1SI04/odvyY/VdMZgj3H1fLnz+X5rc42/wU4974u3iBrKgUnv0fcB4YB+L6Q3GsMbmYzuAbe0HpVA17ud/bVoyQZAkrW2yoSy1x4Ts6XKba6pLifIHf446Bubsf5r7x1kg6Eo7B8zur666NyWOYrglkOzU4IYO8ifJFRZZXazOgk7ggn9obEd78GBc3kjKKZdwaCrLx7WV5y9TMDCf+2FILOJM/MwTUy1dLZiaFHhGdzld2AjbjK1CfVzyPssch0iQYYtbR49GhumvkYl11S4oDfu0c3t/xUCZWg0hoR3XL3B7NjcrlrQinB1KbyTNZccKR0F4Lk9fDgwTVkrAg152UqPyzXxpdzXjfkDkSEgAevXQwVJWBNf18bMIEgdH2usF/XauQoyrne7rcMIWBISPgtBPj3mhcrwscjGVsxqJva8KCVCKD/4Axmo9DISib5/7A6uczJxQG2Bcrdj++vQqK2succ=
{
"controller_id":"example controller id at the processor",
"expected_completion_time":"2018-11-01T15:00:01Z",
"status_callback_url":"https://examplecontroller.com/opengdpr_callbacks",
"Subject_request_id":"a7551968-d5d6-44b2-9831-815ac9017798",
"Request_status":"pending"
}
3. 报告请求
一旦访问请求或数据转移请求完成后, 您就可以通过 HTTP GET 将报告下载到以下端点:
https://hq1.appsflyer.com/gdpr/download/[REQUEST_ID]?api_token=[TOKEN]
生成的报告自完成之日起七天内有效。
4. 发现请求
若要了解 AppsFlyer 支持的格式, 可以通过 HTTP GET 将发现请求提交到以下端点:
https://hq1.appsflyer.com/gdpr/discovery?api_token=[api token]
发现响应示例
HTTP/1.1 200 OK
Content-Type: application/json
{
"api_version": "0.1",
"supported_identities": [
{
"identity_type": "android_advertising_id",
"identity_format": "raw"
},
],
"supported_subject_request_types": [
"erasure", "access", "portability", "rectification"
],
"processor_certificate": "https://exampleprocessor.com/cert.pem"
}
5. 取消请求
可以根据subject_request_id
取消GDPR请求,但仅限待定阶段的请求。
为此,要把HTTP DELETE和subject_request_id
一起提交至:
https://hq1.appsflyer.com/gdpr/opengdpr_requests/[subject_request_id]?api_token=[api token]
取消响应
当收到 GDPR 取消请求时, AppsFlyer 将返回含有状态代码202及其他几个参数的 HTTP 响应。更多详细信息, 请查看 GDPR "取消响应属性" 一章。
一旦请求被取消, AppsFlyer 就会回传其状态为已取消。
6. GDPR 请求测试 API
该AppsFlyer API 是 AppsFlyer GDPR 请求 API 的测试 API。
工作原理
测试 API 的工作原理如下:
1. 一旦提出 GDPR 请求, 该请求将立即处于 "待定" 状态。出于测试目的, 状态每30秒变更一次。
2. 如果在 GDPR 请求中插入了状态回传的端点, 则会在请求之后立即发送第一个回传, 并间隔30秒再发送两次状态回传。
GDPR请求测试端点:
https://hq1.appsflyer.com/gdpr/stub?api_token=[api token]
状态请求测试端点:
https://hq1.appsflyer.com/gdpr/stub/[request id]?api_token=[api token]
发现请求测试端点:
https://hq1.appsflyer.com/gdpr/stub/discovery?api_token=[api token]
取消请求测试端点:
https://hq1.appsflyer.com/gdpr/stub/[request id]?api_token=[api token]
注意
- 必须在请求中插入有效的 API 证书。
- 在 "property _ id" 中的属性, App ID 必须属于帐户所有者 (根据 API 证书)。
请求日志
只有帐户所有者才可以在日志操作面板中查看所有提交的GDPR 请求。
对于已完成的访问和数据转移请求, 也可以从该控制面板中下载报表。
访问日志控制面板:
1. 进入主控制面板, 然后单击您的用户名。
2. 单击日志打开以下窗口:
Opt Out
所有数据对象都有权限制数据处理。当数据对象行使这一权利时,在任何情况下,数据控制人都必须停止处理数据对象的个人数据,包括将数据对象从直接营销活动中移除。
要关闭移动设备上的所有 SDK 追踪, 请使用 isStopTracking API。调用此 API 后, AppsFlyer 的 SDK 将停止工作,并不再向 AppsFlyer 的服务器发送任何信息。
开发人员代码
提供给开发人员的详细信息如下。
Android SDK v. 4.8.7及以上版本可用。
AppsFlyerLib.getInstance().stopTracking(true, context);
在任何情况下,可调用相同 API 以重新激活 SDK,但会传递 False。
警告
仅在您想完全忽略该用户时,使用该 API。使用该 API 会严重影响您的报告和归因。
iOS SDK v. 4.8.3及以上版本可用。
AppsFlyerTracker.shared().isStopTracking = true
[AppsFlyerTracker sharedTracker].isStopTracking = true;
在 SDK 初始化过程中,使用该 API 确认退出:
public void setDeviceTrackingDisabled(boolean isDisabled);
使用示例:
AppsFlyer.setDeviceTrackingDisabled(true);
可以通过再次调用 deviceTrackingDisabled
,并设置为 false,重新启动归因。
警告
剔除用户会严重影响您的归因信息。只在法律要求您不得收集用户信息的地区使用此选项。