Skip to content

aporat/store-receipt-validator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

store-receipt-validator

Latest Stable Version Downloads Codecov GitHub Actions License

A modern PHP library for validating in-app purchase receipts from the Apple App Store (including legacy iTunes) and Amazon Appstore. Supports both production and sandbox environments with detailed response parsing.


✨ Features

  • βœ… Apple App Store Server API (v2) support
  • βœ… Apple iTunes Legacy API support (deprecated by Apple, still available here)
  • βœ… Amazon Appstore receipt validation
  • βœ… App Store Server Notifications v1 & v2 parsing
  • βœ… Strong typing (PHP 8.3+), enums, and modern error handling
  • βœ… Built-in test suite with 100% coverage

πŸ“¦ Requirements

  • PHP >= 8.3

πŸ“₯ Installation

composer require aporat/store-receipt-validator

πŸš€ Quick Start

πŸ“² Apple App Store Server API

use ReceiptValidator\AppleAppStore\ReceiptUtility;
use ReceiptValidator\AppleAppStore\Validator as AppleValidator;
use ReceiptValidator\Environment;

// Credentials
$signingKey = file_get_contents($root . '/examples/SubscriptionKey_RA9DAYVX3X.p8');
$keyId = 'RA9DAYVX3X';
$issuerId = 'xxxxxx-xxxx-xxxx-xxxx-xxxxxxx';
$bundleId = 'com.myapp';

$receiptBase64Data = '...'; // your app receipt here

// πŸ”‘ Tip: Apple's Server API does not accept the full app receipt.
// Use ReceiptUtility to extract the latest transaction ID.
$transactionId = ReceiptUtility::extractTransactionIdFromAppReceipt($receiptBase64Data);

$validator = new AppleValidator(
    signingKey: $signingKey,
    keyId: $keyId,
    issuerId: $issuerId,
    bundleId: $bundleId,
    environment: Environment::PRODUCTION
);

try {
    $response = $validator->setTransactionId($transactionId)->validate();
} catch (ValidationException $e) {
    if ($e->getCode() === APIError::INVALID_TRANSACTION_ID) {
        echo "Invalid Transaction ID: {$e->getMessage()}" . PHP_EOL;
    } else {
        echo "Validation failed: {$e->getMessage()}" . PHP_EOL;
    }

    exit(1);
} catch (Exception $e) {
    echo 'Error validating transaction: ' . $e->getMessage() . PHP_EOL;
    exit(1);
}

echo 'Validation successful.' . PHP_EOL;
echo 'Bundle ID: ' . $response->getBundleId() . PHP_EOL;
echo 'App Apple ID: ' . $response->getAppAppleId() . PHP_EOL;

foreach ($response->getTransactions() as $transaction) {
    echo 'Product ID: ' . $transaction->getProductId() . PHP_EOL;
    echo 'Transaction ID: ' . $transaction->getTransactionId() . PHP_EOL;

    if ($transaction->getPurchaseDate() !== null) {
        echo 'Purchase Date: ' . $transaction->getPurchaseDate()->toIso8601String() . PHP_EOL;
    }
}

🍏 Apple iTunes (Legacy API - Deprecated)

use ReceiptValidator\Environment;
use ReceiptValidator\iTunes\Validator as iTunesValidator;

$validator = new iTunesValidator(Environment::PRODUCTION);

try {
    $response = $validator->setSharedSecret('SHARED_SECRET')->setReceiptData('BASE64_RECEIPT')->validate();
} catch (Exception $e) {
    echo 'Error: ' . $e->getMessage() . PHP_EOL;
    echo $e->getTraceAsString() . PHP_EOL;
    exit;
}

echo 'Receipt is valid.' . PHP_EOL;
echo 'Bundle ID: ' . $response->getBundleId() . PHP_EOL;

foreach ($response->getLatestReceiptInfo() as $transaction) {
    echo 'Product ID: ' . $transaction->getProductId() . PHP_EOL;
    echo 'Transaction ID: ' . $transaction->getTransactionId() . PHP_EOL;

    if ($transaction->getPurchaseDate() !== null) {
        echo 'Purchase Date: ' . $transaction->getPurchaseDate()->toIso8601String() . PHP_EOL;
    }
}

πŸ›’ Amazon Appstore

use ReceiptValidator\Amazon\Validator;

$validator = new Validator();

try {
    $response = $validator
        ->setDeveloperSecret('SECRET')
        ->setReceiptId('RECEIPT_ID')
        ->setUserId('USER_ID')
        ->validate();
} catch (Exception $e) {
    echo 'Error: ' . $e->getMessage() . PHP_EOL;
    echo $e->getTraceAsString() . PHP_EOL;
    exit;
}

echo 'Receipt is valid.' . PHP_EOL;

foreach ($response->getTransactions() as $transaction) {
    echo 'Product ID: ' . $transaction->getProductId() . PHP_EOL;

    if ($transaction->getPurchaseDate() !== null) {
        echo 'Purchase Date: ' . $transaction->getPurchaseDate()->toIso8601String() . PHP_EOL;
    }
}

πŸ“¬ Apple App Store Server Notifications

πŸ”” V2 Notifications (App Store Server API)

use ReceiptValidator\AppleAppStore\ServerNotification;
use ReceiptValidator\Exceptions\ValidationException;

public function subscriptions(Request $request): JsonResponse {
    try {
        $notification = new ServerNotification($request->all());

        echo 'Type: ' . $notification->getNotificationType()->value . PHP_EOL;
        echo 'Subtype: ' . ($notification->getSubtype()?->value ?? 'N/A') . PHP_EOL;
        echo 'Bundle ID: ' . $notification->getBundleId() . PHP_EOL;

        $tx = $notification->getTransaction();
        if ($tx !== null) {
            echo 'Transaction ID: ' . $tx->getTransactionId() . PHP_EOL;
        }

        $renewalInfo = $notification->getRenewalInfo();
        if ($renewalInfo !== null) {
            echo 'Auto-Renew Product ID: ' . $renewalInfo->getAutoRenewProductId() . PHP_EOL;
        }
    } catch (ValidationException $e) {
        echo 'Invalid notification: ' . $e->getMessage() . PHP_EOL;
    }
}

πŸ”” V1 Notifications (iTunes - Deprecated)

use ReceiptValidator\iTunes\ServerNotification;
use ReceiptValidator\Exceptions\ValidationException;

public function subscriptions(Request $request): JsonResponse {
    $sharedSecret = 'your_shared_secret';

    try {
        $notification = new ServerNotification($request->all(), $sharedSecret);

        echo 'Type: ' . $notification->getNotificationType()->value . PHP_EOL;
        echo 'Bundle ID: ' . $notification->getBundleId() . PHP_EOL;

        $transactions = $notification->getLatestReceipt()->getTransactions();

        foreach ($transactions as $tx) {
            echo 'Transaction ID: ' . $tx->getTransactionId() . PHP_EOL;
        }
    } catch (ValidationException $e) {
        echo 'Invalid notification: ' . $e->getMessage() . PHP_EOL;
    }
}

πŸ§ͺ Testing

composer test        # Run tests with PHPUnit
composer check       # Run code style checks with PHP_CodeSniffer
composer analyze     # Run static analysis with PHPStan

πŸ™Œ Contributing

Contributions are welcome!
To get started:

  1. Fork this repo
  2. Create a feature branch
  3. Submit a pull request

Found a bug or want a new feature? Open an issue


πŸ“„ License

MIT License. See LICENSE.


About

PHP receipt validator for Apple iTunes, Google Play and Amazon App Store

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Contributors 35