Last active
July 28, 2023 22:54
-
-
Save wcoastsands/cf6371d2a6aaaa04e4e2 to your computer and use it in GitHub Desktop.
Unity Ads S2S Redeem Callback Example
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 # callback.php (PHP 5) - Evaluates the Unity Ads S2S Redeem Callback. | |
//----------------------------------------------------------------------------- | |
// Each callback should contain the following parameters in the query string: | |
// pid -- product/game ID specified as part of the base callback URL | |
// sid -- user ID set through the Unity Ads SDK | |
// oid -- offer ID unique to each callback | |
// hmac -- HDMAC-MD5 hash of the query string | |
//----------------------------------------------------------------------------- | |
$public_ips_file = 'public_ips.json'; | |
$mysql_connect_file = 'mysql_connect.php'; | |
$header_200 = $_SERVER['SERVER_PROTOCOL'].' 200 OK'; | |
$header_400 = $_SERVER['SERVER_PROTOCOL'].' 400 Bad Request'; | |
$header_403 = $_SERVER['SERVER_PROTOCOL'].' 403 Forbidden'; | |
$header_500 = $_SERVER['SERVER_PROTOCOL'].' 500 Internal Server Error'; | |
$header = $header_500; | |
// Replace examples with valid email addresses. | |
$email = 'alerts@mydomain.com'; | |
$reply = 'noreply@mydomain.com'; | |
$subject = 'S2S Redeem Callback Alert'; | |
if (isset($_GET['pid'])) $subject .= ' for '.$_GET['pid']; | |
$mailheaders = 'From: '.$reply."\r\n". | |
'Reply-To: '.$reply."\r\n". | |
'X-Mailer: PHP/'.phpversion(); | |
$message = ''; | |
function cidr_match ($addr, $prefix) | |
{ | |
list ($subnet, $bits) = explode('/', $prefix); | |
$addr = ip2long($addr); | |
$subnet = ip2long($subnet); | |
$mask = -1 << (32 - $bits); | |
$subnet &= $mask; | |
return ($addr & $mask) == $subnet; | |
} | |
function is_valid_addr ($addr) | |
{ | |
global $public_ips_file; | |
$public_ips = json_decode(file_get_contents($public_ips_file)); | |
foreach ($public_ips->prefixes as $prefix) | |
{ | |
if (cidr_match($addr,$prefix->ip_prefix)) return true; | |
} | |
return false; | |
} | |
function generate_hash ($params, $secret) | |
{ | |
ksort($params); | |
$s = ''; | |
foreach ($params as $key => $value) | |
{ | |
$s .= "$key=$value,"; | |
} | |
$s = substr($s, 0, -1); | |
return hash_hmac('md5', $s, $secret);; | |
} | |
function get_secret ($pid) | |
{ | |
$secret = ''; | |
$query = "SELECT secret FROM games WHERE pid = '$pid'"; | |
$result = @mysql_query($query); | |
if ($result != false) | |
{ | |
$row = mysql_fetch_assoc($result); | |
$secret = $row['secret']; | |
} | |
return $secret; | |
} | |
function is_valid_pid ($pid) | |
{ | |
$query = "SELECT pid FROM games WHERE pid = '$pid'"; | |
$result = @mysql_query($query); | |
return ($result != false && mysql_num_rows($result) > 0); | |
} | |
function is_unique_oid ($oid) | |
{ | |
$query = "SELECT oid FROM callbacks WHERE oid = $oid"; | |
$result = @mysql_query($query); | |
return ($result == false || mysql_num_rows($result) == 0); | |
} | |
function save_callback ($params, $hash) | |
{ | |
$query = "INSERT INTO callbacks (oid, sid, pid, hmac, datetime) ". | |
"VALUES ('".$params['oid']."', '".$params['sid']."', '". | |
$params['pid']."', '".$hash."', UTC_TIMESTAMP())"; | |
return @mysql_query($query); | |
} | |
function get_request_params_message ($params) | |
{ | |
$msg = "Request Parameters:"; | |
foreach ($params as $key => $value) | |
{ | |
$msg .= "\r\n $key = $value"; | |
} | |
return $msg; | |
} | |
function get_server_info_message () | |
{ | |
return "Server Info:". | |
"\r\n Remote IP: ".$_SERVER['REMOTE_ADDR']. | |
"\r\n User Agent: ".$_SERVER['HTTP_USER_AGENT']. | |
"\r\n Query String: ".$_SERVER['QUERY_STRING']; | |
} | |
if (count($_GET) == 0) // Accept 0 parameters as a test. | |
{ | |
if (!file_exists($mysql_connect_file)) | |
{ | |
$header = $header_500; | |
$file_name = basename($mysql_connect_file); | |
$message = "Response: 500 - File not found ({$file_name})."; | |
} | |
else if (!file_exists($public_ips_file)) | |
{ | |
$header = $header_500; | |
$file_name = basename($public_ips_file); | |
$message = "Response: 500 - File not found ({$file_name})."; | |
} | |
else | |
{ | |
$header = $header_200; | |
$message = "Response: 200 - Test OK."; | |
} | |
} | |
else if (empty($_GET['sid'])) // Validate sid parameter. | |
{ | |
$header = $header_400; | |
$message = "Response: 400 - 'sid' value is not set."; | |
} | |
else if (empty($_GET['pid'])) // Verify pid parameter exists and is set. | |
{ | |
$header = $header_400; | |
$message = "Response: 400 - 'pid' " . | |
((array_key_exists('pid',$_GET)) ? | |
"value is not set." : "parameter is missing."); | |
} | |
else if (!file_exists($public_ips_file)) // Verify file exists. | |
{ | |
$header = $header_500; | |
$file_name = basename($public_ips_file); | |
$message = "Response: 500 - File not found ({$file_name})."; | |
} | |
else if (!is_valid_addr($_SERVER['REMOTE_ADDR'])) // Validate remote IP. | |
{ | |
$header = $header_403; | |
$message = "Response: 403 - Request orignated from an invalid address."; | |
} | |
else | |
{ | |
require_once($mysql_connect_file); | |
if ($dbc == false) // Verify DB connection. | |
{ | |
$header = $header_500; | |
$message = "Response: 500 - Failed to connect to DB."; | |
} | |
else if (!is_valid_pid($_GET['pid'])) // Validate pid parameter value. | |
{ | |
$header = $header_400; | |
$message = "Response: 400 - 'pid' value is invalid."; | |
} | |
else | |
{ | |
$hash = $_GET['hmac']; | |
unset($_GET['hmac']); | |
$secret = get_secret($_GET['pid']); | |
if ($hash != generate_hash($_GET,$secret)) // Validate signature. | |
{ | |
$header = $header_400; | |
$message = "Response: 400 - Signatures did not match."; | |
} | |
else if (!is_unique_oid($_GET['oid'])) // Validate oid parameter. | |
{ | |
$header = $header_400; | |
$message = "Response: 400 - 'oid' value is not unique."; | |
} | |
else if (!save_callback($_GET,$hash)) // Attempt to save. | |
{ | |
$header = $header_500; | |
$message = "Response: 500 - Failed to save callback data."; | |
} | |
else // Everything OK. | |
{ | |
$header = $header_200; | |
$message = "Response: 200 - OK"; | |
} | |
} | |
mysql_close(); | |
unset($dbc); | |
} | |
$message .= "\r\n"; | |
header($header); | |
print($message); | |
if ($header != $header_200) // Send email alert with details. | |
{ | |
if (empty($email) || $email == 'alerts@mydomain.com') | |
{ | |
print("Failed to send email alert! Address is invalid."); | |
} | |
else | |
{ | |
$message .= "\r\n".get_request_params_message($_GET)."\r\n"; | |
$message .= "\r\n".get_server_info_message()."\r\n"; | |
mail($email,$subject,$message,$mailheaders); | |
} | |
} | |
exit(); | |
?> |
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 # mysql_connect.php (PHP 5) - Connects to a MySQL database. | |
//----------------------------------------------------------------------------- | |
// For security reasons, the permissions on this file should be set to | |
// read-only for the owner, with no permissions for the group or others. | |
// | |
// Example: | |
// $ chmod 400 mysql_connect.php | |
// | |
// This file should also be placed outside of the web root directory. | |
//----------------------------------------------------------------------------- | |
define ('DB_HOST',''); // Set the hostname of the MySQL server. | |
define ('DB_NAME',''); // Set the database name. | |
define ('DB_USER',''); // Set the username. | |
define ('DB_PASS',''); // Set the password. | |
$dbc = @mysql_connect(DB_HOST,DB_USER,DB_PASS); | |
@mysql_select_db(DB_NAME); | |
?> |
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
mysql> SHOW COLUMNS FROM callbacks; | |
+----------+-------------+------+-----+---------------------+-------+ | |
| Field | Type | Null | Key | Default | Extra | | |
+----------+-------------+------+-----+---------------------+-------+ | |
| oid | varchar(16) | NO | PRI | NULL | | | |
| sid | varchar(24) | NO | | NULL | | | |
| pid | varchar(8) | NO | | NULL | | | |
| hmac | char(32) | NO | | NULL | | | |
| datetime | datetime | NO | | 0000-00-00 00:00:00 | | | |
+----------+-------------+------+-----+---------------------+-------+ | |
5 rows in set (0.03 sec) | |
mysql> SHOW COLUMNS FROM games; | |
+----------+-----------------------+------+-----+---------+-------+ | |
| Field | Type | Null | Key | Default | Extra | | |
+----------+-----------------------+------+-----+---------+-------+ | |
| pid | varchar(8) | NO | PRI | NULL | | | |
| name | varchar(32) | NO | | NULL | | | |
| platform | enum('ios','android') | NO | | NULL | | | |
| secret | varchar(32) | NO | | NULL | | | |
+----------+-----------------------+------+-----+---------+-------+ | |
4 rows in set (0.02 sec) |
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 # update_public_ips.php (PHP 5) - Creates a local copy of public_ips.json | |
//----------------------------------------------------------------------------- | |
// For efficiency and reliability reasons, a local copy of public_ips.json | |
// is used to validate the origin of Unity Ads S2S Redeem Callbacks. | |
// | |
// A cron job should be configured to run this script once every 24 hrs. | |
// | |
// Example: | |
// 50 0 * * * /usr/local/php5/bin/php "$HOME/html/update_public_ips.php" | |
//----------------------------------------------------------------------------- | |
$remote_file = 'http://static.applifier.com/public_ips.json'; | |
$local_file = dirname(__FILE__).'/public_ips.json'; | |
$data = file_get_contents($remote_file); | |
try { file_put_contents($local_file,$data); } | |
catch (Exception $e) | |
{ | |
exit('Caught exception: '.$e->getMessage()."\r\n"); | |
} | |
exit(); | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment