Skip to content

Commit

Permalink
added support for > ios 7 receipt validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Adar Porat committed Aug 13, 2014
1 parent 5e076ec commit 7da1118
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 64 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ extras/documentation
/.buildpath

/.settings/

/.idea/
30 changes: 15 additions & 15 deletions src/ReceiptValidator/GooglePlay/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class Validator
{
/**
* google client
*
*
* @var Google_Client
*/
protected $_client = null;
Expand All @@ -31,7 +31,7 @@ class Validator
* @var string
*/
protected $_product_id = null;

public function __construct(array $options = [])
{
$this->_client = new \Google_Client();
Expand All @@ -46,7 +46,7 @@ public function __construct(array $options = [])
} catch (\Exception $e) {
// skip exceptions when the access token is not valid
}

try {
if ($this->_client->isAccessTokenExpired()) {
$this->_client->refreshToken($options['refresh_token']);
Expand All @@ -57,51 +57,51 @@ public function __construct(array $options = [])
}

$this->_androidPublisherService = new \Google_Service_AndroidPublisher($this->_client);

}


/**
*
*
* @param string $package_name
* @return \ReceiptValidator\GooglePlay\Validator
*/
public function setPackageName($package_name)
{
$this->_package_name = $package_name;

return $this;
}

/**
*
*
* @param string $purchase_token
* @return \ReceiptValidator\GooglePlay\Validator
*/
public function setPurchaseToken($purchase_token)
{
$this->_purchase_token = $purchase_token;

return $this;
}

/**
*
*
* @param string $product_id
* @return \ReceiptValidator\GooglePlay\Validator
*/
public function setProductId($product_id)
{
$this->_product_id = $product_id;

return $this;
}


public function validate()
{
$response = $this->_androidPublisherService->inapppurchases->get($this->_package_name, $this->_product_id, $this->_purchase_token);

return $response;
return $response;
}
}
7 changes: 4 additions & 3 deletions src/ReceiptValidator/RunTimeException.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
<?php
namespace ReceiptValidator;

class RunTimeException extends \Exception {

class RunTimeException extends \Exception
{

}
58 changes: 43 additions & 15 deletions src/ReceiptValidator/iTunes/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Response
const RESULT_RECEIPT_SERVER_UNAVAILABLE = 21005;

const RESULT_RECEIPT_VALID_BUT_SUB_EXPIRED = 21006;

// special case for app review handling - forward any request that is intended for the Sandbox but was sent to Production, this is what the app review team does
const RESULT_SANDBOX_RECEIPT_SENT_TO_PRODUCTION = 21007;

Expand All @@ -36,17 +36,23 @@ class Response
* @var int
*/
protected $_code;

/**
* receipt info
* @var array
*/
protected $_receipt;
protected $_receipt = [];

/**
* purhcases info
* @var array
*/
protected $_purchases = [];

/**
* Constructor
*
* @param array $jsonResponse
* @param array $jsonResponse
* @return Response
*/
public function __construct($jsonResponse = null)
Expand All @@ -69,16 +75,26 @@ public function getResultCode()
/**
* Set Result Code
*
* @param int $code
* @param int $code
* @return Response
*/
public function setResultCode($code)
{
$this->_code = $code;

return $this;
}


/**
* Get purchases info
*
* @return array
*/
public function getPurchases()
{
return $this->_purchases;
}

/**
* Get receipt info
*
Expand All @@ -88,7 +104,7 @@ public function getReceipt()
{
return $this->_receipt;
}

/**
* returns if the receipt is valid or not
*
Expand All @@ -99,28 +115,40 @@ public function isValid()
if ($this->_code == self::RESULT_OK) {
return true;
}

return false;
}

/**
* Parse JSON Response
*
* @param string $jsonResponse
* @param string $jsonResponse
* @return Message
*/
public function parseJsonResponse($jsonResponse)
{
if (!is_array($jsonResponse)) {
throw new RuntimeException('Response must be a scalar value');
}
$this->_code = $jsonResponse['status'];

if (array_key_exists('receipt', $jsonResponse)) {

// ios > 7 receipt validation
if (array_key_exists('receipt', $jsonResponse) && is_array($jsonResponse['receipt']) && array_key_exists('in_app', $jsonResponse['receipt']) && is_array($jsonResponse['receipt']['in_app']) > 0) {
$this->_code = $jsonResponse['status'];
$this->_receipt = $jsonResponse['receipt'];
$this->_purchases = $jsonResponse['receipt']['in_app'];

} elseif (array_key_exists('receipt', $jsonResponse)) {

// ios <= 6.0 validation
$this->_code = $jsonResponse['status'];

if (array_key_exists('receipt', $jsonResponse)) {
$this->_receipt = $jsonResponse['receipt'];
$this->_purchases = [$jsonResponse['receipt']];
}
} else {
$this->_code = self::RESULT_DATA_MALFORMED;
}

return $this;
}
}
58 changes: 29 additions & 29 deletions src/ReceiptValidator/iTunes/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,22 @@ class Validator

/**
* endpoint url
*
*
* @var string
*/
protected $_endpoint;

/**
* itunes receipt data, in base64 format
*
*
* @var string
*/
protected $_receiptData;


/**
* Guzzle http client
*
*
* @var \Guzzle\Http\Client
*/
protected $_client = null;
Expand All @@ -39,7 +39,7 @@ public function __construct($endpoint = ENDPOINT_PRODUCTION)
if ($endpoint != self::ENDPOINT_PRODUCTION && $endpoint != self::ENDPOINT_SANDBOX) {
throw new RunTimeException("Invalid endpoint '{$endpoint}'");
}

$this->_endpoint = $endpoint;
}

