Esempio n. 1
0
    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")
Esempio n. 2
0
    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')
Esempio n. 3
0
    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')
Esempio n. 4
0
    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')
Esempio n. 5
0
    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
Esempio n. 6
0
    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")
Esempio n. 7
0
    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
Esempio n. 8
0
    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")
Esempio n. 9
0
    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')
Esempio n. 10
0
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'}"
Esempio n. 11
0
    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()
Esempio n. 12
0
    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()
Esempio n. 13
0
    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
Esempio n. 14
0
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"
Esempio n. 15
0
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"
Esempio n. 16
0
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:
Esempio n. 17
0
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:
Esempio n. 18
0
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__(