2024.1.25 推荐阅读本人新文章《如何高效学习苹果App Store Connect API用法》,自力更生!
背景
WWDC 2018 苹果把名字iTunes Connect修改为App Store Connect,同时推出了App Store Connect API,用于自动化一些App Store Connect后台操作:
- 管理证书、管理配置文件、管理设备和安装包ID
- 管理用户、设置用户角色以及app的访问权限
- 管理TestFlight、公测用户以及公开链接、查看app的build二进制包信息
- 下载财务和销售报告
最近后台同事问我,能否通过接口从苹果后台获取销售记录(下载量),方便分析和统计。由于网络上关于App Store Connect API的资料甚少,借此机会我自行研究了一下,并用写下这篇博客,算是给“App Store Connect API”关键字贡献一份力量吧(捂脸)。
本文主要介绍App Store Connect API,关于如何自动获取苹果后台的销售报告,见下一篇文章《如何自动化获取AppStore的销售和趋势报告》。
Spaceship VS App Store Connect API
在介绍App Store Connect API之前我想说一下Fastlane,感觉Fastlane比App Store Connect API出名多了,关注的人也更多。
Fastlane是一套用Ruby开发的,用于解决iOS和Android自动化编译打包、上传、发布等的解决方案。
Spaceship是Fastlane的一个子工具,提供了一套Apple Developer Center 和 App Store Connect 的API。
Spaceship非常强大,所有你在浏览器上对Apple Developer Center 和 App Store Connect的操作几乎都可以通过Spaceship命令解决,所有App Store Connect API能实现的功能,Spaceship几乎都能实现,有些App Store Connect API没有的功能,Spaceship也能实现。
Spaceship的原理:通过模拟网页操作,分析和抓取网页的接口和数据来实现相应功能。可以参见Technical Details。
Spaceship这么强大,直接用Spaceship不就行了么。Spaceship当然也有它的缺点。
Spaceship
优点
- 功能强大。几乎所有你在浏览器上对Apple Developer Center 和 App Store Connect的操作都可以通过Spaceship命令解决。
- cookie有效期比较长。上面讲了Spaceship的原理是模拟网页请求,所以得先登录(账号、密码),登录后cookie保留在本地,有效期一个月,cookie失效前请求API不需要在登录。
缺点
- 需要登录两次。Apple Developer Center 和 App Store Connect 需要分别登录,一方生成的cookie对另一方可能不管用。
- 双重认证。如果你的开发者账号开了双重认证,那登录的时候还需要验证码。验证码这个东西使整体可用性大大降低。
- 对Window系统支持不友好。Spaceship是Fastlane的子工具,是Ruby开发的,需要Unix(Mac)环境。有人说Window上也可以跑Ruby。大家可以自行尝试。
- 非官方API。因为是非官方API,所以后续如果苹果有改动,可能就用不了了,得Spaceship作者及时更新才行。
App Store Connect API
优点
- 不需要登录。App Store Connect API是利用私钥生成token来访问API的。不需要提供开发者账号密码,相对也更安全。
- REST API。App Store Connect API是REST API,这决定了其调用更方便,不受操作系统等限制。
- 官方API。维护有保障,即使后续苹果调整了网页内容,其相应API也不会受影响。
缺点
- 功能受限。App Store Connect官方文档列出来的API不多,其实还有很多操作苹果都没有提供相应的API,这使得可用性降低,只能期盼后期苹果能多开放一些API。
- token有效期较短。token有效期只有20分钟,过期后得重新生成token。
生成 App Store Connect API token
App Store Connect API每个请求都需要在请求头中携带token,要想使用App Store Connect API,首先得知道如何生成token。
token是通过App Store Connect后台生成的密钥签名JWT(下文介绍)产生的。
生成密钥(手动操作,只需做一次)
1.请求App Store Connect API访问权限。
App Store Connect后台,“用户和访问” - “密钥”,点击“请求访问权限”。只有agent才有权限。
2.生成密钥。
申请访问权限后,才会看到“生成密钥”的按钮,点击“生成密钥”,根据提示起个名字,完成后会产生一个“Issuer ID”和“密钥ID”,这两个参数后面生成token需要用到。下载密钥,密钥是一个.p8的文件,注意:私钥只能下载一次,永远不会过期,保管好,如果丢失了,去App Store Connect后台撤销密钥,否则别人拿到也可以用。
生成token(用代码生成,有效期20分钟,失效后需要再次生成)
App Store Connect API使用的token采用的是JSON Web Token(JWT)标准,苹果文档介绍了创建JWT并用密钥签名生成token的过程。
不过这里大家可以直接去JWT.io下载各种语言的开源库来生成token。
下面是WWDC上苹果贴的Ruby代码,我稍作了修改。执行下方Ruby代码即可生成一条token(自行替换 ISSUER_ID、KEY_ID、P8_PATH这三个参数,下面代码中贴的是苹果文档上的值)
#生成 App Store Connect API用的token
require "base64"
require "jwt"
ISSUER_ID = "57246542-96fe-1a63-e053-0824d011072a" #ISSUER_ID(App Store Connect后台获取)
KEY_ID = "2X9R4HXF34" #密钥ID(App Store Connect后台获取)
P8_PATH= "/Users/Documents/test_AuthKey_22xxxxxxxx.p8" #密钥文件路径(本地PC文件路径)
private_key = OpenSSL::PKey.read(File.read(P8_PATH))
token = JWT.encode(
{
iss: ISSUER_ID,
exp: Time.now.to_i + 20 * 60, #最多20分钟,设置多了生成的token是无效的
aud: "appstoreconnect-v1"
},
private_key,
"ES256",
header_fields={
kid: KEY_ID }
)
puts token
使用token
调用App Store Connect API时添加下列请求头
'Authorization: Bearer [signed token]'
"Bearer"是固定写法,把[signed token]换成你的token,注意,Bearer和token中间有空格
下面是个例子
curl -v -H 'Authorization: Bearer [signed token]' "https://api.appstoreconnect.apple.com/v1/apps"
token有效期是20分钟,token过期后API会报错,说授权无效。需要重新生成一个新的token使用。
我尝试在JWT配置中让token有效期超过20分钟,但是失败了,这样虽然可以生成token,但用token发起请求会报错,说token无效,看来token最多只能20分钟。
App Store Connect API 部分API介绍
下面介绍部分API功能,大家可自行查看App Store Connect API官方文档查看其它功能。(这里想吐槽一下苹果的文档,真的简洁的不能在简洁了,数据结构层次也不够直观,请求参数、返回响应一个示例都没有给,各个参数代表什么意思也不说明一下,完全靠猜靠试,用户体验很不好)。下面我列出了注意事项,是我对照官方文档反复尝试后的总结,相信对你阅读苹果文档会有帮助。
注意事项:
- 创建接口,请求参数中的id字段,并不是平时我们用到的uuid,bundle identifier等字段,而是查询和创建接口返回的“隐式”id字段,注意区分。
- 查询接口,可选参数fields,表示查询结果展示哪些字段,类似sql语句的查询字段,默认是全部。
- 查询接口,可选参数filter,表示查询某个特定结果,类似sql语句的where,注意,这是模糊查询,效果是contain,而不是match,所以可能查到多条。(怎么精确查找,我也不知道,如果你知道请告知我)
- 查询接口,可选参数include,表示是否关联某个字段详情到响应体中,默认不包含。比如查询profile列表,并不会返回bundle ID,certificates,devices的信息,(连id都不会返回,只会返回links)。请求参数加上 include=bundleId,certificates,devices 后就会返回这些数据了,加上后会新增included字段返回关联字段的详细信息。
- 下载文件,部分查询接口会返回文件内容,比如certificates、profile等,是以base64字符串形式返回的。如有需要可自行解码成文件。参考命令
echo “MIIFnDCC..BISgAwIBAgII” | base64 -D > a.cer
假设下面介绍的请求默认都带了token请求头的。
Device
获取设备信息
可选参数:
fields[devices]:筛选显示的数据,addedDate, deviceClass, model, name, platform, status, udid
filter[id]
filter[name]
filter[platform]:IOS, MAC_OS
filter[status]:ENABLED, DISABLED
filter[udid]:
limit:integer,最大200
sort:id, -id, name, -name, platform, -platform, status, -status, udid, -udid
获取设备列表
GET https://api.appstoreconnect.apple.com/v1/devices
获取某个设备信息(列表API追加id路径)
GET https://api.appstoreconnect.apple.com/v1/devices/539US4VFVK
获取设备列表 返回数据
{
"data": [{
"type": "devices",
"id": "539US4XXXX",
"attributes": {
"addedDate": "2019-07-02T13:11:10.000+0000",
"name": "random1",
"deviceClass": "IPHONE",
"model": "iPhone 6s",
"udid": "e05a144e8ef31c50f12xxxxxxxxxxx",
"platform": "IOS",
"status": "ENABLED"
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/devices/539US4XXXX"
}
}, {
"type": "devices",
"id": "W9W337XXXX",
"attributes": {
"addedDate": "2019-11-08T10:46:34.000+0000",
"name": "test_se",
"deviceClass": "IPHONE",
"model": null,
"udid": "f51f5b7c677565cfa629xxxxxxxxxxxxx",
"platform": "IOS",
"status": "ENABLED"
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/devices/W9W337XXXX"
}
}],
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/devices"
},
"meta": {
"paging": {
"total": 2,
"limit": 20
}
}
}
注册一个设备
添加请求头 'Content-Type: application/json'
POST https://api.appstoreconnect.apple.com/v1/devices
必传参数:(其它没列出来的参数表示写法固定,参考请求示例)
platform:IOS,MAC_OS
name:取个名字
udid:设备标识
Status Code: 201 Created表示成功。
请求示例
{
"data": {
"attributes": {
"name": "test_se",
"udid": "f51f5b7c677565cfa629xxxxxxxxx",
"platform": "IOS"
},
"type": "devices"
}
}
返回数据
{
"data": {
"type": "devices",
"id": "W9W337XXXX",
"attributes": {
"addedDate": "2019-11-08T10:46:34.781+0000",
"name": "test_se",
"deviceClass": "IPHONE",
"model": null,
"udid": "f51f5b7c677565cfa629xxxxxxxxx",
"platform": "IOS",
"status": "ENABLED"
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/devices/W9W337XXXX"
}
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/devices"
}
}
删除一个设备
删除设备只能在开发者网站操作,没有提供API。(这句话是苹果文档上写的)
Bundle IDs
创建bundle id
添加请求头 'Content-Type: application/json'
POST https://api.appstoreconnect.apple.com/v1/bundleIds
必传参数:(其它没列出来的参数表示写法固定,参考请求示例)
name:取个名字
identifier:bundle identifier
seedId:Team ID
platform:IOS,MAC_OS
Status Code: 201 Created,表示成功
请求示例
{
"data": {
"attributes": {
"name": "bundlieAPITest",
"identifier": "cosw.bundlieAPI_test",
"seedId": "U8FBXXXXXX",
"platform": "IOS"
},
"type": "bundleIds"
}
}
返回数据
{
"data": {
"type": "bundleIds",
"id": "Q9XMDVCXXX",
"attributes": {
"name": "bundlieAPITest",
"identifier": "cosw.bundlieAPI_test",
"platform": "IOS",
"seedId": "U8FBXXXXXX"
},
"relationships": {...省略},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/bundleIds/Q9XMDVCXXX"
}
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/bundleIds"
}
}
获取bundle id列表
可选参数:
fields[bundleIds]:attributes字段筛选显示结果,bundleIdCapabilities, identifier, name, platform, profiles, seedId
fields[profiles]:profiles字段筛选显示结果,bundleId, certificates, createdDate, devices, expirationDate, name, platform, profileContent, profileState, profileType, uuid
filter[id]:
filter[identifier]:
filter[name]:
filter[platform]:IOS, MAC_OS
filter[seedId]:
include:bundleIdCapabilities, profiles
limit:integer,最大200
limit[profiles]:integer,最大50
sort:id, -id, name, -name, platform, -platform, seedId, -seedId
fields[bundleIdCapabilities]:bundleId, capabilityType, settings
获取bundle id列表
GET https://api.appstoreconnect.apple.com/v1/bundleIds
获取bundle id列表(精简版,去掉了很多不需要的数据)
GET https://api.appstoreconnect.apple.com/v1/bundleIds?fields[bundleIds]=name,identifier,platform
查找identifier为com.tencent.xin的bundle id(注意这里是模糊查找,比如com.tencent.xin、com.tencent.xin1004都会被列出来)
GET https://api.appstoreconnect.apple.com/v1/bundleIds?fields[bundleIds]=name,identifier,platform&filter[identifier]=com.tencent.xin
获取bundle id列表(精简版) 返回数据
{
"data": [
{
"type": "bundleIds",
"id": "2795XXXXX",
"attributes": {
"name": "XC com.tencent.xin1004",
"identifier": "com.tencent.xin1004",
"platform": "IOS"
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/bundleIds/2795XXXXX"
}
},
{
"type": "bundleIds",
"id": "39VMXXXXX",
"attributes": {
"name": "wechat",
"identifier": "com.tencent.xin",
"platform": "IOS"
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/bundleIds/39VMXXXXX"
}
}
],
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/bundleIds?fields%5BbundleIds%5D=name%2Cidentifier%2Cplatform"
},
"meta": {
"paging": {
"total": 4,
"limit": 20
}
}
}
删除一个 bundle id
DELETE https://api.appstoreconnect.apple.com/v1/bundleIds/{id}
例如
https://api.appstoreconnect.apple.com/v1/bundleIds/2795XXXXX
成功,Status Codes:204 No Content
注意,删除DELETE,而不是POST(后面所有的删除API都符合这一条)
证书
创建证书
添加请求头 Content-Type: application/json
POST https://api.appstoreconnect.apple.com/v1/certificates
必传参数:(其它没列出来的参数表示写法固定,参考请求示例)
csrContent:CSR文件的内容。创建好.csr文件(你可以用 钥匙串-证书助理 来生成CSR文件,或者用代码生成)后,使用命令 cat a.csr
,打印出.csr的内容,作为参数赋值。注意:打印出来的内容带有换行符,得把换行符去掉后在赋值,否则会报错。
certificateType:证书类型,IOS_DEVELOPMENT,IOS_DISTRIBUTION,MAC_APP_DISTRIBUTION,MAC_INSTALLER_DISTRIBUTION,MAC_APP_DEVELOPMENT,DEVELOPER_ID_KEXT,DEVELOPER_ID_APPLICATION
成功,Status Code: 201 Created
请求参数
{
"data": {
"attributes": {
"csrContent": "SDADAD12...3dSD123",
"certificateType": "IOS_DEVELOPMENT"
},
"type": "certificates"
}
}
下图是用cat命令打印 .csr 文件后的效果,取出BEGIN和END中间那一段,再去掉每行末尾的换行符就是csrContent参数值。在线过滤换行符
我写了段代码,传入.csr文件路径,可以输出处理后的csrContent值,大家可以参考。
#从.csr文件取csrContent值(取第二行到倒数第二行的内容,再去掉换行符)
csrFile='/Users/Cocoakier/Movies/CertificateSigningRequest.certSigningRequest'
line=$[`cat $csrFile | wc -l`-1]
cat $csrFile | sed -n "2,$line""p" | awk 'BEGIN{ORS=""}{print $0}'
获取证书信息(下载证书)
可选参数:
fields[certificates]:certificateContent(证书文件), certificateType, csrContent, displayName, expirationDate, name, platform, serialNumber
filter[id]:
filter[serialNumber]:
limit,最大200
sort:certificateType, -certificateType, displayName, -displayName, id, -id, serialNumber, -serialNumber
filter[certificateType]:IOS_DEVELOPMENT, IOS_DISTRIBUTION, MAC_APP_DISTRIBUTION, MAC_INSTALLER_DISTRIBUTION, MAC_APP_DEVELOPMENT, DEVELOPER_ID_KEXT, DEVELOPER_ID_APPLICATION
filter[displayName]:
获取证书列表
GET https://api.appstoreconnect.apple.com/v1/certificates
返回数据
{
"data": [{
"type": "certificates",
"id": "5DW7PXXXX",
"attributes": {
"serialNumber": "1D0160EEXXXXXX",
"certificateContent": "MIIFnDCCBISgAwIBAgIIHQFg7.....4Y2CP+fCYEpTEjXlQtJbOGjaXgwjAIdPhu",
"displayName": "xiao ming",
"name": "iOS Development: xiao ming",
"csrContent": null,
"platform": "IOS",
"expirationDate": "2020-03-28T11:43:19.000+0000",
"certificateType": "IOS_DEVELOPMENT"
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/certificates/5DW7PXXXX"
}
}, {
"type": "certificates",
"id": "D89QXXXXX",
"attributes": {
"serialNumber": "39E252BXXXXXX",
"certificateContent": "MIIFnzCCBIegAwIBAgIIOeJ.....o5zB1YPJtvfX49H2n",
"displayName": "xiao ming",
"name": "iOS Distribution: xiao ming",
"csrContent": null,
"platform": "IOS",
"expirationDate": "2020-04-22T11:56:35.000+0000",
"certificateType": "IOS_DISTRIBUTION"
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/certificates/D89QXXXXX"
}
}],
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/certificates"
},
"meta": {
"paging": {
"total": 2,
"limit": 20
}
}
}
下载证书
上面接口返回的字段中,certificateContent就是证书文件,苹果是以base64的方式通过接口返回的。我们base64解码一下就可以还原成.cer的文件了。
certificateContent="MIIFnDCCBISgAwIB...uG4Y2CP+fCYEpTEjXlQtJbOGjaXgwjAIdPhu"
echo $certificateContent | base64 -D > '/Users/Cocoakier/Movies/a.cer'
删除证书
DELETE https://api.appstoreconnect.apple.com/v1/certificates/{id}
例如
DELETE https://api.appstoreconnect.apple.com/v1/certificates/5DW7PXXXX
成功,Status Codes:204 No Content
Provisioning
获取Provisioning信息(下载)
可选参数:
fields[profiles]:bundleId, certificates, createdDate, devices, expirationDate, name, platform, profileContent(provisioning文件), profileState, profileType, uuid
fields[certificates]:certificateContent, certificateType, csrContent, displayName, expirationDate, name, platform, serialNumber
fields[devices]:addedDate, deviceClass, model, name, platform, status, udid
fields[bundleIds]:bundleIdCapabilities, identifier, name, platform, profiles, seedId
filter[id]:
filter[name]:
filter[profileState]:ACTIVE, INVALID
filter[profileType]:IOS_APP_DEVELOPMENT, IOS_APP_STORE, IOS_APP_ADHOC, IOS_APP_INHOUSE, MAC_APP_DEVELOPMENT, MAC_APP_STORE, MAC_APP_DIRECT, TVOS_APP_DEVELOPMENT, TVOS_APP_STORE, TVOS_APP_ADHOC, TVOS_APP_INHOUSE
include:是否返回具体信息(默认不返回),bundleId, certificates, devices
limit:最大200
limit[certificates]:最大50
limit[devices]:最大50
sort:id, -id, name, -name, profileState, -profileState, profileType, -profileType
获取Provisioning列表 普通版(不包含bundleId、certificates、devices信息)
GET https://api.appstoreconnect.apple.com/v1/profiles
获取Provisioning列表 加强版(包含bundleId、certificates、devices信息)
GET https://api.appstoreconnect.apple.com/v1/profiles?include=bundleId,certificates,devices
获取某个Provisioning devices详情(列表API+profiles id+devices)
GET https://api.appstoreconnect.apple.com/v1/profiles/295XXXXX/devices
获取某个Provisioning certificates详情
GET https://api.appstoreconnect.apple.com/v1/profiles/295XXXXX/certificates
获取某个Provisioning bundleId详情
GET https://api.appstoreconnect.apple.com/v1/profiles/295XXXXX/bundleId
获取Provisioning列表 普通版(不包含bundleId、certificates、devices信息) 返回数据
{
"data": [
{
"type": "profiles",
"id": "295XXXXX",
"attributes": {
"profileState": "ACTIVE",
"createdDate": "2019-03-29T11:54:58.000+0000",
"profileType": "IOS_APP_DEVELOPMENT",
"name": "test_dev",
"profileContent": "MIId9AYJKoZIhvcNAQcC..省略N多字...Syr5TPmPe1DiY4w==",
"uuid": "1d2xxxxxx-f764-4c71-xxxx-1d77xxxxxxxx",
"platform": "IOS",
"expirationDate": "2020-03-28T11:54:58.000+0000"
},
"relationships": {...省略没用的信息}
}
]
}
获取Provisioning列表 加强版(包含bundleId、certificates、devices信息) 返回数据(数据太多,我删除了一些无用字段)
{
"data": [
{
"type": "profiles",
"id": "295XXXXX",
"attributes": {
"profileState": "ACTIVE",
"createdDate": "2019-03-29T11:54:58.000+0000",
"profileType": "IOS_APP_DEVELOPMENT",
"name": "test_dev",
"profileContent": "MIId9AYJKoZIhvcNAQcC..省略N多字...Syr5TPmPe1DiY4w==",
"uuid": "1d2xxxxxx-f764-4c71-xxxx-1d77xxxxxxxx",
"platform": "IOS",
"expirationDate": "2020-03-28T11:54:58.000+0000"
}
}
],
"included": [
{
"type": "certificates",
"id": "5DWXXXXXX",
"attributes": {
"serialNumber": "1D0160XXXXXXX",
"certificateContent": "MIIFnDCC...省略N多字...BIXgwjAIdPhu",
"displayName": "xiao ming",
"name": "iOS Development: xiao ming",
"csrContent": null,
"platform": "IOS",
"expirationDate": "2020-03-28T11:43:19.000+0000",
"certificateType": "IOS_DEVELOPMENT"
}
},
{
"type": "devices",
"id": "KD88XXXXXX",
"attributes": {
"addedDate": "2019-03-29T11:48:47.000+0000",
"name": "xiaoming_5s",
"deviceClass": "IPHONE",
"model": "iPhone 5s (Model A1457, A1518, A1528, A1530)",
"udid": "61262034b932dxxxxxxxxxxxxxxx",
"platform": "IOS",
"status": "ENABLED"
}
},
{
"type": "devices",
"id": "5CXUXXXXXX",
"attributes": {
"addedDate": "2019-03-29T11:49:04.000+0000",
"name": "xiaohong_iPhoneX",
"deviceClass": "IPHONE",
"model": null,
"udid": "f51f5b7c677565cxxxxxxxxxxxxxx",
"platform": "IOS",
"status": "ENABLED"
}
},
{
"type": "bundleIds",
"id": "7RZ4XXXXX",
"attributes": {
"name": "wechat",
"identifier": "com.tencent.xin",
"platform": "IOS",
"seedId": "9RDXXXXXX"
}
}
]
}
下载provisioning文件
上面接口返回的字段中,profileContent字段的值就是provisioning文件,和证书一样,苹果是以base64的方式通过接口返回的。我们base64解码一下就可以还原成.mobileprovision的文件了。
profileContent="MIId9AYJKoZIhvcNAQcC..省略..Syr5TPmPe1DiY4w=="
echo $profileContent | base64 -D > '/Users/Cocoakier/Movies/test.mobileprovision'
注意:如果profile文件无效(profileState=INVALID),接口返回数据中profileContent的值可能为null。
创建一个Provisioning
添加请求头 'Content-Type: application/json’
POST https://api.appstoreconnect.apple.com/v1/profiles
必传参数:(其它没列出来的参数表示写法固定,参考请求示例)
name:起个名字
profileType:IOS_APP_DEVELOPMENT, IOS_APP_STORE, IOS_APP_ADHOC, IOS_APP_INHOUSE, MAC_APP_DEVELOPMENT, MAC_APP_STORE, MAC_APP_DIRECT, TVOS_APP_DEVELOPMENT, TVOS_APP_STORE, TVOS_APP_ADHOC, TVOS_APP_INHOUSE
(bundleId)id:bundle id,是创建或查询接口返回的那个id不是bundle identider,注意区分!
(certificates)id:证书id,可以传多个,外层是数组
(devices)id:设备id(profileType=IOS_APP_STORE时不传),可以传多个,外层是数组。注意,这里的设备id不是平时用的udid,是创建或查询接口返回的那个id!
成功,Status Code: 201 Created
请求示例
{
"data": {
"attributes": {
"name": "test_se",
"profileType": "IOS_APP_DEVELOPMENT"
},
"type": "profiles",
"relationships": {
"bundleId": {
"data": {
"id": "TH9XXXX",
"type": "bundleIds"
}
},
"certificates": {
"data": [
{
"id": "5DWXXXXX",
"type": "certificates"
}
]
},
"devices": {
"data": [
{
"id": "7BUHXXXXX",
"type": "devices"
}
]
}
}
}
}
返回数据(去除了一些无用数据)
{
"data": {
"type": "profiles",
"id": "YFV93T5866",
"attributes": {
"profileState": "ACTIVE",
"createdDate": "2019-11-12T13:00:11.605+0000",
"profileType": "IOS_APP_DEVELOPMENT",
"name": "test_se",
"profileContent": “MIId9AYJKoZIhvcNAQcCoII..省略...e1DiY4w==",
"uuid": "7bbxxxxx-8c21-4xxx-9cc9-3485xxxxx",
"platform": "IOS",
"expirationDate": "2020-11-11T13:00:11.605+0000"
},
"relationships": {
"bundleId": {
"data": {
"type": "bundleIds",
"id": "TH9XXXX"
},
},
"certificates": {
"data": [{
"type": "certificates",
"id": "5DWXXXXX"
}],
},
"devices": {
"data": [{
"type": "devices",
"id": "7BUHXXXXX"
}],
}
},
},
}
删除provisioning**
DELETE https://api.appstoreconnect.apple.com/v1/profiles/{id}
例如
DELETE https://api.appstoreconnect.apple.com/v1/profiles/YFV93T5866
成功,Status Codes:204 No Content
User
获取开发者团队成员列表
GET https://api.appstoreconnect.apple.com/v1/users
可选参数:
fields[apps]:betaAppLocalizations, betaAppReviewDetail, betaGroups, betaLicenseAgreement, betaTesters, builds, bundleId, name, preReleaseVersions, primaryLocale, sku
fields[users]:allAppsVisible, firstName, lastName, provisioningAllowed, roles, username, visibleApps
include:visibleApps
limit:最大200
sort:lastName, -lastName, username, -username
filter[roles]:ADMIN, FINANCE, TECHNICAL, ACCOUNT_HOLDER, READ_ONLY, SALES, MARKETING, APP_MANAGER, DEVELOPER, ACCESS_TO_REPORTS, CUSTOMER_SUPPORT
filter[visibleApps]:
filter[username]:
limit[visibleApps]:最大50
返回数据
{
"data": [{
"type": "users",
"id": "73xxx-09ef-4f4a-af23-abb3xxxxxxx",
"attributes": {
"username": "xiaoming@qq.com",
"firstName": "xiao",
"lastName": "ming",
"roles": ["ADMIN", "ACCOUNT_HOLDER"],
"allAppsVisible": true,
"provisioningAllowed": true
},
"relationships": {
"visibleApps": {
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/users/73xxx-09ef-4f4a-af23-abb3xxxxxxx/relationships/visibleApps",
"related": "https://api.appstoreconnect.apple.com/v1/users/73xxx-09ef-4f4a-af23-abb3xxxxxxx/visibleApps"
}
}
},
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/users/73xxx-09ef-4f4a-af23-abb3xxxxxxx"
}
}],
"links": {
"self": "https://api.appstoreconnect.apple.com/v1/users"
},
"meta": {
"paging": {
"total": 1,
"limit": 50
}
}
}
TestFlight
获取APP列表
GET https://api.appstoreconnect.apple.com/v1/apps
可选参数:
fields[apps]:返回的app信息有限,就是后面这些,builds, bundleId, name, preReleaseVersions, primaryLocale, sku, betaAppLocalizations, betaAppReviewDetail, betaGroups, betaLicenseAgreement, betaTesters
include:betaAppLocalizations, betaAppReviewDetail, betaGroups, betaLicenseAgreement, builds, preReleaseVersions
其它可选参数,请自行查看文档
获取APP列表 返回数据(去除了一些无用字段)
{
"data": [{
"type": "apps",
"id": "1456000000",
"attributes": {
"name": "微信",
"bundleId": "com.tencent.xin",
"sku": "com.tencent.xin",
"primaryLocale": "zh-Hans"
}
}
获取某个app的preReleaseVersions信息(对应App Store Connect后台的version)
GET https://api.appstoreconnect.apple.com/v1/apps/1456000000/preReleaseVersions
获取某个app preReleaseVersions信息 返回数据(去除了一些无用数据)
{
"data": [{
"type": "preReleaseVersions",
"id": "316bxxx-5394-46d7-8e49-4axxxxx",
"attributes": {
"version": "1.0.2",
"platform": "IOS"
}
}, {
"type": "preReleaseVersions",
"id": "8a9xxxx-3620-4bbd-a540-9axxxxx",
"attributes": {
"version": "1.0.0",
"platform": "IOS"
},
}, {
"type": "preReleaseVersions",
"id": "c649xxxx-3229-4e20-8349-2efaxxxxx",
"attributes": {
"version": "1.0.1",
"platform": "IOS"
}
}],
"meta": {
"paging": {
"total": 3,
"limit": 50
}
}
}
获取某个app的builds信息(对应App Store Connect后台的二进制包)
GET https://api.appstoreconnect.apple.com/v1/apps/1456000000/builds
获取某个app build信息 返回数据(我去掉了一些无用数据)
{
"data": [
{
"type": "builds",
"id": "977xxx-c0d1-47a5-b439-7485xxxx",
"attributes": {
"version": "5",
"uploadedDate": "2019-07-05T01:49:16-07:00",
"expirationDate": "2019-10-03T01:49:16-07:00",
"expired": true,
"minOsVersion": "9.0",
"iconAssetToken": {
"templateUrl": "https://is2-ssl.mzstatic.com/image/thumb/Purple113/v4/41/24/46/41xxx-8fa3-142b-b3da-e1xxxxx/Icon-60@2x.png.png/{w}x{h}bb.{f}",
"width": 120,
"height": 120
},
"processingState": "VALID",
"usesNonExemptEncryption": false
},
},
{
"type": "builds",
"id": "de3d9xxx-d87b-449c-b025-a510xxxxx",
"attributes": {
"version": "6",
"uploadedDate": "2019-11-04T00:13:03-08:00",
"expirationDate": "2020-02-02T00:13:03-08:00",
"expired": false,
"minOsVersion": "9.0",
"iconAssetToken": {
"templateUrl": "https://is4-ssl.mzstatic.com/image/thumb/Purple123/v4/4b/f2/41/4bxxx-f059-fe0a-c220-39cxxx/Icon-60@2x.png.png/{w}x{h}bb.{f}",
"width": 120,
"height": 120
},
"processingState": "VALID",
"usesNonExemptEncryption": false
}
}
...
],
"meta": {
"paging": {
"total": 6,
"limit": 50
}
}
}
下载销售和趋势报告
总结
上述只列出了部分App Store Connnect API的功能,还有些功能,比如TestFlight、User、Bundle ID Capabilities等,没有列出来。通过这篇文章想必大家对苹果文档的“风格”也熟悉了,其它功能可以自行查阅官方文档。给大家推荐一个JSON在线编辑工具,对照苹果文档构造请求体很好用。
总的来说,App Store Connect API目前能做的事情很有限,主要还是用于自动化管理证书、设备、bundle identifier这些,一些重要的接口苹果仍然没有开放,比如创建App,编辑元数据,提交审核,查看审核结果等,我想苹果还是考虑到安全问题吧,现在马甲包本来就泛滥成灾,如果开放了这些接口,后果可想而知。对于Mac平台来说,Fastlane - Space 比 App Store Connect API 强大太多,App Store Connect API显得有些鸡肋了,但是对于Window平台来说,App Store Connect API还是不错的选择。
参考链接:
App Store Connect的新特性(WWDC 2018 session 301 & 303)
App Store Connect API苹果官方文档
JWT官网
Spaceship官网
fastlane-windows-cannot-installing
openssl req 生成证书请求和自建CA