store-receipt-validator
=======

[![Latest Stable Version](https://poser.pugx.org/aporat/store-receipt-validator/version.png)](https://packagist.org/packages/aporat/store-receipt-validator)
[![Composer Downloads](https://poser.pugx.org/aporat/store-receipt-validator/d/total.png)](https://packagist.org/packages/aporat/store-receipt-validator)
[![Build Status](https://github.com/aporat/store-receipt-validator/workflows/Tests/badge.svg)](https://github.com/aporat/store-receipt-validator/actions)
[![Code Coverage](https://scrutinizer-ci.com/g/aporat/store-receipt-validator/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/aporat/store-receipt-validator/?branch=master)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/aporat/store-receipt-validator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/aporat/store-receipt-validator/?branch=master)
[![StyleCI](https://github.styleci.io/repos/14928361/shield?branch=master)](https://github.styleci.io/repos/14928361)
[![License](https://poser.pugx.org/aporat/store-receipt-validator/license.svg)](https://packagist.org/packages/aporat/store-receipt-validator)

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

## Requirements ##

* PHP >= 7.3

## Installation ##

 `composer require aporat/store-receipt-validator`

## Quick Example ##

### iTunes ###

```php

use ReceiptValidator\iTunes\Validator as iTunesValidator;

$validator = new iTunesValidator(iTunesValidator::ENDPOINT_PRODUCTION); // Or iTunesValidator::ENDPOINT_SANDBOX if sandbox testing

$receiptBase64Data = ' ';

try {
  $response = $validator->setReceiptData($receiptBase64Data)->validate();
  // $sharedSecret = '1234...'; // Generated in iTunes Connect's In-App Purchase menu
  // $response = $validator->setSharedSecret($sharedSecret)->setReceiptData($receiptBase64Data)->validate(); // use setSharedSecret() if for recurring subscriptions
} catch (Exception $e) {
  echo 'got error = ' . $e->getMessage() . PHP_EOL;
}

if ($response->isValid()) {
  echo 'Receipt is valid.' . PHP_EOL;
  echo 'Receipt data = ' . print_r($response->getReceipt()) . PHP_EOL;
  
  foreach ($response->getPurchases() as $purchase) {
    echo 'getProductId: ' . $purchase->getProductId() . PHP_EOL;
    echo 'getTransactionId: ' . $purchase->getTransactionId() . PHP_EOL;

    if ($purchase->getPurchaseDate() != null) {
      echo 'getPurchaseDate: ' . $purchase->getPurchaseDate()->toIso8601String() . PHP_EOL;
    }
  }
} else {
  echo 'Receipt is not valid.' . PHP_EOL;
  echo 'Receipt result code = ' . $response->getResultCode() . PHP_EOL;
}
```

### Play Store ###

Get the refresh token from [OAuth2 flow](https://developers.google.com/android-publisher/authorization).

```php
use ReceiptValidator\GooglePlay\Validator as PlayValidator;

$client = new \Google_Client();
$client->setApplicationName('...');
$client->setAuthConfig('...');
$client->setScopes('...');

$validator = new PlayValidator(new \Google\Service\AndroidPublisher($client));

try {
  $response = $validator->setPackageName('PACKAGE_NAME')
    ->setProductId('PRODUCT_ID')
    ->setPurchaseToken('PURCHASE_TOKEN')
    ->validatePurchase();
} catch (Exception $e){
  var_dump($e->getMessage());
  // example message: Error calling GET ....: (404) Product not found for this application.
}
// success
```

Or [Using a service account](https://developers.google.com/android-publisher/getting_started#using_a_service_account)

Create service account [Service Account flow](https://developers.google.com/identity/protocols/OAuth2ServiceAccount) and [guide](https://stackoverflow.com/a/24365026/1248595)

```php
$googleClient = new \Google_Client();
$googleClient->setScopes([\Google\Service\AndroidPublisher::ANDROIDPUBLISHER]);
$googleClient->setApplicationName('Your_Purchase_Validator_Name');
$googleClient->setAuthConfig($pathToServiceAccountJsonFile);

$googleAndroidPublisher = new \Google\Service\AndroidPublisher($googleClient);
$validator = new \ReceiptValidator\GooglePlay\Validator($googleAndroidPublisher);

try {
  $response = $validator->setPackageName('PACKAGE_NAME')
      ->setProductId('PRODUCT_ID')
      ->setPurchaseToken('PURCHASE_TOKEN')
      ->validateSubscription();
} catch (\Exception $e){
  var_dump($e->getMessage());
  // example message: Error calling GET ....: (404) Product not found for this application.
}
// success
```

#### Reduce the size of the google sdk ####
To reduce the size of the google sdk you can follow thoses steps on the [google documentation](https://github.com/googleapis/google-api-php-client#cleaning-up-unused-services)
```json
{
    "scripts": {
        "post-install-cmd": [
            "Google_Task_Composer::cleanup"
        ],
        "post-update-cmd": [
            "Google_Task_Composer::cleanup"
        ]
    },
    "extra": {
        "google/apiclient-services": [
            "AndroidPublisher"
        ]
    }
}
```
**IMPORTANT:** If you add any services back in composer.json, you will need to remove the vendor/google/apiclient-services directory explicity for the change you made to have effect:
```sh
rm -r vendor/google/apiclient-services
composer update
```

### Amazon App Store ###

```php
use ReceiptValidator\Amazon\Validator as AmazonValidator;
use ReceiptValidator\Amazon\Response as ValidatorResponse;

$validator = new AmazonValidator;

$response = null;
try {
  $response = $validator->setDeveloperSecret("DEVELOPER_SECRET")->setReceiptId("RECEIPT_ID")->setUserId("USER_ID")->validate();

} catch (Exception $e) {
  echo 'got error = ' . $e->getMessage() . PHP_EOL;
}

if ($response instanceof ValidatorResponse && $response->isValid()) {

  echo 'Receipt is valid.' . PHP_EOL;


  foreach ($response->getPurchases() as $purchase) {
    echo 'getProductId: ' . $purchase->getProductId() . PHP_EOL;

    if ($purchase->getPurchaseDate() != null) {
      echo 'getPurchaseDate: ' . $purchase->getPurchaseDate()->toIso8601String() . PHP_EOL;
    }
  }
} else {
  echo 'Receipt is not valid.' . PHP_EOL;
  echo 'Receipt result code = ' . $response->getResultCode() . PHP_EOL;
}

```
