public static function process_ipn($wp) { //Ignore requests that are not IPN if (RGForms::get("page") != "gf_paypal_ipn") { return; } if (!isset(self::$log)) { self::$log = self::create_logger(); } self::$log->LogDebug("IPN request received. Starting to process..."); self::$log->LogDebug(print_r($_POST, true)); //Send request to paypal and verify it has not been spoofed if (!self::verify_paypal_ipn()) { self::$log->LogError("IPN request could not be verified by PayPal. Aborting."); return; } self::$log->LogDebug("IPN message successfully verified by PayPal"); //Valid IPN requests must have a custom field $custom = RGForms::post("custom"); if (empty($custom)) { self::$log->LogError("IPN request does not have a custom field, so it was not created by Gravity Forms. Aborting."); return; } //Getting entry associated with this IPN message (entry id is sent in the "custom" field) list($entry_id, $hash) = explode("|", $custom); $hash_matches = wp_hash($entry_id) == $hash; //Validates that Entry Id wasn't tampered with if (!RGForms::post("test_ipn") && !$hash_matches) { self::$log->LogError("Entry Id verification failed. Hash does not match. Custom field: {$custom}. Aborting."); return; } self::$log->LogDebug("IPN message has a valid custom field: {$custom}"); //$entry_id = RGForms::post("custom"); $entry = RGFormsModel::get_lead($entry_id); //Ignore orphan IPN messages (ones without an entry) if (!$entry) { self::$log->LogError("Entry could not be found. Entry ID: {$entry_id}. Aborting."); return; } self::$log->LogDebug("Entry has been found." . print_r($entry, true)); $config = self::get_config($entry["form_id"]); //Ignore IPN messages from forms that are no longer configured with the PayPal add-on if (!$config) { self::$log->LogError("Form no longer is configured with PayPal Addon. Form ID: {$entry["form_id"]}. Aborting."); return; } self::$log->LogDebug("Form {$entry["form_id"]} is properly configured."); //Only process test messages coming fron SandBox and only process production messages coming from production PayPal if ($config["meta"]["mode"] == "test" && !RGForms::post("test_ipn") || $config["meta"]["mode"] == "production" && RGForms::post("test_ipn")) { self::$log->LogError("Invalid test/production mode. IPN message mode (test/production) does not match mode configured in the PayPal feed. Configured Mode: {$config["meta"]["mode"]}. IPN test mode: " . RGForms::post("test_ipn")); return; } //Check business email to make sure it matches if (strtolower(trim($_POST["business"])) != strtolower(trim($config["meta"]["email"]))) { self::$log->LogError("PayPal email does not match. Configured email:" . strtolower(trim($config["meta"]["email"])) . " - Email from IPN message: " . strtolower(trim($_POST["business"]))); return; } //Pre IPN processing filter. Allows users to cancel IPN processing $cancel = apply_filters("gform_paypal_pre_ipn", false, $_POST, $entry, $config); if (!$cancel) { self::$log->LogDebug("Setting payment status..."); self::set_payment_status($config, $entry, RGForms::post("payment_status"), RGForms::post("txn_type"), RGForms::post("txn_id"), RGForms::post("parent_txn_id"), RGForms::post("subscr_id"), RGForms::post("mc_gross"), RGForms::post("pending_reason"), RGForms::post("reason_code")); } else { self::$log->LogDebug("IPN processing cancelled by the gform_paypal_pre_ipn filter. Aborting."); } self::$log->LogDebug("Before gform_paypal_post_ipn."); //Post IPN processing action do_action("gform_paypal_post_ipn", $_POST, $entry, $config, $cancel); self::$log->LogDebug("IPN processing complete."); }