function billUser($full_data_array) { /*** * Do a charge against the user, then update the status all around. * * https://developers.braintreepayments.com/android+php/reference/request/transaction/sale ***/ $data_array = $full_data_array["post_data"]; $brainTreeTransaction = array("amount" => $data_array["amount"], "paymentMethodNonce" => $data_array["nonce"], 'customer' => array(), 'options' => array('storeInVaultOnSuccess' => true, 'submitForSettlement' => true)); global $billingTokens; $billingTokens = array(); try { $braintree = Braintree_Transaction::sale($brainTreeTransaction); $result = array("status" => $braintree->success, "braintree_result" => $braintree); if (!$braintree->success) { $result["app_error_code"] = 115; $result["details"] = array(); $result["requested_action"] = "bill"; $result["human_error"] = "Failed to charge your card. Please try again later."; $result["given"] = $data_array; } else { try { # Post-process $billingTokens["status"] = true; $postProcessDetails = array(); $givenArgs = smart_decode64($data_array["billActionDetail"]); $billingTokens["provided"] = $givenArgs; $postAction = $givenArgs["action"]; switch ($postAction) { # Set all the $postProcessDetails data case "session": break; case "roleChange": $newRole = $givenArgs["newRole"]; # Do the role change break; default: throw new Exception("Bad post-process action '{$postAction}'"); break; } $billingTokens["post_process_details"] = $postProcessDetails; } catch (Exception $e) { # An internal error! We want to log this. $error_id = Stronghash::createSalt(); $write = "\n\n\n\n" . datestamp() . " - " . json_encode($_REQUEST); $write .= "\nError ID: {$error_id}"; $write .= "\nGot error: " . $e->getMessage(); $write .= "\n" . implode("\n", jTraceEx($e)); file_put_contents('billing_errors.log', $write, FILE_APPEND | LOCK_EX); $billingTokens["status"] = false; $billingTokens["error"] = $e->getMessage(); $billingTokens["human_error"] = "Your card was charged, but we couldn't update the server. Please contact support with error ID {$error_id}"; $billingTokens["need_popup"] = true; $result["status"] = false; $result["app_error_code"] = 116; } } } catch (Exception $e) { # Just the Braintree errors $result = array("status" => false, "error" => $e->getMessage(), "human_error" => "Unable to charge your card", "app_error_code" => 114, "given" => $data_array); } return $result; }
function authorizeApp($authenticationInformation) { /*** * Primary wrapper function for everything not directly login / auth * related. * * @param array $authenticationInformation * * $authenticationInformation should be an array containing the * following keys: * * @key "auth" - the SHA1 hash of "entropy", server secret key, "action", "app_version" * @key "key" - The encryption key to decrypt the server secret key * @key "app_version" - the application version number * @key "entropy" - A randomly generated string of 16 characters * @key "action" - The action to be executed * @key "data" - A base64-encoded JSON object with structured data for * the action * @key "user_id" - the dblink for the user. This will always be * appended to the values in the "data" key. ***/ if (is_array($authenticationInformation)) { $auth = $authenticationInformation['auth']; $auth_key = $authenticationInformation['key']; $version = $authenticationInformation['app_version']; $entropy = $authenticationInformation['entropy']; $action = $authenticationInformation['action']; $user = $authenticationInformation['user_id']; $device = $authenticationInformation['device_identifier']; $action_data = smart_decode64($authenticationInformation['data']); if (!is_array($action_data)) { returnAjax(array('status' => false, 'human_error' => 'The application and server could not communicate. Please contact support.', 'error' => 'Invalid data object', 'app_error_code' => 101)); } else { # Check structure of variables try { # Reserved for specific data-type checking } catch (Exception $e) { returnAjax(array('status' => false, 'human_error' => 'The application and server could not communicate. Please contact support.', 'error' => $e->getMessage(), 'app_error_code' => 108)); } # Save variables to be used later $action_data['dblink'] = $user; $app_verify = array('device' => $device, 'authorization_token' => $auth, 'auth_prepend' => $entropy, 'auth_postpend' => $action . $version, 'appsecret_key' => $auth_key, 'dblink' => $user); $action_data['application_verification'] = $app_verify; $u = new UserFunctions($user); } } else { returnAjax(array('status' => false, 'human_error' => 'The application and server could not communicate. Please contact support.', 'error' => 'Invalid request', 'app_error_code' => 102)); } /*** * See if the action is a valid action. * Most of these are just going to be wrappers for the * async_login_handler functions. ***/ if (empty($action)) { $action = 'sync'; } $action_function_map = array('save' => 'saveToUser', 'read' => 'getFromUser', 'sync' => 'syncUserData'); if (!array_key_exists($action, $action_function_map)) { returnAjax(array('status' => false, 'human_error' => 'The application and server could not communicate. Please contact support.', 'error' => 'Invalid action', 'app_error_code' => 103)); } # See if the user exists # get the key for $user from the server /*** * Now, we want to authenticate the app against this information. * $auth should be the SHA1 hash of: * * $auth = sha1($entropy.$SERVER_KEY.$action.$version) * * If it isn't, the request is bad. ***/ $r = $u->verifyApp($app_verify); if (!$r['status']) { returnAjax(array('status' => false, 'human_error' => "This app isn't authorized. Please log out and log back in.", 'error' => 'Invalid app credentials', 'app_error_code' => 106)); } # Call the $action $action_data['user_data'] = $r['data']; $action_result = $action_function_map[$action]($action_data); $action_result['elapsed'] = elapsed(); returnAjax($action_result); }
function advancedSearchProjectContains($get) { /*** * Similar to advancedSearchProject, but rather than requiring the * whole project is contained in the bounds, there only has to be * a nonzero amount of area of a project contained within bounds. ***/ global $db; $searchParams = smart_decode64($get["q"], false); $search = array(); $response = array("notices" => array()); foreach ($searchParams as $col => $searchQuery) { if (checkColumnExists($col, false)) { if ($searchQuery["data"] != "*") { $searchQuery["data"] = $db->sanitize($searchQuery["data"]); $search[$col] = $searchQuery; } } else { $response["notices"][] = "'{$col}' is an invalid column and was ignored."; } } $response["search"] = $searchParams; $response['status'] = true; # The API hit returns data from these columns $returnCols = array("public", "project_id", "disease", "project_title", "bounding_box_n", "bounding_box_e", "bounding_box_w", "bounding_box_s", "disease_morbidity", "disease_mortality", "disease_samples", "disease_positive", "includes_anura", "includes_caudata", "includes_gymnophiona", "sampled_species", "carto_id"); # For numerical comparisons, we have to allow a type specification $allowedSearchTypes = array("<", ">", "<=", ">=", "="); $loose = isset($get["loose"]) ? toBool($get["loose"]) : true; $boolean_type = "AND"; $where_arr = array(); foreach ($search as $col => $searchQuery) { $crit = $searchQuery["data"]; $validSearchType = empty($searchQuery["search_type"]) ? true : in_array($searchQuery["search_type"], $allowedSearchTypes); if (!empty($searchQuery["search_type"]) && !$validSearchType) { $response["notices"][] = "'" . $searchQuery["search_type"] . "' isn't a valid search type"; } if ($validSearchType && !is_numeric($crit)) { $response["notices"][] = "Search types may only be specified for numeric data ('" . $searchQuery["search_type"] . "' tried to be specified for '{$crit}')"; } if (!$validSearchType || !is_numeric($crit)) { $where_arr[] = $loose ? 'LOWER(`' . $col . "`) LIKE '%" . $crit . "%'" : '`' . $col . "`='" . $crit . "'"; } else { # The query is numeric AND we have a search type specified $where_arr[] = "`" . $col . "` " . $searchQuery["search_type"] . " " . $crit; } } $where = '(' . implode(' ' . strtoupper($boolean_type) . ' ', $where_arr) . ')'; $query = "SELECT " . implode(",", $returnCols) . " FROM `" . $db->getTable() . "` WHERE {$where}"; $response["query"] = $query; $db->invalidateLink(); $r = mysqli_query($db->getLink(), $query); if ($r === false) { $response["status"] = false; $response["error"] = mysqli_error($db->getLink()); $response["query"] = $query; returnAjax($response); } $queryResult = array(); $baseRows = mysqli_num_rows($r); $boolCols = array("public", "includes_anura", "includes_caudata", "includes_gymnophiona"); while ($row = mysqli_fetch_assoc($r)) { # Authenticate the project against the user if (checkProjectIdAuthorized($row["project_id"], true)) { # Clean up data types foreach ($row as $col => $val) { if (is_numeric($val)) { if (in_array($col, $boolCols)) { $row[$col] = toBool($val); } else { $row[$col] = floatval($val); } } if ($col == "carto_id") { $cartoObj = json_decode($val); if (!is_array($cartoObj)) { $cartoObj = $val; } else { foreach ($cartoObj as $k => $v) { $nk = str_replace("_", "_", $k); try { unset($cartoObj[$k]); } catch (Exception $e) { $response["notices"][] = $e->getMessage(); } $cartoObj[$nk] = $v; } } $row[$col] = $cartoObj; } } $queryResult[] = $row; } } $response['result'] = $queryResult; $response['count'] = sizeof($response['result']); $response['base_count'] = $baseRows; returnAjax($response); }