diff --git a/.travis.yml b/.travis.yml index 0393405..c524add 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,11 +12,12 @@ python: # command to install dependencies install: - "pip install --upgrade pip" - - "pip install flake8 python-coveralls '.[tests]'" + - "pip install flake8 python-coveralls sphinx '.[tests]'" # command to run tests script: - "flake8 --ignore=E501 ." - "pytest --cov=itunesiap -vv tests/" + - "python -msphinx -M html docs build" after_success: - "coveralls" - bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports" diff --git a/README.rst b/README.rst index 56506eb..6dc2dec 100644 --- a/README.rst +++ b/README.rst @@ -6,18 +6,10 @@ itunes-iap v2 .. image:: https://coveralls.io/repos/github/youknowone/itunes-iap/badge.svg?branch=master :target: https://coveralls.io/github/youknowone/itunes-iap?branch=master -Note for v1 users +The quick example ----------------- -There was breaking changes between v1 and v2 APIs. - -- Specify version `0.6.6` for latest v1 API when you don't need new APIs. -- Or use `import itunesiap.legacy as itunesiap` instead of `import itunesiap`. (`from itunesiap import xxx` to `from itunesiap.legacy import xxx`) - -Quick example -------------- - -Create request to create a request to itunes verify api. +Create request to create a request to itunes verifying api. .. sourcecode:: python @@ -28,53 +20,53 @@ Create request to create a request to itunes verify api. >>> print('invalid receipt') >>> print response.receipt.last_in_app.product_id # other values are also available as property! -Practical values are: product_id, original_transaction_id, quantity, unique_identifier +Practically useful attributes are: + `product_id`, `original_transaction_id`, `quantity` and `unique_identifier`. -Quick example with password (Apple Shared Secret) -------------------------------------------------- +See the full document in: + - :func:`itunesiap.verify`: The verifying function. + - :class:`itunesiap.receipt.InApp`: The receipt object. -Create request to create a request to itunes verify api. -.. sourcecode:: python +Installation +------------ - >>> import itunesiap - >>> try: - >>> response = itunesiap.verify(raw_data, password) # Just add password - >>> except itunesiap.exc.InvalidReceipt as e: - >>> print('invalid receipt') - >>> in_app = response.receipt.last_in_app # Get the latest receipt returned by Apple +PyPI is the recommended way. +.. sourcecode:: shell -Verification policy -------------------- + $ pip install itunesiap -Set verification mode for production or sandbox api. Review mode also available for appstore review. +To browse versions and tarballs, visit: + ``_ -.. sourcecode:: python - >>> import itunesiap - >>> # `review` enables both production and sandbox for appstore review. `production`, `sandbox` or `review` is available. - >>> response = request.verify(raw_data, env=itunesiap.env.review) +Apple in-review mode +-------------------- + +In review mode, your actual users who use older versions want to verify in +production server but the reviewers in Apple office want to verify in sandbox +server. -Or +Note: The default env is `production` mode which doesn't allow any sandbox +verifications. + +You can change the verifying mode by specifying `env`. .. sourcecode:: python - >>> import itunesiap - >>> response = itunesiap.verify(raw_data, use_sandbox=True): # additional change for current environment. + >>> # review mode + >>> itunesiap.verify(raw_data, env=itunesiap.env.review) -Proxy ------ -Put `proxy_url` for proxies. +Note for v1 users +----------------- -.. sourcecode:: python +There was breaking changes between v1 and v2 APIs. + +- Specify version `0.6.6` for latest v1 API when you don't need new APIs. +- Or use `import itunesiap.legacy as itunesiap` instead of `import itunesiap`. (`from itunesiap import xxx` to `from itunesiap.legacy import xxx`) - >>> import itunesiap - >>> try: - >>> response = itunesiap.verify(raw_data, proxy_url='https://your.proxy.url/') - >>> except itunesiap.exc.InvalidReceipt as e: - >>> ... Contributors ------------ diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..0b15ec8 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + +def get_version(): + with open('../itunesiap/version.txt') as f: + return f.read().strip() + +# +# itunes-iap documentation build configuration file, created by +# sphinx-quickstart on Sat Jul 22 17:34:06 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.intersphinx', + 'sphinx.ext.coverage'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'itunes-iap' +copyright = '2017, Jeong YunWon' +author = 'Jeong YunWon' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = get_version() +# The full version, including alpha/beta/rc tags. +release = get_version() + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = [] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# This is required for the alabaster theme +# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars +html_sidebars = { + '**': [ + 'about.html', + 'navigation.html', + 'relations.html', # needs 'show_related': True theme option to display + 'searchbox.html', + 'donate.html', + ] +} + + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'itunesiapdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'itunes-iap.tex', 'itunes-iap Documentation', + 'Jeong YunWon', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'itunes-iap', 'itunes-iap Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'itunes-iap', 'itunes-iap Documentation', + author, 'itunes-iap', 'One line description of project.', + 'Miscellaneous'), +] + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'https://docs.python.org/': None} diff --git a/docs/environment.rst b/docs/environment.rst new file mode 100644 index 0000000..7119aea --- /dev/null +++ b/docs/environment.rst @@ -0,0 +1,14 @@ +Environment +=========== + +.. automodule:: itunesiap.environment + + +.. autoclass:: itunesiap.environment.Environment + :members: + +.. autodata:: itunesiap.environment.default +.. autodata:: itunesiap.environment.production +.. autodata:: itunesiap.environment.sandbox +.. autodata:: itunesiap.environment.review +.. autodata:: itunesiap.environment.unsafe diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..4613009 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,25 @@ +.. itunes-iap documentation master file, created by + sphinx-quickstart on Sat Jul 22 17:34:06 2017. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +iTunes In-App purchase verification in Python +============================================= + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + quick.rst + request.rst + receipt.rst + environment.rst + +.. include:: ../README.rst + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/quick.rst b/docs/quick.rst new file mode 100644 index 0000000..45c3171 --- /dev/null +++ b/docs/quick.rst @@ -0,0 +1,71 @@ +The quick guide +=============== + +Create request to create a request to itunes verify api. + +.. sourcecode:: python + + >>> import itunesiap + >>> try: + >>> response = itunesiap.verify(raw_data) # base64-encoded data + >>> except itunesiap.exc.InvalidReceipt as e: + >>> print('invalid receipt') + >>> print response.receipt.last_in_app.product_id + >>> # other values are also available as properties! + +Practically useful attributes are: `product_id`, `original_transaction_id`, `quantity` and `unique_identifier`. +See the full document in :class:`itunesiap.receipt.InApp`. + +itunesiap.verify() +------------------ +Note that most of the use cases are covered by the :func:`itunesiap.verify` +function. + +.. autofunction:: itunesiap.verify + +Apple in-review mode +-------------------- + +In review mode, your actual users who use older versions want to verify in +production server but the reviewers in Apple office want to verify in sandbox +server. + +Note: The default env is `production` mode which doesn't allow any sandbox +verifications. + +You can change the verifying mode by specifying `env`. + +.. sourcecode:: python + + >>> # review mode + >>> itunesiap.verify(raw_data, env=itunesiap.env.review) + >>> # sandbox mode + >>> itunesiap.verify(raw_data, env=itunesiap.env.sandbox) + +Also directly passing arguments are accepted: + +.. sourcecode:: python + + >>> # review mode + >>> itunesiap.verify(raw_data, use_production=True, use_sandbox=True) + + +Password for shared secret +-------------------------- + +When you have shared secret for your app, the verifying process requires a +shared secret password. + +About the shared secret, See: In-App_Purchase_Configuration_Guide_. + +.. sourcecode:: python + + >>> try: + >>> # Add password as a parameter + >>> response = itunesiap.verify(raw_data, password=password) + >>> except itunesiap.exc.InvalidReceipt as e: + >>> print('invalid receipt') + >>> in_app = response.receipt.last_in_app # Get the latest receipt returned by Apple + + +.. _In-App_Purchase_Configuration_Guide: https://developer.apple.com/library/content/documentation/LanguagesUtilities/Conceptual/iTunesConnectInAppPurchase_Guide/Chapters/CreatingInAppPurchaseProducts.html \ No newline at end of file diff --git a/docs/receipt.rst b/docs/receipt.rst new file mode 100644 index 0000000..d31fd08 --- /dev/null +++ b/docs/receipt.rst @@ -0,0 +1,23 @@ +Receipt +======= + +.. automodule:: itunesiap.receipt + +.. autoclass:: itunesiap.receipt.ObjectMapper + :members: + +.. autoclass:: itunesiap.receipt.Response + :members: __EXPORT_FILTERS__, __WHITELIST__ + :special-members: + :undoc-members: + +.. autoclass:: itunesiap.receipt.Receipt + :members: __EXPORT_FILTERS__, __WHITELIST__ + :special-members: + :undoc-members: + +.. autoclass:: itunesiap.receipt.InApp + :members: __EXPORT_FILTERS__, __WHITELIST__ + :special-members: + :undoc-members: + diff --git a/docs/request.rst b/docs/request.rst new file mode 100644 index 0000000..b0257f0 --- /dev/null +++ b/docs/request.rst @@ -0,0 +1,13 @@ +Request +======= + +.. automodule:: itunesiap.request + +.. autoclass:: itunesiap.request.Request + :members: + +.. automodule:: itunesiap.exceptions + +.. autoexception:: itunesiap.exceptions.ItunesServerNotAvailable +.. autoexception:: itunesiap.exceptions.ItunesServerNotReachable +.. autoexception:: itunesiap.exceptions.InvalidReceipt diff --git a/itunesiap/environment.py b/itunesiap/environment.py index 81d31a1..1a95707 100644 --- a/itunesiap/environment.py +++ b/itunesiap/environment.py @@ -1,4 +1,48 @@ +""":mod:`itunesiap.environment` +:class:`Environment` is designed to pass pre-defined policies in easy way. +The major use cases are provided as pre-defined constants. + +How to use environments +----------------------- + +The default policy is `default` and it is the same to `production`. When your +development lifecycle is proceed, you want to change it to `sandbox` or +`review`. + +The recommended way to use environments is passing the value to +:func:`itunesiap.verify` function as keyword argument `env`. + +.. sourcecode:: python + + >>> itunesiap.verify(receipt, env=itunesiap.env.production) + >>> itunesiap.verify(receipt, env=itunesiap.env.sandbox) + >>> itunesiap.verify(receipt, env=itunesiap.env.review) + + +Review mode +----------- + +This is useful when your server is being used both for real users and Apple +reviewers. +Using review mode for a real service is possible, but be aware of: it is not +100% safe. Your testers can getting advantage of free IAP in production +version. +A rough solution what I suggest is: + +.. sourcecode:: python + + >>> if client_version == review_version: + >>> env = itunesiap.env.review + >>> else: + >>> env = itunesiap.env.production + >>> + >>> itunesiap.verify(receipt, env=env) + + +Environment +----------- +""" from itunesiap.tools import deprecated __all__ = ('Environment', 'default', 'production', 'sandbox', 'review') @@ -11,18 +55,29 @@ def push(self, env): class Environment(object): - """Environment provides option preset for `Request`. `default` is default""" + """Environment provides option preset for `Request`. `default` is default. + + By passing an environment object to :func:`itunesiap.verify` or + :func:`itunesiap.request.Request.verify` function, it replaces verifying + policies. + """ - ITEMS = ('use_production', 'use_sandbox') + ITEMS = ( + 'use_production', 'use_sandbox', 'timeout', 'exclude_old_transactions', + 'verify_ssl') def __init__(self, **kwargs): self.use_production = kwargs.get('use_production', True) self.use_sandbox = kwargs.get('use_sandbox', False) self.timeout = kwargs.get('timeout', None) - self.verify_ssl = kwargs.get('verify_ssl', True) self.exclude_old_transactions = kwargs.get('exclude_old_transactions', False) + self.verify_ssl = kwargs.get('verify_ssl', True) + + def __repr__(self): + return u'<{self.__class__.__name__} use_production={self.use_production} use_sandbox={self.use_sandbox} timeout={self.timeout} exclude_old_transactions={self.exclude_old_transactions} verify_ssl={self.verify_ssl}>'.format(self=self) def clone(self, **kwargs): + """Clone the environment with additional parameter override""" options = self.extract() options.update(**kwargs) return self.__class__(**options) @@ -65,15 +120,20 @@ def current(cls): return cls._stack[-1] +#: Use only production server with 30 seconds of timeout. default = Environment(use_production=True, use_sandbox=False, timeout=30.0, verify_ssl=True) +#: Use only production server with 30 seconds of timeout. production = Environment(use_production=True, use_sandbox=False, timeout=30.0, verify_ssl=True) +#: Use only sandbox server with 30 seconds of timeout. sandbox = Environment(use_production=False, use_sandbox=True, timeout=30.0, verify_ssl=True) + review = Environment(use_production=True, use_sandbox=True, timeout=30.0, verify_ssl=True) +#: Use both production and sandbox servers with 30 seconds of timeout. unsafe = Environment(use_production=True, use_sandbox=True, verify_ssl=False) -Environment._stack.push(default) # for backward compatibility +Environment._stack.append(default) # for backward compatibility @deprecated diff --git a/itunesiap/exceptions.py b/itunesiap/exceptions.py index bf74a89..22d5fc1 100644 --- a/itunesiap/exceptions.py +++ b/itunesiap/exceptions.py @@ -1,4 +1,4 @@ - +""":mod:`itunesiap.exceptions`""" from prettyexc import PrettyException as E @@ -7,14 +7,15 @@ class RequestError(E): class ItunesServerNotAvailable(RequestError): - pass + '''iTunes server is not available. No response.''' class ItunesServerNotReachable(ItunesServerNotAvailable): - pass + '''iTunes server is not reachable - including connection timeout.''' class InvalidReceipt(RequestError): + '''A receipt was given by iTunes server but it has error.''' _req_kwargs_keys = ['status'] _descriptions = { 21000: 'The App Store could not read the JSON object you provided.', diff --git a/itunesiap/receipt.py b/itunesiap/receipt.py index 140c69d..49c35f3 100644 --- a/itunesiap/receipt.py +++ b/itunesiap/receipt.py @@ -1,4 +1,8 @@ +""":mod:`itunesiap.receipt` +A successful response returns a JSON object including receipts. To manipulate +them in convinient way, `itunes-iap` wrapped it with :class:`ObjectMapper`. +""" import pytz import dateutil.parser import warnings @@ -40,7 +44,29 @@ def _to_bool(data): class ObjectMapper(object): - """Pretty interface for decoded receipt object. + """A pretty interface for decoded receipt object. + + `__WHITELIST__` is a managed list of names. They are regarded as safe + values and guaranteed to be converted in python representation when needed. + `__EXPORT_FILTERS__` decides how to convert raw data to python + representation. + + To access to the converted value, use a dictionary key as an attribute name. + For example, the key `receipt` is accessible by: + + .. sourcecode:: python + + >>> mapper.receipt # return converted python object Receipt + >>> # == Receipt(mapper._['receipt']) + + To access to the raw JSON value, use a dictionary key as an attribute name + but with the prefix `_`. For example, the key `receipt` is accessible by: + + >>> mapper._receipt # return converted python object Receipt + >>> # == mapper._['receipt'] + + :param dict data: A JSON object. + :return: :class:`ObjectMapper` """ __WHITELIST__ = [] __EXPORT_FILTERS__ = {} @@ -78,7 +104,8 @@ def __getattr__(self, item): class Response(ObjectMapper): """The root response. - status: See https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1 + About the value of status: + - See https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1 """ __WHITELIST__ = ['receipt', 'latest_receipt'] # latest_receipt_info __EXPORT_FILTERS__ = {'status': int} @@ -93,11 +120,22 @@ def latest_receipt(self): class Receipt(ObjectMapper): + """The actual receipt. + + The receipt may hold only one purchase directly in receipt object or may + hold multiple purchases in `in_app` key. + This object encapsulate it to list of :class:`InApp` object in `in_app` + property. + """ __WHITELIST__ = ['in_app'] __EXPORT_FILTERS__ = {} @lazy_property def in_app(self): + """The list of purchases. If the receipt has receipt keys in the + receipt body, it still will be wrapped as an :class:`InApp` and consists + of this property + """ if 'in_app' in self._: return list(map(InApp, self._in_app)) else: @@ -105,11 +143,26 @@ def in_app(self): @property def last_in_app(self): + """The last item in `in_app` property order by purchase_date.""" return sorted( self.in_app, key=lambda x: x['original_purchase_date_ms'])[-1] class InApp(ObjectMapper): + """The individual purchases. + + The major keys are `unique_identifier`, `quantity`, `product_id` and + `transaction_id`. `quantty` and `product_id` mean what kind of product and + and how many of them the customer bought. `unique_identifier` and + `transaction_id` is used to check where it is processed and track related + purchases. + + For the detail, see also Apple docs. + + Any `date` related keys will be converted to python + :class:`datetime.datetime` object. The quantity and any `date_ms` related + keys will be converted to python :class:`int`. + """ __WHITELIST__ = [ 'unique_identifier', 'quantity', 'product_id', 'transaction_id', 'original_transaction_id', 'is_trial_period', diff --git a/itunesiap/request.py b/itunesiap/request.py index b3fbc0b..5b84d83 100644 --- a/itunesiap/request.py +++ b/itunesiap/request.py @@ -1,4 +1,4 @@ - +""":mod:`itunesiap.request`""" import json import functools @@ -17,9 +17,13 @@ class Request(object): """Validation request with raw receipt. Use `verify` method to try verification and get Receipt or exception. + For detail, see also the Apple document: ``_. :param str receipt_data: An iTunes receipt data as Base64 encoded string. + :param str password: Only used for receipts that contain auto-renewable subscriptions. Your app's shared secret (a hexadecimal string). + :param bool exclude_old_transactions: Only used for iOS7 style app receipts that contain auto-renewable or non-renewing subscriptions. If value is true, response includes only the latest renewal transaction for any subscriptions. :param proxy_url: A proxy url to access the iTunes validation url. + (It is an attribute of :func:`verify` but misplaced here) """ def __init__( @@ -29,13 +33,17 @@ def __init__( self.password = password self.exclude_old_transactions = exclude_old_transactions self.proxy_url = kwargs.pop('proxy_url', None) + if kwargs: # pragma: no cover + raise TypeError( + u"__init__ got unexpected keyword argument {}".format( + ', '.join(kwargs.keys()))) def __repr__(self): return u''.format(self.receipt_data[:20]) @property def request_content(self): - """Build request body for iTunes.""" + """Instantly built request body for iTunes.""" request_content = { 'receipt-data': self.receipt_data, 'exclude-old-transactions': self.exclude_old_transactions} @@ -44,9 +52,13 @@ def request_content(self): return request_content def verify_from(self, url, timeout=None, verify_ssl=True): - """Try verification from given url. + """The actual implemention of verification request. + + :func:`verify` calls this method to try to verifying for each servers. :param str url: iTunes verification API URL. + :param float timeout: The value is connection timeout of the verifying + request. The default value is 30.0 when no `env` is given. :param bool verify_ssl: SSL verification. :return: :class:`itunesiap.receipt.Receipt` object if succeed. @@ -78,10 +90,24 @@ def verify(self, **options): verified. The verify_ssl is set to false by default for backwards compatibility. - :param Environment env: Override environment if given - :param bool use_production: Override environment value if given - :param bool use_sandbox: Override environment value if given - :param bool verify_ssl: Override environment value if given + See also: + - Receipt_Validation_Programming_Guide_. + + .. _Receipt_Validation_Programming_Guide: https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html + + :param itunesiap.environment.Environment env: Override the environment. + :param float timeout: The value is connection timeout of the verifying + request. The default value is 30.0 when no `env` is given. + :param bool use_production: The value is weather verifying in + production server or not. The default value is :class:`bool` True + when no `env` is given. + :param bool use_sandbox: The value is weather verifying in + sandbox server or not. The default value is :class:`bool` False + when no `env` is given. + + :param bool verify_ssl: The value is weather enabling SSL verification + or not. WARNING: DO NOT TURN IT OFF WITHOUT A PROPER REASON. IF YOU + DON'T UNDERSTAND WHAT IT MEANS, NEVER SET IT YOURSELF. :return: :class:`itunesiap.receipt.Receipt` object if succeed. :raises: Otherwise raise a request exception. diff --git a/itunesiap/shortcut.py b/itunesiap/shortcut.py index a9d506f..dcc05ae 100644 --- a/itunesiap/shortcut.py +++ b/itunesiap/shortcut.py @@ -1,19 +1,50 @@ - +""":mod:`itunesiap.shortcut`""" from .request import Request def verify( receipt_data, password=None, exclude_old_transactions=False, **kwargs): - """Shortcut API for :class:`itunesiap.request.Request` + """Shortcut API for :class:`itunesiap.request.Request`. + + See also: + - Receipt_Validation_Programming_Guide_. + + .. _Receipt_Validation_Programming_Guide: https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html + + :param str receipt_data: :class:`itunesiap.request.Request` variable. + An iTunes receipt data as Base64 encoded string. + :param str password: :class:`itunesiap.request.Request` variable. Optional. + Only used for receipts that contain auto-renewable subscriptions. Your + app's shared secret (a hexadecimal string). + :param bool exclude_old_transactions: :class:`itunesiap.request.Request` + variable. Optional. Only used for iOS7 style app receipts that contain + auto-renewable or non-renewing subscriptions. If value is true, + response includes only the latest renewal transaction for any + subscriptions. + + :param itunesiap.environment.Environment env: Set base environment value. + See :mod:`itunesiap.environment` for detail. + :param float timeout: :func:`itunesiap.request.Request.verify` variable. + Keyword-only optional. The value is connection timeout of the verifying + request. The default value is 30.0 when no `env` is given. + :param bool use_production: :func:`itunesiap.request.Request.verify` + variable. Keyword-only optional. The value is weather verifying in + production server or not. The default value is :class:`bool` True + when no `env` is given. + :param bool use_sandbox: :func:`itunesiap.request.Request.verify` + variable. Keyword-only optional. The value is weather verifying in + sandbox server or not. The default value is :class:`bool` False + when no `env` is given. - :param str receipt_data: An iTunes receipt data as Base64 encoded string. - :param proxy_url: A proxy url to access the iTunes validation url - :param bool use_production: Override environment value if given - :param bool use_sandbox: Override environment value if given - :param bool verify_ssl: Override environment value if given + :param bool verify_ssl: :func:`itunesiap.request.Request.verify` variable. + Keyword-only optional. The value is weather enabling SSL verification + or not. WARNING: DO NOT TURN IT OFF WITHOUT A PROPER REASON. IF YOU + DON'T UNDERSTAND WHAT IT MEANS, NEVER SET IT YOURSELF. + :param str proxy_url: Keyword-only optional. A proxy url to access the + iTunes validation url. :return: :class:`itunesiap.receipt.Receipt` object if succeed. - :raises: Otherwise raise a request exception. + :raises: Otherwise raise a request exception in :mod:`itunesiap.exceptions`. """ proxy_url = kwargs.pop('proxy_url', None) request = Request( diff --git a/setup.py b/setup.py index 679c16e..541b844 100644 --- a/setup.py +++ b/setup.py @@ -42,6 +42,7 @@ def get_readme(): tests_require=tests_require, extras_require={ 'tests': tests_require, + 'doc': ['sphinx'], }, classifiers=[ 'Intended Audience :: Developers',