概要:本文供AppsFlyer客户使用,通常称为广告主或应用所有者,使用该平台记录应用使用情况和归因。应用程序所有者实施 OpenDSR(数据主体请求)API,以遵守适用的数据保护法律,如 GDPR(欧洲)、CCPA(加利福尼亚)、LGPD(巴西)、PDPA(泰国)和 PIPA(韩国)。
来自我们律师的一句话: 本文所述的所有内容仅供参考,不构成法律意见。您应该与法律和其他专业顾问密切合作,以确定GDPR、CCPA或任何其他法律如何适用于您。 您与 AppsFlyer 之间的隐私关系受 AppsFlyer 服务隐私政策的管辖。如果您对我们的服务隐私政策有任何疑问,或需要联系我们的数据保护负责人,请发邮件至privacy@appsflyer.com。为了帮助您遵守《通用数据保护条例》(General Data Protection Regulation)第27条的规定,特此提供AppsFlyer欧盟境内代表人的联系信息:AppsFlyer Germany GmbH, Schönhauser Allee 180, 10119 Berlin, Germany(联系方式:privacy@appsflyer.com;+49-30-166373500)。
隐私监管政策
在本文中,对隐私法规的引用包括以下表格中列出的法规。
规章 | Logo | 说明 |
---|---|---|
GDPR | GDPR :《通用数据保护条例》是有关欧盟公民数据保护和隐私的欧盟法规 | |
CCPA | CCPA :加州消费者隐私法 | |
LGPD | Lei Geral de Proteção de Dados | |
PDPA | 个人数据保护法 |
在本文中,隐私法规、GDPR、CCPA、LGPD 和 PDPA 这几个术语是可以互换使用的。
开放数据服务资源(OpenDSR)倡议
为了解决和管理数据主体根据隐私法规提交的请求,AppsFlyer 与mParticle、Amplitude和Braze一起启动了OpenGDPR协议。
开放GDPR是一个统一的开源框架, 促进了技术公司之间的合作, 并公正透明地使用消费者数据。该框架使得供应商能够轻松地在多个处理和存储客户数据的系统中执行数据隐私操作。
了解更多关于OpenDSR倡议的信息。
定义
DSR术语 | AppsFlyer条款 | 说明 |
---|---|---|
数据对象 | 应用用户或终端用户 | 被收集数据的应用用户 |
数据控制人 | App 所有者/广告主 | 应用所有者绝定处理个人数据的目的和方式。 |
数据处理人 | AppsFlyer及其合作伙伴 | AppsFlyer及其合作伙伴代表数据控制人处理个人数据 |
DSR要求
GDPR详细说明了数据控制者必须给予数据主体的强制性权利。出于API目的,这些权利被转换为请求类型。以下详细说明AppsFlyer如何处理不同的请求类型。
请求类型 (对) |
GDPR定义 | AppsFlyer处理请求 |
---|---|---|
进入 |
|
应用程序所有者会收到应用程序用户处理过的个人数据的副本。 |
可移植性 | 数据对象需要以结构化、常用和机器可读的格式 (如 CSV 文件) 接收其所有个人数据。 | 应用程序所有者会收到应用程序用户处理过的个人数据的副本。 |
整改 | 允许应用程序用户在看到不正确或不真实的数据时更正其数据。应用程序所有者必须删除或更正不准确或不完整的数据。 |
|
清除 | 删除权要求应用所有者在收到请求后的10天内删除个人数据。 | 数据被删除 |
AppsFlyer的GDPR请求API
使用本节中描述的GDPR请求API来实现DSR合规性。
- GDPR请求 GDPR请求:执行以上请求类型之一:访问权限,可移植性,擦除,纠正。
- 2. 状态请求查询GDPR请求的当前状态。
- 4. 发现请求 查询支持的API版本和数据格式。
- 取消 在待处理阶段取消GDPR请求。
应用程序所有者必须对其应用程序实施用户界面更改,以便应用程序用户可以提交请求。请注意,GDPR请求是针对单个用户的。
1. GDPR请求
GDPR 请求流程
GDPR请求类型,访问权限,可移植性,擦除和纠正具有相同的流程:
- 应用程序用户提交请求。
- 应用程序所有者构造GDPR请求(请参见下文)并将其发送到AppsFlyer。
- AppsFlyer收到请求,并以“ 201 OK”响应有效请求。
-
为了访问和可移植性:请求立即得到满足。
对于删除和更正:- 在接下来的48小时内,请求将排队并且处于待处理状态。应用程序用户可以取消请求。
- 48小时后,请求状态变为in_progress。AppsFlyer发送状态更改回发。此时,请求无法取消。
-
在10天内(见下面的说明),AppsFlyer完成请求,请求状态更改为已完成。AppsFlyer发送状态更改回发。
- 在 擦除 或 校正的情况下,将删除应用程序用户的数据。
- 在的情况下, 可移植性或的访问, 中应用用户的数据可以在AppsFlyer仪表板下访问 GDPR ,或通过请求API(见下文)。
请注意:此10天期限从应用所有者提交请求时开始。48小时后,请求状态将变为进行中,并将在额外的8天内完成,总共10天。
GDPR 请求格式
GDPR 请求 API 可以通过 HTTP POST 提交到以下端点-
https://hq1.appsflyer.com/api/gdpr/v1/opendsr_requests
对于API授权,请使用与Pull API相同的V2 API令牌。管理员用户可以从仪表板中的API令牌页面检索令牌。将令牌添加到请求头中,如下所示:
参数
参数名称 | 必须配置 | 说明 |
---|---|---|
subject_request_id | 是 | UUID v4 字符串。由控制器在将请求提交给处理器时生成。之后,可以用它来查询请求状态、更新或取消请求。 |
subject_request_type | 是 | 表示 GDPR 请求类型的字符串值。支持的值:
|
subject_identities | 是 |
|
submitted_time | 是 |
|
property_id | 是 |
代表此请求范围的移动应用程序的字符串: 示例:
|
api_version | 否 | 表示所需GDPR 请求 API 版本的字符串版本 |
status_callback_urls | 不,但推荐 |
|
platform |
否 | 价值是支持的DSR平台之一: 可能出现的值为 android 、ios 、web 、windowsphone
|
是 |
对于CTV、PC或控制台平台请求,请包括以下任何平台: |
Subject_identities对象
对象类型 | 必须配置 | 说明 |
---|---|---|
identity_type | 是 |
|
identity_value | 是 |
|
identity_format | 是 |
|
举例来说,GDPR 删除请求
针对 , , , 平台的删除请求示例:
curl --location --request POST 'https://hq1.appsflyer.com/api/gdpr/v1/opendsr_requests' \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer %AuthTokenv2% \ --data-raw '{ "subject_request_id":"f4e5a271-f25e-4107-b681-************", "subject_request_type":"erasure", "submitted_time":"2020-07-05T10:00:00Z", "platform": "android", "subject_identities":[ { "identity_type":"android_advertising_id", "identity_value":"55*****-****-****-************", "identity_format":"raw" } ], "api_version":"0.1", "property_id":"com.*********.*******.********", "status_callback_urls":[ "https://examplecontroller.com/opengdpr_callbacks" ] }'
针对、和平台的删除请求示例:
curl --location --request POST 'https://hq1.appsflyer.com/api/gdpr/v1/opendsr_requests' \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer %AuthTokenv2% \ --data-raw ' {"requester": "test@email.com", "subject_request_id":"valid-uuid4-string", "subject_request_type":"erasure", "submitted_time":"2020-07-05T10:00:00Z", "subject_identities":[ { "identity_type":"appsflyer_id", "identity_value":"valid-appsflyer-id-string", "identity_format":"raw" } ], "api_version":"0.1", "property_id":"app-id", "platform": "roku", "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\": \"\"," + "\r\n\"subject_request_type\": \"erasure\"," + "\r\n\"platform\": \"android\"," + "\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\": \"\"," + "\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/api/gdpr/v1/opendsr_requests/opendsr_requests") .post(body) .addHeader("Content-Type", "application/json") .addHeader("Accept", "application/json") .addHeader("Authorization: Bearer", "") .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", "Authorization: Bearer": "" } body = { "subject_request_id": "", "subject_request_type": "erasure", "submitted_time": "2018-10-02T15:00:00Z", "platform": "android", "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/api/gdpr/v1/opendsr_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/api/gdpr/v1/opendsr_requests', headers: { Accept: 'application/json', 'Content-Type': 'application/json', 'Authorization: Bearer': '' }, body: { subject_request_id: '', subject_request_type: 'erasure', submitted_time: '2018-10-02T15:00:00Z', platform: 'android', subject_identities: [ { identity_type: 'android_advertising_id', identity_value: '', 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/api/gdpr/v1/opendsr_requests"; var client = new RestClient(url); var request = new RestRequest(Method.POST); request.AddHeader("Content-Type", "application/json"); request.AddHeader("Accept", "application/json"); request.AddHeader("Authorization: Bearer", "") var body = "{\r\n\"subject_request_id\": \"\"," + "\r\n \"subject_request_type\": \"erasure\"," + "\r\n \"submitted_time\": \"2018-11-02T15:00:00Z\"," + "\r\n \"platform\": \"android\"," + "\r\n \"subject_identities\": [\r\n" + "{ \r\n \"identity_type\": \"android_advertising_id\"," + "\r\n \"identity_value\": \"\"," + "\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. 2. 状态请求
每个已提交的GDPR请求都可以通过subject_request_id
subject_request_idsubject_request_id来查询其后续状态。支持的状态类型有四种:
- 待审核-已收到正确的请求, 并且正处于待审核队列中
- 正在进行中-正在被执行的请求
- 已完成-请求已完成
- 已取消-请求已被取消
状态请求格式
状态请求可以通过 HTTP GET 提交到以下端点:
https://hq1.appsflyer.com/api/gdpr/v1/opendsr_requests/<req_id>
状态响应示例
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", }
状态回传
如上GDPR请求流程所述,当GDPR请求状态改变时,从待定到进行中到完成,AppsFlyer会以status_callback_urls形式给GDPR请求端点回传状态信息。
状态回传示例:
POST /opengdpr_callbacks HTTP/1.1 Host: examplecontroller.com Content-Type: application/json X-OpenGDPR-Processor Domain: gdpr.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" }
验证回调
验证传入回调的真实性:
- 创建一个允许列表,列出您允许进行回调的所有处理器域。
-
如果头部值在您的允许列表中,请获取证书。
- 证书 URL 是响应体中 的值。
- 您还可以直接从 AppsFlyer 端点获取证书,使用 V2 API 令牌作为授权头中的 Bearer 令牌:
-
使用库验证证书以确认证书:
- 由可信的权威机构发布。
- 发放给在头部值中提供的相同字符串。
- 没有过期。
- 一旦您确认证书有效,请使用它来验证头部与原始请求体的匹配。AppsFlyer使用SHA256 RSA作为签名算法。
-
在状态头中获取响应:
- 如果验证成功。
- 如果签名验证失败或处理器域不在您的允许列表中。
3. 3. 报告请求
一旦访问请求或数据转移请求完成后, 您就可以通过 HTTP GET 将报告下载到以下端点:
https://hq1.appsflyer.com/api/gdpr/v1/download/[REQUEST_ID]
生成的报告自完成之日起七天内有效。
4. 发现请求-处理中
若要了解 AppsFlyer 支持的格式, 可以通过 HTTP GET 将发现请求提交到以下端点:
https://hq1.appsflyer.com/api/gdpr/v1/discovery
发现响应示例
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. 5. 取消请求
可以根据其状态取消GDPR请求,但仅在待处理阶段。
要提交一个 HTTP DELETE 请求:
https://hq1.appsflyer.com/api/gdpr/v1/opendsr_requests/<req_id>
取消响应
当收到 GDPR 取消请求时, AppsFlyer 将返回含有状态代码202及其他几个参数的 HTTP 响应。
一旦请求被取消, AppsFlyer 就会回传其状态为已取消。
6. 6. GDPR 请求测试 API
该AppsFlyer API 是 AppsFlyer GDPR 请求 API 的测试 API。
原理
测试 API 的工作原理如下:
1. 1. 一旦提出 GDPR 请求, 该请求将立即处于 "待定" 状态。出于测试目的, 状态每30秒变更一次。
2. 2. 如果在 GDPR 请求中插入了状态回传的端点, 则会在请求之后立即发送第一个回传, 并间隔30秒再发送两次状态回传。
GDPR请求测试端点:POST
https://hq1.appsflyer.com/api/gdpr/v1/stub
状态请求测试端点:GET
https://hq1.appsflyer.com/api/gdpr/v1/stub/:requestId
发现请求测试端点:GET
https://hq1.appsflyer.com/api/gdpr/v1/stub/discovery
取消请求测试端点:删除:
https://hq1.appsflyer.com/api/gdpr/v1/stub/:requestId
证书测试端点:GET
https://hq1.appsflyer.com/api/gdpr/v1/stubcertificate
访问/可移植性报告测试端点GET
https://hq1.appsflyer.com/api/gdpr/v1/stub/download/:requestId
注意
- 有效的 V2 API 令牌必须插入到请求头中,如下所示:
- 在 "property _ id" 中的属性, App ID 必须属于帐户所有者 (根据 API 证书)。
7.请求日志
管理员用户可以访问在日志仪表板中提交的GDPR请求。
对于已完成的访问和数据转移请求, 也可以从该控制面板中下载报表。
访问日志控制面板:
- 从页面顶部导航栏打开账户菜单(点击右上角的邮箱地址,打开下拉菜单)> 用户管理。
- 将打开以下窗口:
8.GDPR API 返回代码和错误信息
本节详细介绍了GDPR API的HTTP返回代码和错误消息。
GDPR API 返回代码
响应码 | 说明 |
---|---|
201 | 已创建 |
202 | 取消请求已收到 |
400 | Bad request(请求无效)该主体包含以下表格中列出的错误代码和消息。 |
HTTP返回代码400 - 错误请求
返回代码为400的消息包含一个带有错误代码和消息的JSON。
{ "error": { "code":400, "af_gdpr_code": "%AF error code%", "message":"%error message text%" } }
返回代码400错误请求消息
错误代码 | 错误描述(消息) |
---|---|
e111 | 速率限制超出 |
e211 | 无法取消状态无效的请求 |
e212 | 请求不被允许。标识符正在进行擦除。 |
e213 | 请求已存在 |
e214 | 请求未找到 |
e311 | 无效的请求内容类型 |
e312 | 无效的API版本 |
e313 | 无效的 subject_request_id |
e314 | 提交时间格式无效 |
e315 | 无效的 status_callback_url 长度 |
e316 | 无效的 status_callback_url 格式 |
e317 | 无效的 app_id 格式 |
e318 | 无效的身份类型 |
e319 | 应用程序平台与身份类型不匹配 |
e320 | 无效的身份类型 |
e321 | LAT用户不支持通过API |
e322 | 无效的主题请求类型 |
e323 | 无效的 subject_identities 格式 |
e324 | 无效的 subject_identities 长度 |
e325 | 无效的 subject_identities 值 |
e411 | AppID不正确或不属于您的帐户 |
e412 | 没有权限取消擦除请求 |
e413 | 没有权限查看请求 |
e511 | 内部问题,请等待60分钟后再试。如果问题仍然存在,请联系 AppsFlyer 支持。support@AppsFlyer.com |
特点与局限性
- OpenDSR API 速率限制:每分钟最多可发送350个请求超出此限制将导致错误,需要重新发送请求。在此速率限制内,每天最多允许504,000个请求。
- 检查来自60天前的请求状态返回“未找到请求”。