{"id":19297994,"url":"https://github.com/cx-lukas-salkauskas-x/InAppPy","last_synced_at":"2026-01-17T07:49:01.868Z","repository":{"id":17131515,"uuid":"81078871","full_name":"dotpot/InAppPy","owner":"dotpot","description":"Python In-app purchase validator for Apple AppStore and GooglePlay.","archived":false,"fork":false,"pushed_at":"2023-10-18T02:23:27.000Z","size":115,"stargazers_count":212,"open_issues_count":13,"forks_count":49,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-03-28T05:11:18.364Z","etag":null,"topics":["appstore","asyncio","googleplay","in-app-purchase","library","purchase","python","python3","validation"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dotpot.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-02-06T11:09:13.000Z","updated_at":"2025-03-08T11:37:07.000Z","dependencies_parsed_at":"2024-06-18T21:21:26.683Z","dependency_job_id":"f2db638a-5414-40e1-b2f3-e24a563de151","html_url":"https://github.com/dotpot/InAppPy","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dotpot%2FInAppPy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dotpot%2FInAppPy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dotpot%2FInAppPy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dotpot%2FInAppPy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dotpot","download_url":"https://codeload.github.com/dotpot/InAppPy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247128747,"owners_count":20888235,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appstore","asyncio","googleplay","in-app-purchase","library","purchase","python","python3","validation"],"created_at":"2024-11-09T23:06:39.786Z","updated_at":"2026-01-17T07:49:01.851Z","avatar_url":"https://github.com/dotpot.png","language":"Python","funding_links":["https://paypal.me/LukasSalkauskas"],"categories":[],"sub_categories":[],"readme":"InAppPy\n=======\n|ci| |pypi| |downloads|\n\n.. |ci| image:: https://github.com/dotpot/InAppPy/actions/workflows/ci.yml/badge.svg\n    :target: https://github.com/dotpot/InAppPy/actions/workflows/ci.yml\n.. |pypi| image:: https://badge.fury.io/py/inapppy.svg\n    :target: https://badge.fury.io/py/inapppy\n.. |downloads| image:: https://img.shields.io/pypi/dm/inapppy.svg\n    :target: https://pypi.python.org/pypi/inapppy\n\n\nTable of contents\n=================\n\n1. Introduction\n2. Installation\n3. Google Play (`receipt` + `signature`)\n4. Google Play (verification)\n\n   - Setting up Google Service Account Credentials\n   - Usage Example (with file path)\n   - Usage Example (with credentials dictionary)\n\n5. Google Play (verification with result)\n6. Google Play (consuming products)\n7. App Store (`receipt` + using optional `shared-secret`)\n8. App Store Response (`validation_result` / `raw_response`) example\n9. App Store, **asyncio** version (available in the inapppy.asyncio package)\n10. Development\n11. Donate\n\n\n1. Introduction\n===============\n\nIn-app purchase validation library for `Apple AppStore` and `GooglePlay` (`App Store` validator have **async** support!). Works on python3.6+\n\n2. Installation\n===============\n::\n\n    pip install inapppy\n\n\n3. Google Play (validates `receipt` against provided `signature` using RSA)\n===========================================================================\n.. code:: python\n\n    from inapppy import GooglePlayValidator, InAppPyValidationError\n\n\n    bundle_id = 'com.yourcompany.yourapp'\n    api_key = 'API key from the developer console'\n    validator = GooglePlayValidator(bundle_id, api_key)\n\n    try:\n        # receipt means `androidData` in result of purchase\n        # signature means `signatureAndroid` in result of purchase\n        validation_result = validator.validate('receipt', 'signature')\n    except InAppPyValidationError:\n        # handle validation error\n        pass\n\n\nAn additional example showing how to authenticate using dict credentials instead of loading from a file\n\n.. code:: python\n\n    import json\n    from inapppy import GooglePlayValidator, InAppPyValidationError\n\n\n    bundle_id = 'com.yourcompany.yourapp'\n    # Avoid hard-coding credential data in your code. This is just an example. \n    api_credentials = json.loads('{'\n                                 '   \"type\": \"service_account\",'\n                                 '   \"project_id\": \"xxxxxxx\",'\n                                 '   \"private_key_id\": \"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\",'\n                                 '   \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==\\n-----END PRIVATE KEY-----\\n\",'\n                                 '   \"client_email\": \"XXXXXXXXX@XXXXXXXX.XXX\",'\n                                 '   \"client_id\": \"XXXXXXXXXXXXXXXXXX\",'\n                                 '   \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",'\n                                 '   \"token_uri\": \"https://oauth2.googleapis.com/token\",'\n                                 '   \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",'\n                                 '   \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/XXXXXXXXXXXXXXXXX.iam.gserviceaccount.com\"'\n                                 ' }')\n    validator = GooglePlayValidator(bundle_id, api_credentials)\n\n    try:\n        # receipt means `androidData` in result of purchase\n        # signature means `signatureAndroid` in result of purchase\n        validation_result = validator.validate('receipt', 'signature')\n    except InAppPyValidationError:\n        # handle validation error\n        pass\n\n\n\n4. Google Play verification\n===========================\n\nSetting up Google Service Account Credentials\n----------------------------------------------\n\nBefore using Google Play verification, you need to set up a Google Service Account and obtain the credentials file. This section explains what ``GOOGLE_SERVICE_ACCOUNT_KEY_FILE`` is and how to obtain it.\n\n**What is GOOGLE_SERVICE_ACCOUNT_KEY_FILE?**\n\n``GOOGLE_SERVICE_ACCOUNT_KEY_FILE`` is a JSON file containing a service account's private key and credentials. This file authorizes your application to access the Google Play Developer API to verify in-app purchases and subscriptions.\n\nThe credentials can be provided in two ways:\n\n1. **As a file path** (string): Path to the JSON key file downloaded from Google Cloud Console\n2. **As a dictionary** (dict): The parsed JSON content of the key file\n\n**How to obtain the Service Account Key File:**\n\n1. **Link Google Cloud Project to Google Play Console**\n\n   - Go to `Google Play Console \u003chttps://play.google.com/console\u003e`_\n   - Select your app\n   - Navigate to **Settings → Developer account → API access**\n   - If you haven't linked a project yet, click **Link** to create or link a Google Cloud project\n   - Accept the terms and conditions\n\n2. **Create a Service Account**\n\n   - In the API access page, scroll to **Service accounts**\n   - Click **Create new service account** or **Learn how to create service accounts** (this will take you to Google Cloud Console)\n   - In Google Cloud Console:\n\n     - Go to **IAM \u0026 Admin → Service Accounts**\n     - Click **+ CREATE SERVICE ACCOUNT**\n     - Enter a name (e.g., \"InAppPy Validator\") and description\n     - Click **CREATE AND CONTINUE**\n     - Skip granting roles (not needed for this step)\n     - Click **DONE**\n\n3. **Grant Permissions in Google Play Console**\n\n   - Return to Google Play Console → **Settings → Developer account → API access**\n   - Find your newly created service account in the list\n   - Click **Grant access**\n   - Under **App permissions**, select your app\n   - Under **Account permissions**, enable:\n\n     - **View financial data** (for viewing purchase/subscription info)\n     - **Manage orders and subscriptions** (if you need to consume products or manage subscriptions)\n\n   - Click **Invite user** and then **Send invitation**\n\n4. **Download the JSON Key File**\n\n   - Go back to **Google Cloud Console → IAM \u0026 Admin → Service Accounts**\n   - Click on your service account email\n   - Go to the **KEYS** tab\n   - Click **ADD KEY → Create new key**\n   - Select **JSON** as the key type\n   - Click **CREATE**\n   - The JSON key file will be automatically downloaded\n   - **IMPORTANT**: Store this file securely! It contains a private key and cannot be recovered if lost\n\n5. **Important Notes**\n\n   - The JSON key file should contain fields like: ``type``, ``project_id``, ``private_key_id``, ``private_key``, ``client_email``, etc.\n   - Keep this file secure and never commit it to version control\n   - In some cases, you may need to create at least one product in your Google Play Console before the API access works properly\n   - It may take a few minutes for permissions to propagate after granting access\n\n**Example JSON key file structure:**\n\n.. code:: json\n\n    {\n      \"type\": \"service_account\",\n      \"project_id\": \"your-project-id\",\n      \"private_key_id\": \"a1b2c3d4e5f6...\",\n      \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nYourPrivateKeyHere\\n-----END PRIVATE KEY-----\\n\",\n      \"client_email\": \"your-service-account@your-project.iam.gserviceaccount.com\",\n      \"client_id\": \"123456789\",\n      \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n      \"token_uri\": \"https://oauth2.googleapis.com/token\",\n      \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n      \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/...\"\n    }\n\nUsage Example (with file path)\n-------------------------------\n\n.. code:: python\n\n    from inapppy import GooglePlayVerifier, errors\n\n\n    def google_validator(receipt):\n        \"\"\"\n        Accepts receipt, validates in Google.\n        \"\"\"\n        purchase_token = receipt['purchaseToken']\n        product_sku = receipt['productId']\n\n        # Pass the path to your service account JSON key file\n        verifier = GooglePlayVerifier(\n            GOOGLE_BUNDLE_ID,\n            '/path/to/your-service-account-key.json',  # Path to the JSON key file\n        )\n        response = {'valid': False, 'transactions': []}\n        try:\n            result = verifier.verify(\n                purchase_token,\n                product_sku,\n\t\t\t\tis_subscription=True\n            )\n            response['valid'] = True\n            response['transactions'].append(\n                (result['orderId'], product_sku)\n            )\n        except errors.GoogleError as exc:\n            logging.error('Purchase validation failed {}'.format(exc))\n        return response\n\n\nUsage Example (with credentials dictionary)\n--------------------------------------------\n\n.. code:: python\n\n    import json\n    from inapppy import GooglePlayVerifier, errors\n\n\n    def google_validator(receipt):\n        \"\"\"\n        Accepts receipt, validates in Google using dict credentials.\n        \"\"\"\n        purchase_token = receipt['purchaseToken']\n        product_sku = receipt['productId']\n\n        # Load credentials from environment variable or secure storage\n        # NEVER hard-code credentials in your source code!\n        credentials_json = os.environ.get('GOOGLE_SERVICE_ACCOUNT_JSON')\n        credentials_dict = json.loads(credentials_json)\n\n        # Pass the credentials as a dictionary\n        verifier = GooglePlayVerifier(\n            GOOGLE_BUNDLE_ID,\n            credentials_dict,  # Dictionary containing the JSON key data\n        )\n        response = {'valid': False, 'transactions': []}\n        try:\n            result = verifier.verify(\n                purchase_token,\n                product_sku,\n\t\t\t\tis_subscription=True\n            )\n            response['valid'] = True\n            response['transactions'].append(\n                (result['orderId'], product_sku)\n            )\n        except errors.GoogleError as exc:\n            logging.error('Purchase validation failed {}'.format(exc))\n        return response\n\n\n5. Google Play verification (with result)\n=========================================\nAlternative to `.verify` method, instead of raising an error result class will be returned.\n\n**Note:** See section 4 for instructions on setting up ``GOOGLE_SERVICE_ACCOUNT_KEY_FILE``.\n\n.. code:: python\n\n    from inapppy import GooglePlayVerifier, errors\n\n\n    def google_validator(receipt):\n        \"\"\"\n        Accepts receipt, validates in Google.\n        \"\"\"\n        purchase_token = receipt['purchaseToken']\n        product_sku = receipt['productId']\n\n        # Use the service account credentials (see section 4 for setup)\n        verifier = GooglePlayVerifier(\n            GOOGLE_BUNDLE_ID,\n            GOOGLE_SERVICE_ACCOUNT_KEY_FILE,  # Path to JSON key file or dict\n        )\n        response = {'valid': False, 'transactions': []}\n\n        result = verifier.verify_with_result(\n            purchase_token,\n            product_sku,\n            is_subscription=True\n        )\n\n        # result contains data\n        raw_response = result.raw_response\n        is_canceled = result.is_canceled\n        is_expired = result.is_expired\n\n        return result\n\n\n6. Google Play (consuming products)\n===================================\nAfter validating a purchase, you can consume a one-time product to prevent refunds and allow the user to purchase it again.\nThis is a separate operation that should be called after verification to handle cases like power outages between validation and consumption.\n\n**Note:** See section 4 for instructions on setting up ``GOOGLE_SERVICE_ACCOUNT_KEY_FILE``.\n\n.. code:: python\n\n    from inapppy import GooglePlayVerifier, errors\n\n\n    def consume_purchase(receipt):\n        \"\"\"\n        Consume a purchase after validation.\n        \"\"\"\n        purchase_token = receipt['purchaseToken']\n        product_sku = receipt['productId']\n\n        # Use the service account credentials (see section 4 for setup)\n        verifier = GooglePlayVerifier(\n            GOOGLE_BUNDLE_ID,\n            GOOGLE_SERVICE_ACCOUNT_KEY_FILE,  # Path to JSON key file or dict\n        )\n\n        try:\n            # First verify the purchase\n            verification_result = verifier.verify(\n                purchase_token,\n                product_sku,\n                is_subscription=False\n            )\n\n            # Then consume it to prevent refunds\n            consume_result = verifier.consume_product(\n                purchase_token,\n                product_sku\n            )\n\n            return {'success': True, 'consumed': True}\n        except errors.GoogleError as exc:\n            logging.error('Purchase consumption failed {}'.format(exc))\n            return {'success': False, 'error': str(exc)}\n\n\n**Note:** Only consumable products (one-time purchases) can be consumed. Subscriptions cannot be consumed.\n\n\n7. App Store (validates `receipt` using optional `shared-secret` against iTunes service)\n========================================================================================\n.. code:: python\n\n    from inapppy import AppStoreValidator, InAppPyValidationError\n\n\n    bundle_id = 'com.yourcompany.yourapp'\n    auto_retry_wrong_env_request=False # if True, automatically query sandbox endpoint if\n                                       # validation fails on production endpoint\n    validator = AppStoreValidator(bundle_id, auto_retry_wrong_env_request=auto_retry_wrong_env_request)\n\n    try:\n        exclude_old_transactions=False # if True, include only the latest renewal transaction\n        validation_result = validator.validate('receipt', 'optional-shared-secret', exclude_old_transactions=exclude_old_transactions)\n    except InAppPyValidationError as ex:\n        # handle validation error\n        response_from_apple = ex.raw_response  # contains actual response from AppStore service.\n        pass\n\n\n\n8. App Store Response (`validation_result` / `raw_response`) example\n====================================================================\n.. code:: json\n\n    {\n        \"latest_receipt\": \"MIIbngYJKoZIhvcNAQcCoIIbj...\",\n        \"status\": 0,\n        \"receipt\": {\n            \"download_id\": 0,\n            \"receipt_creation_date_ms\": \"1486371475000\",\n            \"application_version\": \"2\",\n            \"app_item_id\": 0,\n            \"receipt_creation_date\": \"2017-02-06 08:57:55 Etc/GMT\",\n            \"original_purchase_date\": \"2013-08-01 07:00:00 Etc/GMT\",\n            \"request_date_pst\": \"2017-02-06 04:41:09 America/Los_Angeles\",\n            \"original_application_version\": \"1.0\",\n            \"original_purchase_date_pst\": \"2013-08-01 00:00:00 America/Los_Angeles\",\n            \"request_date_ms\": \"1486384869996\",\n            \"bundle_id\": \"com.yourcompany.yourapp\",\n            \"request_date\": \"2017-02-06 12:41:09 Etc/GMT\",\n            \"original_purchase_date_ms\": \"1375340400000\",\n            \"in_app\": [{\n                \"purchase_date_ms\": \"1486371474000\",\n                \"web_order_line_item_id\": \"1000000034281189\",\n                \"original_purchase_date_ms\": \"1486371475000\",\n                \"original_purchase_date\": \"2017-02-06 08:57:55 Etc/GMT\",\n                \"expires_date_pst\": \"2017-02-06 01:00:54 America/Los_Angeles\",\n                \"original_purchase_date_pst\": \"2017-02-06 00:57:55 America/Los_Angeles\",\n                \"purchase_date_pst\": \"2017-02-06 00:57:54 America/Los_Angeles\",\n                \"expires_date_ms\": \"1486371654000\",\n                \"expires_date\": \"2017-02-06 09:00:54 Etc/GMT\",\n                \"original_transaction_id\": \"1000000271014363\",\n                \"purchase_date\": \"2017-02-06 08:57:54 Etc/GMT\",\n                \"quantity\": \"1\",\n                \"is_trial_period\": \"false\",\n                \"product_id\": \"com.yourcompany.yourapp\",\n                \"transaction_id\": \"1000000271014363\"\n            }],\n            \"version_external_identifier\": 0,\n            \"receipt_creation_date_pst\": \"2017-02-06 00:57:55 America/Los_Angeles\",\n            \"adam_id\": 0,\n            \"receipt_type\": \"ProductionSandbox\"\n        },\n        \"latest_receipt_info\": [{\n                \"purchase_date_ms\": \"1486371474000\",\n                \"web_order_line_item_id\": \"1000000034281189\",\n                \"original_purchase_date_ms\": \"1486371475000\",\n                \"original_purchase_date\": \"2017-02-06 08:57:55 Etc/GMT\",\n                \"expires_date_pst\": \"2017-02-06 01:00:54 America/Los_Angeles\",\n                \"original_purchase_date_pst\": \"2017-02-06 00:57:55 America/Los_Angeles\",\n                \"purchase_date_pst\": \"2017-02-06 00:57:54 America/Los_Angeles\",\n                \"expires_date_ms\": \"1486371654000\",\n                \"expires_date\": \"2017-02-06 09:00:54 Etc/GMT\",\n                \"original_transaction_id\": \"1000000271014363\",\n                \"purchase_date\": \"2017-02-06 08:57:54 Etc/GMT\",\n                \"quantity\": \"1\",\n                \"is_trial_period\": \"true\",\n                \"product_id\": \"com.yourcompany.yourapp\",\n                \"transaction_id\": \"1000000271014363\"\n            }, {\n                \"purchase_date_ms\": \"1486371719000\",\n                \"web_order_line_item_id\": \"1000000034281190\",\n                \"original_purchase_date_ms\": \"1486371720000\",\n                \"original_purchase_date\": \"2017-02-06 09:02:00 Etc/GMT\",\n                \"expires_date_pst\": \"2017-02-06 01:06:59 America/Los_Angeles\",\n                \"original_purchase_date_pst\": \"2017-02-06 01:02:00 America/Los_Angeles\",\n                \"purchase_date_pst\": \"2017-02-06 01:01:59 America/Los_Angeles\",\n                \"expires_date_ms\": \"1486372019000\",\n                \"expires_date\": \"2017-02-06 09:06:59 Etc/GMT\",\n                \"original_transaction_id\": \"1000000271014363\",\n                \"purchase_date\": \"2017-02-06 09:01:59 Etc/GMT\",\n                \"quantity\": \"1\",\n                \"is_trial_period\": \"false\",\n                \"product_id\": \"com.yourcompany.yourapp\",\n                \"transaction_id\": \"1000000271016119\"\n            }],\n        \"environment\": \"Sandbox\"\n    }\n\n\n9. App Store, asyncio version (available in the inapppy.asyncio package)\n========================================================================\n.. code:: python\n\n    from inapppy import InAppPyValidationError\n    from inapppy.asyncio import AppStoreValidator\n\n\n    bundle_id = 'com.yourcompany.yourapp'\n    auto_retry_wrong_env_request=False # if True, automatically query sandbox endpoint if\n                                       # validation fails on production endpoint\n    validator = AppStoreValidator(bundle_id, auto_retry_wrong_env_request=auto_retry_wrong_env_request)\n\n    try:\n        exclude_old_transactions=False # if True, include only the latest renewal transaction\n        async with validator:  # Use async context manager to ensure proper session management\n            validation_result = await validator.validate('receipt', 'optional-shared-secret', exclude_old_transactions=exclude_old_transactions)\n    except InAppPyValidationError as ex:\n        # handle validation error\n        response_from_apple = ex.raw_response  # contains actual response from AppStore service.\n        pass\n\n\n\n10. Development\n===============\n\nPrerequisites\n-------------\n\nInstall `uv \u003chttps://docs.astral.sh/uv/\u003e`_ for fast Python package management:\n\n.. code:: bash\n\n    # macOS/Linux\n    curl -LsSf https://astral.sh/uv/install.sh | sh\n\n    # Windows\n    powershell -c \"irm https://astral.sh/uv/install.ps1 | iex\"\n\nSetup and Testing\n-----------------\n\n.. code:: bash\n\n    # Install development dependencies\n    make dev\n\n    # Run linting checks\n    make lint\n\n    # Format code with ruff\n    make format\n\n    # Run both lint and format checks\n    make check\n\n    # Run tests\n    make test\n\n    # Run tests with pytest directly\n    uv run pytest -v\n\nAvailable Make Commands\n-----------------------\n\n.. code:: bash\n\n    make setup     # Install dependencies with uv\n    make dev       # Install development dependencies with uv\n    make clean     # Remove build artifacts\n    make build     # Build distribution packages\n    make release   # Upload to PyPI\n    make test      # Run tests with pytest\n    make lint      # Run ruff linting\n    make format    # Format code with ruff\n    make check     # Run lint and format check\n    make install   # Install package in editable mode\n    \n11. Donate\n==========\nYou can support development of this project by buying me a coffee ;)\n\n+------+--------------------------------------------+\n| Coin | Wallet \t\t\t            |\n+======+============================================+\n| EUR  | https://paypal.me/LukasSalkauskas          |\n+------+--------------------------------------------+\n| DOGE | DGjSG3T6g9h2k6iSku7mtKCynCpmwowpyN         |\n+------+--------------------------------------------+\n| BTC  | 1LZAiWmLYzZae4hq3ai9hFYD3e3qcwjDsU         |\n+------+--------------------------------------------+\n| ETH  | 0xD62245986345130edE10e4b545fF577Bd5BaE3E4 |\n+------+--------------------------------------------+\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcx-lukas-salkauskas-x%2FInAppPy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcx-lukas-salkauskas-x%2FInAppPy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcx-lukas-salkauskas-x%2FInAppPy/lists"}