Skip to content

Instantly share code, notes, and snippets.

@Opticks-io
Last active August 29, 2018 13:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Opticks-io/232e3bdf93f12f17fe4021a4ea2884e6 to your computer and use it in GitHub Desktop.
Save Opticks-io/232e3bdf93f12f17fe4021a4ea2884e6 to your computer and use it in GitHub Desktop.
Opticks examples

Opticks Examples

Please find various code examples below.

General integration documentation can be found here.

<?php
/**
This page uses the "Bouncer" integration.
It checks for the existance of the OPT paremeter before displaying content.
If OPT does not exist, redirect to Opticks with next_url parameter.
**/
// https://opticks.io/r/{campaign_hash}?next_url={abc}&external_id={xyz}&subpublisher_id={}&var1={...}&var2={...}&var3={...}
/**
* @return string The current URL built from the server variables
*/
function getThisUrl()
{
// parse this page's URL
$url = (isset($_SERVER['HTTPS']) ? "https" : "http") . ":" . "//{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
$urlParts = parse_url($url);
// build this page's URL
$thisUrl = "{$urlParts['scheme']}://{$urlParts['host']}:{$urlParts['port']}{$urlParts['path']}?";
if (isset($urlParts['query'])) {
$thisUrl .= "{$urlParts['query']}";
}
if (isset($urlParts['fragment'])) {
$thisUrl .= $urlParts['fragment'];
}
return $thisUrl;
}
// AES constants
define("CIPHER", "AES-128-CBC");
define("KEY", "1234567812345678"); // KEEP THIS PRIVATE!
$opticksEndpoint = "http://geoff.billysrv.com:8000/unjcv5un";
$campaignHash = "asdfqwerty12345";
$redirectEndpoint = "$opticksEndpoint/g/$campaignHash";
// where do I expect to receive Opticks data
// e.g. http://myurl.com?opticks_analysis={OPT}&opticks_id={click_id}
$opticksInfo = [
'OPT' => 'opticks_analysis',
'click_id' => 'opticks_id'
];
$encryptedAnalysis = $_GET[$opticksInfo['OPT']] ?? null;
$opticksClickId = $_GET[$opticksInfo['click_id']] ?? null;
// no encrypted response from Opticks
if (!$encryptedAnalysis) {
$nextUrl = getThisUrl();
// Tell Opticks where to put the OPT data and opticks click_id
foreach ($opticksInfo as $k => $v) {
$nextUrl .= '&' . $v . '={' . $k . '}';
}
$nextEncodedUrl = urlencode($nextUrl);
// map my URL params to Opticks params
$merchantIdUrlParam = "merchant";
$subPublisherIdUrlParam = "source";
$merchantClickIdUrlParam = "pixel";
$someOtherIdUrlParam = "other";
if (($q = parse_url($nextUrl)['query'])) {
parse_str($q, $getArray);
$external_id = $getArray[$merchantClickIdUrlParam] ?? '';
$subpublisher_id = $getArray[$subPublisherIdUrlParam] ?? '';
$var1 = $getArray[$merchantIdUrlParam] ?? '';
$var2 = $getArray[$someOtherIdUrlParam] ?? '';
}
$nextLocation = "$redirectEndpoint?next_url=$nextEncodedUrl";
$nextLocation .= "&external_id=$external_id&subpublisher_id=$subpublisher_id";
$nextLocation .= "&var1=$var1&var2=$var2";
// redirect user to Opticks for analysis
header("Location: $nextLocation");
/* Make sure that code below does not get executed when we redirect. */
exit;
} else {
// OPT parameter exists, decode it
$iv = substr($opticksClickId ?? '', -16);
$OPT = rawurldecode($encryptedAnalysis);
try {
if (!$original_plaintext = openssl_decrypt($OPT, CIPHER, KEY, 0, $iv)) {
throw new \Exception("Unable to decrypt OPT.");
}
$optDecoded = json_decode($original_plaintext);
if (!is_object($optDecoded) || filter_var($optDecoded->ts, FILTER_VALIDATE_INT) === false) {
var_dump($optDecoded);
throw new \Exception("OPT decrypted incorrectly.");
}
} catch (\Exception $e) {
var_dump(['OPT' => $OPT, 'cipher' => CIPHER, 'key' => KEY, 'iv' => $iv, 'plaintext' => $original_plaintext]);
die($e->getMessage());
}
}
var_dump([$optDecoded, getThisUrl()]);
?>
public String decrypt(byte[] encryptedData, String iv, String key) {
String decryptedString = null;
try {
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec skeyspec = new SecretKeySpec(key.substring(0, 16).getBytes(), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.substring(iv.length() - 16, iv.length()).getBytes());
decryptCipher.init(Cipher.DECRYPT_MODE, skeyspec, ivParameterSpec);
byte[] decrypted = decryptCipher.doFinal(encryptedData);
decryptedString = new String(decrypted);
} catch (Exception e) {
log.error("Error decrypting string:" + e.getLocalizedMessage());
}
return decryptedString;
}
<!--
Capture data returned from the opticksEvent and
add it to the form for server-side decryptio.n
-->
<!--DOCTYPE html-->
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Subscribe</title>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous">
</script>
<script>
window.addEventListener('opticksEvent', function (event) {
var detail = event.detail;
var opticksClickId = document.createElement('input');
opticksClickId.type = 'hidden';
opticksClickId.name = 'opticksClickId';
opticksClickId.value = event.detail.opticksClickId;
document.querySelector('form').appendChild(opticksClickId);
var opticksInfo = document.createElement('input');
opticksInfo.type = 'hidden';
opticksInfo.name = 'opticksInfo';
opticksInfo.value = event.detail.data;
document.querySelector('form').appendChild(opticksInfo);
var opticksHmac = document.createElement('input');
opticksHmac.type = 'hidden';
opticksHmac.name = 'opticksHmac';
opticksHmac.value = event.detail.hmac;
document.querySelector('form').appendChild(opticksHmac);
// optional
$.ajax({
method: "POST",
url: "verifyAndDecryptAjaxWithRedirect.php",
data: JSON.stringify(detail),
success: function (response) {
console.log(response);
}
});
}, true);
</script>
</head>
<body>
<script src="https://track.opticks.io/j/327709153eb8162cd"
data-opticks="subpublisher_id={source}&var1={affiliate_id}&version=v2">
</script>
<form method="post" action="verifyDecryptForm.php">
<input name="fullName"/>
<button type="submit">Submit</button>
</form>
</body>
</html>
<!--
Capture data returned from the opticksEvent and
add it to the form for server-side decryptio.n
-->
<!--DOCTYPE html-->
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Subscribe</title>
</head>
<body>
<script>
(function () {
window.addEventListener('opticksEvent', function (event) {
var opticksClickId = document.createElement('input');
opticksClickId.type = 'hidden';
opticksClickId.name = 'opticksClickId';
opticksClickId.value = event.detail.opticksClickId;
document.querySelector('form').appendChild(opticksClickId);
var opticksInfo = document.createElement('input');
opticksInfo.type = 'hidden';
opticksInfo.name = 'opticksInfo';
opticksInfo.value = event.detail.data;
document.querySelector('form').appendChild(opticksInfo);
var opticksHmac = document.createElement('input');
opticksHmac.type = 'hidden';
opticksHmac.name = 'opticksHmac';
opticksHmac.value = event.detail.hmac;
document.querySelector('form').appendChild(opticksHmac);
}, true);
var opticksScript = document.createElement('script');
opticksScript.src = 'https://track.opticks.io/j/327709153eb8162cd';
opticksScript.setAttribute('data-opticks', 'subpublisher_id={source}&var1={affiliate_id}&version=v2');
document.body.insertBefore(opticksScript, document.body.childNodes[0]);
})();
</script>
<form method="post" action="verifyDecryptForm.php">
<input name="fullName"/>
<button type="submit">Submit</button>
</form>
</body>
</html>
<?php
require "vendor/autoload.php";
try {
// load variables from INI file
$ini = parse_ini_file("../opticks.ini");
define('CIPHER', 'AES-128-CBC');
// default key is 1234567812345678
define('KEY', $ini['secret']);
// read posted data
if(!$body = file_get_contents('php://input')) {
throw new \InvalidArgumentException("Body is empty.");
}
if(!$posted = json_decode($body)) {
throw new \InvalidArgumentException("Body is not valid JSON. ". $body);
}
$opticksClickId = $posted->opticksClickId;
$iv = substr($opticksClickId, -16);
$hmac64 = $posted->hmac;
$encryptedData64 = $posted->data;
// Calculate HMAC of encrypted data
$calcmac = hash_hmac('sha256', $encryptedData64, KEY, $as_binary = true);
$calcmac64 = base64_encode($calcmac);
// Confirm HMAC
if (!hash_equals($hmac64, $calcmac64)) {
throw new \InvalidArgumentException("Message verification (hmac) failure.");
}
// Decrypt
if(!$plaintext = openssl_decrypt($encryptedData64, CIPHER, KEY, 0, $iv)) {
throw new \UnexpectedValueException("Message decryption failure.");
}
// Extra check that decrypted info is valid JSON
if (!$plainJson = json_decode($plaintext)) {
throw new \UnexpectedValueException("Encrypted data is not valid JSON. " . $body);
}
// Compare plain text clickid to encrypted clickid
if($plainJson->opticksClickId != $opticksClickId) {
throw new \UnexpectedValueException("Opticks click ids don't match.");
}
// Check timestamp sanity - adjust according to your needs
$tsDiff = microtime(1) - $plainJson->ts / 1000;
if($tsDiff > 1.5) {
throw new \OutOfBoundsException("timestamp problem: " . microtime(1) . " - " . $plainJson->ts / 1000 . " = $tsDiff");
}
try {
// Ensure clickid is unique using Redis key/value store - adjust to your environment
$redis = new Predis\Client('tcp://' . $ini['redis_host']);
if ($redis->get($opticksClickId)) {
throw new \UnexpectedValueException("Opticks click id has already been used.");
}
$redis->publish("opticks", $plaintext);
} catch (\Exception $e) {
// redis doesn't work
}
// persist data
// don't do this in production
header('Content-Type: application/json');
echo $plaintext;
} catch (\Exception $e) {
header("HTTP/1.1 500 ERROR");
echo "Error: " . $e->getMessage();
}
<?php
require "vendor/autoload.php";
$detections = [];
try {
$ini = parse_ini_file("../opticks.ini");
define('CIPHER', 'AES-128-CBC');
// default key is 1234567812345678
define('KEY', $ini['secret']);
// ensure required variables exist
if(!$opticksClickId = $_REQUEST['opticksClickId']) {
throw new \InvalidArgumentException("Missing opticksClickId.");
}
if (!$encryptedData64 = $_REQUEST['opticksInfo']) {
throw new \InvalidArgumentException("Missing opticksInfo.");
}
if (!$hmac64 = $_REQUEST['opticksHmac']) {
throw new \InvalidArgumentException("Missing opticksHmac.");
}
// Prepare crypto
$iv = substr($opticksClickId, -16);
$calcmac = hash_hmac('sha256', $encryptedData64, KEY, $as_binary = true);
$calcmac64 = base64_encode($calcmac);
// Verify crypto signature
if (!hash_equals($hmac64, $calcmac64)) {
throw new \UnexpectedValueException("Message verification (hmac) failure.");
}
// Decrypt
if(!$plaintext = openssl_decrypt($encryptedData64, CIPHER, KEY, 0, $iv)) {
throw new \UnexpectedValueException("Message decryption failure.");
}
// Convert payload to object
if(!$plainJson = json_decode($plaintext)) {
throw new \InvalidArgumentException("Encrypted data is not valid JSON. " . $body);
}
// Compare encrypted opticksClickId to plain text opticksClickId
if($plainJson->opticksClickId != $opticksClickId) {
throw new \UnexpectedValueException("Opticks click ids don't match.");
}
// simple timestamp check, adjust to your needs
$tsDiff = microtime(1) - $plainJson->ts / 1000;
if($tsDiff > 600) { // 10 minutes
throw new \OutOfBoundsException("timestamp problem: " . microtime(1) . " - " . $plainJson->ts / 1000 . " = $tsDiff");
}
// Ensure clickid is unique using Redis key/value store - adjust to your environment
$redis = new Predis\Client('tcp://'.$ini['redis_host']);
$redis->incr($opticksClickId);
if($redis->get($opticksClickId) > 1) {
throw new \UnexpectedValueException("Opticks click id has already been used.");
}
// Ensure there are no invalidClickReasons
if ($plainJson->invalidClickReasons) {
throw new \UnexpectedValueException("invalidClickReasons exist.");
}
foreach ($plainJson->analysis->detections as $detection) {
$detections = array_merge($detections, $detection->triggers);
}
//var_dump($detections);
//echo $plaintext;
/** Do subscription stuff below **/
} catch (\Exception $e) {
// don't return error string in production
// maybe redirect to $plainJson->fallbackUrl ?
header("HTTP/1.1 500 ERROR");
echo "Error: " . $e->getMessage();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment