Last active
August 29, 2015 13:57
-
-
Save itsbalamurali/9909948 to your computer and use it in GitHub Desktop.
Replace index.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$_SERVER['SERVER_PORT'] = 80; | |
/** | |
* Step 1: Require the Slim Framework | |
* | |
* If you are not using Composer, you need to require the | |
* Slim Framework and register its PSR-0 autoloader. | |
* | |
* If you are using Composer, you can skip this step. | |
*/ | |
require 'Slim/Slim.php'; | |
// Pimple Dependency Injection Container | |
require_once 'Pimple.php'; | |
\Slim\Slim::registerAutoloader(); | |
$app = new \Slim\Slim(); | |
// Set Default Timezone to America/Los_Angeles as this is what all App Store dates are in. | |
date_default_timezone_set('America/Los_Angeles'); | |
// Create Pimple Dependency Injection Container for DB Connection. | |
$dbContainer = new Pimple(); | |
// Global Development Mode Setting | |
$developmentMode = ''; | |
// Global iTunes Production Level Setting | |
$iTunesProductionLevel = ''; | |
// Global iTunes Receipt Validation Caching Setting | |
$iTunesCachingDuration = -1; | |
// Global iTunes Receipt Validation Caching Setting | |
$subscriptionBehavior = ''; | |
// API Version | |
$apiVersion = '1.1'; | |
// DB Setup for Pimple Container | |
// BakerCloud API SETUP CONFIGURATION SETTING | |
// ************************************************************ | |
$dbContainer['db.options'] = array( | |
'host' => getenv('CLOUD_SQL_INSTANCE'), // CONFIGURE TO YOUR DB HOSTNAME | |
'username' => getenv('DB_USERNAME'), // CONFIGURE TO YOUR DB USERNAME | |
'password' => '', // CONFIGURE TO YOUR DB USERNAME'S PASSWORD | |
'dbname' => getenv('DB_NAME') // CONFIGURE TO YOUR DB INSTANCE NAME | |
); | |
//************************************************************* | |
// Using "share" method makes sure that the function is only called when 'db' is retrieved the first time. | |
$dbContainer['db'] = $dbContainer->share(function () use($dbContainer) | |
{ | |
// Get DB handle and create new PDO DB object using configuration settings | |
//$dbHandle = new PDO('mysql:host=' . $dbContainer['db.options']['host'] . ';dbname=' . $dbContainer['db.options']['dbname'], $dbContainer['db.options']['username'], $dbContainer['db.options']['password']); | |
$dbHandle = new PDO('mysql:unix_socket=' . $dbContainer['db.options']['host'] . ';dbname=' . $dbContainer['db.options']['dbname'], $dbContainer['db.options']['username'], $dbContainer['db.options']['password']); | |
// Set Character Set for DB connection to UTF-8 | |
$dbHandle -> exec("SET CHARACTER SET utf8"); | |
// Return DB handle | |
return $dbHandle; | |
}); | |
// ************************************************ | |
// SLIM PHP Methods for handling REST API Methods | |
// ************************************************ | |
// GET route | |
$app->get( | |
'/', | |
function () { | |
$template = <<<EOT | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset='utf-8'/> | |
<title>Baker Cloud Console (CE) REST API</title> | |
<style> | |
html,body,div,span,object,iframe, | |
h1,h2,h3,h4,h5,h6,p,blockquote,pre, | |
abbr,address,cite,code, | |
del,dfn,em,img,ins,kbd,q,samp, | |
small,strong,sub,sup,var, | |
b,i, | |
dl,dt,dd,ol,ul,li, | |
fieldset,form,label,legend, | |
table,caption,tbody,tfoot,thead,tr,th,td, | |
article,aside,canvas,details,figcaption,figure, | |
footer,header,hgroup,menu,nav,section,summary, | |
time,mark,audio,video{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent;} | |
body{line-height:1;} | |
article,aside,details,figcaption,figure, | |
footer,header,hgroup,menu,nav,section{display:block;} | |
nav ul{list-style:none;} | |
blockquote,q{quotes:none;} | |
blockquote:before,blockquote:after, | |
q:before,q:after{content:'';content:none;} | |
a{margin:0;padding:0;font-size:100%;vertical-align:baseline;background:transparent;} | |
ins{background-color:#ff9;color:#000;text-decoration:none;} | |
mark{background-color:#ff9;color:#000;font-style:italic;font-weight:bold;} | |
del{text-decoration:line-through;} | |
abbr[title],dfn[title]{border-bottom:1px dotted;cursor:help;} | |
table{border-collapse:collapse;border-spacing:0;} | |
hr{display:block;height:1px;border:0;border-top:1px solid #cccccc;margin:1em 0;padding:0;} | |
input,select{vertical-align:middle;} | |
html{ background: #EDEDED; height: 100%; } | |
body{background:#FFF;margin:0 auto;min-height:100%;padding:0 30px;width:440px;color:#666;font:14px/23px Arial,Verdana,sans-serif;} | |
h1,h2,h3,p,ul,ol,form,section{margin:0 0 20px 0;} | |
h1{color:#333;font-size:20px;} | |
h2,h3{color:#333;font-size:14px;} | |
h3{margin:0;font-size:12px;font-weight:bold;} | |
ul,ol{list-style-position:inside;color:#999;} | |
ul{list-style-type:square;} | |
code,kbd{background:#EEE;border:1px solid #DDD;border:1px solid #DDD;border-radius:4px;-moz-border-radius:4px;-webkit-border-radius:4px;padding:0 4px;color:#666;font-size:12px;} | |
pre{background:#EEE;border:1px solid #DDD;border-radius:4px;-moz-border-radius:4px;-webkit-border-radius:4px;padding:5px 10px;color:#666;font-size:12px;} | |
pre code{background:transparent;border:none;padding:0;} | |
a{color:#0068ae;} | |
header{padding: 30px 0;text-align:center;} | |
</style> | |
</head> | |
<body> | |
<header> | |
<a href='http://www.bakerframework.com'><img src='data:image/png;base64,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' alt='Baker Cloud (CE)'/></a> | |
</header> | |
<h1>Welcome to Baker Cloud Console (CE) API!</h1> | |
<p> | |
Congratulations! Your Baker Cloud Console (CE) API is up and running. However, you should test the Database connectivity below. If you get a success message, then you should be good to go! | |
</p> | |
<section> | |
<h2>Get Started</h2> | |
<ol> | |
<li><a href='/checkinstall' target='_blank'>Check DB Connectivity</a></li> | |
<li>Read the <a href='http://www.bakerframework.com/' target='_blank'>online documentation</a></li> | |
<li>Follow <a href='http://www.twitter.com/bakerframework' target='_blank'>@bakerframework</a> on Twitter</li> | |
</ol> | |
</section> | |
<section> | |
<h2>Baker Framework Community</h2> | |
<h3>Support Forum and Knowledge Base</h3> | |
<p> | |
Visit the <a href='http://www.github.com/bakerframework/' target='_blank'>Online Support Community</a> | |
to read announcements, chat with fellow Baker Framework users, ask questions, help others, or show off your cool | |
Baker Newsstand apps. | |
</p> | |
<h3>Twitter</h3> | |
<p> | |
Follow <a href='http://www.twitter.com/bakerframework' target='_blank'>@bakerframework</a> on Twitter to receive the very latest news | |
and updates about the framework. | |
</p> | |
</section> | |
</body> | |
</html> | |
EOT; | |
echo $template; | |
} | |
); | |
// Check DB Connectivity | |
// *Makes connection to BakerCloud DB and tries to make a select from the PUBLICATION table | |
$app->get('/checkinstall/', function () | |
{ | |
try { | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
$result = $db->prepare("SELECT * FROM PUBLICATION"); | |
$result->execute(); | |
$checkInstall = $result->fetchAll(); | |
echo '{"BakerCloud API":{"Success":"Database Connection Test Successful"}}'; | |
} | |
catch(PDOException $e) { | |
echo '{"BakerCloud API":{"Error":"' . $e->getMessage() . '"}}'; | |
} | |
}); | |
// Output Debug Information | |
// *Outputs the settings for a given publication that the API is registering, useful when debugging | |
$app->get('/debuginformation/:app_id', function ($app_id) | |
{ | |
global $apiVersion; | |
try { | |
echo 'Baker Cloud CE API: Debug Information'; | |
echo '<BR>API Version: ' . $apiVersion; | |
echo '<BR>Development Mode: ' . isInDevelopmentMode($app_id); | |
echo '<BR>Subscription Behavior: ' . getSubscriptionBehavior($app_id); | |
echo '<BR>iTunes Production Level: ' . getiTunesProductionLevel($app_id); | |
echo '<BR>iTunes Caching Duration: ' . getiTunesCachingDuration($app_id); | |
} | |
catch(PDOException $e) { | |
} | |
}); | |
// Issues List | |
// *Retrieves a list of available issues for the App ID, for population of Baker Shelf | |
$app->get('/issues/:app_id/:user_id', function ($app_id, $user_id) | |
{ | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
$SCRIPT_LOCATION = str_replace('index.php','',$_SERVER['SCRIPT_NAME']); | |
// Lookup Issue Download Security condition for Publication, if true, create secured API Issue download links | |
$result = $db->query("SELECT ISSUE_DOWNLOAD_SECURITY FROM PUBLICATION WHERE APP_ID = '$app_id' LIMIT 0, 1"); | |
$issueDownloadSecurity = $result->fetchColumn(); | |
if(isInDevelopmentMode($app_id)=="TRUE"){logMessage(LogType::Info,"Retrieving Issues for APP ID: " . $app_id . " USER ID: " . $user_id);} | |
// Query all issues for the incoming APP_ID | |
$sql = "SELECT * FROM ISSUES WHERE APP_ID = '$app_id' AND AVAILABILITY = 'published'"; | |
try { | |
$IssuesArray = array(); | |
$i = 0; | |
foreach($db->query($sql) as $row) { | |
$IssuesArray[$i]['name'] = $row['NAME']; | |
$IssuesArray[$i]['title'] = $row['TITLE']; | |
$IssuesArray[$i]['info'] = $row['INFO']; | |
$IssuesArray[$i]['date'] = $row['DATE']; | |
$IssuesArray[$i]['cover'] = $row['COVER']; | |
if ($issueDownloadSecurity == "TRUE") { | |
$IssuesArray[$i]['url'] = "http://" . $_SERVER['HTTP_HOST'] . $SCRIPT_LOCATION . "issue/" . $app_id . "/" . $user_id . "/" . $row['NAME']; | |
} | |
else{ | |
$IssuesArray[$i]['url'] = $row['URL']; | |
} | |
if($row['PRICING'] != 'free') | |
{ | |
$IssuesArray[$i]['product_id'] = $row['PRODUCT_ID']; | |
} | |
$i++; | |
} | |
logAnalyticMetric(AnalyticType::ApiInteraction,1,NULL,$app_id,$user_id); | |
echo json_encode($IssuesArray); | |
} | |
catch(PDOException $e) { | |
logMessage(LogType::Error, $e->getMessage()); | |
echo '{"error":{"text":"' . $e->getMessage() . '"}}'; | |
} | |
}); | |
// Issue Download | |
// *Validates availability of download of a specific named issue, redirects to download if available | |
$app->get('/issue/:app_id/:user_id/:name', function ($app_id, $user_id, $name) use($app) | |
{ | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
try { | |
$result = $db->prepare("SELECT * FROM ISSUES WHERE APP_ID = '$app_id' AND NAME = '$name' LIMIT 0,1"); | |
$result->execute(); | |
$issue = $result->fetch(); | |
// The Issue is not found for App_ID and Name. Throw 404 not found error. | |
if (!$issue) { | |
header('HTTP/1.1 404 Not Found'); | |
die(); | |
} | |
// Retrieve issue Issue Product ID to cross check with purchases | |
$product_id = $issue['PRODUCT_ID']; | |
// Default to not allow download. | |
$allow_download = false; | |
// Validate that the Product ID (from Issue Name) is an available download for given user | |
if ($product_id && $issue['PRICING'] != 'free') { | |
// Allow download if the issue is marked as purchased | |
$result = $db->query("SELECT COUNT(*) FROM PURCHASES | |
WHERE APP_ID = '$app_id' AND USER_ID = '$user_id' AND PRODUCT_ID = '$product_id'"); | |
$allow_download = ($result->fetchColumn() > 0); | |
} else if ($issue['PRICING'] == 'free') { | |
// Issue is marked as free, allow download | |
$allow_download = true; | |
} | |
if ($allow_download) { | |
if((isInDevelopmentMode($app_id)=="TRUE") && !($app->request()->isHead())){logMessage(LogType::Info,"Downloading ISSUE: " . $name . " for APP ID: " . $app_id . " USER ID: " . $user_id);} | |
logAnalyticMetric(AnalyticType::ApiInteraction,1,NULL,$app_id,$user_id); | |
if(!($app->request()->isHead())){logAnalyticMetric(AnalyticType::Download,1,$name,$app_id,$user_id);} | |
// Redirect to the downloadable file, nothing else needed in API call | |
$app->response()->redirect($issue['URL'], 303); | |
} | |
else { | |
header('HTTP/1.1 403 Forbidden'); | |
die(); | |
} | |
} | |
catch(PDOException $e) { | |
// Handle exception | |
logMessage(LogType::Error, $e->getMessage()); | |
} | |
}); | |
// Purchases List | |
// *Returns a list of Purchased Product ID's | |
$app->get('/purchases/:app_id/:user_id', function ($app_id, $user_id) | |
{ | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
$purchased_product_ids = array(); | |
if(isInDevelopmentMode($app_id)=="TRUE"){logMessage(LogType::Info,"Checking purchases for APP ID: " . $app_id . " USER ID: " . $user_id);} | |
try { | |
$subscribed = false; | |
// Retrieve latest receipt for Auto-Renewable-Subscriptions for the APP_ID, USER_ID combination | |
$result = $db->query("SELECT BASE64_RECEIPT FROM RECEIPTS | |
WHERE APP_ID = '$app_id' AND USER_ID = '$user_id' AND TYPE = 'auto-renewable-subscription' | |
ORDER BY TRANSACTION_ID DESC LIMIT 0, 1"); | |
$base64_latest_receipt = $result->fetchColumn(); | |
if($base64_latest_receipt) | |
{ | |
$userSubscription = checkSubscription($app_id, $user_id); | |
$dateLastValidated = new DateTime($userSubscription["LAST_VALIDATED"]); | |
$dateExpiration = new DateTime($userSubscription["EXPIRATION_DATE"]); | |
$dateCurrent = new DateTime('now'); | |
$interval = $dateCurrent->diff($dateLastValidated); | |
if(isInDevelopmentMode($app_id)=="TRUE"){logMessage(LogType::Info,"Time since last validating receipt for APP ID: " . $app_id . " USER ID: " . $user_id . " = " . $interval->format('%h hours %i minutes') );} | |
// Only refresh and re-verify receipt if greater than the iTunesCachingDuration - or greater than 1 whole day | |
if ((getiTunesCachingDuration($app_id) == -1) || ($interval->format('%h') > getiTunesCachingDuration($app_id)) || ($interval->format('%a') > 1)) { | |
// Check the latest receipt from the subscription table | |
if ($base64_latest_receipt) { | |
// Verify Receipt - with logic to fall back to Sandbox test if Production Receipt fails (error code 21007) | |
try{ | |
$data = verifyReceipt($base64_latest_receipt, $app_id, $user_id); | |
} | |
catch(Exception $e) { | |
if($e->getCode() == "21007"){ | |
logMessage(LogType::Info,"Confirming purchase for APP ID - Sandbox Receipt used in Production, retrying against Sandbox iTunes API: " . $app_id . " USER ID: " . $user_id . " TYPE: " . $type); | |
$data = verifyReceipt($base64_latest_receipt, $app_id, $user_id, TRUE); | |
} | |
} | |
markIssuesAsPurchased($data, $app_id, $user_id); | |
// Check if there is an active subscription for the user. Status=0 is true. | |
$subscribed = ($data->status == 0); | |
} | |
else { | |
// There is no receipt for this user, there is no active subscription | |
$subscribed = false; | |
} | |
} | |
else { | |
// We aren't going to re-verify the receipt now, but we should determine if the Expiration Date is beyond now | |
if ($dateCurrent > $dateExpiration) { | |
$subscribed = false; | |
} | |
else { | |
$subscribed = true; | |
} | |
} | |
} | |
else | |
{ | |
// There is no Auto-Renewable-Subscription for the APP_ID, USER_ID combination - check if there is a Free-Subscription | |
$result = $db->query("SELECT BASE64_RECEIPT FROM RECEIPTS | |
WHERE APP_ID = '$app_id' AND USER_ID = '$user_id' AND TYPE = 'free-subscription' | |
ORDER BY TRANSACTION_ID DESC LIMIT 0, 1"); | |
$base64_latest_receipt = $result->fetchColumn(); | |
// If there is a receipt for a free-subscription then we will return that the USER_ID is subscribed. Since a free | |
// subscription really doesn't have and valid term then we will just ignore any dates in the Apple receipt. | |
// However the list of purchased product_ids can still be blank because the issues should be marked as free. | |
if($base64_latest_receipt){ | |
$subscribed = true; | |
} | |
} | |
// Return list of purchased product_ids for the user | |
$result = $db->query("SELECT PRODUCT_ID FROM PURCHASES | |
WHERE APP_ID = '$app_id' AND USER_ID = '$user_id'"); | |
$purchased_product_ids = $result->fetchAll(PDO::FETCH_COLUMN); | |
logAnalyticMetric(AnalyticType::ApiInteraction,1,NULL,$app_id,$user_id); | |
echo json_encode(array( | |
'issues' => $purchased_product_ids, | |
'subscribed' => $subscribed | |
)); | |
} | |
catch(PDOException $e) { | |
logMessage(LogType::Error, $e->getMessage()); | |
echo '{"error":{"text":"' . $e->getMessage() . '"}}'; | |
} | |
}); | |
// iTunes List | |
// *Returns a list of Issues in an iTunes ATOM Feed XML Format. This can be hooked up to the FEED URL within | |
// iTunes connect to display up to date information in the Newsstand App Store listing | |
$app->get('/itunes/:app_id', function ($app_id) | |
{ | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
try { | |
$result = $db->query("SELECT ITUNES_UPDATED FROM PUBLICATION WHERE APP_ID = '$app_id' LIMIT 0, 1"); | |
$ITUNES_UPDATED = $result->fetchColumn(); | |
if(!$ITUNES_UPDATED) | |
throw new Exception('Invalid APP ID'); | |
$iTunesUpdateDate = new DateTime($ITUNES_UPDATED); | |
$sql = "SELECT * FROM ISSUES WHERE APP_ID = '$app_id' AND AVAILABILITY = 'published'"; | |
$AtomXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"" . "?>"; | |
$AtomXML.= "<feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:news=\"http://itunes.apple.com/2011/Newsstand\">"; | |
$AtomXML.= "<updated>" . date_format($iTunesUpdateDate, DateTime::ATOM) . "</updated>"; | |
foreach($db->query($sql) as $row) { | |
$iTunesIssueUpdateDate = new DateTime($row['ITUNES_UPDATED']); | |
$iTunesPublishedDate = new DateTime($row['DATE']); | |
$AtomXML.= "<entry>"; | |
$AtomXML.= "<id>" . $row['NAME'] . "</id>"; | |
$AtomXML.= "<updated>" . date_format($iTunesIssueUpdateDate, DateTime::ATOM) . "</updated>"; | |
$AtomXML.= "<published>" . date_format($iTunesPublishedDate, DateTime::ATOM) . "</published>"; | |
$AtomXML.= "<summary>" . $row['ITUNES_SUMMARY'] . "</summary>"; | |
$AtomXML.= "<news:cover_art_icons>"; | |
$AtomXML.= "<news:cover_art_icon size=\"SOURCE\" src=\"" . $row['ITUNES_COVERART_URL'] . "\"/>"; | |
$AtomXML.= "</news:cover_art_icons>"; | |
$AtomXML.= "</entry>"; | |
} | |
$AtomXML.= "</feed>"; | |
logAnalyticMetric(AnalyticType::ApiInteraction,1,NULL,$app_id,$user_id); | |
echo utf8_encode($AtomXML); | |
} | |
catch(Exception $e) { | |
logMessage(LogType::Error, $e->getMessage()); | |
echo '{"error":{"text":"' . $e->getMessage() . '"}}'; | |
} | |
}); | |
// Confirm Purchase | |
// *Confirms the purchase by validating the Receipt_Data received for the in app purchase. Records the receipt data | |
// in the database and adds the available issues to the user's Purchased List | |
$app->post('/confirmpurchase/:app_id/:user_id', function ($app_id, $user_id) use($app) | |
{ | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
$body = $app->request()->getBody(); | |
$receiptdata = $app->request()->post('receipt_data'); | |
$type = $app->request()->post('type'); | |
if(isInDevelopmentMode($app_id)=="TRUE"){logMessage(LogType::Info,"Confirming purchase for APP ID: " . $app_id . " USER ID: " . $user_id . " TYPE: " . $type);} | |
try { | |
// Verify Receipt - with logic to fall back to Sandbox test if Production Receipt fails (error code 21007) | |
try{ | |
$iTunesReceiptInfo = verifyReceipt($receiptdata, $app_id, $user_id); | |
} | |
catch(Exception $e) { | |
if($e->getCode() == "21007"){ | |
logMessage(LogType::Info,"Confirming purchase for APP ID - Sandbox Receipt used in Production, retrying against Sandbox iTunes API: " . $app_id . " USER ID: " . $user_id . " TYPE: " . $type); | |
$iTunesReceiptInfo = verifyReceipt($receiptdata, $app_id, $user_id, TRUE); | |
} | |
} | |
$sql = "INSERT IGNORE INTO RECEIPTS (APP_ID, QUANTITY, PRODUCT_ID, TYPE, TRANSACTION_ID, USER_ID, PURCHASE_DATE, | |
ORIGINAL_TRANSACTION_ID, ORIGINAL_PURCHASE_DATE, APP_ITEM_ID, VERSION_EXTERNAL_IDENTIFIER, BID, BVRS, BASE64_RECEIPT) | |
VALUES (:app_id, :quantity, :product_id, :type, :transaction_id, :user_id, :purchase_date, :original_transaction_id, | |
:original_purchase_date, :app_item_id, :version_external_identifier, :bid, :bvrs, :base64_receipt)"; | |
try { | |
$stmt = $db->prepare($sql); | |
$stmt->bindParam("app_id", $app_id); | |
$stmt->bindParam("quantity", $iTunesReceiptInfo->receipt->quantity); | |
$stmt->bindParam("product_id", $iTunesReceiptInfo->receipt->product_id); | |
$stmt->bindParam("type", $type); | |
$stmt->bindParam("transaction_id", $iTunesReceiptInfo->receipt->transaction_id); | |
$stmt->bindParam("user_id", $user_id); | |
$stmt->bindParam("purchase_date", $iTunesReceiptInfo->receipt->purchase_date); | |
$stmt->bindParam("original_transaction_id", $iTunesReceiptInfo->receipt->original_transaction_id); | |
$stmt->bindParam("original_purchase_date", $iTunesReceiptInfo->receipt->original_purchase_date); | |
$stmt->bindParam("app_item_id", $iTunesReceiptInfo->receipt->item_id); | |
$stmt->bindParam("version_external_identifier", $iTunesReceiptInfo->receipt->version_external_identifier); | |
$stmt->bindParam("bid", $iTunesReceiptInfo->receipt->bid); | |
$stmt->bindParam("bvrs", $iTunesReceiptInfo->receipt->bvrs); | |
$stmt->bindParam("base64_receipt", $receiptdata); | |
$stmt->execute(); | |
// If successful, record the user's purchase | |
if($type == 'auto-renewable-subscription'){ | |
markIssuesAsPurchased($iTunesReceiptInfo,$app_id,$user_id); | |
}else if($type == 'issue'){ | |
markIssueAsPurchased($iTunesReceiptInfo->receipt->product_id, $app_id, $user_id); | |
}else if($type == 'free-subscription'){ | |
// Nothing to do, as the server assumes free subscriptions don't need to be handled in this way | |
} | |
logAnalyticMetric(AnalyticType::ApiInteraction,1,NULL,$app_id,$user_id); | |
} | |
catch(PDOException $e) { | |
logMessage(LogType::Error, $e->getMessage()); | |
echo '{"error":{"text":"' . $e->getMessage() . '"}}'; | |
} | |
} | |
catch(Exception $e) { | |
logMessage(LogType::Error, $e->getMessage()); | |
echo '{"error":{"text":"' . $e->getMessage() . '"}}'; | |
} | |
}); | |
// APNS Token | |
// *Stores the APNS Token in the database for the given App ID and User ID | |
$app->post('/apns/:app_id/:user_id', function ($app_id, $user_id) use($app) | |
{ | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
$apns_token = $app->request()->post('apns_token'); | |
if(isInDevelopmentMode($app_id)=="TRUE"){logMessage(LogType::Info,"Storing APNS Token for APP ID: " . $app_id . " USER ID: " . $user_id);} | |
$sql = "INSERT IGNORE INTO APNS_TOKENS (APP_ID, USER_ID, APNS_TOKEN) | |
VALUES (:app_id, :user_id, :apns_token)"; | |
try { | |
$stmt = $db->prepare($sql); | |
$stmt->bindParam("app_id", $app_id); | |
$stmt->bindParam("user_id", $user_id); | |
$stmt->bindParam("apns_token", $apns_token); | |
$stmt->execute(); | |
logAnalyticMetric(AnalyticType::ApiInteraction,1,NULL,$app_id,$user_id); | |
echo '{"success":{"message":"' . $apns_token . '"}}'; | |
} | |
catch(PDOException $e) { | |
logMessage(LogType::Error, $e->getMessage()); | |
echo '{"error":{"text":"' . $e->getMessage() . '"}}'; | |
} | |
}); | |
// ************************************************ | |
// Utility Functions | |
// ************************************************ | |
// Log Error Messages for tracking and debugging purposes, also displayed in the BakerCloud Console for issue debugging | |
function logMessage($logType, $logMessage) | |
{ | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
$sql = "INSERT INTO SYSTEM_LOG (TYPE, MESSAGE) | |
VALUES (:logtype, :logmessage)"; | |
try { | |
$stmt = $db->prepare($sql); | |
$stmt->bindParam("logtype", $logType); | |
$stmt->bindParam("logmessage", $logMessage); | |
$stmt->execute(); | |
} | |
catch(PDOException $e) { | |
// Error occurred, just ignore because if it failed in this logMessage method not much we can do, ignore | |
} | |
} | |
// Log Analytic Metrics for tracking purposes and basic display in the dashboard | |
function logAnalyticMetric($analytic_type, $analytic_value, $metadata, $app_id, $user_id) | |
{ | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
$sql = "INSERT INTO ANALYTICS (APP_ID, USER_ID, TYPE, VALUE, METADATA) | |
VALUES (:app_id, :user_id, :analytic_type, :analytic_value, :metadata)"; | |
try { | |
$stmt = $db->prepare($sql); | |
$stmt->bindParam("app_id", $app_id); | |
$stmt->bindParam("user_id", $user_id); | |
$stmt->bindParam("analytic_type", $analytic_type); | |
$stmt->bindParam("analytic_value", $analytic_value); | |
$stmt->bindParam("metadata", $metadata); | |
$stmt->execute(); | |
} | |
catch(PDOException $e) { | |
// Error occurred, just ignore because if it failed in this logAnalyticMetric method not much we can do, ignore | |
} | |
} | |
// Check if this publication is in development mode, useful for installation and non-production debugging | |
function isInDevelopmentMode($app_id) | |
{ | |
global $developmentMode; | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
if($developmentMode != ""){ | |
return $developmentMode; | |
} | |
else{ | |
$result = $db->query("SELECT DEVELOPMENT_MODE FROM PUBLICATION WHERE APP_ID = '$app_id' LIMIT 0, 1"); | |
return $result->fetchColumn(); | |
} | |
} | |
// Retrieve the iTunes Production Level for Apple API calls | |
function getiTunesProductionLevel($app_id) | |
{ | |
global $iTunesProductionLevel; | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
if($iTunesProductionLevel != ""){ | |
return $iTunesProductionLevel; | |
} | |
else{ | |
$result = $db->query("SELECT ITUNES_PRODUCTION_LEVEL FROM PUBLICATION WHERE APP_ID = '$app_id' LIMIT 0, 1"); | |
return $result->fetchColumn(); | |
} | |
} | |
// Retrieve the Subscription Behavior setting for marking issues as purchased | |
function getSubscriptionBehavior($app_id) | |
{ | |
global $subscriptionBehavior; | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
if($subscriptionBehavior != ""){ | |
return $subscriptionBehavior; | |
} | |
else{ | |
$result = $db->query("SELECT SUBSCRIPTION_BEHAVIOR FROM PUBLICATION WHERE APP_ID = '$app_id' LIMIT 0, 1"); | |
return $result->fetchColumn(); | |
} | |
} | |
// Retrieve the iTunes Caching Duration for re-validating Apple Receipts | |
function getiTunesCachingDuration($app_id) | |
{ | |
global $iTunesCachingDuration; | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
if($iTunesCachingDuration != -1){ | |
return $iTunesCachingDuration; | |
} | |
else{ | |
$result = $db->query("SELECT ITUNES_REVALIDATION_DURATION FROM PUBLICATION WHERE APP_ID = '$app_id' LIMIT 0, 1"); | |
return $result->fetchColumn(); | |
} | |
} | |
// Mark all available (paid) issues as purchased for a given user | |
function markIssuesAsPurchased($app_store_data, $app_id, $user_id) | |
{ | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
if(isInDevelopmentMode($app_id)=="TRUE"){logMessage(LogType::Info,"Marking Issues as Purchased for APP ID: " . $app_id . " USER ID: " . $user_id);} | |
$receipt = $app_store_data->receipt; | |
$startDate = new DateTime($receipt->purchase_date_pst); | |
if ($app_store_data->status == 0) { | |
$endDate = new DateTime($app_store_data->latest_receipt_info->expires_date_formatted_pst); | |
} | |
else | |
if ($app_store_data->status == 21006) { | |
$endDate = new DateTime($app_store_data->latest_expired_receipt_info->expires_date_formatted_pst); | |
} | |
// Now update the Purchases table with all Issues that fall within the subscription start and expiration date | |
$startDateFormatted = $startDate->format('Y-m-d H:i:s'); | |
$endDateFormatted = $endDate->format('Y-m-d H:i:s'); | |
// Get First Day of the Month that the Receipt was generated for (Start) | |
$issuesStartDateFormatted = $startDate->format('Y-m-01 00:00:00'); | |
// Get Last Day of the Month that the Receipt was generated for (Expiration) | |
$issuesEndDateFormatted = $endDate->format('Y-m-t 23:59:59'); | |
// Update Subscriptions Table for user with current active subscription start and expiration date | |
updateSubscription($app_id, $user_id, $startDateFormatted, $endDateFormatted); | |
// If we are in Sandbox Mode, unlock all issues by default for testing purposes | |
if(getiTunesProductionLevel($app_id)=="sandbox"){ | |
$result = $db->query("SELECT PRODUCT_ID FROM ISSUES | |
WHERE APP_ID = '$app_id' | |
AND PRICING = 'paid'"); | |
} | |
else{ | |
// If we are in Production - determine based on Subscription Behavior setting | |
if(getSubscriptionBehavior($app_id)=="all"){ | |
$result = $db->query("SELECT PRODUCT_ID FROM ISSUES | |
WHERE APP_ID = '$app_id' | |
AND PRICING = 'paid'"); | |
}else if(getSubscriptionBehavior($app_id)=="term"){ | |
$result = $db->query("SELECT PRODUCT_ID FROM ISSUES | |
WHERE APP_ID = '$app_id' | |
AND `DATE` >= '$issuesStartDateFormatted' | |
AND `DATE` <= '$issuesEndDateFormatted' | |
AND PRICING = 'paid' | |
AND AVAILABILITY = 'published'"); | |
}else{ | |
//Default to 'term' if for some reason the above fails | |
$result = $db->query("SELECT PRODUCT_ID FROM ISSUES | |
WHERE APP_ID = '$app_id' | |
AND `DATE` >= '$issuesStartDateFormatted' | |
AND `DATE` <= '$issuesEndDateFormatted' | |
AND PRICING = 'paid' | |
AND AVAILABILITY = 'published'"); | |
} | |
} | |
$product_ids_to_mark = $result->fetchAll(PDO::FETCH_COLUMN); | |
$insert = "INSERT IGNORE INTO PURCHASES (APP_ID, USER_ID, PRODUCT_ID) | |
VALUES ('$app_id', '$user_id', :product_id)"; | |
$stmt = $db->prepare($insert); | |
foreach($product_ids_to_mark as $key => $product_id) { | |
$stmt->bindParam('product_id', $product_id); | |
$stmt->execute(); | |
} | |
} | |
// Mark all available issues as purchased for a given user | |
function markIssueAsPurchased($product_id, $app_id, $user_id) | |
{ | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
if(isInDevelopmentMode($app_id)=="TRUE"){logMessage(LogType::Info,"Marking single issue as purchased for APP ID: " . $app_id . " USER ID: " . $user_id . " PRODUCT ID: " . $product_id);} | |
$sql = "INSERT IGNORE INTO PURCHASES (APP_ID, USER_ID, PRODUCT_ID) | |
VALUES (:app_id, :user_id, :product_id)"; | |
try { | |
$stmt = $db->prepare($sql); | |
$stmt->bindParam("app_id", $app_id); | |
$stmt->bindParam("user_id", $user_id); | |
$stmt->bindParam("product_id", $product_id); | |
$stmt->execute(); | |
} | |
catch(PDOException $e) { | |
logMessage(LogType::Error, $e->getMessage()); | |
echo '{"error":{"text":"' . $e->getMessage() . '"}}'; | |
} | |
} | |
// Update the Subscription Record for a specific user with Effective Date and Expiration Date | |
function updateSubscription($app_id, $user_id, $effective_date, $expiration_date) | |
{ | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
$currentDate = new DateTime('now'); | |
$lastValidated = $currentDate->format('Y-m-d H:i:s'); | |
if(isInDevelopmentMode($app_id)=="TRUE"){logMessage(LogType::Info,"Updating subscription effective dates for APP ID: " . $app_id . " USER ID: " . $user_id);} | |
$sql = "INSERT INTO SUBSCRIPTIONS (APP_ID, USER_ID, EFFECTIVE_DATE, EXPIRATION_DATE, LAST_VALIDATED) | |
VALUES (:app_id, :user_id, :effective_date, :expiration_date, :last_validated) | |
ON DUPLICATE KEY UPDATE EFFECTIVE_DATE=:effective_date, EXPIRATION_DATE=:expiration_date, LAST_VALIDATED=:last_validated"; | |
try { | |
$stmt = $db->prepare($sql); | |
$stmt->bindParam("app_id", $app_id); | |
$stmt->bindParam("user_id", $user_id); | |
$stmt->bindParam("effective_date", $effective_date); | |
$stmt->bindParam("expiration_date", $expiration_date); | |
$stmt->bindParam("last_validated", $lastValidated); | |
$stmt->execute(); | |
} | |
catch(PDOException $e) { | |
logMessage(LogType::Error, $e->getMessage()); | |
echo '{"error":{"text":"' . $e->getMessage() . '"}}'; | |
} | |
} | |
// Check if the user has a current active Subscription and determine expiration date | |
function checkSubscription($app_id, $user_id) | |
{ | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
if(isInDevelopmentMode($app_id)=="TRUE"){logMessage(LogType::Info,"Checking subscription for APP ID: " . $app_id . " USER ID: " . $user_id);} | |
$result = $db->prepare("SELECT EFFECTIVE_DATE, EXPIRATION_DATE, LAST_VALIDATED FROM SUBSCRIPTIONS | |
WHERE APP_ID = '$app_id' AND USER_ID = '$user_id' LIMIT 0,1"); | |
$result->execute(); | |
$data = $result->fetch(); | |
return $data; | |
} | |
// Validate InApp Purchase Receipt, by calling the Apple iTunes verifyReceipt method | |
// *Note that this seems to take between 2-4 seconds on average | |
function verifyReceipt($receipt, $app_id, $user_id, $sandbox_override = FALSE) | |
{ | |
global $dbContainer; | |
$db = $dbContainer['db']; | |
if(isInDevelopmentMode($app_id)=="TRUE"){logMessage(LogType::Info,"Verifying receipt with Apple for APP ID: " . $app_id . " USER ID: " . $user_id);} | |
// Lookup shared secret from Publication table | |
$result = $db->query("SELECT ITUNES_SHARED_SECRET FROM PUBLICATION WHERE APP_ID = '$app_id' LIMIT 0, 1"); | |
$sharedSecret = $result->fetchColumn(); | |
if (getiTunesProductionLevel($app_id)=="sandbox" || $sandbox_override == TRUE) { | |
$endpoint = 'https://sandbox.itunes.apple.com/verifyReceipt'; | |
} | |
else { | |
$endpoint = 'https://buy.itunes.apple.com/verifyReceipt'; | |
} | |
// If no shared secret exists, don't send it to the verifyReceipt call, however it should exist! | |
if($sharedSecret){ | |
$postData = json_encode(array( | |
'receipt-data' => $receipt, | |
'password' => $sharedSecret)); | |
}else{ | |
$postData = json_encode(array( | |
'receipt-data' => $receipt)); | |
} | |
$post = $postData; | |
$context = | |
array("http"=> | |
array( | |
"method" => "POST", | |
//"header" => "custom-header: if-any\r\n" . | |
//"custom-header-two: custome-value-2\r\n" . | |
"content" => $post | |
) | |
); | |
// Create Stream Context so that it can pass in Header | |
$context = stream_context_create($context); | |
$response = file_get_contents($endpoint, false, $context); | |
if ($errno != 0) { | |
throw new Exception($errmsg, $errno); | |
} | |
$data = json_decode($response); | |
if (!is_object($data)) { | |
throw new Exception('Invalid Response Data'); | |
} | |
if (!isset($data->status) || ($data->status != 0 && $data->status != 21006)) { | |
logMessage(LogType::Warning, "Invalid receipt for APP ID: " . $app_id . " USER ID: " . $user_id . " STATUS: " . $data->status); | |
throw new Exception('Invalid Receipt', $data->status); | |
} | |
return $data; | |
} | |
/** | |
* Step 4: Run the Slim application | |
* | |
* This method should be called last. This executes the Slim application | |
* and returns the HTTP response to the HTTP client. | |
*/ | |
$app->run(); | |
// PHP doesn't support Enums so make a simple class for LogType | |
abstract class LogType | |
{ | |
const Info = 'Info'; | |
const Warning = 'Warning'; | |
const Error = 'Error'; | |
} | |
// PHP doesn't support Enums so make a simple class for AnalyticType | |
abstract class AnalyticType | |
{ | |
const Download = 'download'; | |
const ApiInteraction = 'api_interaction'; | |
} | |
// Timer class for debugging and logging use | |
class timer | |
{ | |
var $start; | |
var $pause_time; | |
/* start the timer */ | |
function timer($start = 0) | |
{ | |
if ($start) { | |
$this->start(); | |
} | |
} | |
/* start the timer */ | |
function start() | |
{ | |
$this->start = $this->get_time(); | |
$this->pause_time = 0; | |
} | |
/* pause the timer */ | |
function pause() | |
{ | |
$this->pause_time = $this->get_time(); | |
} | |
/* unpause the timer */ | |
function unpause() | |
{ | |
$this->start+= ($this->get_time() - $this->pause_time); | |
$this->pause_time = 0; | |
} | |
/* get the current timer value */ | |
function get($decimals = 8) | |
{ | |
return round(($this->get_time() - $this->start) , $decimals); | |
} | |
/* format the time in seconds */ | |
function get_time() | |
{ | |
list($usec, $sec) = explode(' ', microtime()); | |
return ((float)$usec + (float)$sec); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment