├── .gitignore
├── README.md
├── main.py
├── reqs
├── __init__.py
├── itunes.py
├── schemas
│ ├── README.md
│ ├── __init__.py
│ ├── itunes_lookup_resp.py
│ ├── schema_defs
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ ├── itunes_lookup_resp.json
│ │ ├── store_authenticate_req.json
│ │ ├── store_authenticate_resp.json
│ │ ├── store_buyproduct_req.json
│ │ ├── store_buyproduct_resp.json
│ │ ├── store_download_req.json
│ │ └── store_download_resp.json
│ ├── schema_examples
│ │ ├── itunes_lookup_resp.log
│ │ ├── store_buyproduct_req.log
│ │ └── store_buyproduct_resp.log
│ ├── store_authenticate_req.py
│ ├── store_authenticate_resp.py
│ ├── store_buyproduct_req.py
│ ├── store_buyproduct_resp.py
│ ├── store_download_req.py
│ └── store_download_resp.py
└── store.py
└── requirements.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | __pycache__
3 | venv/
4 | downloaded/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # IPATool-py
2 |
3 | Python version of IPATool!
4 |
5 | **Now Supports**:
6 | - **Purchasing App via `--purchase`**
7 | - **Downloading Old IPA via iTunes Server**
8 |
9 | ## Installation
10 |
11 | ```
12 | pip3 install -r requirements.txt
13 | ```
14 |
15 | ## Usage
16 |
17 | There're three commands: lookup, historyver, download. Each command's usage can be found by `-h` option.
18 |
19 | You can also chain multiple command, last command's output will be passed to next command (so you don't need to supply some arguments like appVerId)
20 |
21 | ### Download Newest Version
22 |
23 | Download an app using bundleID (-o can be omited)
24 | ```
25 | python3 main.py lookup -b com.touchingapp.potatsolite -c JP download -e APPLE_EMAIL -p APPLE_PWD -o DIR
26 | ```
27 |
28 | Or appID (`XXXXX` in the `apps.apple.com/app/xxxx/idXXXXXX`)
29 | ```
30 | python3 main.py download -i XXXXX
31 | ```
32 |
33 | You can also purchase apps when downloading using `--purchase`:
34 | ```
35 | python3 main.py lookup -b com.touchingapp.potatsolite -c JP download --purchase -e APPLE_EMAIL -p APPLE_PWD -o DIR
36 | ```
37 |
38 | ### Download OLD Version
39 |
40 | Old versions must be downloaded together with `iTunes Server`. You can get `iTunes Server` in several ways:
41 | - Using [action-ipadown](https://github.com/NyaMisty/action-ipadown) directly, which integrated this tool
42 | - NOTE: this method does not support 2FA
43 | - Manually way with Windows: (supports 2FA)
44 | - download this repo: https://github.com/NyaMisty/actions-iTunes-header
45 | - install iTunes 12.6.5.3, from https://secure-appldnld.apple.com/itunes12/091-87819-20180912-69177170-B085-11E8-B6AB-C1D03409AD2A6/iTunes64Setup.exe
46 | - patch the iTunes using `actions-iTunes-header/workflow_helper/iTunesInstall/patch_itunes.py`
47 | - install frida: `pip3 install frida`
48 | - open iTunes, sign out & re-login your account
49 | - run: `actions-iTunes-header/workflow_helper/iTunesDownload/get_header.py`
50 | - Manually way with jailbroken iOS device: (supports 2FA)
51 | - download [KbsyncTool](https://github.com/Lessica/KbsyncTool/releases)
52 | - install `com.darwindev.kbsync_XXX.deb` on your jailbroken device
53 | - exec `kbsynctool -s 9000` on your jailbroken device
54 | - you will find log `Using -s http://192.168.100.227:9000/...`, use it as server address in next step
55 |
56 | After setting up the server, you can run this tool to download a specific version
57 | ```
58 | python3 main.py lookup -b com.touchingapp.potatsolite -c JP download -s http://127.0.0.1:9000 --appVerId 833889087
59 | ```
60 |
61 | NOTE: Some users are reporting that you need to **authorize computer** and make first purchase in iTunes with marked "do not ask for password" before using the iTunes server. (See #26)
62 |
63 | ### Get History Ver (usually used together with JSON)
64 |
65 | Get all appVerId of app from Apple
66 | ```
67 | python3 main.py lookup -b com.touchingapp.potatsolite -c JP historyver -e APPLE_EMAIL -p APPLE_PWD
68 | ```
69 |
70 | ### Lookup (usually used together with JSON)
71 |
72 | Query app basic information:
73 | - By bundleID: `python3 main.py lookup -b com.touchingapp.potatsolite -c JP`
74 | - By appID: `python3 main.py lookup -i 1239860606 -c JP`
75 |
76 | Query appVerId:
77 | ```
78 | python3 main.py lookup -b com.touchingapp.potatsolite -c JP --get-verid
79 | ```
80 |
81 | ### Headless Usage
82 |
83 | For each command you can use `--json` switch to get result of command in JSON
84 |
85 | ```
86 | python3 main.py --json lookup -b com.touchingapp.potatsolite -c JP --get-verid
87 | python3 main.py --json lookup -b com.touchingapp.potatsolite -c JP historyver -e APPLE_EMAIL -p APPLE_PWD
88 | ```
89 |
90 | ### For Large Scale Scraping
91 |
92 | You can download all versions of an app like this:
93 | ```
94 | python3 main.py --json download --itunes-server http://XXX.XXX.XXX.XXX:9000 --appId 414478124 --purchase --downloadAllVersion
95 | ```
96 |
97 | - In this mode, errors will only be logged instead of interrupting the whole process
98 | - For each downloaded app version, it will output a line of json in stdout like this:
99 | ```
100 | {"appName": "WeChat", "appBundleId": "com.tencent.xin", "appVer": "6.5.13.34", "appId": 414478124, "appVerId": 822899148, "downloadedIPA": "wechat\\com.tencent.xin-6.5.13.34-414478124-822899148.ipa", "downloadedVerId": 822899148}
101 | ```
102 | Logs will only be printed to stderr, so you can parse this line for automation.
103 |
104 |
105 | ## Development
106 |
107 | - All requests' reqBody and respBody are modeled using modified JSONSchema2PoPo2 (see my NyaMisty/JSONSchema2PoPo2), you can regenerate the binding by cd into `reqs/schemas` and execute `python3 -m schema_defs`
108 | - See more information on how to generate schema in reqs/schemas directory
109 |
110 | ## Credit
111 |
112 | - Thanks @majd's ipatool, which is written in swift
113 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import os
3 | import pickle
4 | import sys
5 | import time
6 | import zipfile
7 |
8 | from requests.adapters import HTTPAdapter
9 | from urllib3 import Retry
10 |
11 | from reqs.itunes import *
12 | from reqs.store import *
13 | import reprlib
14 |
15 | reprlib.aRepr.maxstring = 200
16 |
17 | import argparse
18 |
19 | import logging
20 | from rich.logging import RichHandler
21 | from rich.console import Console
22 | import rich
23 |
24 | rich.get_console().file = sys.stderr
25 | if rich.get_console().width < 100:
26 | rich.get_console().width = 100
27 |
28 | logging_handler = RichHandler(rich_tracebacks=True)
29 | logging.basicConfig(
30 | level="INFO",
31 | format="%(message)s",
32 | datefmt="[%X]",
33 | handlers=[logging_handler]
34 | )
35 | logging.getLogger('urllib3').setLevel(logging.WARNING)
36 | retryLogger = logging.getLogger('urllib3.util.retry')
37 | retryLogger.setLevel(logging.DEBUG)
38 | retryLogger.handlers = [logging_handler]
39 | retryLogger.propagate = False
40 |
41 | logger = logging.getLogger('main')
42 |
43 | import requests
44 |
45 | def get_zipinfo_datetime(timestamp=None):
46 | # Some applications need reproducible .whl files, but they can't do this without forcing
47 | # the timestamp of the individual ZipInfo objects. See issue #143.
48 | timestamp = int(timestamp or time.time())
49 | return time.gmtime(timestamp)[0:6]
50 |
51 | def downloadFile(url, outfile, retries=10):
52 | for retry in range(retries):
53 | try:
54 | _downloadFile(url, outfile)
55 | break
56 | except Exception as e:
57 | logger.info("Error during downloading %s (retry %d/%d), error %s", url, retry, retries, e)
58 | os.remove(outfile)
59 | logger.info("Download success in retry %d", retry)
60 |
61 | download_sess = requests.Session()
62 | download_sess.headers = {"User-Agent": CONFIGURATOR_UA}
63 | DOWNLOAD_READ_TIMEOUT = 25.0
64 | def _downloadFile(url, outfile):
65 | with download_sess.get(url, stream=True, timeout=DOWNLOAD_READ_TIMEOUT) as r:
66 | if not r.headers.get('Content-Length'):
67 | raise Exception("server is not returning Content-Length!")
68 | totalLen = int(r.headers.get('Content-Length', '0'))
69 | downLen = 0
70 | r.raise_for_status()
71 | try:
72 | with open(outfile, 'wb') as f:
73 | lastLen = 0
74 | for chunk in r.iter_content(chunk_size=1 * 1024 * 1024):
75 | # If you have chunk encoded response uncomment if
76 | # and set chunk_size parameter to None.
77 | # if chunk:
78 | f.write(chunk)
79 | downLen += len(chunk)
80 | if totalLen and downLen - lastLen > totalLen * 0.05:
81 | logger.info("Download progress: %3.2f%% (%5.1fM /%5.1fM)" % (
82 | downLen / totalLen * 100, downLen / 1024 / 1024, totalLen / 1024 / 1024))
83 | lastLen = downLen
84 | finally:
85 | if downLen != totalLen: # ensure no partial downloaded files exists
86 | os.unlink(outfile)
87 | if downLen != totalLen:
88 | raise Exception("failed to completely download the IPA file")
89 |
90 | return outfile
91 |
92 | class IPATool(object):
93 | def __init__(self):
94 | self.sess = requests.Session()
95 |
96 | retry_strategy = Retry(
97 | connect=4,
98 | read=4,
99 | # total=8,
100 | status=20,
101 | allowed_methods=None,
102 | status_forcelist=[429, 502, 503],
103 | backoff_factor=1.0,
104 | respect_retry_after_header=False,
105 | )
106 | self.sess.mount("https://", HTTPAdapter(max_retries=retry_strategy))
107 | self.sess.mount("http://", HTTPAdapter(max_retries=retry_strategy))
108 | IPATOOL_PROXY = os.getenv("IPATOOL_PROXY")
109 | if IPATOOL_PROXY is not None:
110 | self.sess.proxies.update(
111 | {'http': IPATOOL_PROXY, 'https': IPATOOL_PROXY})
112 | # self.sess.headers = {}
113 | self.sess.headers = {"Connection": "close"}
114 | self.sess.keep_alive = False
115 |
116 | self.appId = None
117 | # self.appInfo = None
118 | self.appVerId = None
119 | self.appVerIds = None
120 |
121 | self.jsonOut = None
122 |
123 | def tool_main(self):
124 | commparser = argparse.ArgumentParser(description='IPATool-Python Commands.', add_help=False)
125 | subp = commparser.add_subparsers(dest='command', required=True)
126 | lookup_p = subp.add_parser('lookup')
127 | id_group = lookup_p.add_mutually_exclusive_group(required=True)
128 | id_group.add_argument('--bundle-id', '-b', dest='bundle_id')
129 | id_group.add_argument('--appId', '-i', dest='appId')
130 | lookup_p.add_argument('--country', '-c', dest='country', required=True)
131 | lookup_p.add_argument('--get-verid', dest='get_verid', action='store_true')
132 | lookup_p.set_defaults(func=self.handleLookup)
133 |
134 | def add_auth_options(p):
135 | auth_p = p.add_argument_group('Auth Options', 'Must specify either Apple ID & Password, or iTunes Server URL')
136 | appleid = auth_p.add_argument('--appleid', '-e')
137 | passwd = auth_p.add_argument('--password', '-p')
138 | sessdir = auth_p.add_argument('--session-dir', dest='session_dir', default=None)
139 |
140 | itunessrv = auth_p.add_argument('--itunes-server', '-s', dest='itunes_server')
141 |
142 | ## Multiple hack here just to achieve (appleid & password) || itunes_server
143 | # p._optionals.conflict_handler = 'ignore'
144 | # p._optionals._handle_conflict_ignore = lambda *args: None
145 | auth_p = p.add_mutually_exclusive_group(required=True)
146 | auth_p._group_actions.append(appleid)
147 | auth_p._group_actions.append(itunessrv)
148 | # auth_p._group_actions.append(sessdir)
149 |
150 | auth_p = p.add_mutually_exclusive_group(required=True)
151 | auth_p._group_actions.append(passwd)
152 | auth_p._group_actions.append(itunessrv)
153 |
154 | purchase_p = subp.add_parser('purchase')
155 | add_auth_options(purchase_p)
156 | purchase_p.add_argument('--appId', '-i', dest='appId')
157 | purchase_p.set_defaults(func=self.handlePurchase)
158 |
159 | down_p = subp.add_parser('download')
160 | add_auth_options(down_p)
161 | down_p.add_argument('--appId', '-i', dest='appId')
162 | down_p.add_argument('--appVerId', dest='appVerId')
163 | down_p.add_argument('--purchase', action='store_true')
164 | down_p.add_argument('--downloadAllVersion', action='store_true')
165 | down_p.add_argument('--output-dir', '-o', dest='output_dir', default='.')
166 | down_p.set_defaults(func=self.handleDownload)
167 |
168 | his_p = subp.add_parser('historyver')
169 | his_p.add_argument('--appId', '-i', dest='appId')
170 | his_p.add_argument('--purchase', action='store_true')
171 | his_p.add_argument('--output-dir', '-o', dest='output_dir', default='.')
172 | add_auth_options(his_p)
173 | his_p.set_defaults(func=self.handleHistoryVersion)
174 |
175 | parser = argparse.ArgumentParser(description='IPATool-Python.', parents=[commparser])
176 | parser.add_argument('--log-level', '-l', dest='log_level', default='info',
177 | help='output level (default: info)')
178 | parser.add_argument('--json', dest='out_json', action='store_true',
179 | help='output json in stdout (log will always be put into stderr)')
180 |
181 | # parse global flags & first comm's arguments
182 | args, rest = parser.parse_known_args()
183 | logging.getLogger().setLevel(args.log_level.upper())
184 | outJson = args.out_json
185 |
186 | while True:
187 | args.func(args)
188 | if not rest:
189 | break
190 | args, rest = commparser.parse_known_args(rest)
191 |
192 | if outJson and self.jsonOut:
193 | print(json.dumps(self.jsonOut, ensure_ascii=False))
194 |
195 | def _outputJson(self, obj):
196 | self.jsonOut = obj
197 |
198 | def handleLookup(self, args):
199 | if args.bundle_id:
200 | s = 'BundleID %s' % args.bundle_id
201 | else:
202 | s = 'AppID %s' % args.appId
203 | logger.info('Looking up app in country %s with BundleID %s' % (args.country, s))
204 | iTunes = iTunesClient(self.sess)
205 | appInfos = iTunes.lookup(bundleId=args.bundle_id, appId=args.appId, country=args.country)
206 | if appInfos.resultCount != 1:
207 | logger.fatal("Failed to find app in country %s with %s" % (args.country, s))
208 | return
209 | appInfo = appInfos.results[0]
210 | logger.info("Found app:\n\tName: %s\n\tVersion: %s\n\tbundleId: %s\n\tappId: %s" % (appInfo.trackName, appInfo.version, appInfo.bundleId, appInfo.trackId))
211 | self.appId = appInfo.trackId
212 | # self.appInfo = appInfo
213 |
214 | ret = {
215 | "name": appInfo.trackName,
216 | "version": appInfo.version,
217 | "appId": appInfo.trackId,
218 | "bundleId": appInfo.bundleId,
219 | }
220 |
221 | if args.get_verid:
222 | logger.info("Retrieving verId using iTunes webpage...")
223 | verId = iTunes.getAppVerId(self.appId, args.country)
224 | logger.info("Got current verId using iTunes webpage: %s" % verId)
225 | ret["appVerId"] = verId
226 |
227 | self._outputJson(ret)
228 |
229 | storeClientCache = {}
230 | def _get_StoreClient(self, args):
231 | to_delete = []
232 | for k, v in self.storeClientCache.items():
233 | if time.time() - v < 30.0:
234 | return k
235 | else:
236 | to_delete.append(k)
237 | for k in to_delete:
238 | self.storeClientCache.pop(k)
239 | newSess = pickle.loads(pickle.dumps(self.sess))
240 | Store = StoreClient(newSess)
241 |
242 | if args.itunes_server:
243 | logger.info("Using iTunes interface %s to download app!" % args.itunes_server)
244 | servUrl = args.itunes_server
245 | def handle_iTunes_provider(url):
246 | startTime = time.time()
247 | r = requests.get(servUrl, params={
248 | 'url': url
249 | })
250 | logger.debug("got itunes header in %.2f seconds", time.time() - startTime)
251 |
252 | ret = r.json()
253 | kbsync = bytes.fromhex(ret.pop('kbsync'))
254 | guid = ret.pop('guid')
255 | retHdrs = ret.pop('headers')
256 | handled = {
257 | 'headers': retHdrs,
258 | 'kbsync': kbsync,
259 | 'guid': guid,
260 | }
261 | if 'sbsync' in ret:
262 | handled['sbsync'] = bytes.fromhex(ret.pop('sbsync'))
263 | if 'afds' in ret:
264 | handled['afds'] = ret.pop('afds')
265 | return handled
266 | Store.iTunes_provider = handle_iTunes_provider
267 | else:
268 | appleid = args.appleid
269 | applepass = args.password
270 |
271 | needLogin = True
272 | session_cache = os.path.join(args.session_dir, args.appleid) if args.session_dir else None
273 | if session_cache and os.path.exists(session_cache):
274 | needLogin = False
275 | try:
276 | # inside try in case the file format changed
277 | with open(session_cache, "r") as f:
278 | content = f.read()
279 | Store.authenticate_load_session(content)
280 | except Exception as e:
281 | logger.warning(f"Error loading session {session_cache}")
282 | os.unlink(session_cache)
283 | needLogin = True
284 | else:
285 | logger.info('Loaded session for %s' % (str(Store.authInfo)))
286 | if needLogin:
287 | logger.info("Logging into iTunes as %s ..." % appleid)
288 |
289 | Store.authenticate(appleid, applepass)
290 | logger.info('Logged in as %s' % (str(Store.authInfo)))
291 |
292 | if session_cache:
293 | with open(session_cache, "w") as f:
294 | f.write(Store.authenticate_save_session())
295 |
296 | def authedPost(*args, **kwargs):
297 | if 'MZFinance.woa/wa/authenticate' in args[0]:
298 | return Store.sess.original_post(*args, **kwargs)
299 | for i in range(3):
300 | r = Store.sess.original_post(*args, **kwargs)
301 | isAuthFail = False
302 | try:
303 | d = plistlib.loads(r.content)
304 | if str(d['failureType']) in ("2034", "1008"):
305 | isAuthFail = True
306 | except:
307 | return r
308 | if not isAuthFail:
309 | return r
310 | Store.authenticate(appleid, applepass)
311 | if session_cache:
312 | with open(session_cache, "w") as f:
313 | f.write(Store.authenticate_save_session())
314 | continue
315 | Store.sess.original_post = Store.sess.post
316 | Store.sess.post = authedPost
317 |
318 | self.storeClientCache[Store] = time.time()
319 | return Store
320 |
321 | def _handleStoreException(self, _e):
322 | e = _e # type: StoreException
323 | logger.fatal("Store %s failed! Message: %s%s" % (e.req, e.errMsg, " (errorType %s)" % e.errType if e.errType else ''))
324 | logger.fatal(" Raw Response: %s" % (e.resp))
325 |
326 | def handlePurchase(self, args):
327 | Store = self._get_StoreClient(args)
328 | logger.info('Try to purchase appId %s' % (self.appId))
329 | try:
330 | Store.purchase(self.appId)
331 | except StoreException as e:
332 | if e.errMsg == 'purchased_before':
333 | logger.warning('You have already purchased appId %s before' % (self.appId))
334 | else:
335 | raise
336 |
337 | def handleHistoryVersion(self, args, caches=True):
338 | if args.appId:
339 | self.appId = args.appId
340 |
341 | if not self.appId:
342 | logger.fatal("appId not supplied!")
343 | return
344 |
345 | versionsJsonPath = args.output_dir + f"/historyver_{self.appId}.json"
346 | if caches:
347 | if os.path.exists(versionsJsonPath):
348 | cacheContent = None
349 | try:
350 | with open(versionsJsonPath) as f:
351 | cacheContent = json.load(f)
352 | except:
353 | pass
354 | if cacheContent is not None:
355 | logger.info('Loaded history version cache for appId %s' % self.appId)
356 | self.appVerIds = cacheContent['appVerIds']
357 | return
358 |
359 | logger.info('Retrieving history version for appId %s' % self.appId)
360 |
361 | try:
362 | Store = self._get_StoreClient(args)
363 |
364 | logger.info('Retrieving download info for appId %s' % (self.appId))
365 | if args.purchase:
366 | logger.info('Purchasing appId %s' % (self.appId))
367 | # We have already successfully purchased, so don't purchase again :)
368 | self.handlePurchase(args)
369 | args.purchase = False
370 |
371 | downResp = Store.download(self.appId, '', isRedownload=not args.purchase)
372 | logger.debug('Got download info: %s', downResp)
373 | if args.purchase:
374 | # We have already successfully purchased, so don't purchase again :)
375 | args.purchase = False
376 |
377 | if not downResp.songList:
378 | logger.fatal("failed to get app download info!")
379 | raise StoreException('download', downResp, 'no songList')
380 | downInfo = downResp.songList[0]
381 | logger.info('Got available version ids %s', downInfo.metadata.softwareVersionExternalIdentifiers)
382 | self._outputJson({
383 | "appVerIds": downInfo.metadata.softwareVersionExternalIdentifiers
384 | })
385 | self.appVerIds = downInfo.metadata.softwareVersionExternalIdentifiers
386 | if caches:
387 | with open(versionsJsonPath, 'w') as f:
388 | json.dump({
389 | 'appVerIds': self.appVerIds,
390 | }, f)
391 | except StoreException as e:
392 | self._handleStoreException(e)
393 | if not e.errMsg.startswith('http error status') and not e.errMsg.startswith(
394 | 'We are temporarily unable to process your request') and not e.errMsg.startswith(
395 | "License not found"):
396 | # this error is persistent (e.g. app deleted)
397 | self.appVerIds = []
398 | if caches:
399 | with open(versionsJsonPath, 'w') as f:
400 | json.dump({
401 | 'appVerIds': self.appVerIds,
402 | 'error': str(e),
403 | 'errorResp': str(e.resp),
404 | }, f)
405 |
406 | def handleDownload(self, args):
407 | os.makedirs(args.output_dir, exist_ok=True)
408 | if args.downloadAllVersion:
409 | if os.path.exists(args.output_dir + "/all_done"):
410 | logger.info('Already fully finished, skipping!')
411 | return
412 | self.handleHistoryVersion(args, caches=True)
413 | if not self.appVerIds:
414 | logger.fatal('failed to retrive history versions for appId %s', args.appId)
415 | return
416 | everything_succ = True
417 | for appVerId in self.appVerIds:
418 | self.jsonOut = None
419 | stateFile = args.output_dir + '/' + str(appVerId) + '.finish'
420 | if os.path.exists(stateFile):
421 | logger.info('Skipping already downloaded')
422 | continue
423 | try:
424 | self.appVerId = appVerId
425 | self.downloadOne(args)
426 | if args.out_json and self.jsonOut:
427 | print(json.dumps(self.jsonOut, ensure_ascii=False))
428 | if self.jsonOut is not None: # successfully finished
429 | with open(stateFile, 'w') as f:
430 | f.write('1')
431 | except Exception as e:
432 | logger.fatal("error during downloading appVerId %s", appVerId, exc_info=1)
433 | everything_succ = False
434 | finally:
435 | self.jsonOut = None
436 | if everything_succ:
437 | with open(args.output_dir + "/all_done", 'w') as f:
438 | f.write("1")
439 | else:
440 | self.downloadOne(args)
441 |
442 | def downloadOne(self, args):
443 | if args.appId:
444 | self.appId = args.appId
445 | if args.appVerId:
446 | self.appVerId = args.appVerId
447 |
448 | if not self.appId:
449 | logger.fatal("appId not supplied!")
450 | return
451 |
452 | logger.info("Downloading appId %s appVerId %s", self.appId, self.appVerId)
453 | try:
454 | appleid = args.appleid
455 | Store = self._get_StoreClient(args)
456 |
457 | if args.purchase:
458 | self.handlePurchase(args)
459 |
460 | logger.info('Retrieving download info for appId %s%s' % (self.appId, " with versionId %s" % self.appVerId if self.appVerId else ""))
461 |
462 | downResp = Store.download(self.appId, self.appVerId, isRedownload=not args.purchase)
463 | logger.debug('Got download info: %s', downResp.as_dict())
464 |
465 | if not downResp.songList:
466 | logger.fatal("failed to get app download info!")
467 | raise StoreException('download', downResp, 'no songList')
468 | downInfo = downResp.songList[0]
469 |
470 | appName = downInfo.metadata.bundleDisplayName
471 | appId = downInfo.songId
472 | appBundleId = downInfo.metadata.softwareVersionBundleId
473 | appVerId = downInfo.metadata.softwareVersionExternalIdentifier
474 | # when downloading history versions, bundleShortVersionString will always give a wrong version number (the newest one)
475 | # should use bundleVersion in these cases
476 | appVer = downInfo.metadata.bundleShortVersionString if not self.appVerId else downInfo.metadata.bundleVersion
477 |
478 | logger.info(f'Downloading app {appName} ({appBundleId}) with appId {appId} (version {appVer}, versionId {appVerId})')
479 |
480 | # if self.appInfo:
481 | filename = '%s-%s-%s-%s.ipa' % (appBundleId,
482 | appVer,
483 | appId,
484 | appVerId)
485 | # else:
486 | # filename = '%s-%s.ipa' % (self.appId, appVerId)
487 |
488 | filepath = os.path.join(args.output_dir, filename)
489 | logger.info("Downloading ipa to %s" % filepath)
490 | downloadFile(downInfo.URL, filepath)
491 | metadata = downInfo.metadata.as_dict()
492 | if appleid:
493 | metadata["apple-id"] = appleid
494 | metadata["userName"] = appleid
495 | logger.info("Writing out iTunesMetadata.plist...")
496 | if zipfile.is_zipfile(filepath):
497 | with zipfile.ZipFile(filepath, 'a') as ipaFile:
498 | logger.debug("Writing iTunesMetadata.plist")
499 | ipaFile.writestr(zipfile.ZipInfo("iTunesMetadata.plist", get_zipinfo_datetime()), plistlib.dumps(metadata))
500 | logger.debug("Writing IPAToolInfo.plist")
501 | ipaFile.writestr(zipfile.ZipInfo("IPAToolInfo.plist", get_zipinfo_datetime()), plistlib.dumps(downResp.as_dict()))
502 |
503 | def findAppContentPath(c):
504 | if not c.startswith('Payload/'):
505 | return False
506 | pathparts = c.strip('/').split('/')
507 | if len(pathparts) != 2:
508 | return False
509 | if not pathparts[1].endswith(".app"):
510 | return False
511 | return True
512 | appContentDirChoices = [c for c in ipaFile.namelist() if findAppContentPath(c)]
513 | if len(appContentDirChoices) != 1:
514 | raise Exception("failed to find appContentDir, choices %s", appContentDirChoices)
515 | appContentDir = appContentDirChoices[0].rstrip('/')
516 |
517 | processedSinf = False
518 | if (appContentDir + '/SC_Info/Manifest.plist') in ipaFile.namelist():
519 | #Try to get the Manifest.plist file, since it doesn't always exist.
520 | scManifestData = ipaFile.read(appContentDir + '/SC_Info/Manifest.plist')
521 | logger.debug("Got SC_Info/Manifest.plist: %s", scManifestData)
522 | scManifest = plistlib.loads(scManifestData)
523 | sinfs = {c.id: c.sinf for c in downInfo.sinfs}
524 | if 'SinfPaths' in scManifest:
525 | for i, sinfPath in enumerate(scManifest['SinfPaths']):
526 | logger.debug("Writing sinf to %s", sinfPath)
527 | ipaFile.writestr(appContentDir + '/' + sinfPath, sinfs[i])
528 | processedSinf = True
529 | if not processedSinf:
530 | logger.info('Manifest.plist does not exist! Assuming it is an old app without one...')
531 | infoListData = ipaFile.read(appContentDir + '/Info.plist') #Is this not loaded anywhere yet?
532 | infoList = plistlib.loads(infoListData)
533 | sinfPath = appContentDir + '/SC_Info/'+infoList['CFBundleExecutable']+".sinf"
534 | logger.debug("Writing sinf to %s", sinfPath)
535 | #Assuming there is only one .sinf file, hence the 0
536 | ipaFile.writestr(sinfPath, downInfo.sinfs[0].sinf)
537 | processedSinf = True
538 |
539 | logger.info("Downloaded ipa to %s" % filename)
540 | else:
541 | plist = filepath[:-4]+".info.plist"
542 | with open(plist, "wb") as f:
543 | f.write(plistlib.dumps(downResp.as_dict()))
544 | plist = filepath[:-4]+".plist"
545 | with open(plist, "wb") as f:
546 | f.write(plistlib.dumps(metadata))
547 | logger.info("Downloaded ipa to %s and plist to %s" % (filename, plist))
548 |
549 | self._outputJson({
550 | "appName": appName,
551 | "appBundleId": appBundleId,
552 | "appVer": appVer,
553 | "appId": appId,
554 | "appVerId": appVerId,
555 |
556 | "downloadedIPA": filepath,
557 | "downloadedVerId": appVerId,
558 | "downloadURL": downInfo.URL,
559 | })
560 | except StoreException as e:
561 | self._handleStoreException(e)
562 |
563 | def main():
564 | tool = IPATool()
565 | tool.tool_main()
566 |
567 | if __name__ == '__main__':
568 | main()
--------------------------------------------------------------------------------
/reqs/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NyaMisty/ipatool-py/d4ff581f110d184229aa92477a5e3b4e81099b9a/reqs/__init__.py
--------------------------------------------------------------------------------
/reqs/itunes.py:
--------------------------------------------------------------------------------
1 | __all__ = ['iTunesClient']
2 |
3 | import json
4 | import re
5 |
6 | from reqs.schemas.itunes_lookup_resp import ItunesLookupResp
7 | import requests
8 |
9 | STORE_TABLE = {"AE":"143481-2,32","AG":"143540-2,32","AI":"143538-2,32","AL":"143575-2,32","AM":"143524-2,32","AO":"143564-2,32","AR":"143505-28,32","AT":"143445-4,32","AU":"143460-27,32","AZ":"143568-2,32","BB":"143541-2,32","BE":"143446-2,32","BF":"143578-2,32","BG":"143526-2,32","BH":"143559-2,32","BJ":"143576-2,32","BM":"143542-2,32","BN":"143560-2,32","BO":"143556-28,32","BR":"143503-15,32","BS":"143539-2,32","BT":"143577-2,32","BW":"143525-2,32","BY":"143565-2,32","BZ":"143555-2,32","CA":"143455-6,32","CG":"143582-2,32","CH":"143459-57,32","CL":"143483-28,32","CN":"143465-19,32","CO":"143501-28,32","CR":"143495-28,32","CV":"143580-2,32","CY":"143557-2,32","CZ":"143489-2,32","DE":"143443-4,32","DK":"143458-2,32","DM":"143545-2,32","DO":"143508-28,32","DZ":"143563-2,32","EC":"143509-28,32","EE":"143518-2,32","EG":"143516-2,32","ES":"143454-8,32","FI":"143447-2,32","FJ":"143583-2,32","FM":"143591-2,32","FR":"143442-3,32","GB":"143444-2,32","GD":"143546-2,32","GH":"143573-2,32","GM":"143584-2,32","GR":"143448-2,32","GT":"143504-28,32","GW":"143585-2,32","GY":"143553-2,32","HK":"143463-45,32","HN":"143510-28,32","HR":"143494-2,32","HU":"143482-2,32","ID":"143476-2,32","IE":"143449-2,32","IL":"143491-2,32","IN":"143467-2,32","IS":"143558-2,32","IT":"143450-7,32","JM":"143511-2,32","JO":"143528-2,32","JP":"143462-9,32","KE":"143529-2,32","KG":"143586-2,32","KH":"143579-2,32","KN":"143548-2,32","KR":"143466-13,32","KW":"143493-2,32","KY":"143544-2,32","KZ":"143517-2,32","LA":"143587-2,32","LB":"143497-2,32","LC":"143549-2,32","LK":"143486-2,32","LR":"143588-2,32","LT":"143520-2,32","LU":"143451-2,32","LV":"143519-2,32","MD":"143523-2,32","MG":"143531-2,32","MK":"143530-2,32","ML":"143532-2,32","MN":"143592-2,32","MO":"143515-45,32","MR":"143590-2,32","MS":"143547-2,32","MT":"143521-2,32","MU":"143533-2,32","MW":"143589-2,32","MX":"143468-28,32","MY":"143473-2,32","MZ":"143593-2,32","NA":"143594-2,32","NE":"143534-2,32","NG":"143561-2,32","NI":"143512-28,32","NL":"143452-10,32","NO":"143457-2,32","NP":"143484-2,32","NZ":"143461-27,32","OM":"143562-2,32","PA":"143485-28,32","PE":"143507-28,32","PG":"143597-2,32","PH":"143474-2,32","PK":"143477-2,32","PL":"143478-2,32","PT":"143453-24,32","PW":"143595-2,32","PY":"143513-28,32","QA":"143498-2,32","RO":"143487-2,32","RU":"143469-16,32","SA":"143479-2,32","SB":"143601-2,32","SC":"143599-2,32","SE":"143456-17,32","SG":"143464-19,32","SI":"143499-2,32","SK":"143496-2,32","SL":"143600-2,32","SN":"143535-2,32","SR":"143554-2,32","ST":"143598-2,32","SV":"143506-28,32","SZ":"143602-2,32","TC":"143552-2,32","TD":"143581-2,32","TH":"143475-2,32","TJ":"143603-2,32","TM":"143604-2,32","TN":"143536-2,32","TR":"143480-2,32","TT":"143551-2,32","TW":"143470-18,32","TZ":"143572-2,32","UA":"143492-2,32","UG":"143537-2,32","US":"143441-1,32","UY":"143514-2,32","UZ":"143566-2,32","VC":"143550-2,32","VE":"143502-28,32","VG":"143543-2,32","VN":"143471-2,32","YE":"143571-2,32","ZA":"143472-2,32","ZW":"143605-2,32"}
10 |
11 |
12 | class iTunesClient(object):
13 | def __init__(self, sess: requests.Session):
14 | self.sess = sess
15 |
16 | # curl -k -X GET \
17 | # -H "Content-Type: application/x-www-form-urlencoded" \
18 | # https://itunes.apple.com/lookup?bundleId=com.touchingapp.potatsolite&limit=1&media=software
19 | def lookup(self, bundleId=None, appId=None, term=None, country="US", limit=1, media="software") -> ItunesLookupResp:
20 | r = self.sess.get("https://itunes.apple.com/lookup?",
21 | params={
22 | "bundleId": bundleId,
23 | "id": appId,
24 | "term": term,
25 | "country": country,
26 | "limit": limit,
27 | "media": media,
28 | },
29 | headers={
30 | "Content-Type": "application/x-www-form-urlencoded",
31 | })
32 | return ItunesLookupResp.from_dict(r.json())
33 |
34 | def getAppVerId(self, appId, country):
35 | if not ',' in country:
36 | storeFront = STORE_TABLE[country.upper()]
37 | else:
38 | storeFront = country
39 | appInfo = requests.get("https://apps.apple.com/app/id%s" % appId, headers={"X-Apple-Store-Front": storeFront}).text
40 | try:
41 | appParam = re.findall(r'"buyParams":"(.*?)"', appInfo)[0]
42 | except:
43 | appParam = re.findall(r'buy-params="(.*?)"', appInfo)[0]
44 | appParam = appParam.replace('&', '&')
45 | appParamDict = dict((c.split('=') for c in json.loads('"%s"' % appParam).split('&')))
46 | appVer = appParamDict['appExtVrsId']
47 | return appVer
--------------------------------------------------------------------------------
/reqs/schemas/README.md:
--------------------------------------------------------------------------------
1 | # Schema Definitions for ipatool-py
2 |
3 | In this directory, JSON Schema Definition files are stored in `schema_defs`, with corresponding plist examples in `schema_examples`
4 |
5 | ## How to generate schema from plist
6 |
7 | 1. Convert plist to corresponding JSON, using either `plistlib` or online tools
8 | 2. Use https://www.liquid-technologies.com/online-json-to-schema-converter to convert JSON to schema
9 | - You have to switch the `Array Rules` option to `List Validation`
10 | 3. Merge different request / response body's schema manually
11 | - Usually the "required" field can help you merging the json schema
12 |
13 | ## Regenerate schema bindings
14 |
15 | Run this in current folder:
16 | ```
17 | python3 -m schema_defs
18 | ```
--------------------------------------------------------------------------------
/reqs/schemas/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NyaMisty/ipatool-py/d4ff581f110d184229aa92477a5e3b4e81099b9a/reqs/schemas/__init__.py
--------------------------------------------------------------------------------
/reqs/schemas/schema_defs/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NyaMisty/ipatool-py/d4ff581f110d184229aa92477a5e3b4e81099b9a/reqs/schemas/schema_defs/__init__.py
--------------------------------------------------------------------------------
/reqs/schemas/schema_defs/__main__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import os.path as path
3 | import subprocess
4 |
5 | curpath = path.dirname(__file__)
6 | outpath = path.dirname(curpath)
7 | for p in os.listdir(curpath):
8 | if not p.endswith('.json'):
9 | continue
10 | #subprocess.call(["jsonschema2popo2", "--translate-properties", "--use-types", "-o", path.join(outpath, p.split('.')[0] + '.py'), path.join(curpath, p)])
11 | print("Converting %s" % p)
12 | subprocess.call(["jsonschema2popo2", "--use-types", "-o", path.join(outpath, p.split('.')[0] + '.py'), path.join(curpath, p)])
--------------------------------------------------------------------------------
/reqs/schemas/schema_defs/itunes_lookup_resp.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "iTunes Lookup Resp",
3 | "type": "object",
4 | "properties": {
5 | "resultCount": {
6 | "type": "integer"
7 | },
8 | "results": {
9 | "type": "array",
10 | "items": {
11 | "type": "object",
12 | "properties": {
13 | "appletvScreenshotUrls": {
14 | "type": "array",
15 | "items": {
16 | "type": ["number","string","boolean","object","array", "null"]
17 | }
18 | },
19 | "screenshotUrls": {
20 | "type": "array",
21 | "items": {
22 | "type": "string"
23 | }
24 | },
25 | "ipadScreenshotUrls": {
26 | "type": "array",
27 | "items": {
28 | "type": "string"
29 | }
30 | },
31 | "artworkUrl60": {
32 | "type": "string"
33 | },
34 | "artworkUrl512": {
35 | "type": "string"
36 | },
37 | "artworkUrl100": {
38 | "type": "string"
39 | },
40 | "artistViewUrl": {
41 | "type": "string"
42 | },
43 | "supportedDevices": {
44 | "type": "array",
45 | "items": {
46 | "type": "string"
47 | }
48 | },
49 | "advisories": {
50 | "type": "array",
51 | "items": {
52 | "type": ["number","string","boolean","object","array", "null"]
53 | }
54 | },
55 | "isGameCenterEnabled": {
56 | "type": "boolean"
57 | },
58 | "kind": {
59 | "type": "string"
60 | },
61 | "features": {
62 | "type": "array",
63 | "items": {
64 | "type": "string"
65 | }
66 | },
67 | "minimumOsVersion": {
68 | "type": "string"
69 | },
70 | "trackCensoredName": {
71 | "type": "string"
72 | },
73 | "languageCodesISO2A": {
74 | "type": "array",
75 | "items": {
76 | "type": "string"
77 | }
78 | },
79 | "fileSizeBytes": {
80 | "type": "string"
81 | },
82 | "formattedPrice": {
83 | "type": "string"
84 | },
85 | "contentAdvisoryRating": {
86 | "type": "string"
87 | },
88 | "averageUserRatingForCurrentVersion": {
89 | "type": "number"
90 | },
91 | "userRatingCountForCurrentVersion": {
92 | "type": "integer"
93 | },
94 | "averageUserRating": {
95 | "type": "number"
96 | },
97 | "trackViewUrl": {
98 | "type": "string"
99 | },
100 | "trackContentRating": {
101 | "type": "string"
102 | },
103 | "releaseDate": {
104 | "type": "string"
105 | },
106 | "genreIds": {
107 | "type": "array",
108 | "items": {
109 | "type": "string"
110 | }
111 | },
112 | "primaryGenreName": {
113 | "type": "string"
114 | },
115 | "trackId": {
116 | "type": "integer"
117 | },
118 | "trackName": {
119 | "type": "string"
120 | },
121 | "sellerName": {
122 | "type": "string"
123 | },
124 | "isVppDeviceBasedLicensingEnabled": {
125 | "type": "boolean"
126 | },
127 | "currentVersionReleaseDate": {
128 | "type": "string"
129 | },
130 | "releaseNotes": {
131 | "type": "string"
132 | },
133 | "primaryGenreId": {
134 | "type": "integer"
135 | },
136 | "currency": {
137 | "type": "string"
138 | },
139 | "version": {
140 | "type": "string"
141 | },
142 | "wrapperType": {
143 | "type": "string"
144 | },
145 | "artistId": {
146 | "type": "integer"
147 | },
148 | "artistName": {
149 | "type": "string"
150 | },
151 | "genres": {
152 | "type": "array",
153 | "items": {
154 | "type": "string"
155 | }
156 | },
157 | "price": {
158 | "type": "number"
159 | },
160 | "description": {
161 | "type": "string"
162 | },
163 | "bundleId": {
164 | "type": "string"
165 | },
166 | "userRatingCount": {
167 | "type": "integer"
168 | }
169 | },
170 | "required": [
171 | "appletvScreenshotUrls",
172 | "screenshotUrls",
173 | "ipadScreenshotUrls",
174 | "artworkUrl60",
175 | "artworkUrl512",
176 | "artworkUrl100",
177 | "artistViewUrl",
178 | "supportedDevices",
179 | "advisories",
180 | "isGameCenterEnabled",
181 | "kind",
182 | "features",
183 | "minimumOsVersion",
184 | "trackCensoredName",
185 | "languageCodesISO2A",
186 | "fileSizeBytes",
187 | "formattedPrice",
188 | "contentAdvisoryRating",
189 | "averageUserRatingForCurrentVersion",
190 | "userRatingCountForCurrentVersion",
191 | "averageUserRating",
192 | "trackViewUrl",
193 | "trackContentRating",
194 | "releaseDate",
195 | "genreIds",
196 | "primaryGenreName",
197 | "trackId",
198 | "trackName",
199 | "sellerName",
200 | "isVppDeviceBasedLicensingEnabled",
201 | "currentVersionReleaseDate",
202 | "releaseNotes",
203 | "primaryGenreId",
204 | "currency",
205 | "version",
206 | "wrapperType",
207 | "artistId",
208 | "artistName",
209 | "genres",
210 | "price",
211 | "description",
212 | "bundleId",
213 | "userRatingCount"
214 | ]
215 | }
216 | }
217 | },
218 | "required": [
219 | "resultCount",
220 | "results"
221 | ]
222 | }
--------------------------------------------------------------------------------
/reqs/schemas/schema_defs/store_authenticate_req.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Store Authenticate Req",
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "type": "object",
5 | "properties": {
6 | "appleId": {
7 | "type": "string"
8 | },
9 | "attempt": {
10 | "type": "string"
11 | },
12 | "createSession": {
13 | "type": "string"
14 | },
15 | "guid": {
16 | "type": "string"
17 | },
18 | "password": {
19 | "type": "string"
20 | },
21 | "rmp": {
22 | "type": "string"
23 | },
24 | "why": {
25 | "type": "string"
26 | }
27 | },
28 | "required": [
29 | "appleId",
30 | "attempt",
31 | "createSession",
32 | "guid",
33 | "password",
34 | "rmp",
35 | "why"
36 | ]
37 | }
--------------------------------------------------------------------------------
/reqs/schemas/schema_defs/store_authenticate_resp.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Store Authenticate Resp",
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "type": "object",
5 | "properties": {
6 | "pings": {
7 | "type": "array",
8 | "items": {
9 | "type": ["number","string","boolean","object","array", "null"]
10 | }
11 | },
12 | "cancel-purchase-batch": {
13 | "type": "boolean"
14 | },
15 | "customerMessage": {
16 | "type": "string"
17 | },
18 | "failureType": {
19 | "type": "string"
20 | },
21 | "accountInfo": {
22 | "type": "object",
23 | "properties": {
24 | "appleId": {
25 | "type": "string"
26 | },
27 | "address": {
28 | "type": "object",
29 | "properties": {
30 | "firstName": {
31 | "type": "string"
32 | },
33 | "lastName": {
34 | "type": "string"
35 | }
36 | },
37 | "required": [
38 | "firstName",
39 | "lastName"
40 | ]
41 | }
42 | },
43 | "required": [
44 | "appleId",
45 | "address"
46 | ]
47 | },
48 | "passwordToken": {
49 | "type": "string"
50 | },
51 | "clearToken": {
52 | "type": "string"
53 | },
54 | "m-allowed": {
55 | "type": "boolean"
56 | },
57 | "is-cloud-enabled": {
58 | "type": "string"
59 | },
60 | "dsPersonId": {
61 | "type": "string"
62 | },
63 | "creditDisplay": {
64 | "type": "string"
65 | },
66 | "creditBalance": {
67 | "type": "string"
68 | },
69 | "freeSongBalance": {
70 | "type": "string"
71 | },
72 | "isManagedStudent": {
73 | "type": "boolean"
74 | },
75 | "action": {
76 | "type": "object",
77 | "properties": {
78 | "kind": {
79 | "type": "string"
80 | }
81 | },
82 | "required": [
83 | "kind"
84 | ]
85 | },
86 | "subscriptionStatus": {
87 | "type": "object",
88 | "properties": {
89 | "terms": {
90 | "type": "array",
91 | "items": {
92 | "type": "object",
93 | "properties": {
94 | "type": {
95 | "type": "string"
96 | },
97 | "latestTerms": {
98 | "type": "integer"
99 | },
100 | "agreedToTerms": {
101 | "type": "integer"
102 | },
103 | "source": {
104 | "type": "string"
105 | }
106 | },
107 | "required": [
108 | "type",
109 | "latestTerms",
110 | "agreedToTerms",
111 | "source"
112 | ]
113 | }
114 | },
115 | "account": {
116 | "type": "object",
117 | "properties": {
118 | "isMinor": {
119 | "type": "boolean"
120 | },
121 | "suspectUnderage": {
122 | "type": "boolean"
123 | }
124 | },
125 | "required": [
126 | "isMinor",
127 | "suspectUnderage"
128 | ]
129 | },
130 | "family": {
131 | "type": "object",
132 | "properties": {
133 | "hasFamily": {
134 | "type": "boolean"
135 | }
136 | },
137 | "required": [
138 | "hasFamily"
139 | ]
140 | }
141 | },
142 | "required": [
143 | "terms",
144 | "account",
145 | "family"
146 | ]
147 | },
148 | "accountFlags": {
149 | "type": "object",
150 | "properties": {
151 | "personalization": {
152 | "type": "boolean"
153 | },
154 | "underThirteen": {
155 | "type": "boolean"
156 | },
157 | "identityLastVerified": {
158 | "type": "integer"
159 | },
160 | "verifiedExpirationDate": {
161 | "type": "integer"
162 | },
163 | "retailDemo": {
164 | "type": "boolean"
165 | },
166 | "autoPlay": {
167 | "type": "boolean"
168 | },
169 | "isDisabledAccount": {
170 | "type": "boolean"
171 | },
172 | "isRestrictedAccount": {
173 | "type": "boolean"
174 | },
175 | "isManagedAccount": {
176 | "type": "boolean"
177 | },
178 | "isInRestrictedRegion": {
179 | "type": "boolean"
180 | },
181 | "accountFlagsVersion": {
182 | "type": "integer"
183 | },
184 | "hasAgreedToTerms": {
185 | "type": "boolean"
186 | },
187 | "hasAgreedToAppClipTerms": {
188 | "type": "boolean"
189 | },
190 | "hasWatchHardwareOffer": {
191 | "type": "boolean"
192 | },
193 | "isInFamily": {
194 | "type": "boolean"
195 | },
196 | "hasSubscriptionFamilySharingEnabled": {
197 | "type": "boolean"
198 | }
199 | },
200 | "required": [
201 | "personalization",
202 | "underThirteen",
203 | "identityLastVerified",
204 | "verifiedExpirationDate",
205 | "retailDemo",
206 | "autoPlay",
207 | "isDisabledAccount",
208 | "isRestrictedAccount",
209 | "isManagedAccount",
210 | "isInRestrictedRegion",
211 | "accountFlagsVersion",
212 | "hasAgreedToTerms",
213 | "hasAgreedToAppClipTerms",
214 | "hasWatchHardwareOffer",
215 | "isInFamily",
216 | "hasSubscriptionFamilySharingEnabled"
217 | ]
218 | },
219 | "status": {
220 | "type": "integer"
221 | },
222 | "download-queue-info": {
223 | "type": "object",
224 | "properties": {
225 | "download-queue-item-count": {
226 | "type": "integer"
227 | },
228 | "dsid": {
229 | "type": "integer"
230 | },
231 | "is-auto-download-machine": {
232 | "type": "boolean"
233 | }
234 | },
235 | "required": [
236 | "download-queue-item-count",
237 | "dsid",
238 | "is-auto-download-machine"
239 | ]
240 | },
241 | "privacyAcknowledgement": {
242 | "type": "object",
243 | "properties": {
244 | "com.apple.onboarding.appstore": {
245 | "type": "integer"
246 | },
247 | "com.apple.onboarding.applemusic": {
248 | "type": "integer"
249 | },
250 | "com.apple.onboarding.videos": {
251 | "type": "integer"
252 | },
253 | "com.apple.onboarding.itunesstore": {
254 | "type": "integer"
255 | },
256 | "com.apple.onboarding.itunesu": {
257 | "type": "integer"
258 | },
259 | "com.apple.onboarding.applearcade": {
260 | "type": "integer"
261 | }
262 | },
263 | "required": [
264 | "com.apple.onboarding.appstore",
265 | "com.apple.onboarding.applemusic",
266 | "com.apple.onboarding.videos",
267 | "com.apple.onboarding.itunesstore",
268 | "com.apple.onboarding.itunesu",
269 | "com.apple.onboarding.applearcade"
270 | ]
271 | },
272 | "dialog": {
273 | "type": "object",
274 | "properties": {
275 | "m-allowed": {
276 | "type": "boolean"
277 | },
278 | "message": {
279 | "type": "string"
280 | },
281 | "explanation": {
282 | "type": "string"
283 | },
284 | "defaultButton": {
285 | "type": "string"
286 | },
287 | "okButtonString": {
288 | "type": "string"
289 | },
290 | "initialCheckboxValue": {
291 | "type": "boolean"
292 | }
293 | },
294 | "required": [
295 | "m-allowed",
296 | "message",
297 | "explanation",
298 | "defaultButton",
299 | "okButtonString",
300 | "initialCheckboxValue"
301 | ]
302 | }
303 | },
304 | "required": [
305 | "pings",
306 | "accountInfo",
307 | "passwordToken",
308 | "clearToken",
309 | "m-allowed",
310 | "is-cloud-enabled",
311 | "dsPersonId",
312 | "creditDisplay",
313 | "creditBalance",
314 | "freeSongBalance",
315 | "isManagedStudent",
316 | "action",
317 | "subscriptionStatus",
318 | "accountFlags",
319 | "status",
320 | "download-queue-info",
321 | "privacyAcknowledgement",
322 | "dialog"
323 | ]
324 | }
--------------------------------------------------------------------------------
/reqs/schemas/schema_defs/store_buyproduct_req.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Store BuyProduct Req",
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "type": "object",
5 | "properties": {
6 | "ageCheck": {
7 | "type": "string"
8 | },
9 | "appExtVrsId": {
10 | "type": "string"
11 | },
12 | "guid": {
13 | "type": "string"
14 | },
15 | "hasBeenAuthedForBuy": {
16 | "type": "string"
17 | },
18 | "isInApp": {
19 | "type": "string"
20 | },
21 | "kbsync": {
22 | "type": "string"
23 | },
24 | "sbsync": {
25 | "type": "string"
26 | },
27 | "afds": {
28 | "type": "string"
29 | },
30 | "machineName": {
31 | "type": "string"
32 | },
33 | "mtApp": {
34 | "type": "string"
35 | },
36 | "mtClientId": {
37 | "type": "string"
38 | },
39 | "mtEventTime": {
40 | "type": "string"
41 | },
42 | "mtPageId": {
43 | "type": "string"
44 | },
45 | "mtPageType": {
46 | "type": "string"
47 | },
48 | "mtPrevPage": {
49 | "type": "string"
50 | },
51 | "mtRequestId": {
52 | "type": "string"
53 | },
54 | "mtTopic": {
55 | "type": "string"
56 | },
57 | "needDiv": {
58 | "type": "string"
59 | },
60 | "pg": {
61 | "type": "string"
62 | },
63 | "price": {
64 | "type": "string"
65 | },
66 | "pricingParameters": {
67 | "type": "string"
68 | },
69 | "productType": {
70 | "type": "string"
71 | },
72 | "salableAdamId": {
73 | "type": "string"
74 | },
75 |
76 | "hasAskedToFulfillPreorder": {
77 | "type": "string"
78 | },
79 | "buyWithoutAuthorization": {
80 | "type": "string"
81 | },
82 | "hasDoneAgeCheck": {
83 | "type": "string"
84 | },
85 | "hasConfirmedPaymentSheet": {
86 | "type": "string"
87 | },
88 | "asn": {
89 | "type": "string"
90 | }
91 | },
92 | "required": [
93 | "guid",
94 | "kbsync",
95 | "price",
96 | "pricingParameters",
97 | "productType",
98 | "salableAdamId",
99 | "appExtVrsId"
100 | ],
101 | "optional": [
102 | "ageCheck",
103 | "hasBeenAuthedForBuy",
104 | "hasConfirmedPaymentSheet",
105 | "asn",
106 | "isInApp",
107 |
108 | "machineName",
109 | "mtApp",
110 | "mtClientId",
111 | "mtEventTime",
112 | "mtPageId",
113 | "mtPageType",
114 | "mtPrevPage",
115 | "mtRequestId",
116 | "mtTopic",
117 | "needDiv",
118 | "pg"
119 | ]
120 | }
--------------------------------------------------------------------------------
/reqs/schemas/schema_defs/store_buyproduct_resp.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Store BuyProduct Resp",
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "type": "object",
5 | "properties": {
6 | "pings": {
7 | "type": "array",
8 | "items": {
9 | "type": ["number","string","boolean","object","array", "null"]
10 | }
11 | },
12 | "jingleDocType": {
13 | "type": "string"
14 | },
15 | "jingleAction": {
16 | "type": "string"
17 | },
18 | "status": {
19 | "type": "integer"
20 | },
21 | "dsPersonId": {
22 | "type": "string"
23 | },
24 | "creditDisplay": {
25 | "type": "string"
26 | },
27 | "creditBalance": {
28 | "type": "string"
29 | },
30 | "freeSongBalance": {
31 | "type": "string"
32 | },
33 | "creditDisplayInternal": {
34 | "type": "string"
35 | },
36 | "authorized": {
37 | "type": "boolean"
38 | },
39 | "download-queue-item-count": {
40 | "type": "integer"
41 | },
42 | "songList": {
43 | "type": "array",
44 | "items": {
45 | "type": "object",
46 | "properties": {
47 | "songId": {
48 | "type": "integer"
49 | },
50 | "URL": {
51 | "type": "string"
52 | },
53 | "downloadKey": {
54 | "type": "string"
55 | },
56 | "artworkURL": {
57 | "type": "string"
58 | },
59 | "artwork-urls": {
60 | "type": "object",
61 | "properties": {
62 | "image-type": {
63 | "type": "string"
64 | },
65 | "default": {
66 | "type": "object",
67 | "properties": {
68 | "url": {
69 | "type": "string"
70 | }
71 | },
72 | "required": [
73 | "url"
74 | ]
75 | }
76 | },
77 | "required": [
78 | "image-type",
79 | "default"
80 | ]
81 | },
82 | "md5": {
83 | "type": "string"
84 | },
85 | "chunks": {
86 | "type": "object",
87 | "properties": {
88 | "chunkSize": {
89 | "type": "integer"
90 | },
91 | "hashes": {
92 | "type": "array",
93 | "items": {
94 | "type": "string"
95 | }
96 | }
97 | },
98 | "required": [
99 | "chunkSize",
100 | "hashes"
101 | ]
102 | },
103 | "isStreamable": {
104 | "type": "boolean"
105 | },
106 | "uncompressedSize": {
107 | "type": "integer"
108 | },
109 | "sinfs": {
110 | "type": "array",
111 | "items": {
112 | "type": "object",
113 | "properties": {
114 | "id": {
115 | "type": "integer"
116 | },
117 | "sinf": {
118 | "type": "string"
119 | }
120 | },
121 | "required": [
122 | "id",
123 | "sinf"
124 | ]
125 | }
126 | },
127 | "purchaseDate": {
128 | "type": "string"
129 | },
130 | "download-id": {
131 | "type": "string"
132 | },
133 | "is-in-queue": {
134 | "type": "boolean"
135 | },
136 | "asset-info": {
137 | "type": "object",
138 | "properties": {
139 | "file-size": {
140 | "type": "integer"
141 | },
142 | "flavor": {
143 | "type": "string"
144 | }
145 | },
146 | "required": [
147 | "file-size",
148 | "flavor"
149 | ]
150 | },
151 | "metadata": {
152 | "type": "object",
153 | "properties": {
154 | "MacUIRequiredDeviceCapabilities": {
155 | "type": "object",
156 | "properties": {
157 | "arm64": {
158 | "type": "boolean"
159 | }
160 | },
161 | "required": [
162 | "arm64"
163 | ]
164 | },
165 | "UIRequiredDeviceCapabilities": {
166 | "type": "object",
167 | "properties": {
168 | "arm64": {
169 | "type": "boolean"
170 | }
171 | },
172 | "required": [
173 | "arm64"
174 | ]
175 | },
176 | "WKRunsIndependentlyOfCompanionApp": {
177 | "type": "boolean"
178 | },
179 | "WKWatchOnly": {
180 | "type": "boolean"
181 | },
182 | "appleWatchEnabled": {
183 | "type": "boolean"
184 | },
185 | "artistId": {
186 | "type": "integer"
187 | },
188 | "artistName": {
189 | "type": "string"
190 | },
191 | "bundleDisplayName": {
192 | "type": "string"
193 | },
194 | "bundleShortVersionString": {
195 | "type": "string"
196 | },
197 | "bundleVersion": {
198 | "type": "string"
199 | },
200 | "copyright": {
201 | "type": "string"
202 | },
203 | "fileExtension": {
204 | "type": "string"
205 | },
206 | "gameCenterEnabled": {
207 | "type": "boolean"
208 | },
209 | "gameCenterEverEnabled": {
210 | "type": "boolean"
211 | },
212 | "genre": {
213 | "type": "string"
214 | },
215 | "genreId": {
216 | "type": "integer"
217 | },
218 | "itemId": {
219 | "type": "integer"
220 | },
221 | "itemName": {
222 | "type": "string"
223 | },
224 | "kind": {
225 | "type": "string"
226 | },
227 | "nameTranscriptions": {
228 | "type": "object",
229 | "properties": {
230 | "zh-Hans-CN": {
231 | "type": "array",
232 | "items": {
233 | "type": "string"
234 | }
235 | }
236 | },
237 | "required": [
238 | "zh-Hans-CN"
239 | ]
240 | },
241 | "playlistName": {
242 | "type": "string"
243 | },
244 | "product-type": {
245 | "type": "string"
246 | },
247 | "rating": {
248 | "type": "object",
249 | "properties": {
250 | "content": {
251 | "type": "string"
252 | },
253 | "label": {
254 | "type": "string"
255 | },
256 | "rank": {
257 | "type": "integer"
258 | },
259 | "system": {
260 | "type": "string"
261 | }
262 | },
263 | "required": [
264 | "content",
265 | "label",
266 | "rank",
267 | "system"
268 | ]
269 | },
270 | "releaseDate": {
271 | "type": "string"
272 | },
273 | "requiresRosetta": {
274 | "type": "boolean"
275 | },
276 | "runsOnAppleSilicon": {
277 | "type": "boolean"
278 | },
279 | "runsOnIntel": {
280 | "type": "boolean"
281 | },
282 | "s": {
283 | "type": "integer"
284 | },
285 | "software-platform": {
286 | "type": "string"
287 | },
288 | "softwareIcon57x57URL": {
289 | "type": "string"
290 | },
291 | "softwareIconNeedsShine": {
292 | "type": "boolean"
293 | },
294 | "softwareSupportedDeviceIds": {
295 | "type": "array",
296 | "items": {
297 | "type": "integer"
298 | }
299 | },
300 | "softwareVersionBundleId": {
301 | "type": "string"
302 | },
303 | "softwareVersionExternalIdentifier": {
304 | "type": "integer"
305 | },
306 | "softwareVersionExternalIdentifiers": {
307 | "type": "array",
308 | "items": {
309 | "type": "integer"
310 | }
311 | },
312 | "vendorId": {
313 | "type": "integer"
314 | },
315 | "drmVersionNumber": {
316 | "type": "integer"
317 | },
318 | "versionRestrictions": {
319 | "type": "integer"
320 | },
321 | "storeCohort": {
322 | "type": "string"
323 | },
324 | "hasOrEverHasHadIAP": {
325 | "type": "boolean"
326 | }
327 | },
328 | "required": [
329 | "MacUIRequiredDeviceCapabilities",
330 | "UIRequiredDeviceCapabilities",
331 | "WKRunsIndependentlyOfCompanionApp",
332 | "WKWatchOnly",
333 | "appleWatchEnabled",
334 | "artistId",
335 | "artistName",
336 | "bundleDisplayName",
337 | "bundleShortVersionString",
338 | "bundleVersion",
339 | "copyright",
340 | "fileExtension",
341 | "gameCenterEnabled",
342 | "gameCenterEverEnabled",
343 | "genre",
344 | "genreId",
345 | "itemId",
346 | "itemName",
347 | "kind",
348 | "nameTranscriptions",
349 | "playlistName",
350 | "product-type",
351 | "rating",
352 | "releaseDate",
353 | "requiresRosetta",
354 | "runsOnAppleSilicon",
355 | "runsOnIntel",
356 | "s",
357 | "software-platform",
358 | "softwareIcon57x57URL",
359 | "softwareIconNeedsShine",
360 | "softwareSupportedDeviceIds",
361 | "softwareVersionBundleId",
362 | "softwareVersionExternalIdentifier",
363 | "softwareVersionExternalIdentifiers",
364 | "vendorId",
365 | "drmVersionNumber",
366 | "versionRestrictions",
367 | "storeCohort",
368 | "hasOrEverHasHadIAP"
369 | ]
370 | }
371 | },
372 | "required": [
373 | "songId",
374 | "URL",
375 | "downloadKey",
376 | "artworkURL",
377 | "artwork-urls",
378 | "md5",
379 | "chunks",
380 | "isStreamable",
381 | "uncompressedSize",
382 | "sinfs",
383 | "purchaseDate",
384 | "download-id",
385 | "is-in-queue",
386 | "asset-info",
387 | "metadata"
388 | ]
389 | }
390 | },
391 | "download-queue-info": {
392 | "type": "object",
393 | "properties": {
394 | "download-queue-item-count": {
395 | "type": "integer"
396 | },
397 | "dsid": {
398 | "type": "integer"
399 | },
400 | "is-auto-download-machine": {
401 | "type": "boolean"
402 | }
403 | },
404 | "required": [
405 | "download-queue-item-count",
406 | "dsid",
407 | "is-auto-download-machine"
408 | ]
409 | },
410 | "metrics": {
411 | "type": "object",
412 | "properties": {
413 | "itemIds": {
414 | "type": "array",
415 | "items": {
416 | "type": "integer"
417 | }
418 | },
419 | "price": {
420 | "type": "integer"
421 | },
422 | "priceType": {
423 | "type": "string"
424 | },
425 | "productTypes": {
426 | "type": "array",
427 | "items": {
428 | "type": "string"
429 | }
430 | },
431 | "mtApp": {
432 | "type": "string"
433 | },
434 | "mtClientId": {
435 | "type": "string"
436 | },
437 | "mtEventTime": {
438 | "type": "string"
439 | },
440 | "mtPageId": {
441 | "type": "string"
442 | },
443 | "mtPageType": {
444 | "type": "string"
445 | },
446 | "mtPrevPage": {
447 | "type": "string"
448 | },
449 | "mtRequestId": {
450 | "type": "string"
451 | },
452 | "mtTopic": {
453 | "type": "string"
454 | },
455 | "currency": {
456 | "type": "string"
457 | },
458 | "exchangeRateToUSD": {
459 | "type": "number"
460 | },
461 | "commerceEvent_purchase_priceType": {
462 | "type": "string"
463 | },
464 | "commerceEvent_storeFrontId": {
465 | "type": "string"
466 | },
467 | "commerceEvent_result_resultType": {
468 | "type": "integer"
469 | },
470 | "commerceEvent_flowType": {
471 | "type": "integer"
472 | },
473 | "commerceEvent_flowStep": {
474 | "type": "integer"
475 | },
476 |
477 | "dialogId": {
478 | "type": "string"
479 | },
480 | "message": {
481 | "type": "string"
482 | },
483 | "messageCode": {
484 | "type": "string"
485 | },
486 | "options": {
487 | "type": "array",
488 | "items": {
489 | "type": "string"
490 | }
491 | },
492 | "actionUrl": {
493 | "type": "string"
494 | },
495 | "asnState": {
496 | "type": "integer"
497 | },
498 | "eventType": {
499 | "type": "string"
500 | }
501 | },
502 | "required": [
503 | "mtApp",
504 | "mtClientId",
505 | "mtEventTime",
506 | "mtPageId",
507 | "mtPageType",
508 | "mtPrevPage",
509 | "mtRequestId",
510 | "mtTopic"
511 | ],
512 | "optional": [
513 | "itemIds",
514 | "price",
515 | "priceType",
516 | "productTypes",
517 | "currency",
518 | "exchangeRateToUSD",
519 | "commerceEvent_purchase_priceType",
520 | "commerceEvent_storeFrontId",
521 | "commerceEvent_result_resultType",
522 | "commerceEvent_flowType",
523 | "commerceEvent_flowStep",
524 |
525 | "dialogId",
526 | "message",
527 | "messageCode",
528 | "options",
529 | "actionUrl",
530 | "asnState",
531 | "eventType"
532 | ]
533 | },
534 | "duAnonymousPings": {
535 | "type": "array",
536 | "items": {
537 | "type": "string"
538 | }
539 | },
540 | "subscriptionStatus": {
541 | "type": "object",
542 | "properties": {
543 | "music": {
544 | "type": "object",
545 | "properties": {
546 | "status": {
547 | "type": "string"
548 | },
549 | "reason": {
550 | "type": "string"
551 | },
552 | "isAdmin": {
553 | "type": "boolean"
554 | },
555 | "isNotEligibleForFreeTrial": {
556 | "type": "boolean"
557 | }
558 | },
559 | "required": [
560 | "status",
561 | "reason",
562 | "isAdmin",
563 | "isNotEligibleForFreeTrial"
564 | ]
565 | },
566 | "terms": {
567 | "type": "array",
568 | "items": {
569 | "type": "object",
570 | "properties": {
571 | "type": {
572 | "type": "string"
573 | },
574 | "latestTerms": {
575 | "type": "integer"
576 | },
577 | "agreedToTerms": {
578 | "type": "integer"
579 | },
580 | "source": {
581 | "type": "string"
582 | }
583 | },
584 | "required": [
585 | "type",
586 | "latestTerms",
587 | "agreedToTerms",
588 | "source"
589 | ]
590 | }
591 | },
592 | "account": {
593 | "type": "object",
594 | "properties": {
595 | "isMinor": {
596 | "type": "boolean"
597 | },
598 | "suspectUnderage": {
599 | "type": "boolean"
600 | }
601 | },
602 | "required": [
603 | "isMinor",
604 | "suspectUnderage"
605 | ]
606 | },
607 | "family": {
608 | "type": "object",
609 | "properties": {
610 | "hasFamily": {
611 | "type": "boolean"
612 | }
613 | },
614 | "required": [
615 | "hasFamily"
616 | ]
617 | }
618 | },
619 | "required": [
620 | "music",
621 | "terms",
622 | "account",
623 | "family"
624 | ]
625 | },
626 | "cancel-purchase-batch": {
627 | "type": "boolean"
628 | },
629 | "failureType": {
630 | "type": "string"
631 | },
632 | "customerMessage": {
633 | "type": "string"
634 | },
635 | "m-allowed": {
636 | "type": "boolean"
637 | },
638 | "dialog": {
639 | "type": "object",
640 | "properties": {
641 | "kind": {
642 | "type": "string"
643 | },
644 | "m-allowed": {
645 | "type": "boolean"
646 | },
647 | "use-keychain": {
648 | "type": "boolean"
649 | },
650 | "isFree": {
651 | "type": "boolean"
652 | },
653 | "message": {
654 | "type": "string"
655 | },
656 | "explanation": {
657 | "type": "string"
658 | },
659 | "defaultButton": {
660 | "type": "string"
661 | },
662 | "okButtonString": {
663 | "type": "string"
664 | },
665 | "okButtonAction": {
666 | "type": "object",
667 | "properties": {
668 | "kind": {
669 | "type": "string"
670 | },
671 | "buyParams": {
672 | "type": "string"
673 | },
674 | "itemName": {
675 | "type": "string"
676 | }
677 | },
678 | "required": [
679 | "kind",
680 | "buyParams",
681 | "itemName"
682 | ]
683 | },
684 | "cancelButtonString": {
685 | "type": "string"
686 | },
687 | "initialCheckboxValue": {
688 | "type": "boolean"
689 | }
690 | },
691 | "required": [
692 | "kind",
693 | "m-allowed",
694 | "use-keychain",
695 | "isFree",
696 | "message",
697 | "explanation",
698 | "defaultButton",
699 | "okButtonString",
700 | "okButtonAction",
701 | "cancelButtonString",
702 | "initialCheckboxValue"
703 | ]
704 | }
705 | },
706 | "required": [
707 | "pings",
708 | "metrics"
709 | ],
710 | "optional": [
711 | "jingleDocType",
712 | "jingleAction",
713 | "status",
714 | "dsPersonId",
715 | "creditDisplay",
716 | "creditBalance",
717 | "freeSongBalance",
718 | "creditDisplayInternal",
719 | "authorized",
720 | "download-queue-item-count",
721 | "songList",
722 | "download-queue-info",
723 | "duAnonymousPings",
724 | "subscriptionStatus",
725 |
726 | "failureType",
727 | "customerMessage",
728 | "m-allowed",
729 | "dialog",
730 | "cancel-purchase-batch"
731 | ]
732 | }
--------------------------------------------------------------------------------
/reqs/schemas/schema_defs/store_download_req.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Store Download Req",
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "type": "object",
5 | "properties": {
6 | "creditDisplay": {
7 | "type": "string"
8 | },
9 | "guid": {
10 | "type": "string"
11 | },
12 | "salableAdamId": {
13 | "type": "string"
14 | },
15 | "externalVersionId": {
16 | "type": "string"
17 | }
18 | },
19 | "required": [
20 | "creditDisplay",
21 | "guid",
22 | "salableAdamId"
23 | ]
24 | }
--------------------------------------------------------------------------------
/reqs/schemas/schema_defs/store_download_resp.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Store Download Resp",
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "type": "object",
5 | "properties": {
6 | "pings": {
7 | "type": "array",
8 | "items": {
9 | "type": ["number","string","boolean","object","array", "null"]
10 | }
11 | },
12 | "cancel-purchase-batch": {
13 | "type": "boolean"
14 | },
15 | "customerMessage": {
16 | "type": "string"
17 | },
18 | "failureType": {
19 | "type": "string"
20 | },
21 | "jingleDocType": {
22 | "type": "string"
23 | },
24 | "jingleAction": {
25 | "type": "string"
26 | },
27 | "status": {
28 | "type": "integer"
29 | },
30 | "dsPersonId": {
31 | "type": "string"
32 | },
33 | "creditDisplay": {
34 | "type": "string"
35 | },
36 | "creditBalance": {
37 | "type": "string"
38 | },
39 | "freeSongBalance": {
40 | "type": "string"
41 | },
42 | "authorized": {
43 | "type": "boolean"
44 | },
45 | "download-queue-item-count": {
46 | "type": "integer"
47 | },
48 | "songList": {
49 | "type": "array",
50 | "items": {
51 | "type": "object",
52 | "properties": {
53 | "songId": {
54 | "type": "integer"
55 | },
56 | "URL": {
57 | "type": "string"
58 | },
59 | "downloadKey": {
60 | "type": "string"
61 | },
62 | "artworkURL": {
63 | "type": "string"
64 | },
65 | "artwork-urls": {
66 | "type": "object",
67 | "properties": {
68 | "image-type": {
69 | "type": "string"
70 | },
71 | "default": {
72 | "type": "object",
73 | "properties": {
74 | "url": {
75 | "type": "string"
76 | }
77 | },
78 | "required": [
79 | "url"
80 | ]
81 | }
82 | },
83 | "required": [
84 | "image-type",
85 | "default"
86 | ]
87 | },
88 | "md5": {
89 | "type": "string"
90 | },
91 | "chunks": {
92 | "type": "object",
93 | "properties": {
94 | "chunkSize": {
95 | "type": "integer"
96 | },
97 | "hashes": {
98 | "type": "array",
99 | "items": {
100 | "type": "string"
101 | }
102 | }
103 | },
104 | "required": [
105 | "chunkSize",
106 | "hashes"
107 | ]
108 | },
109 | "isStreamable": {
110 | "type": "boolean"
111 | },
112 | "uncompressedSize": {
113 | "type": "string"
114 | },
115 | "sinfs": {
116 | "type": "array",
117 | "items": {
118 | "type": "object",
119 | "properties": {
120 | "id": {
121 | "type": "integer"
122 | },
123 | "sinf": {
124 | "type": "string"
125 | }
126 | },
127 | "required": [
128 | "id",
129 | "sinf"
130 | ]
131 | }
132 | },
133 | "purchaseDate": {
134 | "type": "string"
135 | },
136 | "download-id": {
137 | "type": "string"
138 | },
139 | "is-in-queue": {
140 | "type": "boolean"
141 | },
142 | "asset-info": {
143 | "type": "object",
144 | "properties": {
145 | "file-size": {
146 | "type": "integer"
147 | },
148 | "flavor": {
149 | "type": "string"
150 | }
151 | },
152 | "required": [
153 | "file-size",
154 | "flavor"
155 | ]
156 | },
157 | "metadata": {
158 | "type": "object",
159 | "properties": {
160 | "MacUIRequiredDeviceCapabilities": {
161 | "type": "object",
162 | "properties": {
163 | "arm64": {
164 | "type": "boolean"
165 | },
166 | "gamekit": {
167 | "type": "boolean"
168 | },
169 | "metal": {
170 | "type": "boolean"
171 | }
172 | },
173 | "required": [
174 | "arm64",
175 | "gamekit",
176 | "metal"
177 | ]
178 | },
179 | "UIRequiredDeviceCapabilities": {
180 | "type": "object",
181 | "properties": {
182 | "arm64": {
183 | "type": "boolean"
184 | },
185 | "gamekit": {
186 | "type": "boolean"
187 | },
188 | "metal": {
189 | "type": "boolean"
190 | }
191 | },
192 | "required": [
193 | "arm64",
194 | "gamekit",
195 | "metal"
196 | ]
197 | },
198 | "artistId": {
199 | "type": "integer"
200 | },
201 | "artistName": {
202 | "type": "string"
203 | },
204 | "bundleDisplayName": {
205 | "type": "string"
206 | },
207 | "bundleShortVersionString": {
208 | "type": "string"
209 | },
210 | "bundleVersion": {
211 | "type": "string"
212 | },
213 | "copyright": {
214 | "type": "string"
215 | },
216 | "fileExtension": {
217 | "type": "string"
218 | },
219 | "gameCenterEnabled": {
220 | "type": "boolean"
221 | },
222 | "gameCenterEverEnabled": {
223 | "type": "boolean"
224 | },
225 | "genre": {
226 | "type": "string"
227 | },
228 | "genreId": {
229 | "type": "integer"
230 | },
231 | "itemId": {
232 | "type": "integer"
233 | },
234 | "itemName": {
235 | "type": "string"
236 | },
237 | "kind": {
238 | "type": "string"
239 | },
240 | "playlistName": {
241 | "type": "string"
242 | },
243 | "product-type": {
244 | "type": "string"
245 | },
246 | "rating": {
247 | "type": "object",
248 | "properties": {
249 | "content": {
250 | "type": "string"
251 | },
252 | "label": {
253 | "type": "string"
254 | },
255 | "rank": {
256 | "type": "integer"
257 | },
258 | "system": {
259 | "type": "string"
260 | }
261 | },
262 | "required": [
263 | "content",
264 | "label",
265 | "rank",
266 | "system"
267 | ]
268 | },
269 | "releaseDate": {
270 | "type": "string"
271 | },
272 | "requiresRosetta": {
273 | "type": "boolean"
274 | },
275 | "runsOnAppleSilicon": {
276 | "type": "boolean"
277 | },
278 | "runsOnIntel": {
279 | "type": "boolean"
280 | },
281 | "s": {
282 | "type": "integer"
283 | },
284 | "software-platform": {
285 | "type": "string"
286 | },
287 | "softwareIcon57x57URL": {
288 | "type": "string"
289 | },
290 | "softwareIconNeedsShine": {
291 | "type": "boolean"
292 | },
293 | "softwareSupportedDeviceIds": {
294 | "type": "array",
295 | "items": {
296 | "type": "integer"
297 | }
298 | },
299 | "softwareVersionBundleId": {
300 | "type": "string"
301 | },
302 | "softwareVersionExternalIdentifier": {
303 | "type": "integer"
304 | },
305 | "softwareVersionExternalIdentifiers": {
306 | "type": "array",
307 | "items": {
308 | "type": "integer"
309 | }
310 | },
311 | "subgenres": {
312 | "type": "array",
313 | "items": {
314 | "type": "object",
315 | "properties": {
316 | "genre": {
317 | "type": "string"
318 | },
319 | "genreId": {
320 | "type": "integer"
321 | }
322 | },
323 | "required": [
324 | "genre",
325 | "genreId"
326 | ]
327 | }
328 | },
329 | "vendorId": {
330 | "type": "integer"
331 | },
332 | "drmVersionNumber": {
333 | "type": "integer"
334 | },
335 | "versionRestrictions": {
336 | "type": "integer"
337 | }
338 | },
339 | "required": [
340 | "MacUIRequiredDeviceCapabilities",
341 | "UIRequiredDeviceCapabilities",
342 | "artistId",
343 | "artistName",
344 | "bundleDisplayName",
345 | "bundleShortVersionString",
346 | "bundleVersion",
347 | "copyright",
348 | "fileExtension",
349 | "gameCenterEnabled",
350 | "gameCenterEverEnabled",
351 | "genre",
352 | "genreId",
353 | "itemId",
354 | "itemName",
355 | "kind",
356 | "playlistName",
357 | "product-type",
358 | "rating",
359 | "releaseDate",
360 | "requiresRosetta",
361 | "runsOnAppleSilicon",
362 | "runsOnIntel",
363 | "s",
364 | "software-platform",
365 | "softwareIcon57x57URL",
366 | "softwareIconNeedsShine",
367 | "softwareSupportedDeviceIds",
368 | "softwareVersionBundleId",
369 | "softwareVersionExternalIdentifier",
370 | "softwareVersionExternalIdentifiers",
371 | "subgenres",
372 | "vendorId",
373 | "drmVersionNumber",
374 | "versionRestrictions"
375 | ]
376 | }
377 | },
378 | "required": [
379 | "songId",
380 | "URL",
381 | "downloadKey",
382 | "artworkURL",
383 | "artwork-urls",
384 | "md5",
385 | "chunks",
386 | "isStreamable",
387 | "uncompressedSize",
388 | "sinfs",
389 | "purchaseDate",
390 | "download-id",
391 | "is-in-queue",
392 | "asset-info",
393 | "metadata"
394 | ]
395 | }
396 | },
397 | "metrics": {
398 | "type": "object",
399 | "properties": {
400 | "itemIds": {
401 | "type": "array",
402 | "items": {
403 | "type": "integer"
404 | }
405 | },
406 | "currency": {
407 | "type": "string"
408 | },
409 | "exchangeRateToUSD": {
410 | "type": "number"
411 | }
412 | },
413 | "required": [
414 | "itemIds",
415 | "currency",
416 | "exchangeRateToUSD"
417 | ]
418 | },
419 | "subscriptionStatus": {
420 | "type": "object",
421 | "properties": {
422 | "terms": {
423 | "type": "array",
424 | "items": {
425 | "type": "object",
426 | "properties": {
427 | "type": {
428 | "type": "string"
429 | },
430 | "latestTerms": {
431 | "type": "integer"
432 | },
433 | "agreedToTerms": {
434 | "type": "integer"
435 | },
436 | "source": {
437 | "type": "string"
438 | }
439 | },
440 | "required": [
441 | "type",
442 | "latestTerms",
443 | "agreedToTerms",
444 | "source"
445 | ]
446 | }
447 | },
448 | "account": {
449 | "type": "object",
450 | "properties": {
451 | "isMinor": {
452 | "type": "boolean"
453 | },
454 | "suspectUnderage": {
455 | "type": "boolean"
456 | }
457 | },
458 | "required": [
459 | "isMinor",
460 | "suspectUnderage"
461 | ]
462 | },
463 | "family": {
464 | "type": "object",
465 | "properties": {
466 | "hasFamily": {
467 | "type": "boolean"
468 | }
469 | },
470 | "required": [
471 | "hasFamily"
472 | ]
473 | }
474 | },
475 | "required": [
476 | "terms",
477 | "account",
478 | "family"
479 | ]
480 | }
481 | },
482 | "required": [
483 | "pings",
484 | "jingleDocType",
485 | "jingleAction",
486 | "status",
487 | "dsPersonId",
488 | "creditDisplay",
489 | "creditBalance",
490 | "freeSongBalance",
491 | "authorized",
492 | "download-queue-item-count",
493 | "songList",
494 | "metrics",
495 | "subscriptionStatus"
496 | ]
497 | }
--------------------------------------------------------------------------------
/reqs/schemas/schema_examples/itunes_lookup_resp.log:
--------------------------------------------------------------------------------
1 | {
2 | "resultCount":1,
3 | "results": [
4 | {
5 | "screenshotUrls":["https://is1-ssl.mzstatic.com/image/thumb/Purple123/v4/c7/9d/5a/c79d5ac9-4543-e9d7-acb3-4753704f1488/pr_source.png/392x696bb.png", "https://is5-ssl.mzstatic.com/image/thumb/Purple123/v4/e4/38/3a/e4383af3-d9ed-3d0c-9c80-ca0d413c0f06/pr_source.png/392x696bb.png", "https://is5-ssl.mzstatic.com/image/thumb/Purple123/v4/08/b2/39/08b23995-3c76-3e74-5af4-3edb4914dc4c/pr_source.png/392x696bb.png"],
6 | "ipadScreenshotUrls":["https://is5-ssl.mzstatic.com/image/thumb/Purple113/v4/67/74/39/6774390f-7a12-3d9f-ded7-392b9af90663/pr_source.png/576x768bb.png", "https://is4-ssl.mzstatic.com/image/thumb/Purple123/v4/88/62/85/886285f0-b1c2-ad6b-3ab3-2a5ee70d7c9d/pr_source.png/576x768bb.png", "https://is4-ssl.mzstatic.com/image/thumb/Purple123/v4/29/39/68/29396836-1b94-1561-4bd1-cde929ae5baa/pr_source.png/576x768bb.png"], "appletvScreenshotUrls":[],
7 | "artworkUrl60":"https://is3-ssl.mzstatic.com/image/thumb/Purple123/v4/6b/ed/31/6bed31bd-42d1-5ae9-04fd-a04c865af27d/AppIcon-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/60x60bb.jpg",
8 | "artworkUrl512":"https://is3-ssl.mzstatic.com/image/thumb/Purple123/v4/6b/ed/31/6bed31bd-42d1-5ae9-04fd-a04c865af27d/AppIcon-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/512x512bb.jpg",
9 | "artworkUrl100":"https://is3-ssl.mzstatic.com/image/thumb/Purple123/v4/6b/ed/31/6bed31bd-42d1-5ae9-04fd-a04c865af27d/AppIcon-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/100x100bb.jpg", "artistViewUrl":"https://apps.apple.com/us/developer/potatso-lab-ltd/id1267906737?uo=4",
10 | "supportedDevices":["iPhone5s-iPhone5s", "iPadAir-iPadAir", "iPadAirCellular-iPadAirCellular", "iPadMiniRetina-iPadMiniRetina", "iPadMiniRetinaCellular-iPadMiniRetinaCellular", "iPhone6-iPhone6", "iPhone6Plus-iPhone6Plus", "iPadAir2-iPadAir2", "iPadAir2Cellular-iPadAir2Cellular", "iPadMini3-iPadMini3", "iPadMini3Cellular-iPadMini3Cellular", "iPodTouchSixthGen-iPodTouchSixthGen", "iPhone6s-iPhone6s", "iPhone6sPlus-iPhone6sPlus", "iPadMini4-iPadMini4", "iPadMini4Cellular-iPadMini4Cellular", "iPadPro-iPadPro", "iPadProCellular-iPadProCellular", "iPadPro97-iPadPro97", "iPadPro97Cellular-iPadPro97Cellular", "iPhoneSE-iPhoneSE", "iPhone7-iPhone7", "iPhone7Plus-iPhone7Plus", "iPad611-iPad611", "iPad612-iPad612", "iPad71-iPad71", "iPad72-iPad72", "iPad73-iPad73", "iPad74-iPad74", "iPhone8-iPhone8", "iPhone8Plus-iPhone8Plus", "iPhoneX-iPhoneX", "iPad75-iPad75", "iPad76-iPad76", "iPhoneXS-iPhoneXS", "iPhoneXSMax-iPhoneXSMax", "iPhoneXR-iPhoneXR", "iPad812-iPad812", "iPad834-iPad834", "iPad856-iPad856", "iPad878-iPad878", "iPadMini5-iPadMini5", "iPadMini5Cellular-iPadMini5Cellular", "iPadAir3-iPadAir3", "iPadAir3Cellular-iPadAir3Cellular", "iPodTouchSeventhGen-iPodTouchSeventhGen", "iPhone11-iPhone11", "iPhone11Pro-iPhone11Pro", "iPadSeventhGen-iPadSeventhGen", "iPadSeventhGenCellular-iPadSeventhGenCellular", "iPhone11ProMax-iPhone11ProMax", "iPhoneSESecondGen-iPhoneSESecondGen", "iPadProSecondGen-iPadProSecondGen", "iPadProSecondGenCellular-iPadProSecondGenCellular", "iPadProFourthGen-iPadProFourthGen", "iPadProFourthGenCellular-iPadProFourthGenCellular", "iPhone12Mini-iPhone12Mini", "iPhone12-iPhone12", "iPhone12Pro-iPhone12Pro", "iPhone12ProMax-iPhone12ProMax", "iPadAir4-iPadAir4", "iPadAir4Cellular-iPadAir4Cellular", "iPadEighthGen-iPadEighthGen", "iPadEighthGenCellular-iPadEighthGenCellular", "iPadProThirdGen-iPadProThirdGen", "iPadProThirdGenCellular-iPadProThirdGenCellular", "iPadProFifthGen-iPadProFifthGen", "iPadProFifthGenCellular-iPadProFifthGenCellular", "iPhone13Pro-iPhone13Pro", "iPhone13ProMax-iPhone13ProMax", "iPhone13Mini-iPhone13Mini", "iPhone13-iPhone13", "iPadMiniSixthGen-iPadMiniSixthGen", "iPadMiniSixthGenCellular-iPadMiniSixthGenCellular", "iPadNinthGen-iPadNinthGen", "iPadNinthGenCellular-iPadNinthGenCellular", "iPhoneSEThirdGen-iPhoneSEThirdGen", "iPadAirFifthGen-iPadAirFifthGen", "iPadAirFifthGenCellular-iPadAirFifthGenCellular"], "features":["iosUniversal"], "advisories":[], "isGameCenterEnabled":false, "kind":"software", "minimumOsVersion":"13.0", "trackCensoredName":"Potatso Lite", "languageCodesISO2A":["EN", "ZH"], "fileSizeBytes":"18907136", "sellerUrl":"https://potatso.com/en", "formattedPrice":"Free", "contentAdvisoryRating":"4+", "averageUserRatingForCurrentVersion":4.595620000000000260342858382500708103179931640625, "userRatingCountForCurrentVersion":2923, "averageUserRating":4.595620000000000260342858382500708103179931640625, "trackViewUrl":"https://apps.apple.com/us/app/potatso-lite/id1239860606?uo=4", "trackContentRating":"4+", "trackId":1239860606, "trackName":"Potatso Lite", "bundleId":"com.touchingapp.potatsolite", "primaryGenreName":"Utilities", "releaseDate":"2017-06-01T02:34:35Z", "genreIds":["6002", "6007"], "isVppDeviceBasedLicensingEnabled":true, "sellerName":"Potatso Lab LTD", "currentVersionReleaseDate":"2019-12-16T23:27:27Z",
11 | "releaseNotes":"With this update, we're bringing you some exciting new features and changes. \n\n=====================\n===== What's New =====\n=====================\n\n• The app will support iOS 13+ from now on.\n• We're introducing the brand new logo with clarity and simplicity.\n• We've redeisnged the whole UI to improve your using experience.\n• Some other internal performance improvements and bug fixes.", "primaryGenreId":6002, "currency":"USD", "version":"2.5.0", "wrapperType":"software", "artistId":1267906737, "artistName":"Potatso Lab LTD", "genres":["Utilities", "Productivity"], "price":0.00,
12 | "description":"Potatso Lite is a powerful network tool which empowers your phone to have fully customized network environment. It's friendly for both beginners and power users. \n\nPotatso now supports Shadowsocks, ShadowsocksR, HTTP and Socks5 proxies. You can either setup one by yourself or buy from any proxy providers.\n\nEmbedded smart rouing feature for Chinese users is super helpful which can cost less data in proxy servers and speed up domestic network traffic.\n\n=== Features ===\n- Custom proxy supports Shadowsocks, ShadowsocksR, HTTP and Socks5 \n- Run in the background sustainably without interrupting you\n- Both cellular and Wi-Fi are supported\n- Custom DNS support\n- Smart Routing for Chinese users \n\n=== Privacy ===\nWe respect your privacy so NO confidential data will be uploaded or shared with third parties\n\n=== Feedback ===\nPlease contact on hi@potatso.com", "userRatingCount":2923}]
13 | }
--------------------------------------------------------------------------------
/reqs/schemas/schema_examples/store_buyproduct_req.log:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | appExtVrsId
5 | 848463733
6 | guid
7 | 22330C8C.C2E39C5C.06C40B91.D0990F95.9A890F9D.AB7F7EB4.56507025
8 | kbsync
9 |
10 | AAQAA5nBc3Q7ETi+TD1x8AM4wbw9sqWglXBaXwuIrlMR9OnAPY89zaoTvbe6PneuS1x1
11 | 31NxVIqrAsIZLmxy5be8dQtq8je/rtYTRlxduU9NwW4DBcplBx6vs9qhS3Y8B45Zz4T5
12 | dkmDG4UnS7xnAPwew7jEX/uY38zZhtKu4IN+sl/Whvyh2SkZg/5vGCtjav17CGbP8ZWo
13 | Ci3FhEqAByOL0g6zhPdTHyqF84Apg9fh395tGpzzAWB8mYsRQXJcUHH1cuJjO/qMTkZ4
14 | ZxIJqfMaDJpS20nFq+/Bfg9FvC/83AOPnDfXZTsol3PFKqQ6sLgz6dKIho4Qd2UPABnj
15 | kBx4TFPeYBlm2T6GKfi8tr+rDhsMrbNczpnaUS+3cesIOvDsE3YCX4isOmMtg5yrJ5pi
16 | 2GHuofHD2I7Dj6fOh79I7F5OZb71PUvbeABjxvS5b57LGNICSBc/GJ0CEja27kpYals+
17 | bgYG0rVm+vqlAHwRpka5jzeK8DLrvTr22vtBLv62LpTVpVVglr5nbk99BcXG5gA=
18 |
19 | machineName
20 | DESKTOP-697LVJS
21 | mtApp
22 | com.apple.iTunes
23 | mtClientId
24 | 3z21abvCzFDuz5CYz9bdz19maFVKge
25 | mtEventTime
26 | 1652006678827
27 | mtPageId
28 | ccfce0ef-4ac8-4d5a-8e5f-79876ac474a4
29 | mtPageType
30 | Search
31 | mtPrevPage
32 | Purchases
33 | mtRequestId
34 | 3z21abvCzFDuz5CYz9bdz19maFVKgezL2X64FFVz1MGG
35 | mtTopic
36 | xp_its_main
37 | needDiv
38 | 0
39 | pg
40 | default
41 | price
42 | 0
43 | pricingParameters
44 | STDQ
45 | productType
46 | C
47 | salableAdamId
48 | 444934666
49 |
50 |
51 |
52 |
53 |
54 |
55 | ageCheck
56 | true
57 | appExtVrsId
58 | 848463733
59 | guid
60 | 22330C8C.C2E39C5C.06C40B91.D0990F95.9A890F9D.AB7F7EB4.56507025
61 | hasBeenAuthedForBuy
62 | true
63 | isInApp
64 | false
65 | kbsync
66 |
67 | AAQAA1c+HfGD4vNLZJYBMpBucSo1bxeaeTEZ3FGUKLq0skmzTxVik6IGoQMGaP6OWsJU
68 | lBoDb3hxaacD57bkiAgRXc/vr21/CipX55hKLoTE53yah3DwBR9tS1cG7oaHFLIh1Vmn
69 | RV7G9LJCQqwSAbr4ugEIVmLULkqaHDfTm8VNDXxYej1p8ghKggMcBT0se5cpDqpVn/bE
70 | qehnOl6QsupUNjjLzDm58bmERm/AjGJVBHQveG+4Y+Y4e1eUO6QQcntmypjmSLDqhI60
71 | 31rCV3zwTTZrHmmXEwsZiGgYlSVHR3ne+O9BE+LIPiQxDwIMvjfV6SrzoOUOLlOKvBsk
72 | kI29+6H0QNyMUXojWPQf7bfr+9NBTMgJoDNJd5hEGHqiSKnp9V1ALU8S8QwcYzi3ZDPu
73 | A36lMtgMOFEnYibnGnP5S8i2t43ZSBglExE9LGTGO0/IEWX9gEhKjvMDuIwMt9zdtpye
74 | CXO8siqHY4MtmLlfwZZc/480ZSHfRF8ZQHBa0J0/pgByNQiNe9KMihz+QN7cQI4=
75 |
76 | machineName
77 | DESKTOP-697LVJS
78 | mtApp
79 | com.apple.iTunes
80 | mtClientId
81 | 3z21abvCzFDuz5CYz9bdz19maFVKge
82 | mtEventTime
83 | 1652006678827
84 | mtPageId
85 | ccfce0ef-4ac8-4d5a-8e5f-79876ac474a4
86 | mtPageType
87 | Search
88 | mtPrevPage
89 | Purchases
90 | mtRequestId
91 | 3z21abvCzFDuz5CYz9bdz19maFVKgezL2X64FFVz1MGG
92 | mtTopic
93 | xp_its_main
94 | needDiv
95 | 0
96 | pg
97 | default
98 | price
99 | 0
100 | pricingParameters
101 | STDQ
102 | productType
103 | C
104 | salableAdamId
105 | 444934666
106 |
107 |
--------------------------------------------------------------------------------
/reqs/schemas/schema_examples/store_buyproduct_resp.log:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | pings
5 | https://xp.apple.com/report/2/xp_its_main?app=com.apple.iTunes&code=MZCommerce.ASN.ExpiredPasswordToken&buttons=%E8%8E%B7%E5%8F%96%3A%E5%8F%96%E6%B6%88&baseVersion=1&dsId=16916646015&eventVersion=1&storeFrontHeader=143465-19%2C32&eventTime=1652006682067&eventType=dialog&message=%E9%9C%80%E8%A6%81%E7%99%BB%E5%BD%95
6 |
7 | metrics
8 |
9 | dialogIdMZCommerce.ASN.ExpiredPasswordToken
10 | messageéè¦ç»å½
11 | messageCode2072
12 | options
13 |
14 | Get
15 | Cancel
16 |
17 | actionUrlp36-buy.itunes.apple.com/WebObjects/MZBuy.woa/wa/buyProduct
18 | asnState2
19 | mtAppcom.apple.iTunes
20 | mtClientId3z21abvCzFDuz5CYz9bdz19maFVKge
21 | mtEventTime2022-05-08 10:44:38 Etc/GMT
22 | mtPageIdccfce0ef-4ac8-4d5a-8e5f-79876ac474a4
23 | mtPageTypeSearch
24 | mtPrevPagePurchases
25 | mtRequestId3z21abvCzFDuz5CYz9bdz19maFVKgezL2X64FFVz1MGG
26 | mtTopicxp_its_main
27 | eventTypedialog
28 |
29 | failureType2072
30 | customerMessageéè¦ç»å½
31 | m-allowed
32 | dialog
33 | kindauthorization
34 | m-allowed
35 | use-keychain
36 | isFree
37 | messageéè¦ç»å½
38 | explanationå¦ææ¨æ Apple ID åå¯ç ï¼è¯·å¨æ¤å¤è¾å
¥ãä¾å¦ï¼å¦ææ¨ä½¿ç¨è¿ iTunes Store æ iCloudï¼é£ä¹æ¨å·²æ Apple IDã
39 | defaultButtonok
40 | okButtonStringè·å
41 | okButtonActionkindBuy
42 | buyParamsmtEventTime=1652006678827&salableAdamId=444934666&mtRequestId=3z21abvCzFDuz5CYz9bdz19maFVKgezL2X64FFVz1MGG&appExtVrsId=848463733&mtTopic=xp_its_main&guid=22330C8C.C2E39C5C.06C40B91.D0990F95.9A890F9D.AB7F7EB4.56507025&hasBeenAuthedForBuy=true&isInApp=false&price=0&mtClientId=3z21abvCzFDuz5CYz9bdz19maFVKge&productType=C&mtPageType=Search&mtPageId=ccfce0ef-4ac8-4d5a-8e5f-79876ac474a4&machineName=DESKTOP-697LVJS&ageCheck=true&pg=default&mtApp=com.apple.iTunes&needDiv=0&mtPrevPage=Purchases&pricingParameters=STDQ
43 | itemNameQQ
44 |
45 | cancelButtonStringåæ¶
46 | initialCheckboxValue
47 | cancel-purchase-batch
48 |
49 |
50 |
51 |
52 |
53 |
54 | pings
55 |
56 | jingleDocTypepurchaseSuccess
57 | jingleActionpurchaseProduct
58 | status0
59 |
60 | dsPersonId10964418715
61 | creditDisplay
62 | creditBalance1311811
63 | freeSongBalance1311811
64 |
65 | authorizeddownload-queue-item-count0
66 | songList
67 |
68 |
69 | metrics
70 |
71 |
72 | itemIds
73 |
74 | 580311103
75 |
76 | price0.00
77 | priceTypeSTDQ
78 | productTypes
79 |
80 | C
81 |
82 | currencyJPY
83 | exchangeRateToUSD0.0076722418
84 | commerceEvent_purchase_priceTypeSTDQ
85 | commerceEvent_storeFrontId143462
86 | commerceEvent_result_resultType0
87 | commerceEvent_flowType4
88 | commerceEvent_flowStep6
89 |
90 |
91 | duAnonymousPings
92 |
93 | https://xp.apple.com/report/2/xp_app_buy?clientId=0&sf=143462&adamId=580311103
94 |
95 | subscriptionStatus
96 |
97 | terms
98 |
99 |
100 | typeStore
101 | latestTerms28
102 | agreedToTerms31
103 | sourceaccount
104 |
105 |
106 | account
107 |
108 | isMinor
109 | suspectUnderage
110 |
111 | family
112 |
113 | hasFamily
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | pings
124 |
125 | jingleDocTypepurchaseSuccess
126 | jingleActionpurchaseProduct
127 | status0
128 |
129 | dsPersonId16916646015
130 | creditDisplay
131 | creditBalance1311811
132 | freeSongBalance1311811
133 | creditDisplayInternal¥0.00+0+0+0+0+0
134 |
135 | authorized
136 |
137 | download-queue-item-count1
138 | songList
139 |
140 |
141 | songId444934666
142 | URLhttps://iosapps.itunes.apple.com/itunes-assets/Purple112/v4/8c/5d/34/8c5d343a-2132-8690-c6b1-866ec2f6b2f6/extDirwkazoqouvdywwgjk.lc.14519290919268642.5LAVFF7SAES2W.signed.dpkg.ipa?accessKey=1652201151_1282442801256894451_c%2FtMyevC%2FbCtPTcx3kpjPzNcDfSmgpz1CnzuNFgB%2F1n13VEU1IYDwlq1Xie8WXNHq4U4t341RRlyT3J1OI1Doy1%2FKOG8Pk4u38Kn30NHbCnmgCRC2r9lGlND9ZjU7AxGCQeVb5iHc74Vf5i7Exbg3hq5UfUddWq%2BBe7s3VEyvOX9Rikq0Hzj4OkYUkklrks95yuvrEhXjMFYO8YpQAr0tCjKoyU2rvjS%2BwnPYk5U0Hy6DnusEJ3JmJpJKC6EiLvb
143 | downloadKeyartworkURLhttps://is5-ssl.mzstatic.com/image/thumb/Purple112/v4/b9/ac/4e/b9ac4ef3-0152-fa92-872f-fe773d799117/AppIcon-1-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/2000x2000bb.jpgartwork-urls
144 |
145 | image-typedownload-queue-item
146 | default
147 |
148 | url
149 | https://is4-ssl.mzstatic.com/image/thumb/Purple112/v4/b9/ac/4e/b9ac4ef3-0152-fa92-872f-fe773d799117/AppIcon-1-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/57x57bb.jpg
150 |
151 |
152 | md5cb3a13a60ac4c9f507b0fefa351e0351chunks
153 |
154 | chunkSize10485760
155 | hashes
156 |
157 | a9c3b17b7fd5ce1380580c6f063c469b
158 | 8dfa7efed3cccca58987ec5fb5aad752
159 | c636e4bfcdc17f5ce23a8ef104a44fa3
160 | 90911e7b4d63bf8d4fc1c0bb2756211b
161 | 0c7ae0bfa13df1a3cb25d18eccf79083
162 | 4edbba71a6baabeba209cbe495144017
163 | 2b915fa08b532ef007787f3cf9ed68e6
164 | e8ae486763cd40c730a6f2008bb212df
165 | a91b20af08349a115d318a3c1a9ac69c
166 | 001a0a9e6f0f779365fd1a9e08993afa
167 | d9c965a523b445f662aac8eb49fa271e
168 | 22f61327c32a4569a4d81f29ae93f238
169 | 3caf910c1da7640a89a51c2ae33e8a3b
170 | 70fcd6e5517144b1298848bd964f4dcc
171 | 9fb4924aed5006d2c7037be6532d1652
172 | fb341fcaaf6027182f885628e15542f6
173 | a0999c33fa684fa21cf2d8c8a4d45e27
174 | 28025c9522f1dc05360e36a9e6da508b
175 | ea980ee5fae29238778cdd16a1f0a2fa
176 | 699c2c8bcd5b4b9353bba25a58bb45f8
177 | 10423be39910d01b9e763cc1514fef05
178 | 3da83694d61846e91a0a21981e88652a
179 | 937391e158c46e385bf1ebd2f17501aa
180 | 18a12b305b3a0086d76d4f9c72764a00
181 | c19f2b5c1eeb909db77b5d715e023511
182 | 80552d4ed44472cf0f307bc6e6e4180c
183 | 9af9bebfb73a15084186032efd64f95b
184 | bdc8305c863637d6031c079f3438b2b2
185 | 406fff31a2d714ffa1eb7c417232c478
186 | f29550bd0e718ca8588d13654a14e669
187 | ea916c4a8e8f772610bd4de2549ec891
188 | 9ff8f440fb8260ffe7e39e17215e8e9f
189 | b596fe96037d6e0e52401676b87fc6c0
190 | 1be6f6b3141c80d2b3ad952db9e92ebb
191 | 4a9061c50a209184f7d4a54f80ce61ef
192 | e6505a456ca2273700dcad1dead1f625
193 | 9ac28b8e7dd43be5fd309f16403afef8
194 | 5088792cdb4928532714a7023df1275d
195 | c0b0d04fd38919c8688c5e793b33d5e4
196 | a87331d5e314e6708f6cc67075e36c56
197 | 63ea3257f99d04b44730924cb4a0d553
198 | b3d953d072a3b0e8e264c16d96cebeaf
199 | 5eff58a53e7f7e87f57fcb038a4ad0a4
200 | e37467e9f2c7d336c3742b63ee70b4ab
201 | abfcdd0ba248ee9c5bc1b4bf8ab03c15
202 | 7c4d4ccfcaaef4708f3d1b0fce994294
203 | 927273c03da6f0fcdc674b40224c0a6d
204 | 648761edcf4c3d69e147b3671abee7a8
205 | 146e363283ed83bfd72a8c4d6b8fa7ac
206 | 69c8e188357d463d00bd7f8134786b1c
207 | babbb9b2f0c3d31a4d9e0e0d77852f5e
208 | ae46925c45e689783c38f065a9d1e7ea
209 | c7f4967cb830f008417c02009ae2196a
210 | 9b8f17524eb05a512c26bb9e2b89220b
211 | 11cdc44dd07fe5d9fa3eaba0a179704c
212 | d0f17891f1c141e39cfcacfb58c1bb3c
213 | 3273d004f5971ebee5625868d4978f69
214 | 0aa895b7d15ad0760156b3062fb6deeb
215 | 7b27b6e73b0731b307399aa4da837df9
216 |
217 |
218 | isStreamable
219 | uncompressedSize795979776
220 | sinfs id 0 sinf AAAEIHNpbmYAAAAMZnJtYWdhbWUAAAAUc2NobQAAAABpdHVuAAAAAAAAA3BzY2hpAAAADHVzZXLwT4h/AAAADGNyZHTenU/fAAAADGFzZHQAAAAAAAAADGtleSAAAAACAAAAGGl2aXaD6ySnLt5l3Pdp75fJ0mjxAAAAWHJpZ2h2ZUlEAAEOnHBsYXQAAAABYXZlcgEBAQB0cmFu3p1P33NpbmcAAAAAc29uZxqFKgp0b29sUDYwNG1lZGkAAACAbW9kZQAAIABoaTMyAAAAAwAAAQhuYW1l5q63IOWmueS7jQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcBwcml2eDOeoQvqIT8I9Y9+tU7xixDGAGJI8TvGWPhI2r7kjf9vKy/JunWkBOLN7Ft3eFaFidNSVs3VTlvYSulf3VT0QAc0+3/pFznoHsNC5pMpQ6y0HdBW+8nG+JbiLjeTU3Nj2akunI45wVAiE2IWr1mERJwFG5CavbGOvHM9JBxkHaczFWUUm60zUHuMiBGZiZVDbVI4Oilxdn0vRb3c2P0zDrhOyxez9B7ppEMzJpFkDa2ieFSE0peNxsx8xi+BLWqQ88IZiCpCrLma3P5Wj40hb0nXclQY2t9Zvnw54A2DC/NHEUEDq1T/cZvAi16AdDRB0/GWOTLf7RNf1R8glahArjoD+fddyk9MuVwEN63YqCH/bNh1L7+hpk0Vh2tDGuEy/KDA30kZgF9Z2FED9LAQpCmx1BHUc60NDq++mCImFNiO528OnwirxjZoMWCZWsyCBL+RDTLcNLZOVDwKJcCqP+xLhbbSD8yoQhug/5reFOIUieODiQHGnfVmUmVe9sDuoJmEME3MHMwUHIpGkVfXVE6SPxA2eoN6EReeKwVwbDeLsp/r/dA8jX7BWW9WSjjfAAAAAAAAAAAAAACIc2lnbggl0CkIwRg6tBbdAWQKT2ZlMkYC2bZvWrsyeLzUy+BSYJAs7v2jr1DkRH/xdJbaDjvmkCvS75EUlCXU0oJYT8BydxZzTpJNMALVfwuJNELOkm8sqlCN1/zP1KM3XOeEMu/k6MmtV4nG3zOtLRcKHg5YRz17WcccdTSz251J2Ugn purchaseDate2022-05-08T10:45:51Z
221 | download-id501382282407089488
222 | is-in-queue
223 |
224 | asset-info
225 |
226 | file-size611092999
227 | flavor10:purple
228 |
229 | metadata
230 |
231 | MacUIRequiredDeviceCapabilities
232 |
233 | arm64
234 |
235 | UIRequiredDeviceCapabilities
236 |
237 | arm64
238 |
239 | WKRunsIndependentlyOfCompanionApp
240 | WKWatchOnly
241 | appleWatchEnabled
242 | artistId292374531
243 | artistNameTencent Technology (Shenzhen) Company Limited
244 | bundleDisplayNameQQ
245 | bundleShortVersionString8.8.88
246 | bundleVersion8.8.88.662
247 | copyrightCopyright © 1998- 2022 Tencent. All Rights Reserved
248 | fileExtension.app
249 | gameCenterEnabled
250 | gameCenterEverEnabled
251 | genre社交
252 | genreId6005
253 | itemId444934666
254 | itemNameQQ
255 | kindsoftware
256 | nameTranscriptions
257 |
258 | zh-Hans-CN
259 |
260 | QQ
261 |
262 |
263 | playlistNameTencent Technology (Shenzhen) Company Limited
264 | product-typeios-app
265 | rating
266 |
267 | contentå¶å°/轻微çè²æ
å
容æè£¸é² , Advisory.NO.GAMBLING_CONTESTS , å¶å°/轻微çæäººææ§æç¤ºé¢æ , Advisory.NO.UNRESTRICTED_WEB_ACCESS and Advisory.NO.TRUE_GAMBLING
268 | label12+
269 | rank300
270 | systemitunes-games
271 |
272 | releaseDate2011-06-23T03:33:55Z
273 | requiresRosetta
274 | runsOnAppleSilicon
275 | runsOnIntel
276 | s143465
277 | software-platformios
278 | softwareIcon57x57URLhttps://is4-ssl.mzstatic.com/image/thumb/Purple112/v4/b9/ac/4e/b9ac4ef3-0152-fa92-872f-fe773d799117/AppIcon-1-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/114x114bb.jpg
279 | softwareIconNeedsShine
280 | softwareSupportedDeviceIds
281 |
282 | 2
283 | 9
284 | 4
285 |
286 | softwareVersionBundleIdcom.tencent.mqq
287 | softwareVersionExternalIdentifier848463733
288 | softwareVersionExternalIdentifiers
289 |
290 | 3843900
291 | 3876776
292 | 3941034
293 | 3973775
294 | 4070873
295 | 4135846
296 | 4321059
297 | 4492645
298 | 4917185
299 | 5632593
300 | 6232232
301 | 6860432
302 | 7792605
303 | 9642362
304 | 11556077
305 | 11818464
306 | 12638046
307 | 13422327
308 | 14959445
309 | 15410008
310 | 15765932
311 | 15854719
312 | 16122679
313 | 31562763
314 | 275882650
315 | 385122645
316 | 580102645
317 | 595393136
318 | 608133076
319 | 629072654
320 | 687502658
321 | 747082669
322 | 811253584
323 | 811445779
324 | 811179715
325 | 811445780
326 | 811590055
327 | 811669050
328 | 812133257
329 | 812375519
330 | 812625692
331 | 812972631
332 | 813031156
333 | 813192464
334 | 813298393
335 | 813463229
336 | 813961231
337 | 813962159
338 | 814174262
339 | 814318376
340 | 814527796
341 | 814531991
342 | 814639613
343 | 814754396
344 | 814882132
345 | 815144899
346 | 815147083
347 | 815188393
348 | 815573881
349 | 815574602
350 | 815607136
351 | 815810968
352 | 815897122
353 | 815938087
354 | 815938591
355 | 816130933
356 | 816210673
357 | 816305041
358 | 816356364
359 | 816843896
360 | 816912335
361 | 817028549
362 | 817235792
363 | 817473714
364 | 817549698
365 | 817788181
366 | 817933532
367 | 818110324
368 | 818431910
369 | 818825180
370 | 818979113
371 | 819096686
372 | 819416223
373 | 819489902
374 | 819842838
375 | 819893353
376 | 820113905
377 | 820199943
378 | 820442929
379 | 820548304
380 | 820595060
381 | 821268583
382 | 821341311
383 | 821500924
384 | 821954014
385 | 822037007
386 | 822096329
387 | 822279520
388 | 822523036
389 | 822895308
390 | 822957820
391 | 823194852
392 | 823309872
393 | 823713346
394 | 824097583
395 | 824171129
396 | 824301257
397 | 824389600
398 | 825024981
399 | 825145808
400 | 825307653
401 | 825347730
402 | 825611268
403 | 825729315
404 | 825895542
405 | 825933124
406 | 826313718
407 | 826632543
408 | 826837026
409 | 827460275
410 | 828106847
411 | 828385681
412 | 828600919
413 | 828666943
414 | 828716670
415 | 828897691
416 | 829301009
417 | 829496800
418 | 829679760
419 | 829821912
420 | 830133231
421 | 830530856
422 | 830742895
423 | 831337375
424 | 831405629
425 | 831472755
426 | 831824011
427 | 832139548
428 | 832542612
429 | 832827329
430 | 833393416
431 | 833855517
432 | 834104017
433 | 834138755
434 | 834768091
435 | 834880993
436 | 834939578
437 | 835135459
438 | 835524672
439 | 835716180
440 | 835976856
441 | 836375483
442 | 836825545
443 | 836945925
444 | 837334930
445 | 837735298
446 | 837835768
447 | 837881604
448 | 838238560
449 | 839235987
450 | 839700113
451 | 840003771
452 | 840041841
453 | 840921423
454 | 841227891
455 | 841968948
456 | 842099251
457 | 842166600
458 | 842303496
459 | 842463280
460 | 842670770
461 | 842892434
462 | 843105491
463 | 843416604
464 | 843638409
465 | 843881653
466 | 844115468
467 | 844170367
468 | 844356613
469 | 844638728
470 | 844786323
471 | 844986204
472 | 845375859
473 | 846332674
474 | 846748317
475 | 847332305
476 | 847747163
477 | 848017318
478 | 848463733
479 |
480 | vendorId69276
481 |
482 | drmVersionNumber0
483 | versionRestrictions16843008
484 | storeCohort10|date=1652005800000&sf=143465&pgtp=Search&pgid=ccfce0ef-4ac8-4d5a-8e5f-79876ac474a4&prpg=Purchases
485 | hasOrEverHasHadIAP
486 |
487 |
488 |
489 |
490 |
491 | download-queue-info
492 |
493 | download-queue-item-count0
494 | dsid16916646015
495 | is-auto-download-machine
496 |
497 |
498 | metrics
499 |
500 |
501 | itemIds
502 |
503 | 444934666
504 |
505 | price0.00
506 | priceTypeSTDQ
507 | productTypes
508 |
509 | C
510 |
511 | mtAppcom.apple.iTunes
512 | mtClientId3z21abvCzFDuz5CYz9bdz19maFVKge
513 | mtEventTime2022-05-08 10:44:38 Etc/GMT
514 | mtPageIdccfce0ef-4ac8-4d5a-8e5f-79876ac474a4
515 | mtPageTypeSearch
516 | mtPrevPagePurchases
517 | mtRequestId3z21abvCzFDuz5CYz9bdz19maFVKgezL2X64FFVz1MGG
518 | mtTopicxp_its_main
519 | currencyCNY
520 | exchangeRateToUSD0.1490268546
521 | commerceEvent_purchase_priceTypeSTDQ
522 | commerceEvent_storeFrontId143465
523 | commerceEvent_result_resultType0
524 | commerceEvent_flowType4
525 | commerceEvent_flowStep6
526 |
527 |
528 | duAnonymousPings
529 |
530 | https://xp.apple.com/report/2/xp_app_buy?clientId=0&sf=143465&adamId=444934666
531 |
532 | subscriptionStatus
533 |
534 | music
535 |
536 | statusDisabled
537 | reason
538 | isAdmin
539 | isNotEligibleForFreeTrial
540 |
541 | terms
542 |
543 |
544 | typeStore
545 | latestTerms22
546 | agreedToTerms22
547 | sourceaccount
548 |
549 |
550 | account
551 |
552 | isMinor
553 | suspectUnderage
554 |
555 | family
556 |
557 | hasFamily
558 |
559 |
560 |
561 |
562 |
--------------------------------------------------------------------------------
/reqs/schemas/store_authenticate_req.py:
--------------------------------------------------------------------------------
1 | from reprlib import repr as limitedRepr
2 |
3 |
4 | class StoreAuthenticateReq:
5 |
6 | _types_map = {
7 | "appleId": {"type": str, "subtype": None},
8 | "attempt": {"type": str, "subtype": None},
9 | "createSession": {"type": str, "subtype": None},
10 | "guid": {"type": str, "subtype": None},
11 | "password": {"type": str, "subtype": None},
12 | "rmp": {"type": str, "subtype": None},
13 | "why": {"type": str, "subtype": None},
14 | }
15 | _formats_map = {}
16 | _validations_map = {
17 | "appleId": {
18 | "required": True,
19 | },
20 | "attempt": {
21 | "required": True,
22 | },
23 | "createSession": {
24 | "required": True,
25 | },
26 | "guid": {
27 | "required": True,
28 | },
29 | "password": {
30 | "required": True,
31 | },
32 | "rmp": {
33 | "required": True,
34 | },
35 | "why": {
36 | "required": True,
37 | },
38 | }
39 |
40 | def __init__(
41 | self,
42 | appleId: str = None,
43 | attempt: str = None,
44 | createSession: str = None,
45 | guid: str = None,
46 | password: str = None,
47 | rmp: str = None,
48 | why: str = None,
49 | ):
50 | pass
51 | self.__appleId = appleId
52 | self.__attempt = attempt
53 | self.__createSession = createSession
54 | self.__guid = guid
55 | self.__password = password
56 | self.__rmp = rmp
57 | self.__why = why
58 |
59 | def _get_appleId(self):
60 | return self.__appleId
61 |
62 | def _set_appleId(self, value):
63 | if not isinstance(value, str):
64 | raise TypeError("appleId must be str")
65 |
66 | self.__appleId = value
67 |
68 | appleId = property(_get_appleId, _set_appleId)
69 |
70 | def _get_attempt(self):
71 | return self.__attempt
72 |
73 | def _set_attempt(self, value):
74 | if not isinstance(value, str):
75 | raise TypeError("attempt must be str")
76 |
77 | self.__attempt = value
78 |
79 | attempt = property(_get_attempt, _set_attempt)
80 |
81 | def _get_createSession(self):
82 | return self.__createSession
83 |
84 | def _set_createSession(self, value):
85 | if not isinstance(value, str):
86 | raise TypeError("createSession must be str")
87 |
88 | self.__createSession = value
89 |
90 | createSession = property(_get_createSession, _set_createSession)
91 |
92 | def _get_guid(self):
93 | return self.__guid
94 |
95 | def _set_guid(self, value):
96 | if not isinstance(value, str):
97 | raise TypeError("guid must be str")
98 |
99 | self.__guid = value
100 |
101 | guid = property(_get_guid, _set_guid)
102 |
103 | def _get_password(self):
104 | return self.__password
105 |
106 | def _set_password(self, value):
107 | if not isinstance(value, str):
108 | raise TypeError("password must be str")
109 |
110 | self.__password = value
111 |
112 | password = property(_get_password, _set_password)
113 |
114 | def _get_rmp(self):
115 | return self.__rmp
116 |
117 | def _set_rmp(self, value):
118 | if not isinstance(value, str):
119 | raise TypeError("rmp must be str")
120 |
121 | self.__rmp = value
122 |
123 | rmp = property(_get_rmp, _set_rmp)
124 |
125 | def _get_why(self):
126 | return self.__why
127 |
128 | def _set_why(self, value):
129 | if not isinstance(value, str):
130 | raise TypeError("why must be str")
131 |
132 | self.__why = value
133 |
134 | why = property(_get_why, _set_why)
135 |
136 | @staticmethod
137 | def from_dict(d):
138 | v = {}
139 | if "appleId" in d:
140 | v["appleId"] = (
141 | str.from_dict(d["appleId"])
142 | if hasattr(str, "from_dict")
143 | else d["appleId"]
144 | )
145 | if "attempt" in d:
146 | v["attempt"] = (
147 | str.from_dict(d["attempt"])
148 | if hasattr(str, "from_dict")
149 | else d["attempt"]
150 | )
151 | if "createSession" in d:
152 | v["createSession"] = (
153 | str.from_dict(d["createSession"])
154 | if hasattr(str, "from_dict")
155 | else d["createSession"]
156 | )
157 | if "guid" in d:
158 | v["guid"] = (
159 | str.from_dict(d["guid"]) if hasattr(str, "from_dict") else d["guid"]
160 | )
161 | if "password" in d:
162 | v["password"] = (
163 | str.from_dict(d["password"])
164 | if hasattr(str, "from_dict")
165 | else d["password"]
166 | )
167 | if "rmp" in d:
168 | v["rmp"] = (
169 | str.from_dict(d["rmp"]) if hasattr(str, "from_dict") else d["rmp"]
170 | )
171 | if "why" in d:
172 | v["why"] = (
173 | str.from_dict(d["why"]) if hasattr(str, "from_dict") else d["why"]
174 | )
175 | return StoreAuthenticateReq(**v)
176 |
177 | def as_dict(self):
178 | d = {}
179 | if self.__appleId is not None:
180 | d["appleId"] = (
181 | self.__appleId.as_dict()
182 | if hasattr(self.__appleId, "as_dict")
183 | else self.__appleId
184 | )
185 | if self.__attempt is not None:
186 | d["attempt"] = (
187 | self.__attempt.as_dict()
188 | if hasattr(self.__attempt, "as_dict")
189 | else self.__attempt
190 | )
191 | if self.__createSession is not None:
192 | d["createSession"] = (
193 | self.__createSession.as_dict()
194 | if hasattr(self.__createSession, "as_dict")
195 | else self.__createSession
196 | )
197 | if self.__guid is not None:
198 | d["guid"] = (
199 | self.__guid.as_dict()
200 | if hasattr(self.__guid, "as_dict")
201 | else self.__guid
202 | )
203 | if self.__password is not None:
204 | d["password"] = (
205 | self.__password.as_dict()
206 | if hasattr(self.__password, "as_dict")
207 | else self.__password
208 | )
209 | if self.__rmp is not None:
210 | d["rmp"] = (
211 | self.__rmp.as_dict() if hasattr(self.__rmp, "as_dict") else self.__rmp
212 | )
213 | if self.__why is not None:
214 | d["why"] = (
215 | self.__why.as_dict() if hasattr(self.__why, "as_dict") else self.__why
216 | )
217 | return d
218 |
219 | def __repr__(self):
220 | return "".format(
221 | limitedRepr(
222 | self.__appleId[:20]
223 | if isinstance(self.__appleId, bytes)
224 | else self.__appleId
225 | ),
226 | limitedRepr(
227 | self.__attempt[:20]
228 | if isinstance(self.__attempt, bytes)
229 | else self.__attempt
230 | ),
231 | limitedRepr(
232 | self.__createSession[:20]
233 | if isinstance(self.__createSession, bytes)
234 | else self.__createSession
235 | ),
236 | limitedRepr(
237 | self.__guid[:20] if isinstance(self.__guid, bytes) else self.__guid
238 | ),
239 | limitedRepr(
240 | self.__password[:20]
241 | if isinstance(self.__password, bytes)
242 | else self.__password
243 | ),
244 | limitedRepr(
245 | self.__rmp[:20] if isinstance(self.__rmp, bytes) else self.__rmp
246 | ),
247 | limitedRepr(
248 | self.__why[:20] if isinstance(self.__why, bytes) else self.__why
249 | ),
250 | )
251 |
--------------------------------------------------------------------------------
/reqs/schemas/store_buyproduct_req.py:
--------------------------------------------------------------------------------
1 | from reprlib import repr as limitedRepr
2 |
3 |
4 | class StoreBuyproductReq:
5 |
6 | _types_map = {
7 | "ageCheck": {"type": str, "subtype": None},
8 | "appExtVrsId": {"type": str, "subtype": None},
9 | "guid": {"type": str, "subtype": None},
10 | "hasBeenAuthedForBuy": {"type": str, "subtype": None},
11 | "isInApp": {"type": str, "subtype": None},
12 | "kbsync": {"type": str, "subtype": None},
13 | "sbsync": {"type": str, "subtype": None},
14 | "afds": {"type": str, "subtype": None},
15 | "machineName": {"type": str, "subtype": None},
16 | "mtApp": {"type": str, "subtype": None},
17 | "mtClientId": {"type": str, "subtype": None},
18 | "mtEventTime": {"type": str, "subtype": None},
19 | "mtPageId": {"type": str, "subtype": None},
20 | "mtPageType": {"type": str, "subtype": None},
21 | "mtPrevPage": {"type": str, "subtype": None},
22 | "mtRequestId": {"type": str, "subtype": None},
23 | "mtTopic": {"type": str, "subtype": None},
24 | "needDiv": {"type": str, "subtype": None},
25 | "pg": {"type": str, "subtype": None},
26 | "price": {"type": str, "subtype": None},
27 | "pricingParameters": {"type": str, "subtype": None},
28 | "productType": {"type": str, "subtype": None},
29 | "salableAdamId": {"type": str, "subtype": None},
30 | "hasAskedToFulfillPreorder": {"type": str, "subtype": None},
31 | "buyWithoutAuthorization": {"type": str, "subtype": None},
32 | "hasDoneAgeCheck": {"type": str, "subtype": None},
33 | "hasConfirmedPaymentSheet": {"type": str, "subtype": None},
34 | "asn": {"type": str, "subtype": None},
35 | }
36 | _formats_map = {}
37 | _validations_map = {
38 | "ageCheck": {
39 | "required": False,
40 | },
41 | "appExtVrsId": {
42 | "required": True,
43 | },
44 | "guid": {
45 | "required": True,
46 | },
47 | "hasBeenAuthedForBuy": {
48 | "required": False,
49 | },
50 | "isInApp": {
51 | "required": False,
52 | },
53 | "kbsync": {
54 | "required": True,
55 | },
56 | "sbsync": {
57 | "required": False,
58 | },
59 | "afds": {
60 | "required": False,
61 | },
62 | "machineName": {
63 | "required": False,
64 | },
65 | "mtApp": {
66 | "required": False,
67 | },
68 | "mtClientId": {
69 | "required": False,
70 | },
71 | "mtEventTime": {
72 | "required": False,
73 | },
74 | "mtPageId": {
75 | "required": False,
76 | },
77 | "mtPageType": {
78 | "required": False,
79 | },
80 | "mtPrevPage": {
81 | "required": False,
82 | },
83 | "mtRequestId": {
84 | "required": False,
85 | },
86 | "mtTopic": {
87 | "required": False,
88 | },
89 | "needDiv": {
90 | "required": False,
91 | },
92 | "pg": {
93 | "required": False,
94 | },
95 | "price": {
96 | "required": True,
97 | },
98 | "pricingParameters": {
99 | "required": True,
100 | },
101 | "productType": {
102 | "required": True,
103 | },
104 | "salableAdamId": {
105 | "required": True,
106 | },
107 | "hasAskedToFulfillPreorder": {
108 | "required": False,
109 | },
110 | "buyWithoutAuthorization": {
111 | "required": False,
112 | },
113 | "hasDoneAgeCheck": {
114 | "required": False,
115 | },
116 | "hasConfirmedPaymentSheet": {
117 | "required": False,
118 | },
119 | "asn": {
120 | "required": False,
121 | },
122 | }
123 |
124 | def __init__(
125 | self,
126 | ageCheck: str = None,
127 | appExtVrsId: str = None,
128 | guid: str = None,
129 | hasBeenAuthedForBuy: str = None,
130 | isInApp: str = None,
131 | kbsync: str = None,
132 | sbsync: str = None,
133 | afds: str = None,
134 | machineName: str = None,
135 | mtApp: str = None,
136 | mtClientId: str = None,
137 | mtEventTime: str = None,
138 | mtPageId: str = None,
139 | mtPageType: str = None,
140 | mtPrevPage: str = None,
141 | mtRequestId: str = None,
142 | mtTopic: str = None,
143 | needDiv: str = None,
144 | pg: str = None,
145 | price: str = None,
146 | pricingParameters: str = None,
147 | productType: str = None,
148 | salableAdamId: str = None,
149 | hasAskedToFulfillPreorder: str = None,
150 | buyWithoutAuthorization: str = None,
151 | hasDoneAgeCheck: str = None,
152 | hasConfirmedPaymentSheet: str = None,
153 | asn: str = None,
154 | ):
155 | pass
156 | self.__ageCheck = ageCheck
157 | self.__appExtVrsId = appExtVrsId
158 | self.__guid = guid
159 | self.__hasBeenAuthedForBuy = hasBeenAuthedForBuy
160 | self.__isInApp = isInApp
161 | self.__kbsync = kbsync
162 | self.__sbsync = sbsync
163 | self.__afds = afds
164 | self.__machineName = machineName
165 | self.__mtApp = mtApp
166 | self.__mtClientId = mtClientId
167 | self.__mtEventTime = mtEventTime
168 | self.__mtPageId = mtPageId
169 | self.__mtPageType = mtPageType
170 | self.__mtPrevPage = mtPrevPage
171 | self.__mtRequestId = mtRequestId
172 | self.__mtTopic = mtTopic
173 | self.__needDiv = needDiv
174 | self.__pg = pg
175 | self.__price = price
176 | self.__pricingParameters = pricingParameters
177 | self.__productType = productType
178 | self.__salableAdamId = salableAdamId
179 | self.__hasAskedToFulfillPreorder = hasAskedToFulfillPreorder
180 | self.__buyWithoutAuthorization = buyWithoutAuthorization
181 | self.__hasDoneAgeCheck = hasDoneAgeCheck
182 | self.__hasConfirmedPaymentSheet = hasConfirmedPaymentSheet
183 | self.__asn = asn
184 |
185 | def _get_ageCheck(self):
186 | return self.__ageCheck
187 |
188 | def _set_ageCheck(self, value):
189 | if value is not None and not isinstance(value, str):
190 | raise TypeError("ageCheck must be str")
191 |
192 | self.__ageCheck = value
193 |
194 | ageCheck = property(_get_ageCheck, _set_ageCheck)
195 |
196 | def _get_appExtVrsId(self):
197 | return self.__appExtVrsId
198 |
199 | def _set_appExtVrsId(self, value):
200 | if not isinstance(value, str):
201 | raise TypeError("appExtVrsId must be str")
202 |
203 | self.__appExtVrsId = value
204 |
205 | appExtVrsId = property(_get_appExtVrsId, _set_appExtVrsId)
206 |
207 | def _get_guid(self):
208 | return self.__guid
209 |
210 | def _set_guid(self, value):
211 | if not isinstance(value, str):
212 | raise TypeError("guid must be str")
213 |
214 | self.__guid = value
215 |
216 | guid = property(_get_guid, _set_guid)
217 |
218 | def _get_hasBeenAuthedForBuy(self):
219 | return self.__hasBeenAuthedForBuy
220 |
221 | def _set_hasBeenAuthedForBuy(self, value):
222 | if value is not None and not isinstance(value, str):
223 | raise TypeError("hasBeenAuthedForBuy must be str")
224 |
225 | self.__hasBeenAuthedForBuy = value
226 |
227 | hasBeenAuthedForBuy = property(_get_hasBeenAuthedForBuy, _set_hasBeenAuthedForBuy)
228 |
229 | def _get_isInApp(self):
230 | return self.__isInApp
231 |
232 | def _set_isInApp(self, value):
233 | if value is not None and not isinstance(value, str):
234 | raise TypeError("isInApp must be str")
235 |
236 | self.__isInApp = value
237 |
238 | isInApp = property(_get_isInApp, _set_isInApp)
239 |
240 | def _get_kbsync(self):
241 | return self.__kbsync
242 |
243 | def _set_kbsync(self, value):
244 | if not isinstance(value, str):
245 | raise TypeError("kbsync must be str")
246 |
247 | self.__kbsync = value
248 |
249 | kbsync = property(_get_kbsync, _set_kbsync)
250 |
251 | def _get_sbsync(self):
252 | return self.__sbsync
253 |
254 | def _set_sbsync(self, value):
255 | if not isinstance(value, str):
256 | raise TypeError("sbsync must be str")
257 |
258 | self.__sbsync = value
259 |
260 | sbsync = property(_get_sbsync, _set_sbsync)
261 |
262 | def _get_afds(self):
263 | return self.__afds
264 |
265 | def _set_afds(self, value):
266 | if not isinstance(value, str):
267 | raise TypeError("afds must be str")
268 |
269 | self.__afds = value
270 |
271 | afds = property(_get_afds, _set_afds)
272 |
273 | def _get_machineName(self):
274 | return self.__machineName
275 |
276 | def _set_machineName(self, value):
277 | if value is not None and not isinstance(value, str):
278 | raise TypeError("machineName must be str")
279 |
280 | self.__machineName = value
281 |
282 | machineName = property(_get_machineName, _set_machineName)
283 |
284 | def _get_mtApp(self):
285 | return self.__mtApp
286 |
287 | def _set_mtApp(self, value):
288 | if value is not None and not isinstance(value, str):
289 | raise TypeError("mtApp must be str")
290 |
291 | self.__mtApp = value
292 |
293 | mtApp = property(_get_mtApp, _set_mtApp)
294 |
295 | def _get_mtClientId(self):
296 | return self.__mtClientId
297 |
298 | def _set_mtClientId(self, value):
299 | if value is not None and not isinstance(value, str):
300 | raise TypeError("mtClientId must be str")
301 |
302 | self.__mtClientId = value
303 |
304 | mtClientId = property(_get_mtClientId, _set_mtClientId)
305 |
306 | def _get_mtEventTime(self):
307 | return self.__mtEventTime
308 |
309 | def _set_mtEventTime(self, value):
310 | if value is not None and not isinstance(value, str):
311 | raise TypeError("mtEventTime must be str")
312 |
313 | self.__mtEventTime = value
314 |
315 | mtEventTime = property(_get_mtEventTime, _set_mtEventTime)
316 |
317 | def _get_mtPageId(self):
318 | return self.__mtPageId
319 |
320 | def _set_mtPageId(self, value):
321 | if value is not None and not isinstance(value, str):
322 | raise TypeError("mtPageId must be str")
323 |
324 | self.__mtPageId = value
325 |
326 | mtPageId = property(_get_mtPageId, _set_mtPageId)
327 |
328 | def _get_mtPageType(self):
329 | return self.__mtPageType
330 |
331 | def _set_mtPageType(self, value):
332 | if value is not None and not isinstance(value, str):
333 | raise TypeError("mtPageType must be str")
334 |
335 | self.__mtPageType = value
336 |
337 | mtPageType = property(_get_mtPageType, _set_mtPageType)
338 |
339 | def _get_mtPrevPage(self):
340 | return self.__mtPrevPage
341 |
342 | def _set_mtPrevPage(self, value):
343 | if value is not None and not isinstance(value, str):
344 | raise TypeError("mtPrevPage must be str")
345 |
346 | self.__mtPrevPage = value
347 |
348 | mtPrevPage = property(_get_mtPrevPage, _set_mtPrevPage)
349 |
350 | def _get_mtRequestId(self):
351 | return self.__mtRequestId
352 |
353 | def _set_mtRequestId(self, value):
354 | if value is not None and not isinstance(value, str):
355 | raise TypeError("mtRequestId must be str")
356 |
357 | self.__mtRequestId = value
358 |
359 | mtRequestId = property(_get_mtRequestId, _set_mtRequestId)
360 |
361 | def _get_mtTopic(self):
362 | return self.__mtTopic
363 |
364 | def _set_mtTopic(self, value):
365 | if value is not None and not isinstance(value, str):
366 | raise TypeError("mtTopic must be str")
367 |
368 | self.__mtTopic = value
369 |
370 | mtTopic = property(_get_mtTopic, _set_mtTopic)
371 |
372 | def _get_needDiv(self):
373 | return self.__needDiv
374 |
375 | def _set_needDiv(self, value):
376 | if value is not None and not isinstance(value, str):
377 | raise TypeError("needDiv must be str")
378 |
379 | self.__needDiv = value
380 |
381 | needDiv = property(_get_needDiv, _set_needDiv)
382 |
383 | def _get_pg(self):
384 | return self.__pg
385 |
386 | def _set_pg(self, value):
387 | if value is not None and not isinstance(value, str):
388 | raise TypeError("pg must be str")
389 |
390 | self.__pg = value
391 |
392 | pg = property(_get_pg, _set_pg)
393 |
394 | def _get_price(self):
395 | return self.__price
396 |
397 | def _set_price(self, value):
398 | if not isinstance(value, str):
399 | raise TypeError("price must be str")
400 |
401 | self.__price = value
402 |
403 | price = property(_get_price, _set_price)
404 |
405 | def _get_pricingParameters(self):
406 | return self.__pricingParameters
407 |
408 | def _set_pricingParameters(self, value):
409 | if not isinstance(value, str):
410 | raise TypeError("pricingParameters must be str")
411 |
412 | self.__pricingParameters = value
413 |
414 | pricingParameters = property(_get_pricingParameters, _set_pricingParameters)
415 |
416 | def _get_productType(self):
417 | return self.__productType
418 |
419 | def _set_productType(self, value):
420 | if not isinstance(value, str):
421 | raise TypeError("productType must be str")
422 |
423 | self.__productType = value
424 |
425 | productType = property(_get_productType, _set_productType)
426 |
427 | def _get_salableAdamId(self):
428 | return self.__salableAdamId
429 |
430 | def _set_salableAdamId(self, value):
431 | if not isinstance(value, str):
432 | raise TypeError("salableAdamId must be str")
433 |
434 | self.__salableAdamId = value
435 |
436 | salableAdamId = property(_get_salableAdamId, _set_salableAdamId)
437 |
438 | def _get_hasAskedToFulfillPreorder(self):
439 | return self.__hasAskedToFulfillPreorder
440 |
441 | def _set_hasAskedToFulfillPreorder(self, value):
442 | if value is not None and not isinstance(value, str):
443 | raise TypeError("hasAskedToFulfillPreorder must be str")
444 |
445 | self.__hasAskedToFulfillPreorder = value
446 |
447 | hasAskedToFulfillPreorder = property(
448 | _get_hasAskedToFulfillPreorder, _set_hasAskedToFulfillPreorder
449 | )
450 |
451 | def _get_buyWithoutAuthorization(self):
452 | return self.__buyWithoutAuthorization
453 |
454 | def _set_buyWithoutAuthorization(self, value):
455 | if value is not None and not isinstance(value, str):
456 | raise TypeError("buyWithoutAuthorization must be str")
457 |
458 | self.__buyWithoutAuthorization = value
459 |
460 | buyWithoutAuthorization = property(
461 | _get_buyWithoutAuthorization, _set_buyWithoutAuthorization
462 | )
463 |
464 | def _get_hasDoneAgeCheck(self):
465 | return self.__hasDoneAgeCheck
466 |
467 | def _set_hasDoneAgeCheck(self, value):
468 | if value is not None and not isinstance(value, str):
469 | raise TypeError("hasDoneAgeCheck must be str")
470 |
471 | self.__hasDoneAgeCheck = value
472 |
473 | hasDoneAgeCheck = property(_get_hasDoneAgeCheck, _set_hasDoneAgeCheck)
474 |
475 | def _get_hasConfirmedPaymentSheet(self):
476 | return self.__hasConfirmedPaymentSheet
477 |
478 | def _set_hasConfirmedPaymentSheet(self, value):
479 | if value is not None and not isinstance(value, str):
480 | raise TypeError("hasConfirmedPaymentSheet must be str")
481 |
482 | self.__hasConfirmedPaymentSheet = value
483 |
484 | hasConfirmedPaymentSheet = property(_get_hasConfirmedPaymentSheet, _set_hasConfirmedPaymentSheet)
485 |
486 | def _get_asn(self):
487 | return self.__asn
488 |
489 | def _set_asn(self, value):
490 | if value is not None and not isinstance(value, str):
491 | raise TypeError("asn must be str")
492 |
493 | self.__asn = value
494 |
495 | asn = property(_get_asn, _set_asn)
496 |
497 | @staticmethod
498 | def from_dict(d):
499 | v = {}
500 | if "ageCheck" in d:
501 | v["ageCheck"] = (
502 | str.from_dict(d["ageCheck"])
503 | if hasattr(str, "from_dict")
504 | else d["ageCheck"]
505 | )
506 | if "appExtVrsId" in d:
507 | v["appExtVrsId"] = (
508 | str.from_dict(d["appExtVrsId"])
509 | if hasattr(str, "from_dict")
510 | else d["appExtVrsId"]
511 | )
512 | if "guid" in d:
513 | v["guid"] = (
514 | str.from_dict(d["guid"]) if hasattr(str, "from_dict") else d["guid"]
515 | )
516 | if "hasBeenAuthedForBuy" in d:
517 | v["hasBeenAuthedForBuy"] = (
518 | str.from_dict(d["hasBeenAuthedForBuy"])
519 | if hasattr(str, "from_dict")
520 | else d["hasBeenAuthedForBuy"]
521 | )
522 | if "isInApp" in d:
523 | v["isInApp"] = (
524 | str.from_dict(d["isInApp"])
525 | if hasattr(str, "from_dict")
526 | else d["isInApp"]
527 | )
528 | if "kbsync" in d:
529 | v["kbsync"] = (
530 | str.from_dict(d["kbsync"]) if hasattr(str, "from_dict") else d["kbsync"]
531 | )
532 | if "sbsync" in d:
533 | v["sbsync"] = (
534 | str.from_dict(d["sbsync"]) if hasattr(str, "from_dict") else d["sbsync"]
535 | )
536 | if "afds" in d:
537 | v["afds"] = (
538 | str.from_dict(d["afds"]) if hasattr(str, "from_dict") else d["afds"]
539 | )
540 | if "machineName" in d:
541 | v["machineName"] = (
542 | str.from_dict(d["machineName"])
543 | if hasattr(str, "from_dict")
544 | else d["machineName"]
545 | )
546 | if "mtApp" in d:
547 | v["mtApp"] = (
548 | str.from_dict(d["mtApp"]) if hasattr(str, "from_dict") else d["mtApp"]
549 | )
550 | if "mtClientId" in d:
551 | v["mtClientId"] = (
552 | str.from_dict(d["mtClientId"])
553 | if hasattr(str, "from_dict")
554 | else d["mtClientId"]
555 | )
556 | if "mtEventTime" in d:
557 | v["mtEventTime"] = (
558 | str.from_dict(d["mtEventTime"])
559 | if hasattr(str, "from_dict")
560 | else d["mtEventTime"]
561 | )
562 | if "mtPageId" in d:
563 | v["mtPageId"] = (
564 | str.from_dict(d["mtPageId"])
565 | if hasattr(str, "from_dict")
566 | else d["mtPageId"]
567 | )
568 | if "mtPageType" in d:
569 | v["mtPageType"] = (
570 | str.from_dict(d["mtPageType"])
571 | if hasattr(str, "from_dict")
572 | else d["mtPageType"]
573 | )
574 | if "mtPrevPage" in d:
575 | v["mtPrevPage"] = (
576 | str.from_dict(d["mtPrevPage"])
577 | if hasattr(str, "from_dict")
578 | else d["mtPrevPage"]
579 | )
580 | if "mtRequestId" in d:
581 | v["mtRequestId"] = (
582 | str.from_dict(d["mtRequestId"])
583 | if hasattr(str, "from_dict")
584 | else d["mtRequestId"]
585 | )
586 | if "mtTopic" in d:
587 | v["mtTopic"] = (
588 | str.from_dict(d["mtTopic"])
589 | if hasattr(str, "from_dict")
590 | else d["mtTopic"]
591 | )
592 | if "needDiv" in d:
593 | v["needDiv"] = (
594 | str.from_dict(d["needDiv"])
595 | if hasattr(str, "from_dict")
596 | else d["needDiv"]
597 | )
598 | if "pg" in d:
599 | v["pg"] = str.from_dict(d["pg"]) if hasattr(str, "from_dict") else d["pg"]
600 | if "price" in d:
601 | v["price"] = (
602 | str.from_dict(d["price"]) if hasattr(str, "from_dict") else d["price"]
603 | )
604 | if "pricingParameters" in d:
605 | v["pricingParameters"] = (
606 | str.from_dict(d["pricingParameters"])
607 | if hasattr(str, "from_dict")
608 | else d["pricingParameters"]
609 | )
610 | if "productType" in d:
611 | v["productType"] = (
612 | str.from_dict(d["productType"])
613 | if hasattr(str, "from_dict")
614 | else d["productType"]
615 | )
616 | if "salableAdamId" in d:
617 | v["salableAdamId"] = (
618 | str.from_dict(d["salableAdamId"])
619 | if hasattr(str, "from_dict")
620 | else d["salableAdamId"]
621 | )
622 | if "hasAskedToFulfillPreorder" in d:
623 | v["hasAskedToFulfillPreorder"] = (
624 | str.from_dict(d["hasAskedToFulfillPreorder"])
625 | if hasattr(str, "from_dict")
626 | else d["hasAskedToFulfillPreorder"]
627 | )
628 | if "buyWithoutAuthorization" in d:
629 | v["buyWithoutAuthorization"] = (
630 | str.from_dict(d["buyWithoutAuthorization"])
631 | if hasattr(str, "from_dict")
632 | else d["buyWithoutAuthorization"]
633 | )
634 | if "hasDoneAgeCheck" in d:
635 | v["hasDoneAgeCheck"] = (
636 | str.from_dict(d["hasDoneAgeCheck"])
637 | if hasattr(str, "from_dict")
638 | else d["hasDoneAgeCheck"]
639 | )
640 | if "hasConfirmedPaymentSheet" in d:
641 | v["hasConfirmedPaymentSheet"] = (
642 | str.from_dict(d["hasConfirmedPaymentSheet"])
643 | if hasattr(str, "from_dict")
644 | else d["hasConfirmedPaymentSheet"]
645 | )
646 | if "asn" in d:
647 | v["asn"] = (
648 | str.from_dict(d["asn"])
649 | if hasattr(str, "from_dict")
650 | else d["asn"]
651 | )
652 | return StoreBuyproductReq(**v)
653 |
654 | def as_dict(self):
655 | d = {}
656 | if self.__ageCheck is not None:
657 | d["ageCheck"] = (
658 | self.__ageCheck.as_dict()
659 | if hasattr(self.__ageCheck, "as_dict")
660 | else self.__ageCheck
661 | )
662 | if self.__appExtVrsId is not None:
663 | d["appExtVrsId"] = (
664 | self.__appExtVrsId.as_dict()
665 | if hasattr(self.__appExtVrsId, "as_dict")
666 | else self.__appExtVrsId
667 | )
668 | if self.__guid is not None:
669 | d["guid"] = (
670 | self.__guid.as_dict()
671 | if hasattr(self.__guid, "as_dict")
672 | else self.__guid
673 | )
674 | if self.__hasBeenAuthedForBuy is not None:
675 | d["hasBeenAuthedForBuy"] = (
676 | self.__hasBeenAuthedForBuy.as_dict()
677 | if hasattr(self.__hasBeenAuthedForBuy, "as_dict")
678 | else self.__hasBeenAuthedForBuy
679 | )
680 | if self.__isInApp is not None:
681 | d["isInApp"] = (
682 | self.__isInApp.as_dict()
683 | if hasattr(self.__isInApp, "as_dict")
684 | else self.__isInApp
685 | )
686 | if self.__kbsync is not None:
687 | d["kbsync"] = (
688 | self.__kbsync.as_dict()
689 | if hasattr(self.__kbsync, "as_dict")
690 | else self.__kbsync
691 | )
692 | if self.__sbsync is not None:
693 | d["sbsync"] = (
694 | self.__sbsync.as_dict()
695 | if hasattr(self.__sbsync, "as_dict")
696 | else self.__sbsync
697 | )
698 | if self.__afds is not None:
699 | d["afds"] = (
700 | self.__afds.as_dict()
701 | if hasattr(self.__afds, "as_dict")
702 | else self.__afds
703 | )
704 | if self.__machineName is not None:
705 | d["machineName"] = (
706 | self.__machineName.as_dict()
707 | if hasattr(self.__machineName, "as_dict")
708 | else self.__machineName
709 | )
710 | if self.__mtApp is not None:
711 | d["mtApp"] = (
712 | self.__mtApp.as_dict()
713 | if hasattr(self.__mtApp, "as_dict")
714 | else self.__mtApp
715 | )
716 | if self.__mtClientId is not None:
717 | d["mtClientId"] = (
718 | self.__mtClientId.as_dict()
719 | if hasattr(self.__mtClientId, "as_dict")
720 | else self.__mtClientId
721 | )
722 | if self.__mtEventTime is not None:
723 | d["mtEventTime"] = (
724 | self.__mtEventTime.as_dict()
725 | if hasattr(self.__mtEventTime, "as_dict")
726 | else self.__mtEventTime
727 | )
728 | if self.__mtPageId is not None:
729 | d["mtPageId"] = (
730 | self.__mtPageId.as_dict()
731 | if hasattr(self.__mtPageId, "as_dict")
732 | else self.__mtPageId
733 | )
734 | if self.__mtPageType is not None:
735 | d["mtPageType"] = (
736 | self.__mtPageType.as_dict()
737 | if hasattr(self.__mtPageType, "as_dict")
738 | else self.__mtPageType
739 | )
740 | if self.__mtPrevPage is not None:
741 | d["mtPrevPage"] = (
742 | self.__mtPrevPage.as_dict()
743 | if hasattr(self.__mtPrevPage, "as_dict")
744 | else self.__mtPrevPage
745 | )
746 | if self.__mtRequestId is not None:
747 | d["mtRequestId"] = (
748 | self.__mtRequestId.as_dict()
749 | if hasattr(self.__mtRequestId, "as_dict")
750 | else self.__mtRequestId
751 | )
752 | if self.__mtTopic is not None:
753 | d["mtTopic"] = (
754 | self.__mtTopic.as_dict()
755 | if hasattr(self.__mtTopic, "as_dict")
756 | else self.__mtTopic
757 | )
758 | if self.__needDiv is not None:
759 | d["needDiv"] = (
760 | self.__needDiv.as_dict()
761 | if hasattr(self.__needDiv, "as_dict")
762 | else self.__needDiv
763 | )
764 | if self.__pg is not None:
765 | d["pg"] = (
766 | self.__pg.as_dict() if hasattr(self.__pg, "as_dict") else self.__pg
767 | )
768 | if self.__price is not None:
769 | d["price"] = (
770 | self.__price.as_dict()
771 | if hasattr(self.__price, "as_dict")
772 | else self.__price
773 | )
774 | if self.__pricingParameters is not None:
775 | d["pricingParameters"] = (
776 | self.__pricingParameters.as_dict()
777 | if hasattr(self.__pricingParameters, "as_dict")
778 | else self.__pricingParameters
779 | )
780 | if self.__productType is not None:
781 | d["productType"] = (
782 | self.__productType.as_dict()
783 | if hasattr(self.__productType, "as_dict")
784 | else self.__productType
785 | )
786 | if self.__salableAdamId is not None:
787 | d["salableAdamId"] = (
788 | self.__salableAdamId.as_dict()
789 | if hasattr(self.__salableAdamId, "as_dict")
790 | else self.__salableAdamId
791 | )
792 | if self.__hasAskedToFulfillPreorder is not None:
793 | d["hasAskedToFulfillPreorder"] = (
794 | self.__hasAskedToFulfillPreorder.as_dict()
795 | if hasattr(self.__hasAskedToFulfillPreorder, "as_dict")
796 | else self.__hasAskedToFulfillPreorder
797 | )
798 | if self.__buyWithoutAuthorization is not None:
799 | d["buyWithoutAuthorization"] = (
800 | self.__buyWithoutAuthorization.as_dict()
801 | if hasattr(self.__buyWithoutAuthorization, "as_dict")
802 | else self.__buyWithoutAuthorization
803 | )
804 | if self.__hasDoneAgeCheck is not None:
805 | d["hasDoneAgeCheck"] = (
806 | self.__hasDoneAgeCheck.as_dict()
807 | if hasattr(self.__hasDoneAgeCheck, "as_dict")
808 | else self.__hasDoneAgeCheck
809 | )
810 | if self.__hasConfirmedPaymentSheet is not None:
811 | d["hasConfirmedPaymentSheet"] = (
812 | self.__hasConfirmedPaymentSheet.as_dict()
813 | if hasattr(self.__hasConfirmedPaymentSheet, "as_dict")
814 | else self.__hasConfirmedPaymentSheet
815 | )
816 | if self.__asn is not None:
817 | d["asn"] = (
818 | self.__asn.as_dict()
819 | if hasattr(self.__asn, "as_dict")
820 | else self.__asn
821 | )
822 | return d
823 |
824 | def __repr__(self):
825 | return "".format(
826 | limitedRepr(
827 | self.__ageCheck[:20]
828 | if isinstance(self.__ageCheck, bytes)
829 | else self.__ageCheck
830 | ),
831 | limitedRepr(
832 | self.__appExtVrsId[:20]
833 | if isinstance(self.__appExtVrsId, bytes)
834 | else self.__appExtVrsId
835 | ),
836 | limitedRepr(
837 | self.__guid[:20] if isinstance(self.__guid, bytes) else self.__guid
838 | ),
839 | limitedRepr(
840 | self.__hasBeenAuthedForBuy[:20]
841 | if isinstance(self.__hasBeenAuthedForBuy, bytes)
842 | else self.__hasBeenAuthedForBuy
843 | ),
844 | limitedRepr(
845 | self.__isInApp[:20]
846 | if isinstance(self.__isInApp, bytes)
847 | else self.__isInApp
848 | ),
849 | limitedRepr(
850 | self.__kbsync[:20]
851 | if isinstance(self.__kbsync, bytes)
852 | else self.__kbsync
853 | ),
854 | limitedRepr(
855 | self.__sbsync[:20]
856 | if isinstance(self.__sbsync, bytes)
857 | else self.__sbsync
858 | ),
859 | limitedRepr(
860 | self.__afds[:20]
861 | if isinstance(self.__afds, bytes)
862 | else self.__afds
863 | ),
864 | limitedRepr(
865 | self.__machineName[:20]
866 | if isinstance(self.__machineName, bytes)
867 | else self.__machineName
868 | ),
869 | limitedRepr(
870 | self.__mtApp[:20] if isinstance(self.__mtApp, bytes) else self.__mtApp
871 | ),
872 | limitedRepr(
873 | self.__mtClientId[:20]
874 | if isinstance(self.__mtClientId, bytes)
875 | else self.__mtClientId
876 | ),
877 | limitedRepr(
878 | self.__mtEventTime[:20]
879 | if isinstance(self.__mtEventTime, bytes)
880 | else self.__mtEventTime
881 | ),
882 | limitedRepr(
883 | self.__mtPageId[:20]
884 | if isinstance(self.__mtPageId, bytes)
885 | else self.__mtPageId
886 | ),
887 | limitedRepr(
888 | self.__mtPageType[:20]
889 | if isinstance(self.__mtPageType, bytes)
890 | else self.__mtPageType
891 | ),
892 | limitedRepr(
893 | self.__mtPrevPage[:20]
894 | if isinstance(self.__mtPrevPage, bytes)
895 | else self.__mtPrevPage
896 | ),
897 | limitedRepr(
898 | self.__mtRequestId[:20]
899 | if isinstance(self.__mtRequestId, bytes)
900 | else self.__mtRequestId
901 | ),
902 | limitedRepr(
903 | self.__mtTopic[:20]
904 | if isinstance(self.__mtTopic, bytes)
905 | else self.__mtTopic
906 | ),
907 | limitedRepr(
908 | self.__needDiv[:20]
909 | if isinstance(self.__needDiv, bytes)
910 | else self.__needDiv
911 | ),
912 | limitedRepr(self.__pg[:20] if isinstance(self.__pg, bytes) else self.__pg),
913 | limitedRepr(
914 | self.__price[:20] if isinstance(self.__price, bytes) else self.__price
915 | ),
916 | limitedRepr(
917 | self.__pricingParameters[:20]
918 | if isinstance(self.__pricingParameters, bytes)
919 | else self.__pricingParameters
920 | ),
921 | limitedRepr(
922 | self.__productType[:20]
923 | if isinstance(self.__productType, bytes)
924 | else self.__productType
925 | ),
926 | limitedRepr(
927 | self.__salableAdamId[:20]
928 | if isinstance(self.__salableAdamId, bytes)
929 | else self.__salableAdamId
930 | ),
931 | limitedRepr(
932 | self.__hasAskedToFulfillPreorder[:20]
933 | if isinstance(self.__hasAskedToFulfillPreorder, bytes)
934 | else self.__hasAskedToFulfillPreorder
935 | ),
936 | limitedRepr(
937 | self.__buyWithoutAuthorization[:20]
938 | if isinstance(self.__buyWithoutAuthorization, bytes)
939 | else self.__buyWithoutAuthorization
940 | ),
941 | limitedRepr(
942 | self.__hasDoneAgeCheck[:20]
943 | if isinstance(self.__hasDoneAgeCheck, bytes)
944 | else self.__hasDoneAgeCheck
945 | ),
946 | limitedRepr(
947 | self.__hasConfirmedPaymentSheet[:20]
948 | if isinstance(self.__hasConfirmedPaymentSheet, bytes)
949 | else self.__hasConfirmedPaymentSheet
950 | ),
951 | limitedRepr(
952 | self.__asn[:20]
953 | if isinstance(self.__asn, bytes)
954 | else self.__asn
955 | ),
956 | )
957 |
--------------------------------------------------------------------------------
/reqs/schemas/store_download_req.py:
--------------------------------------------------------------------------------
1 | from reprlib import repr as limitedRepr
2 |
3 |
4 | class StoreDownloadReq:
5 |
6 | _types_map = {
7 | "creditDisplay": {"type": str, "subtype": None},
8 | "guid": {"type": str, "subtype": None},
9 | "salableAdamId": {"type": str, "subtype": None},
10 | "externalVersionId": {"type": str, "subtype": None},
11 | }
12 | _formats_map = {}
13 | _validations_map = {
14 | "creditDisplay": {
15 | "required": True,
16 | },
17 | "guid": {
18 | "required": True,
19 | },
20 | "salableAdamId": {
21 | "required": True,
22 | },
23 | "externalVersionId": {
24 | "required": False,
25 | },
26 | }
27 |
28 | def __init__(
29 | self,
30 | creditDisplay: str = None,
31 | guid: str = None,
32 | salableAdamId: str = None,
33 | externalVersionId: str = None,
34 | ):
35 | pass
36 | self.__creditDisplay = creditDisplay
37 | self.__guid = guid
38 | self.__salableAdamId = salableAdamId
39 | self.__externalVersionId = externalVersionId
40 |
41 | def _get_creditDisplay(self):
42 | return self.__creditDisplay
43 |
44 | def _set_creditDisplay(self, value):
45 | if not isinstance(value, str):
46 | raise TypeError("creditDisplay must be str")
47 |
48 | self.__creditDisplay = value
49 |
50 | creditDisplay = property(_get_creditDisplay, _set_creditDisplay)
51 |
52 | def _get_guid(self):
53 | return self.__guid
54 |
55 | def _set_guid(self, value):
56 | if not isinstance(value, str):
57 | raise TypeError("guid must be str")
58 |
59 | self.__guid = value
60 |
61 | guid = property(_get_guid, _set_guid)
62 |
63 | def _get_salableAdamId(self):
64 | return self.__salableAdamId
65 |
66 | def _set_salableAdamId(self, value):
67 | if not isinstance(value, str):
68 | raise TypeError("salableAdamId must be str")
69 |
70 | self.__salableAdamId = value
71 |
72 | salableAdamId = property(_get_salableAdamId, _set_salableAdamId)
73 |
74 | def _get_externalVersionId(self):
75 | return self.__externalVersionId
76 |
77 | def _set_externalVersionId(self, value):
78 | if value is not None and not isinstance(value, str):
79 | raise TypeError("externalVersionId must be str")
80 |
81 | self.__externalVersionId = value
82 |
83 | externalVersionId = property(_get_externalVersionId, _set_externalVersionId)
84 |
85 | @staticmethod
86 | def from_dict(d):
87 | v = {}
88 | if "creditDisplay" in d:
89 | v["creditDisplay"] = (
90 | str.from_dict(d["creditDisplay"])
91 | if hasattr(str, "from_dict")
92 | else d["creditDisplay"]
93 | )
94 | if "guid" in d:
95 | v["guid"] = (
96 | str.from_dict(d["guid"]) if hasattr(str, "from_dict") else d["guid"]
97 | )
98 | if "salableAdamId" in d:
99 | v["salableAdamId"] = (
100 | str.from_dict(d["salableAdamId"])
101 | if hasattr(str, "from_dict")
102 | else d["salableAdamId"]
103 | )
104 | if "externalVersionId" in d:
105 | v["externalVersionId"] = (
106 | str.from_dict(d["externalVersionId"])
107 | if hasattr(str, "from_dict")
108 | else d["externalVersionId"]
109 | )
110 | return StoreDownloadReq(**v)
111 |
112 | def as_dict(self):
113 | d = {}
114 | if self.__creditDisplay is not None:
115 | d["creditDisplay"] = (
116 | self.__creditDisplay.as_dict()
117 | if hasattr(self.__creditDisplay, "as_dict")
118 | else self.__creditDisplay
119 | )
120 | if self.__guid is not None:
121 | d["guid"] = (
122 | self.__guid.as_dict()
123 | if hasattr(self.__guid, "as_dict")
124 | else self.__guid
125 | )
126 | if self.__salableAdamId is not None:
127 | d["salableAdamId"] = (
128 | self.__salableAdamId.as_dict()
129 | if hasattr(self.__salableAdamId, "as_dict")
130 | else self.__salableAdamId
131 | )
132 | if self.__externalVersionId is not None:
133 | d["externalVersionId"] = (
134 | self.__externalVersionId.as_dict()
135 | if hasattr(self.__externalVersionId, "as_dict")
136 | else self.__externalVersionId
137 | )
138 | return d
139 |
140 | def __repr__(self):
141 | return "".format(
142 | limitedRepr(
143 | self.__creditDisplay[:20]
144 | if isinstance(self.__creditDisplay, bytes)
145 | else self.__creditDisplay
146 | ),
147 | limitedRepr(
148 | self.__guid[:20] if isinstance(self.__guid, bytes) else self.__guid
149 | ),
150 | limitedRepr(
151 | self.__salableAdamId[:20]
152 | if isinstance(self.__salableAdamId, bytes)
153 | else self.__salableAdamId
154 | ),
155 | limitedRepr(
156 | self.__externalVersionId[:20]
157 | if isinstance(self.__externalVersionId, bytes)
158 | else self.__externalVersionId
159 | ),
160 | )
161 |
--------------------------------------------------------------------------------
/reqs/store.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 | import json
3 | import pickle
4 | import plistlib
5 | import requests
6 | from reqs.schemas.store_authenticate_req import StoreAuthenticateReq
7 | from reqs.schemas.store_authenticate_resp import StoreAuthenticateResp
8 | from reqs.schemas.store_buyproduct_req import StoreBuyproductReq
9 | from reqs.schemas.store_buyproduct_resp import StoreBuyproductResp
10 | from reqs.schemas.store_download_req import StoreDownloadReq
11 | from reqs.schemas.store_download_resp import StoreDownloadResp
12 |
13 | class StoreException(Exception):
14 | def __init__(self, req, resp, errMsg, errType=None):
15 | self.req = req
16 | self.resp = resp # type: StoreDownloadResp
17 | self.errMsg = errMsg
18 | self.errType = errType
19 | super().__init__(
20 | "Store %s error: %s" % (self.req, self.errMsg) if not self.errType else
21 | "Store %s error: %s, errorType: %s" % (self.req, self.errMsg, self.errType)
22 | )
23 |
24 | #CONFIGURATOR_UA = "Configurator/2.0 (Macintosh; OS X 10.12.6; 16G29) AppleWebKit/2603.3.8"
25 | CONFIGURATOR_UA = 'Configurator/2.0 (Macintosh; OS X 10.12.6; 16G29) AppleWebKit/2603.3.8 iOS/14.2 hwp/t8020'
26 |
27 | class StoreClientAuth(object):
28 | def __init__(self, appleId=None, password=None):
29 | self.appleId = appleId
30 | self.password = password
31 | self.guid = None # the guid will not be used in itunes server mode
32 | self.accountName = None
33 | self.authHeaders = None
34 | self.authCookies = None
35 |
36 | def __str__(self):
37 | return f"<{self.accountName} [{self.guid}]>"
38 |
39 | def _generateGuid(self, appleId):
40 | '''
41 | Derive a GUID for an appleId. For each appleId, the GUID will always remain the same
42 | :param appleId:
43 | :return:
44 | '''
45 | DEFAULT_GUID = '000C2941396B' # this GUID is blocked
46 | # number of chars to use from DEFAULT_GUID as prefix (0..12)
47 | GUID_DEFAULT_PREFIX = 2
48 | # something unique
49 | GUID_SEED = 'CAFEBABE'
50 | # something between 0 and 30
51 | GUID_POS = 10
52 |
53 | # generate a unique guid out of the appleId
54 | h = hashlib.sha1((GUID_SEED + appleId + GUID_SEED).encode("utf-8")).hexdigest()
55 | defaultPart = DEFAULT_GUID[:GUID_DEFAULT_PREFIX]
56 | hashPart = h[GUID_POS: GUID_POS + (len(DEFAULT_GUID) - GUID_DEFAULT_PREFIX)]
57 | guid = (defaultPart + hashPart).upper()
58 | return guid
59 |
60 | def login(self, sess):
61 | if not self.guid:
62 | self.guid = self._generateGuid(self.appleId)
63 |
64 | req = StoreAuthenticateReq(appleId=self.appleId, password=self.password, attempt='4', createSession="true",
65 | guid=self.guid, rmp='0', why='signIn')
66 | url = "https://p46-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/authenticate?guid=%s" % self.guid
67 | while True:
68 | r = sess.post(url,
69 | headers={
70 | "Accept": "*/*",
71 | "Content-Type": "application/x-www-form-urlencoded",
72 | "User-Agent": CONFIGURATOR_UA,
73 | }, data=plistlib.dumps(req.as_dict()), allow_redirects=False)
74 | if r.status_code == 302:
75 | url = r.headers['Location']
76 | continue
77 | break
78 | d = plistlib.loads(r.content)
79 | resp = StoreAuthenticateResp.from_dict(d)
80 | if not resp.m_allowed:
81 | raise StoreException("authenticate", d, resp.customerMessage, resp.failureType)
82 |
83 | self.authHeaders = {}
84 | self.authHeaders['X-Dsid'] = self.authHeaders['iCloud-Dsid'] = str(resp.download_queue_info.dsid)
85 | self.authHeaders['X-Apple-Store-Front'] = r.headers.get('x-set-apple-store-front')
86 | self.authHeaders['X-Token'] = resp.passwordToken
87 | self.authCookies = pickle.dumps(sess.cookies).hex()
88 |
89 | self.accountName = resp.accountInfo.address.firstName + " " + resp.accountInfo.address.lastName
90 | def save(self):
91 | return json.dumps(self.__dict__)
92 |
93 | @classmethod
94 | def load(cls, j):
95 | obj = json.loads(j)
96 | ret = cls()
97 | ret.__dict__.update(obj)
98 | return ret
99 |
100 | class StoreClient(object):
101 | def __init__(self, sess: requests.Session):
102 | self.sess = sess
103 | self.iTunes_provider = None
104 | self.authInfo = None
105 |
106 | def authenticate_load_session(self, sessionContent):
107 | self.authInfo = StoreClientAuth.load(sessionContent)
108 | if self.authInfo.authHeaders is None or self.authInfo.authCookies is None:
109 | raise Exception("invalid auth session")
110 | self.sess.headers = dict(self.authInfo.authHeaders)
111 | self.sess.cookies = pickle.loads(bytes.fromhex(self.authInfo.authCookies))
112 |
113 | def authenticate_save_session(self):
114 | return self.authInfo.save()
115 |
116 | def authenticate(self, appleId, password):
117 | if not self.authInfo:
118 | self.authInfo = StoreClientAuth(appleId, password)
119 | self.authInfo.login(self.sess)
120 | self.sess.headers = dict(self.authInfo.authHeaders)
121 | self.sess.cookies = pickle.loads(bytes.fromhex(self.authInfo.authCookies))
122 |
123 | # ==> 🛠 [Verbose] Performing request: curl -k -X POST \
124 | # -H "iCloud-DSID: 12263680861" \
125 | # -H "Content-Type: application/x-www-form-urlencoded" \
126 | # -H "User-Agent: Configurator/2.0 (Macintosh; OS X 10.12.6; 16G29) AppleWebKit/2603.3.8" \
127 | # -H "X-Dsid: 12263680861" \
128 | # -d '
129 | #
130 | #
131 | #
132 | # creditDisplay
133 | #
134 | # guid
135 | # 000C2941396B
136 | # salableAdamId
137 | # 1239860606
138 | #
139 | #
140 | # ' \
141 | # https://p25-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/volumeStoreDownloadProduct?guid=000C2941396Bk
142 | def volumeStoreDownloadProduct(self, appId, appVerId=""):
143 | req = StoreDownloadReq(creditDisplay="", guid=self.authInfo.guid, salableAdamId=appId, externalVersionId=appVerId)
144 | hdrs = {
145 | "Content-Type": "application/x-www-form-urlencoded",
146 | "User-Agent": CONFIGURATOR_UA,
147 | }
148 | url = "https://p25-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/volumeStoreDownloadProduct?guid=%s" % self.authInfo.guid
149 | payload = req.as_dict()
150 | r = self.sess.post(url,
151 | headers=hdrs,
152 | data=plistlib.dumps(payload))
153 | d = plistlib.loads(r.content)
154 | resp = StoreDownloadResp.from_dict(d)
155 | if resp.cancel_purchase_batch:
156 | raise StoreException("volumeStoreDownloadProduct", d, resp.customerMessage, '%s-%s' % (resp.failureType, resp.metrics))
157 | return resp
158 |
159 | def buyProduct(self, appId, appVer='', productType='C', pricingParameters='STDQ'):
160 | # STDQ - buy, STDRDL - redownload, SWUPD - update
161 | url = "https://p25-buy.itunes.apple.com/WebObjects/MZBuy.woa/wa/buyProduct"
162 |
163 | itunes_internal = self.iTunes_provider(url)
164 | hdrs = itunes_internal.pop('headers')
165 | guid = itunes_internal.pop('guid')
166 | kbsync = itunes_internal.pop('kbsync')
167 |
168 | if not appVer:
169 | from reqs.itunes import iTunesClient
170 | iTunes = iTunesClient(self.sess)
171 | appVer = iTunes.getAppVerId(appId, hdrs['X-Apple-Store-Front'])
172 |
173 | req = StoreBuyproductReq(
174 | guid=guid,
175 | salableAdamId=str(appId),
176 | appExtVrsId=str(appVer) if appVer else None,
177 |
178 | price='0',
179 | productType=productType,
180 | pricingParameters=pricingParameters,
181 |
182 | ageCheck='true',
183 | hasBeenAuthedForBuy='true',
184 | isInApp='false',
185 | hasConfirmedPaymentSheet='true',
186 | asn='1',
187 | )
188 | payload = req.as_dict()
189 | payload['kbsync'] = kbsync # kbsync is bytes, but json schema does not support it, so we have to assign it
190 | if 'sbsync' in itunes_internal:
191 | payload['sbsync'] = itunes_internal.pop('sbsync') # sbsync is the same as kbsync
192 | if 'afds' in itunes_internal:
193 | payload['afds'] = itunes_internal.pop('afds')
194 |
195 | hdrs = dict(hdrs)
196 | hdrs["Content-Type"] = "application/x-apple-plist"
197 |
198 | r = self.sess.post(url,
199 | headers=hdrs,
200 | data=plistlib.dumps(payload)
201 | )
202 |
203 | d = plistlib.loads(r.content)
204 | resp = StoreBuyproductResp.from_dict(d)
205 | if resp.cancel_purchase_batch:
206 | raise StoreException("buyProduct", d, resp.customerMessage, '%s-%s' % (resp.failureType, resp.metrics))
207 | return resp
208 |
209 | def buyProduct_purchase(self, appId, productType='C'):
210 | url = "https://buy.itunes.apple.com/WebObjects/MZBuy.woa/wa/buyProduct"
211 | req = StoreBuyproductReq(
212 | guid=self.authInfo.guid,
213 | salableAdamId=str(appId),
214 | appExtVrsId='0',
215 |
216 | price='0',
217 | productType=productType,
218 | pricingParameters='STDQ',
219 |
220 | hasAskedToFulfillPreorder='true',
221 | buyWithoutAuthorization='true',
222 | hasDoneAgeCheck='true',
223 | hasConfirmedPaymentSheet='true',
224 | )
225 | payload = req.as_dict()
226 |
227 | r = self.sess.post(url,
228 | headers={
229 | "Content-Type": "application/x-apple-plist",
230 | "User-Agent": "Configurator/2.15 (Macintosh; OS X 11.0.0; 16G29) AppleWebKit/2603.3.8",
231 | },
232 | data=plistlib.dumps(payload))
233 |
234 | if r.status_code == 500:
235 | raise StoreException("buyProduct_purchase", None, 'purchased_before')
236 |
237 | d = plistlib.loads(r.content)
238 | resp = StoreBuyproductResp.from_dict(d)
239 | if resp.status != 0 or resp.jingleDocType != 'purchaseSuccess':
240 | raise StoreException("buyProduct_purchase", d, resp.customerMessage,
241 | '%s-%s' % (resp.status, resp.jingleDocType))
242 | return resp
243 |
244 | def purchase(self, appId):
245 | if self.iTunes_provider:
246 | return None # iTunes mode will automatically purchase the app if not purchased
247 | else:
248 | return self.buyProduct_purchase(appId)
249 |
250 | def download(self, appId, appVer='', isRedownload=True):
251 | if self.iTunes_provider:
252 | return self.buyProduct(appId, appVer, pricingParameters='STDRDL' if isRedownload else 'STDQ')
253 | else:
254 | return self.volumeStoreDownloadProduct(appId, appVer)
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | rich>=10.2.2
2 | requests>=2.25.0
3 |
--------------------------------------------------------------------------------