本文介绍: 我们去年开源AppleParty(苹果派)用于批量应用内购商品创建更新的方案,具体的技术方案是使用 XML Feed 格式处理。而今年苹果在 WWDC22 宣布,2022 年 11 月开始,不再允许使用 XML 方式上传数据内购商品苹果在 7 月公告即将从 XML Feed 过渡到 App Store Connect APIapp metadataapp数据,如截图、预览描述等)inapp purchases, and subscriptions内购商品,包括订阅类型

一、前言

我们去年开源 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 禁止上传内容

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 并完成以下步骤

  1. 选择用户访问”,然后选择密钥” 子标签页。
  2. 在 “密钥类型” 下选择 “App Store Connect API”。
  3. 单击 “生成 API 密钥”(如果之前创建过,则点击添加(+)” 按钮新增。)。
  4. 输入密钥的名称。该名称仅供您参考,名字不作为密钥的一部分。
  5. 单击 “生成”。

AppStoreConnectAPI-01.png

用户访问” -> “密钥” -> “App Store Connect API” -> “生成 API 密钥”

AppStoreConnectAPI-02.png

AppStoreConnectAPI-03.png

注:访问权限
根据密钥使用场景访问权限也不一样。要创建管理 App 内购买项目,请确保您拥有以下用户角色之一:

详细权限,可参考文档 职能权限

AppStoreConnectAPI-04.png

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-xxxxxxxxxxx-5bc37c11a4d1 iss,Issuer ID,发行人 您的发卡机构ID,值来自 App Store Connect 的 API 密钥页面。
2.2 App Store Connect API 使用示例

这里我们使用 python3 创建 API 请求示例需要依赖 jwtrequests 库,所以需要终端安装

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 部分,keyAuthorizationvalueBearer [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

更新重点内容

表格格式更新,删除无法字段支持多种本地语言

AppStoreConnectAPI-05.png

支持多种本地语言,通过在表格最后的列增加,本地语言标识,每种语言增加2列,分别对应本地化的名字和描述

内购列表更新支持不同的价格国家地区的价格显示

AppStoreConnectAPI-06.png

导入表格后,首次需要设置 API 密钥:
AppStoreConnectAPI-07.png

密钥获取,参考本文的第二章内容
AppStoreConnectAPI-08.png

提交后,会自动执行上传,如果存在的商品会更新内容,成功时:
AppStoreConnectAPI-09.png

四、总结

App Store Connect API 功能非常多,包括元数据管理构建版本管理、TextFlight 管理证书管理等等,Apple Party(苹果派)从日常使用场景最多的内购商品批量创建入手,未来依然有非常多的生效力效率提升,欢迎大家一起迭代和 PR 提交

欢迎你一起体验和参考 37iOS/AppleParty~

欢迎大家评论区一起讨论交流~

欢迎关注我们,了解更多 iOS 和 Apple 的动态~

参考引用

原文地址:https://blog.csdn.net/iOSTeam37/article/details/128490925

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_11141.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注