def __init__(self, bundle_id: str, api_key: str, default_valid_purchase_state: int = 0) -> None: """ Arguments: bundle_id: str - Also known as Android app's package name. E.g.: "com.example.calendar". api_key: str - Application's Base64-encoded RSA public key. As of 03.19 this can be found in Google Play Console under Services & APIs. default_valid_purchase_state: int - Accepted purchase state. """ if not bundle_id: raise InAppPyValidationError("bundle_id cannot be empty.") elif not api_key: raise InAppPyValidationError("api_key cannot be empty.") self.bundle_id = bundle_id self.purchase_state_ok = default_valid_purchase_state pem = make_pem(api_key) try: self.public_key = rsa.PublicKey.load_pkcs1_openssl_pem(pem) except TypeError: raise InAppPyValidationError("Bad API key")
def __init__(self, bundle_id, api_key): self.bundle_id = bundle_id if not self.bundle_id: raise InAppPyValidationError('`bundle_id` cannot be empty.') pem = make_pem(api_key) try: self.public_key = rsa.PublicKey.load_pkcs1_openssl_pem(pem) except TypeError: raise InAppPyValidationError('Bad api key')
def __init__(self, bundle_id: str, api_key: str, default_valid_purchase_state: int = 0): if not bundle_id: raise InAppPyValidationError('bundle_id cannot be empty.') if not api_key: raise InAppPyValidationError('api_key cannot be empty.') self.bundle_id = bundle_id self.purchase_state_ok = default_valid_purchase_state pem = make_pem(api_key) try: self.public_key = rsa.PublicKey.load_pkcs1_openssl_pem(pem) except TypeError: raise InAppPyValidationError('Bad api key')
def validate(self, receipt: str, signature: str) -> dict: if not self._validate_signature(receipt, signature): raise InAppPyValidationError('Bad signature') try: receipt_json = json.loads(receipt) if receipt_json['packageName'] != self.bundle_id: raise InAppPyValidationError('Bundle id mismatch') if receipt_json['purchaseState'] != self.purchase_state_ok: raise InAppPyValidationError('Item is not purchased') return receipt_json except (KeyError, ValueError): raise InAppPyValidationError('Bad receipt')
def validate(self, receipt, shared_secret=None): """ Validates receipt against apple services. :param receipt: receipt :param shared_secret: optional shared secret. :return: validation result or exception. """ receipt_json = {'receipt-data': receipt} if shared_secret: receipt_json['password'] = shared_secret # Do a request. api_response = self.post_json(receipt_json) status = api_response['status'] # Check retry case. if self.auto_retry_wrong_env_request and status in [21007, 21008]: # switch environment self.sandbox = not self.sandbox api_response = self.post_json(receipt_json) status = api_response['status'] if status != api_result_ok: error = api_result_errors.get(status, InAppPyValidationError('Unknown API status')) error.raw_response = api_response raise error return api_response
def validate(self, receipt: str, signature: str) -> dict: if not self._validate_signature(receipt, signature): raise InAppPyValidationError("Bad signature") try: receipt_json = json.loads(receipt) if receipt_json["packageName"] != self.bundle_id: raise InAppPyValidationError("Bundle ID mismatch") elif receipt_json["purchaseState"] != self.purchase_state_ok: raise InAppPyValidationError("Item is not purchased") return receipt_json except (KeyError, ValueError): raise InAppPyValidationError("Bad receipt")
def validate(self, receipt: str, shared_secret: str = None, exclude_old_transactions: bool = False) -> dict: """Validates receipt against apple services. :param receipt: receipt :param shared_secret: optional shared secret. :param exclude_old_transactions: optional to include only the latest renewal transaction :return: validation result or exception. """ receipt_json = self._prepare_receipt(receipt, shared_secret, exclude_old_transactions) api_response = self.post_json(receipt_json) status = api_response.get("status", "unknown") # Check retry case. if self.auto_retry_wrong_env_request and status in [21007, 21008]: # switch environment self.sandbox = not self.sandbox api_response = self.post_json(receipt_json) status = api_response["status"] if status != api_result_ok: error = api_result_errors.get( status, InAppPyValidationError("Unknown API status")) error.raw_response = api_response raise error return api_response
def post_json(self, request_json: dict) -> dict: self._change_url_by_sandbox() try: return requests.post(self.url, json=request_json, timeout=self.http_timeout).json() except (ValueError, RequestException): raise InAppPyValidationError("HTTP error")
def post_json(self, request_json): self._change_url_by_sandbox() try: return requests.post(self.url, json=request_json).json() except (ValueError, RequestException): raise InAppPyValidationError('HTTP error')
def test_base_error_message_and_raw_response_stringify(generic_error_message, generic_raw_response): error = InAppPyValidationError(generic_error_message, generic_raw_response) assert ( repr(error) == f"InAppPyValidationError(" f"message={generic_error_message!r}, raw_response={generic_raw_response!r})" ) assert str(error) == "InAppPyValidationError error message {'foo': 'bar'}"
def __init__(self, bundle_id, sandbox=False, auto_retry_wrong_env_request=False): """ Constructor for AppStoreValidator :param bundle_id: apple bundle id :param sandbox: sandbox mode ? :param auto_retry_wrong_env_request: auto retry on wrong env ? """ self.bundle_id = bundle_id self.sandbox = sandbox if not self.bundle_id: raise InAppPyValidationError('`bundle_id` cannot be empty') self.auto_retry_wrong_env_request = auto_retry_wrong_env_request self._change_url_by_sandbox()
def __init__(self, bundle_id: str, sandbox: bool = False, auto_retry_wrong_env_request: bool = False, http_timeout: int = None): """ Constructor for AppStoreValidator :param bundle_id: apple bundle id :param sandbox: sandbox mode ? :param auto_retry_wrong_env_request: auto retry on wrong env ? """ if not bundle_id: raise InAppPyValidationError('bundle_id cannot be empty') self.bundle_id = bundle_id self.sandbox = sandbox self.http_timeout = http_timeout self.auto_retry_wrong_env_request = auto_retry_wrong_env_request self._change_url_by_sandbox()
def validate(self, receipt: str, shared_secret: str = None, exclude_old_transactions: bool = False) -> dict: """ Validates receipt against apple services. :param receipt: receipt :param shared_secret: optional shared secret. :param exclude_old_transactions: optional to include only the latest renewal transaction :return: validation result or exception. """ receipt_json = {'receipt-data': receipt} if shared_secret: receipt_json['password'] = shared_secret if exclude_old_transactions: receipt_json['exclude-old-transcations'] = True api_response = self.post_json(receipt_json) status = api_response['status'] # Check retry case. if self.auto_retry_wrong_env_request and status in [21007, 21008]: # switch environment self.sandbox = not self.sandbox api_response = self.post_json(receipt_json) status = api_response['status'] if status != api_result_ok: error = api_result_errors.get( status, InAppPyValidationError('Unknown API status')) error.raw_response = api_response raise error return api_response
def test_base_error_plain_stringify(): error = InAppPyValidationError() assert error assert repr( error) == "InAppPyValidationError(message=None, raw_response=None)" assert str(error) == "InAppPyValidationError None None"
def test_base_error_message_stringify(generic_error_message): error = InAppPyValidationError(generic_error_message) assert repr( error ) == f"InAppPyValidationError(message={generic_error_message!r}, raw_response=None)" assert str(error) == f"InAppPyValidationError {generic_error_message} None"
import warnings import requests from requests.exceptions import RequestException from inapppy.errors import InAppPyValidationError # https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html # `Table 2-1 Status codes` api_result_ok = 0 api_result_errors = { 21000: InAppPyValidationError("Bad json"), 21002: InAppPyValidationError("Bad data"), 21003: InAppPyValidationError("Receipt authentication"), 21004: InAppPyValidationError("Shared secret mismatch"), 21005: InAppPyValidationError("Server is unavailable"), 21006: InAppPyValidationError("Subscription has expired"), # two following errors can use auto_retry_wrong_env_request. 21007: InAppPyValidationError("Sandbox receipt was sent to the production env"), 21008: InAppPyValidationError("Production receipt was sent to the sandbox env"), 21009: InAppPyValidationError("Internal data access error"), 21010:
import requests from requests.exceptions import RequestException from inapppy.errors import InAppPyValidationError # https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html # `Table 2-1 Status codes` api_result_ok = 0 api_result_errors = { 21000: InAppPyValidationError('Bad json'), 21002: InAppPyValidationError('Bad data'), 21003: InAppPyValidationError('Receipt authentication'), 21004: InAppPyValidationError('Shared secret mismatch'), 21005: InAppPyValidationError('Server is unavailable'), 21006: InAppPyValidationError('Subscription has expired'), # two following errors can use auto_retry_wrong_env_request. 21007: InAppPyValidationError('Sandbox receipt was sent to the production env'), 21008: InAppPyValidationError('Production receipt was sent to the sandbox env'), } class AppStoreValidator:
import requests from requests.exceptions import RequestException from inapppy.errors import InAppPyValidationError # https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html # `Table 2-1 Status codes` api_result_ok = 0 api_result_errors = { 21000: InAppPyValidationError("Bad json"), 21002: InAppPyValidationError("Bad data"), 21003: InAppPyValidationError("Receipt authentication"), 21004: InAppPyValidationError("Shared secret mismatch"), 21005: InAppPyValidationError("Server is unavailable"), 21006: InAppPyValidationError("Subscription has expired"), # two following errors can use auto_retry_wrong_env_request. 21007: InAppPyValidationError("Sandbox receipt was sent to the production env"), 21008: InAppPyValidationError("Production receipt was sent to the sandbox env"), } class AppStoreValidator: def __init__(