一、前言
咱们上一年开源 AppleParty(苹果派) 用于批量运用内购产品的创立和更新的方案,具体的技术方案是运用 XML Feed 格局来处理。而今年苹果在 WWDC22 宣告,2022 年 11 月开始,不再答应运用 XML 方法上传元数据和内购产品。
苹果在 7 月公告 即将从 XML Feed 过渡到 App Store Connect API,而且一向邮件告诉开发者,截止 11月 9 日之前:
We noticed you recently used the XML feed to manage and deliver content to App Store Connect. As we wrote to you previously, as of November 9, 2022, you’ll need to use the App Store Connect REST API to manage in-app purchases, subscriptions, metadata, and app pricing. The XML feed no longer supports this content, but continues to support existing Game Center management functionality.
If you have any questions, contact us.
Apple Developer Relations
假如现在还运用 XML feed 上传,会收到以下告警:
ERROR ITMS-6036: "XML schemas software5.12 and earlier have been deprecated and uploads of app metadata, in-app purchases, and subscriptions are no longer supported through the XML feed. You can use the App Store Connect API instead.
Game Center will continue to be supported with XML schema software6.0." at Software/SoftwareMetadata
所以,XML feed 制止上传的内容:
- app metadata(app元数据,如截图、预览、描绘等)
- in-app purchases, and subscriptions(内购产品,包含订阅类型)
- app pricing(app定价)
而 Game Center 和上传 ipa 文件等方法,现在还能上传,现在来看,是因为 App Store Connect API 还不支撑!所以,期望明日 WWDC23 苹果能支撑上传 ipa 文件,这样就更加方便~
二、App Store Connect API
App Store Connect API 需求生成密钥才干调用运用,所以,咱们先来介绍一下密钥的生成,然后在以运用内购产品的创立和更新为例,展示 API 运用示例。
2.1 App Store Connect API 密钥生成
生成密钥 ID(kid)和 Issuer ID(iss)
要生成密钥,您必须在 App Store Connect 中具有办理员人物或帐户持有人人物。登录 App Store Connect 并完结以下步骤:
- 挑选 “用户和拜访”,然后挑选 “密钥” 子标签页。
- 在 “密钥类型” 下挑选 “App Store Connect API”。
- 单击 “生成 API 密钥”(假如之前创立过,则点击 “添加(+)” 按钮新增。)。
- 输入密钥的称号。该称号仅供您参阅,姓名不作为密钥的一部分。
- 单击 “生成”。
“用户和拜访” -> “密钥” -> “App Store Connect API” -> “生成 API 密钥”
注:拜访权限: 根据密钥运用场景,拜访的权限也不一样。要创立和办理 App 内购买项目,请确保您具有以下用户人物之一:
- 帐户持有人
- 办理
- App 办理(这个要求人物权限最低)
详细权限,可参阅文档 功用权限。
1、Issuer ID:仿制仿制内容 2、密钥 ID: 生成的密钥,有一列名为 “密钥 ID” 便是 kid 的值,鼠标移动到文字就会显现 仿制密钥 ID,点击按钮就能够仿制 kid 值。 3、API 密钥文件,下载 API 密钥 按钮(仅当您没有下载私钥时,才会显现下载链接。),此私钥只能一次性下载!。
注意:将您的私钥存放在安全的当地。不要共享密钥,不要将密钥存储在代码库房中,不要将密钥放在客户端代码中。假如您怀疑私钥被盗,请立即在 App Store Connect 中撤销密钥。有关详细信息,请参阅 撤销API密钥。
终究,生成以下参数和文件:
| 姓名 | 值示例 | 阐明 | 字段值阐明 |
|---|---|---|---|
| 密钥ID | GC8HS3SX37 | kid,Key ID,密钥ID | 您的私钥ID,值来自API 密钥页面。 |
| 密钥内容文件 | SubscriptionKey_GC8HS3SX37.p8 | 密钥文件(p8) | 用来拜访和运用App Store Connect API接口的服务。 |
| Issuer ID | 69a6de92-xxx-xxxx-xxxx-5bc37c11a4d1 | iss,Issuer ID,发行人 | 您的发卡组织ID,值来自 App Store Connect 的 API 密钥页面。 |
2.2 App Store Connect API 运用示例
这儿咱们运用 python3 创立 API 恳求示例,需求依赖 jwt 和 requests 库,所以需求在终端装置:
pip3 install jwt
pip3 install requests
怎样恳求 App Store Connect API ?苹果给出了一个示例:
curl -v -H 'Authorization: Bearer [signed token]'
"https://api.appstoreconnect.apple.com/v1/apps"
也便是用 JWT 生成的 token,放到 App Store Connect API 恳求链接的 header 部分,key 为 Authorization,value为 Bearer [signed token]。
接下来,咱们通过 Python 的 requests 来恳求 App Store Connect API。咱们也能够用其它的东西来模拟,比如在线东西或者 Postman 等。
import jwt
import time
import requests
def createASCToken(p8KeyPath, kid, iss):
try:
header = {
"alg": "ES256",
"typ": "JWT",
"kid": kid
}
payload = {
"iss": iss,
"aud": "appstoreconnect-v1",
"iat": int(time.time()),
"exp": int(round(time.time() + (20.0 * 60.0))) # 20 minutes timestamp
}
file = open(p8KeyPath)
key_data = file.read()
file.close()
token = jwt.encode(headers=header, payload=payload, key=key_data, algorithm="ES256")
return token
except Exception as e:
print(e)
return ""
# 密钥路径
p8 = "/Users/iHTCboy/Downloads/AppStoreConnectAPI/AuthKey_GC8HS3SX37.p8"
kid = "GC8HS3SX37"
iss = "69a6de92-xxx-xxxx-xxxx-5bc37c11a4d1"
# 生成恳求 token
token = createASCToken(p8, kid, iss)
接下来,以获取 app 列表为例,恳求也十分简略:
# 获取悉数 app
url = "https://api.appstoreconnect.apple.com/v1/apps"
header = {
"Authorization": f"Bearer {token}"
}
rs1 = requests.get(url, headers=header)
data = json.loads(rs1.text)
print(data)
回来内容示例:
{
"data" : [ {
"type" : "apps",
"id" : "123456737",
"attributes" : {
"name" : "AppleParty - 37手游 iOS 技术团队",
"bundleId" : "cn.com.37iOS.AppleParty",
"sku" : "2021.04.25",
"primaryLocale" : "zh-Hans",
"isOrEverWasMadeForKids" : false,
"subscriptionStatusUrl" : null,
"subscriptionStatusUrlVersion" : null,
"subscriptionStatusUrlForSandbox" : null,
"subscriptionStatusUrlVersionForSandbox" : null,
"availableInNewTerritories" : true,
"contentRightsDeclaration" : null
},
"relationships" : {
xxxx
}
}],
"links" : {
"self" : "https://api.appstoreconnect.apple.com/v1/apps"
},
"meta" : {
"paging" : {
"total" : 1,
"limit" : 50
}
}
}
2.3 App Store Connect API 运用阐明
App Store Connect API 能够根据官方文档就能大概了解,但是仍然十分难,便是 POST 接口的 body 和上传文件的流程。
POST body
以 Create an In-App Purchase 为例,恳求的 body:
{
'data': {
'attributes': {
'availableInAllTerritories': True,
'familySharable': False,
'inAppPurchaseType': 'NON_CONSUMABLE',
'name': '我是测验产品01',
'productId': 'com.apple.iap01',
'reviewNote': '审阅补白',
},
'relationships': {
'app': {
'data': {
'id': "{app_id}",
'type': 'apps'
}
}
},
'type': 'inAppPurchases'
}
}
其间 inAppPurchaseType 可能为:
- CONSUMABLE
- NON_CONSUMABLE
- NON_RENEWING_SUBSCRIPTION
而订阅类型的产品,是另一个 API Create an Auto-Renewable Subscription,对应的恳求的 body:
{
"data": {
"type": "subscriptions",
"attributes": {
"name": "一个月订阅会员",
"productId": "com.apple.mon01",
"subscriptionPeriod": "ONE_MONTH",
"familySharable": False,
"reviewNote": "审阅补白",
"groupLevel": 1,
"availableInAllTerritories": True
},
"relationships": {
"group": {
"data": {
"type": "subscriptionGroups",
"id": "{app_iap_grop_id}"
}
}
}
}
}
其间 subscriptionPeriod 能够为:
- ONE_WEEK
- ONE_MONTH
- TWO_MONTHS
- THREE_MONTHS
- SIX_MONTHS
- ONE_YEAR
上传文件
上传文件的流程,刚开始看文档没有看明白,最终又仔细查文档才找到 Uploading Assets to App Store Connect,以上传运用内购买的送审图片为例,Create an In-App Purchase Review Screenshot,需求对应的恳求的 body:
{
'data': {
'attributes': {
'fileName': 'test.png',
'fileSize': '1000',
},
'relationships': {
'inAppPurchaseV2': {
'data': {
'id': '{app_iap_id}',
'type': 'inAppPurchases'
}
}
},
'type': 'inAppPurchaseAppStoreReviewScreenshots'
}
}
恳求成功后,Response Code 为 201 时:
{
"data" : {
"type" : "inAppPurchaseAppStoreReviewScreenshots",
"id" : "caeda501-xxxx-xxxx-8fb3-6a3c0f462720",
"attributes" : {
"fileSize" : 1000,
"fileName" : "test.png",
"sourceFileChecksum" : "",
"imageAsset" : {
"templateUrl" : "",
"width" : 0,
"height" : 0
},
"assetToken" : "",
"assetType" : "SCREENSHOT",
"uploadOperations" : [ {
"method" : "PUT",
"url" : "https://store-032.blobstore.apple.com/itmspod11-assets-massilia-032001/PurpleSource112%2Fv4%2F2c%2F3f%2Fe1%2F2c3fe12e-a9ea-xxx-xxx-12a8c02df932%2FieKZRQnL0o2fK4sbeFRXOQ8tVRjPIVyJaGCNLsLg2Dc_U003d-1669087039587?uploadId=2c75a0f0-6a14-11ed-93d1-d8c4978a0739&Signature=OWuT65nZNeMgWMNbaZtEGc9lcDU%3D&AWSAccessKeyId=MKIA474WIEZZVU5QMKHI&partNumber=1&Expires=1669691839",
"length" : 1000,
"offset" : 0,
"requestHeaders" : [ {
"name" : "Content-Type",
"value" : "application/octet-stream"
} ]
} ],
"assetDeliveryState" : {
"errors" : null,
"warnings" : null,
"state" : "AWAITING_UPLOAD"
}
},
"links" : {
"self" : ""
}
},
"links" : {
"self" : "
}
}
回来的呼应内容 uploadOperations 中的 url 便是上传图片文件的恳求 url,对应的 requestHeaders 也是拼装 request 必备的 headers 属性,图片文件的大小要与 length 长度共同。
2.4 App Store Connect Swift SDK
从上文就能够看出来,假如自己悉数的 API 都实现一次,工作时是十分大,所以咱们十分感谢 AvdLee/appstoreconnect-swift-sdk,运用 Xcode 的 Swift Package Manager 导入 https://github.com/AvdLee/appstoreconnect-swift-sdk.git 就能够运用!
以创立内购产品为例:
func createInAppPurchases(appId: String, product: IAPProduct) async -> ASCInAppPurchaseV2? {
let body = [
"data": [
"attributes": [
"availableInAllTerritories": product.availableInAllTerritories,
"familySharable": product.familySharable,
// CONSUMABLE、NON_CONSUMABLE、NON_RENEWING_SUBSCRIPTION
"inAppPurchaseType": product.inAppPurchaseType.rawValue,
"name": product.name,
"productId": product.productId,
"reviewNote": product.reviewNote,
],
"relationships": [
"app": [
"data": [
"id": appId,
"type": "apps"
]
]
],
"type": "inAppPurchases"
]
]
do {
guard let provider = provider else {
return nil
}
let json = try JSONSerialization.data(withJSONObject: body, options: .prettyPrinted)
let model = try JSONDecoder().decode(InAppPurchaseV2CreateRequest.self, from: json)
let request = APIEndpoint.v2.inAppPurchases.post(model)
let data = try await provider.request(request).data
return data
} catch APIProvider.Error.requestFailure(let statusCode, let errorResponse, _) {
handleRequestFailure(statusCode, errorResponse)
} catch {
handleError("创立内购产品失利: \(error.localizedDescription)")
}
return nil
}
这儿就不再打开,详细能够参阅咱们开源项目代码:AppStoreConnectAPI.swift。
3、Apple Party(苹果派)更新
下载 2.1.0 更新版别:Releases 37iOS/AppleParty
更新重点内容
- 截图不再是必需项
- 支撑多种本地化言语
表格格局更新,删除无法字段,支撑多种本地化言语:
支撑多种本地化言语,通过在表格最终的列添加,本地化言语标识,每种言语添加2列,分别对应本地化的姓名和描绘。
内购列表更新支撑不同的价格国家区域的价格显现:
四、总结
App Store Connect API 功用十分多,包含元数据的办理,构建版别的办理、TextFlight 办理、证书办理等等,Apple Party(苹果派)从日常运用场景最多的内购产品批量创立入手,未来仍然有十分多的生效能功率提高,欢迎咱们一同迭代和 PR 提交!
欢迎你一同体会和参阅 37iOS/AppleParty~
欢迎咱们评论区一同评论交流~
欢迎重视咱们,了解更多 iOS 和 Apple 的动态~
参阅引证
- 即将从 XML Feed 过渡到 App Store Connect API – 最新动态 – Apple Developer
- 功用权限 – App Store Connect 协助
- Revoking API Keys | Apple Developer Documentation
- Create an In-App Purchase | Apple Developer Documentation
- Create an Auto-Renewable Subscription | Apple Developer Documentation
- Uploading Assets to App Store Connect | Apple Developer Documentation
- Create an In-App Purchase Review Screenshot | Apple Developer Documentation
- AvdLee/appstoreconnect-swift-sdk: The Swift SDK to work with the App Store Connect API from Apple.
- Releases 37iOS/AppleParty