Expand All @@ -56,7 +56,7 @@ public function getReceiptData()
/**
* set receipt data, either in base64, or in json
*
* @param string $receiptData
* @param string $receiptData
* @return \ReceiptValidator\iTunes\Validator;
*/
function setReceiptData($receiptData)
Expand All @@ -66,7 +66,7 @@ function setReceiptData($receiptData)
} else {
$this->_receiptData = $receiptData;
}

return $this;
}

Expand All @@ -83,13 +83,13 @@ public function getEndpoint()
/**
* set endpoint
*
* @param string $endpoint
* @param string $endpoint
* @return\ReceiptValidator\iTunes\Validator;
*/
function setEndpoint($endpoint)
{
$this->_endpoint = $endpoint;

return $this;
}

Expand All @@ -103,7 +103,7 @@ protected function getClient()
if ($this->_client == null) {
$this->_client = new GuzzleClient($this->_endpoint);
}

return $this->_client;
}

Expand All @@ -118,43 +118,43 @@ private function encodeRequest()
'receipt-data' => $this->getReceiptData()
));
}

/**
* validate the receipt data
*
*
* @param string $receiptData
*
*
* @return Response
*/
public function validate($receiptData = null)
{
if ($receiptData!=null) {

if ($receiptData != null) {
$this->setReceiptData($receiptData);
}
$httpResponse = $this->getClient()->post(null, null, $this->encodeRequest(), array('verify'=> false))->send();
if ($httpResponse->getStatusCode()!=200) {

$httpResponse = $this->getClient()->post(null, null, $this->encodeRequest(), array('verify' => false))->send();

if ($httpResponse->getStatusCode() != 200) {
throw new RunTimeException('Unable to get response from itunes server');
}

$response = new Response($httpResponse->json());

// on a 21007 error retry the request in the sandbox environment (if the current environment is Production)
// these are receipts from apple review team
if ($this->_endpoint == self::ENDPOINT_PRODUCTION && $response->getResultCode()==Response::RESULT_SANDBOX_RECEIPT_SENT_TO_PRODUCTION) {
// these are receipts from apple review team
if ($this->_endpoint == self::ENDPOINT_PRODUCTION && $response->getResultCode() == Response::RESULT_SANDBOX_RECEIPT_SENT_TO_PRODUCTION) {
$client = new GuzzleClient(self::ENDPOINT_SANDBOX);

$httpResponse = $client->post(null, null, $this->encodeRequest(), array('verify'=> false))->send();

if ($httpResponse->getStatusCode()!=200) {
$httpResponse = $client->post(null, null, $this->encodeRequest(), array('verify' => false))->send();

if ($httpResponse->getStatusCode() != 200) {
throw new RunTimeException('Unable to get response from itunes server');
}

$response = new Response($httpResponse->json());
}

return $response;
}
}
4 changes: 2 additions & 2 deletions tests/iTunes/ResponseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public function testInvalidOptionsToConstructor()

public function testInvalidReceipt()
{
$response = new Response(array('status' => 21002));
$response = new Response(array('status' => 21002, 'receipt' => []));

$this->assertFalse($response->isValid(), 'receipt must be invalid');
}

public function testValidReceipt()
{
$response = new Response(array('status' => 0));
$response = new Response(array('status' => 0, 'receipt' => []));

$this->assertTrue($response->isValid(), 'receipt must be valid');
}
Expand Down

0 comments on commit 7da1118

Please sign in to comment.