A minimal, self-contained Python example that shows how to server-side validate iOS in-app purchases using Apple's App Store Server API and Apple's official app-store-server-library.
It is intentionally small so the flow (load certs → build verifier → call API → verify signed JWS → check bundle) is easy to follow.
- Loading the Apple Root Certificates required to verify Apple-signed JWS payloads.
- Building a
SignedDataVerifierwith the correct environment (sandbox vs production) and bundle ID. - Building an
AppStoreServerAPIClientusing a.p8signing key from App Store Connect. - Fetching a single transaction via
get_transaction_info. - Walking paginated transaction history via
get_transaction_historyand verifying each signed transaction. - Matching the target
transactionIdand confirming the decodedbundleIdmatches your app.
- Python 3.9+
app-store-server-library(Apple's official Python library)- An App Store Connect API key with "In-App Purchase" access (
.p8,KEY_ID,ISSUER_ID) - Your app's
BUNDLE_IDand numericAPP_APPLE_ID - The four Apple Root Certificates used to verify JWS signatures
Download the four root CA certificates from Apple's Certificate Authority page and save them under ./certs/:
AppleComputerRootCertificate.cerAppleIncRootCertificate.cerAppleRootCA-G2.cerAppleRootCA-G3.cer
git clone https://github.com/omidsakhi/app-store-purchase-validator-python.git
cd app-store-purchase-validator-python
python -m venv .venv
# Windows: .venv\Scripts\activate
source .venv/bin/activate
pip install -r requirements.txtThen open example.py and fill in the configuration block:
KEY_ID = "YOUR_KEY_ID"
ISSUER_ID = "YOUR_ISSUER_ID"
BUNDLE_ID = "com.example.yourapp"
APP_APPLE_ID = 0000000000
ENVIRONMENT = Environment.SANDBOX # or Environment.PRODUCTION
PRIVATE_KEY_PATH = "path/to/AuthKey_XXXXXXXX.p8"Run it:
python example.py- Scope:
validate_app_store_purchasecurrently filters history byProductType.CONSUMABLE. If you sell subscriptions or non-consumables, adjustproductTypesaccordingly. - Environment matters: Sandbox transactions will only validate against
Environment.SANDBOX, and production againstEnvironment.PRODUCTION. App Review runs in sandbox — if you handle both in one backend, use Apple's recommended fallback (try production first, then retry in sandbox onAPP_TRANSACTION_ID_NOT_SUPPORTED-style errors). - Security: Never commit your
.p8signing key or any certificates containing private material. The included.gitignoreexcludes*.p8,certs/, and common secret files. - Replay protection: In real use, record validated
transactionIds in your own database before granting entitlement, and short-circuit re-validation on replay.
Licensed under the Apache License 2.0 — see LICENSE.
Issues and PRs are welcome. If Apple changes an API surface or a better pattern emerges in app-store-server-library, feel free to open a PR.