Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/dotpot/inapppy
Python In-app purchase validator for Apple AppStore and GooglePlay.
https://github.com/dotpot/inapppy
appstore asyncio googleplay in-app-purchase library purchase python python3 validation
Last synced: 4 days ago
JSON representation
Python In-app purchase validator for Apple AppStore and GooglePlay.
- Host: GitHub
- URL: https://github.com/dotpot/inapppy
- Owner: dotpot
- License: mit
- Created: 2017-02-06T11:09:13.000Z (almost 8 years ago)
- Default Branch: master
- Last Pushed: 2023-10-18T02:23:27.000Z (about 1 year ago)
- Last Synced: 2024-10-28T12:13:10.521Z (17 days ago)
- Topics: appstore, asyncio, googleplay, in-app-purchase, library, purchase, python, python3, validation
- Language: Python
- Homepage:
- Size: 112 KB
- Stars: 204
- Watchers: 12
- Forks: 50
- Open Issues: 12
-
Metadata Files:
- Readme: README.rst
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
Awesome Lists containing this project
README
InAppPy
=======
|travis| |pypi| |downloads|.. |travis| image:: https://travis-ci.org/dotpot/InAppPy.svg?branch=master
:target: https://travis-ci.org/dotpot/InAppPy
.. |pypi| image:: https://badge.fury.io/py/inapppy.svg
:target: https://badge.fury.io/py/inapppy
.. |downloads| image:: https://img.shields.io/pypi/dm/inapppy.svg
:target: https://pypi.python.org/pypi/inapppyTable of contents
=================1. Introduction
2. Installation
3. Google Play (`receipt` + `signature`)
4. Google Play (verification)
5. Google Play (verification with result)
6. App Store (`receipt` + using optional `shared-secret`)
7. App Store Response (`validation_result` / `raw_response`) example
8. App Store, **asyncio** version (available in the inapppy.asyncio package)
9. Development
10. Donate1. Introduction
===============In-app purchase validation library for `Apple AppStore` and `GooglePlay` (`App Store` validator have **async** support!). Works on python3.6+
2. Installation
===============
::pip install inapppy
3. Google Play (validates `receipt` against provided `signature` using RSA)
===========================================================================
.. code:: pythonfrom inapppy import GooglePlayValidator, InAppPyValidationError
bundle_id = 'com.yourcompany.yourapp'
api_key = 'API key from the developer console'
validator = GooglePlayValidator(bundle_id, api_key)try:
# receipt means `androidData` in result of purchase
# signature means `signatureAndroid` in result of purchase
validation_result = validator.validate('receipt', 'signature')
except InAppPyValidationError:
# handle validation error
passAn additional example showing how to authenticate using dict credentials instead of loading from a file
.. code:: python
import json
from inapppy import GooglePlayValidator, InAppPyValidationErrorbundle_id = 'com.yourcompany.yourapp'
# Avoid hard-coding credential data in your code. This is just an example.
api_credentials = json.loads('{'
' "type": "service_account",'
' "project_id": "xxxxxxx",'
' "private_key_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",'
' "private_key": "-----BEGIN PRIVATE KEY-----\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==\n-----END PRIVATE KEY-----\n",'
' "client_email": "[email protected]",'
' "client_id": "XXXXXXXXXXXXXXXXXX",'
' "auth_uri": "https://accounts.google.com/o/oauth2/auth",'
' "token_uri": "https://oauth2.googleapis.com/token",'
' "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",'
' "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/XXXXXXXXXXXXXXXXX.iam.gserviceaccount.com"'
' }')
validator = GooglePlayValidator(bundle_id, api_credentials)try:
# receipt means `androidData` in result of purchase
# signature means `signatureAndroid` in result of purchase
validation_result = validator.validate('receipt', 'signature')
except InAppPyValidationError:
# handle validation error
pass4. Google Play verification
===========================
.. code:: pythonfrom inapppy import GooglePlayVerifier, errors
def google_validator(receipt):
"""
Accepts receipt, validates in Google.
"""
purchase_token = receipt['purchaseToken']
product_sku = receipt['productId']
verifier = GooglePlayVerifier(
GOOGLE_BUNDLE_ID,
GOOGLE_SERVICE_ACCOUNT_KEY_FILE,
)
response = {'valid': False, 'transactions': []}
try:
result = verifier.verify(
purchase_token,
product_sku,
is_subscription=True
)
response['valid'] = True
response['transactions'].append(
(result['orderId'], product_sku)
)
except errors.GoogleError as exc:
logging.error('Purchase validation failed {}'.format(exc))
return response5. Google Play verification (with result)
=========================================
Alternative to `.verify` method, instead of raising an error result class will be returned... code:: python
from inapppy import GooglePlayVerifier, errors
def google_validator(receipt):
"""
Accepts receipt, validates in Google.
"""
purchase_token = receipt['purchaseToken']
product_sku = receipt['productId']
verifier = GooglePlayVerifier(
GOOGLE_BUNDLE_ID,
GOOGLE_SERVICE_ACCOUNT_KEY_FILE,
)
response = {'valid': False, 'transactions': []}result = verifier.verify_with_result(
purchase_token,
product_sku,
is_subscription=True
)# result contains data
raw_response = result.raw_response
is_canceled = result.is_canceled
is_expired = result.is_expiredreturn result
6. App Store (validates `receipt` using optional `shared-secret` against iTunes service)
========================================================================================
.. code:: pythonfrom inapppy import AppStoreValidator, InAppPyValidationError
bundle_id = 'com.yourcompany.yourapp'
auto_retry_wrong_env_request=False # if True, automatically query sandbox endpoint if
# validation fails on production endpoint
validator = AppStoreValidator(bundle_id, auto_retry_wrong_env_request=auto_retry_wrong_env_request)try:
exclude_old_transactions=False # if True, include only the latest renewal transaction
validation_result = validator.validate('receipt', 'optional-shared-secret', exclude_old_transactions=exclude_old_transactions)
except InAppPyValidationError as ex:
# handle validation error
response_from_apple = ex.raw_response # contains actual response from AppStore service.
pass7. App Store Response (`validation_result` / `raw_response`) example
====================================================================
.. code:: json{
"latest_receipt": "MIIbngYJKoZIhvcNAQcCoIIbj...",
"status": 0,
"receipt": {
"download_id": 0,
"receipt_creation_date_ms": "1486371475000",
"application_version": "2",
"app_item_id": 0,
"receipt_creation_date": "2017-02-06 08:57:55 Etc/GMT",
"original_purchase_date": "2013-08-01 07:00:00 Etc/GMT",
"request_date_pst": "2017-02-06 04:41:09 America/Los_Angeles",
"original_application_version": "1.0",
"original_purchase_date_pst": "2013-08-01 00:00:00 America/Los_Angeles",
"request_date_ms": "1486384869996",
"bundle_id": "com.yourcompany.yourapp",
"request_date": "2017-02-06 12:41:09 Etc/GMT",
"original_purchase_date_ms": "1375340400000",
"in_app": [{
"purchase_date_ms": "1486371474000",
"web_order_line_item_id": "1000000034281189",
"original_purchase_date_ms": "1486371475000",
"original_purchase_date": "2017-02-06 08:57:55 Etc/GMT",
"expires_date_pst": "2017-02-06 01:00:54 America/Los_Angeles",
"original_purchase_date_pst": "2017-02-06 00:57:55 America/Los_Angeles",
"purchase_date_pst": "2017-02-06 00:57:54 America/Los_Angeles",
"expires_date_ms": "1486371654000",
"expires_date": "2017-02-06 09:00:54 Etc/GMT",
"original_transaction_id": "1000000271014363",
"purchase_date": "2017-02-06 08:57:54 Etc/GMT",
"quantity": "1",
"is_trial_period": "false",
"product_id": "com.yourcompany.yourapp",
"transaction_id": "1000000271014363"
}],
"version_external_identifier": 0,
"receipt_creation_date_pst": "2017-02-06 00:57:55 America/Los_Angeles",
"adam_id": 0,
"receipt_type": "ProductionSandbox"
},
"latest_receipt_info": [{
"purchase_date_ms": "1486371474000",
"web_order_line_item_id": "1000000034281189",
"original_purchase_date_ms": "1486371475000",
"original_purchase_date": "2017-02-06 08:57:55 Etc/GMT",
"expires_date_pst": "2017-02-06 01:00:54 America/Los_Angeles",
"original_purchase_date_pst": "2017-02-06 00:57:55 America/Los_Angeles",
"purchase_date_pst": "2017-02-06 00:57:54 America/Los_Angeles",
"expires_date_ms": "1486371654000",
"expires_date": "2017-02-06 09:00:54 Etc/GMT",
"original_transaction_id": "1000000271014363",
"purchase_date": "2017-02-06 08:57:54 Etc/GMT",
"quantity": "1",
"is_trial_period": "true",
"product_id": "com.yourcompany.yourapp",
"transaction_id": "1000000271014363"
}, {
"purchase_date_ms": "1486371719000",
"web_order_line_item_id": "1000000034281190",
"original_purchase_date_ms": "1486371720000",
"original_purchase_date": "2017-02-06 09:02:00 Etc/GMT",
"expires_date_pst": "2017-02-06 01:06:59 America/Los_Angeles",
"original_purchase_date_pst": "2017-02-06 01:02:00 America/Los_Angeles",
"purchase_date_pst": "2017-02-06 01:01:59 America/Los_Angeles",
"expires_date_ms": "1486372019000",
"expires_date": "2017-02-06 09:06:59 Etc/GMT",
"original_transaction_id": "1000000271014363",
"purchase_date": "2017-02-06 09:01:59 Etc/GMT",
"quantity": "1",
"is_trial_period": "false",
"product_id": "com.yourcompany.yourapp",
"transaction_id": "1000000271016119"
}],
"environment": "Sandbox"
}8. App Store, asyncio version (available in the inapppy.asyncio package)
========================================================================
.. code:: pythonfrom inapppy import InAppPyValidationError
from inapppy.asyncio import AppStoreValidatorbundle_id = 'com.yourcompany.yourapp'
auto_retry_wrong_env_request=False # if True, automatically query sandbox endpoint if
# validation fails on production endpoint
validator = AppStoreValidator(bundle_id, auto_retry_wrong_env_request=auto_retry_wrong_env_request)try:
exclude_old_transactions=False # if True, include only the latest renewal transaction
validation_result = await validator.validate('receipt', 'optional-shared-secret', exclude_old_transactions=exclude_old_transactions)
except InAppPyValidationError as ex:
# handle validation error
response_from_apple = ex.raw_response # contains actual response from AppStore service.
pass9. Development
==============.. code:: bash
# run checks and tests
tox# setup project
make setup# check for lint errors
make lint# run tests
make test# run black
make black
10. Donate
==========
You can support development of this project by buying me a coffee ;)+------+--------------------------------------------+
| Coin | Wallet |
+======+============================================+
| EUR | https://paypal.me/LukasSalkauskas |
+------+--------------------------------------------+
| DOGE | DGjSG3T6g9h2k6iSku7mtKCynCpmwowpyN |
+------+--------------------------------------------+
| BTC | 1LZAiWmLYzZae4hq3ai9hFYD3e3qcwjDsU |
+------+--------------------------------------------+
| ETH | 0xD62245986345130edE10e4b545fF577Bd5BaE3E4 |
+------+--------------------------------------------+