public static function logging_enable($old_val, $new_val) { global $EM_Notices; if ($new_val && $new_val != $old_val) { if (!EM_Pro::log('Logging Enabled', 'general', true)) { $EM_Notices->add_error(__('Could not create a log directory, please make sure your wp-content is writeable.' . 'em-pro')); } } }
/** * Runs when PayPal sends IPNs to the return URL provided during bookings and EM setup. * Bookings are updated and transactions are recorded accordingly. */ function handle_payment_return() { // Read POST data // reading posted data directly from $_POST causes serialization issues with // array data in POST. Reading raw POST data from input stream instead. $raw_post_data = file_get_contents('php://input'); $post = $this->decodePayPalIPN($raw_post_data); // PayPal IPN handling code if ((isset($post['status']) || isset($post['transaction_type'])) && isset($post['tracking_id'])) { //Verify IPN request if (get_option('em_' . $this->gateway . "_status") == 'live') { $domain = 'https://www.paypal.com/cgi-bin/webscr'; } else { $domain = 'https://www.sandbox.paypal.com/cgi-bin/webscr'; } $req = 'cmd=_notify-validate&' . $raw_post_data; @set_time_limit(60); //add a CA certificate so that SSL requests always go through add_action('http_api_curl', 'EM_Gateway_Paypal_Chained::payment_return_local_ca_curl', 10, 1); //using WP's HTTP class $ipn_verification_result = wp_remote_get($domain . '?' . $req, array('httpversion', '1.1')); remove_action('http_api_curl', 'EM_Gateway_Paypal_Chained::payment_return_local_ca_curl', 10, 1); if (!is_wp_error($ipn_verification_result) && $ipn_verification_result['body'] == 'VERIFIED') { //log ipn request if needed, then move on EM_Pro::log($post['transaction_type'] . " successfully received for {$post['transaction'][0]['amount']} (TXN ID {$post['transaction'][0]['id']}) - Booking: {$post['tracking_id']}", 'paypal_chained'); } else { //log error if needed, send error header and exit EM_Pro::log(array('IPN Verification Error', 'WP_Error' => $ipn_verification_result, '$_POST' => $post, '$req' => $domain . '?' . $req), 'paypal_chained'); header('HTTP/1.0 502 Bad Gateway'); exit; } //if we get past this, then the IPN went ok // handle cases that the system must ignore //Common variables $primary_transaction = null; // Locate primary transaction: foreach ($post['transaction'] as $transaction) { if ($transaction['is_primary_receiver']) { $primary_transaction = $transaction; break; } } // We're interested in the primary receiver transaction as that is the main payment for the booking // Any subsequent receivers is just the money being distributed based on the the em_gateway_paypal_chained_receivers hook // As we don't know what they could be we won't try to save that information $currency_amount = explode(' ', $primary_transaction['amount']); $amount = $currency_amount[1]; $currency = $currency_amount[0]; $timestamp = date('Y-m-d H:i:s', strtotime($post['payment_request_date'])); $booking_id = $post['tracking_id']; $EM_Booking = em_get_booking($booking_id); if (!empty($EM_Booking->booking_id)) { //booking exists $EM_Booking->manage_override = true; //since we're overriding the booking ourselves. $user_id = $EM_Booking->person_id; // process PayPal response switch ($primary_transaction['status']) { case 'Completed': // case: successful payment $this->record_transaction($EM_Booking, $amount, $currency, $timestamp, $primary_transaction['id'], $primary_transaction['status'], ''); if ($amount >= $EM_Booking->get_price() && (!get_option('em_' . $this->gateway . '_manual_approval', false) || !get_option('dbem_bookings_approval'))) { $EM_Booking->approve(true, true); //approve and ignore spaces } else { //TODO do something if pp payment not enough $EM_Booking->set_status(0); //Set back to normal "pending" } do_action('em_payment_processed', $EM_Booking, $this); break; case 'Error': $note = 'The payment failed and all attempted transfers failed or all completed transfers were successfully reversed'; $this->record_transaction($EM_Booking, $amount, $currency, $timestamp, $primary_transaction['id'], $primary_transaction['status'], $note); $EM_Booking->cancel(); do_action('em_payment_denied', $EM_Booking, $this); break; case 'Processing': case 'Pending': // case: payment is pending $pending_str = array('address' => 'Customer did not include a confirmed shipping address', 'authorization' => 'Funds not captured yet', 'echeck' => 'eCheck that has not cleared yet', 'intl' => 'Payment waiting for aproval by service provider', 'multi-currency' => 'Payment waiting for service provider to handle multi-currency process', 'unilateral' => 'Customer did not register or confirm his/her email yet', 'upgrade' => 'Waiting for service provider to upgrade the PayPal account', 'verify' => 'Waiting for service provider to verify his/her PayPal account', 'paymentreview' => 'Paypal is currently reviewing the payment and will approve or reject within 24 hours', '*' => ''); $reason = @$primary_transaction['pending_reason']; $note = 'Last transaction is pending. Reason: ' . (isset($pending_str[$reason]) ? $pending_str[$reason] : $pending_str['*']); $this->record_transaction($EM_Booking, $amount, $currency, $timestamp, $primary_transaction['id'], $primary_transaction['status'], $note); do_action('em_payment_pending', $EM_Booking, $this); break; case 'Reversed': // case: charge back $note = 'Last transaction has been reversed. Reason: Payment has been reversed (charge back)'; $this->record_transaction($EM_Booking, $amount, $currency, $timestamp, $primary_transaction['id'], $primary_transaction['status'], $note); //We need to cancel their booking. $EM_Booking->cancel(); do_action('em_payment_reversed', $EM_Booking, $this); break; case 'Refunded': // case: refund $note = 'Last transaction has been reversed. Reason: Payment has been refunded'; $this->record_transaction($EM_Booking, $amount, $currency, $timestamp, $primary_transaction['id'], $primary_transaction['status'], $note); if ($EM_Booking->get_price() >= $amount) { $EM_Booking->cancel(); } else { $EM_Booking->set_status(0); //Set back to normal "pending" } do_action('em_payment_refunded', $EM_Booking, $this); break; default: // case: various error cases // https://developer.paypal.com/docs/classic/api/adaptive-payments/PaymentDetails_API_Operation/ } } else { if (is_numeric($booking_id) && $primary_transaction['status'] == 'Completed') { $message = apply_filters('em_gateway_paypal_chained_bad_booking_email', "\nA Payment has been received by PayPal for a non-existent booking.\n\nIt may be that this user's booking has timed out yet they proceeded with payment at a later stage.\n\nIn some cases, it could be that other payments not related to Events Manager are triggering this error. If that's the case, you can prevent this from happening by changing the URL in your IPN settings to:\n\n" . get_home_url() . "\n\nTo refund this transaction, you must go to your PayPal account and search for this transaction:\n\nTransaction ID : %transaction_id%\nEmail : %payer_email%\n\nWhen viewing the transaction details, you should see an option to issue a refund.\n\nIf there is still space available, the user must book again.\n\nSincerely,\nEvents Manager\n\t\t\t\t\t", $booking_id); $message = str_replace(array('%transaction_id%', '%payer_email%'), array($primary_transaction['id'], $post['sender_email']), $message); wp_mail(get_option('em_' . $this->gateway . "_email"), __('Unprocessed payment needs refund'), $message); } else { //header('Status: 404 Not Found'); $error = 'Error: Bad IPN request, custom ID does not correspond with any pending booking.'; echo $error; error_log($error); exit; } } //fclose($log); } else { // Did not find expected POST variables. Possible access attempt from a non PayPal site. //header('Status: 404 Not Found'); echo 'Error: Missing POST variables. Identification is not possible. If you are not PayPal and are visiting this page directly in your browser, this error does not indicate a problem, but simply means EM is correctly set up and ready to receive IPNs from PayPal only.'; error_log('PayPal Chained IPN error: Missing POST variables. Identification is not possible.'); exit; } }
/** * Runs when PayPal sends IPNs to the return URL provided during bookings and EM setup. Bookings are updated and transactions are recorded accordingly. */ function handle_payment_return() { // PayPal IPN handling code if ((isset($_POST['payment_status']) || isset($_POST['txn_type'])) && isset($_POST['custom'])) { //Verify IPN request if (get_option('em_' . $this->gateway . "_status") == 'live') { $domain = 'https://www.paypal.com/cgi-bin/webscr'; } else { $domain = 'https://www.sandbox.paypal.com/cgi-bin/webscr'; } $req = 'cmd=_notify-validate'; if (!isset($_POST)) { $_POST = $HTTP_POST_VARS; } foreach ($_POST as $k => $v) { $req .= '&' . $k . '=' . urlencode(stripslashes($v)); } @set_time_limit(60); //add a CA certificate so that SSL requests always go through add_action('http_api_curl', 'EM_Gateway_Paypal::payment_return_local_ca_curl', 10, 1); //using WP's HTTP class $ipn_verification_result = wp_remote_get($domain . '?' . $req, array('httpversion', '1.1')); remove_action('http_api_curl', 'EM_Gateway_Paypal::payment_return_local_ca_curl', 10, 1); if (!is_wp_error($ipn_verification_result) && $ipn_verification_result['body'] == 'VERIFIED') { //log ipn request if needed, then move on EM_Pro::log($_POST['payment_status'] . " successfully received for {$_POST['mc_gross']} {$_POST['mc_currency']} (TXN ID {$_POST['txn_id']}) - Custom Info: {$_POST['custom']}", 'paypal'); } else { //log error if needed, send error header and exit EM_Pro::log(array('IPN Verification Error', 'WP_Error' => $ipn_verification_result, '$_POST' => $_POST, '$req' => $domain . '?' . $req), 'paypal'); header('HTTP/1.0 502 Bad Gateway'); exit; } //if we get past this, then the IPN went ok // handle cases that the system must ignore $new_status = false; //Common variables $amount = $_POST['mc_gross']; $currency = $_POST['mc_currency']; $timestamp = date('Y-m-d H:i:s', strtotime($_POST['payment_date'])); $custom_values = explode(':', $_POST['custom']); $booking_id = $custom_values[0]; $event_id = !empty($custom_values[1]) ? $custom_values[1] : 0; $EM_Booking = em_get_booking($booking_id); if (!empty($EM_Booking->booking_id) && count($custom_values) == 2) { //booking exists $EM_Booking->manage_override = true; //since we're overriding the booking ourselves. $user_id = $EM_Booking->person_id; // process PayPal response switch ($_POST['payment_status']) { case 'Partially-Refunded': break; case 'Completed': case 'Processed': // case: successful payment $this->record_transaction($EM_Booking, $amount, $currency, $timestamp, $_POST['txn_id'], $_POST['payment_status'], ''); if ($_POST['mc_gross'] >= $EM_Booking->get_price() && (!get_option('em_' . $this->gateway . '_manual_approval', false) || !get_option('dbem_bookings_approval'))) { $EM_Booking->approve(true, true); //approve and ignore spaces } else { //TODO do something if pp payment not enough $EM_Booking->set_status(0); //Set back to normal "pending" } do_action('em_payment_processed', $EM_Booking, $this); break; case 'Reversed': // case: charge back $note = 'Last transaction has been reversed. Reason: Payment has been reversed (charge back)'; $this->record_transaction($EM_Booking, $amount, $currency, $timestamp, $_POST['txn_id'], $_POST['payment_status'], $note); //We need to cancel their booking. $EM_Booking->cancel(); do_action('em_payment_reversed', $EM_Booking, $this); break; case 'Refunded': // case: refund $note = 'Last transaction has been reversed. Reason: Payment has been refunded'; $this->record_transaction($EM_Booking, $amount, $currency, $timestamp, $_POST['txn_id'], $_POST['payment_status'], $note); if ($EM_Booking->get_price() >= $amount) { $EM_Booking->cancel(); } else { $EM_Booking->set_status(0); //Set back to normal "pending" } do_action('em_payment_refunded', $EM_Booking, $this); break; case 'Denied': // case: denied $note = 'Last transaction has been reversed. Reason: Payment Denied'; $this->record_transaction($EM_Booking, $amount, $currency, $timestamp, $_POST['txn_id'], $_POST['payment_status'], $note); $EM_Booking->cancel(); do_action('em_payment_denied', $EM_Booking, $this); break; case 'In-Progress': case 'Pending': // case: payment is pending $pending_str = array('address' => 'Customer did not include a confirmed shipping address', 'authorization' => 'Funds not captured yet', 'echeck' => 'eCheck that has not cleared yet', 'intl' => 'Payment waiting for aproval by service provider', 'multi-currency' => 'Payment waiting for service provider to handle multi-currency process', 'unilateral' => 'Customer did not register or confirm his/her email yet', 'upgrade' => 'Waiting for service provider to upgrade the PayPal account', 'verify' => 'Waiting for service provider to verify his/her PayPal account', 'paymentreview' => 'Paypal is currently reviewing the payment and will approve or reject within 24 hours', '*' => ''); $reason = @$_POST['pending_reason']; $note = 'Last transaction is pending. Reason: ' . (isset($pending_str[$reason]) ? $pending_str[$reason] : $pending_str['*']); $this->record_transaction($EM_Booking, $amount, $currency, $timestamp, $_POST['txn_id'], $_POST['payment_status'], $note); do_action('em_payment_pending', $EM_Booking, $this); break; default: // case: various error cases } } else { if (is_numeric($event_id) && is_numeric($booking_id) && ($_POST['payment_status'] == 'Completed' || $_POST['payment_status'] == 'Processed')) { $message = apply_filters('em_gateway_paypal_bad_booking_email', "\nA Payment has been received by PayPal for a non-existent booking. \n\nEvent Details : %event%\n\nIt may be that this user's booking has timed out yet they proceeded with payment at a later stage. \n\t\t\t\t\t\t\t\nIn some cases, it could be that other payments not related to Events Manager are triggering this error. If that's the case, you can prevent this from happening by changing the URL in your IPN settings to:\n\n" . get_home_url() . " \n\nTo refund this transaction, you must go to your PayPal account and search for this transaction:\n\nTransaction ID : %transaction_id%\nEmail : %payer_email%\n\nWhen viewing the transaction details, you should see an option to issue a refund.\n\nIf there is still space available, the user must book again.\n\nSincerely,\nEvents Manager\n\t\t\t\t\t", $booking_id, $event_id); $EM_Event = new EM_Event($event_id); $event_details = $EM_Event->name . " - " . date_i18n(get_option('date_format'), $EM_Event->start); $message = str_replace(array('%transaction_id%', '%payer_email%', '%event%'), array($_POST['txn_id'], $_POST['payer_email'], $event_details), $message); wp_mail(get_option('em_' . $this->gateway . "_email"), __('Unprocessed payment needs refund'), $message); } else { //header('Status: 404 Not Found'); echo 'Error: Bad IPN request, custom ID does not correspond with any pending booking.'; //echo "<pre>"; print_r($_POST); echo "</pre>"; exit; } } //fclose($log); } else { // Did not find expected POST variables. Possible access attempt from a non PayPal site. //header('Status: 404 Not Found'); echo 'Error: Missing POST variables. Identification is not possible. If you are not PayPal and are visiting this page directly in your browser, this error does not indicate a problem, but simply means EM is correctly set up and ready to receive IPNs from PayPal only.'; exit; } }
/** * Retreive the paypal pro vars needed to send to the gateway to proceed with payment * @param EM_Booking $EM_Booking */ function processStripe($EM_Booking) { global $EM_Notices; if (empty($_POST['stripe_card_num'])) { $EM_Booking->add_error(__('Please enter credit card number', 'em-pro') . '"'); return false; } if (empty($_POST['stripe_exp_date_month'])) { $EM_Booking->add_error(__('Please select expire month', 'em-pro') . '"'); return false; } if (empty($_POST['stripe_exp_date_year'])) { $EM_Booking->add_error(__('Please select expire year', 'em-pro') . '"'); return false; } if (empty($_POST['stripe_card_code'])) { $EM_Booking->add_error(__('Please enter CVV number', 'em-pro') . '"'); return false; } if ($this->debug == 'yes') { // Send request to paypal EM_Pro::log(sprintf(__('Payment Processing Start here', 'emp_stripe'))); } // Get the credit card details submitted by the form include "lib/Stripe.php"; if ($this->debug == 'yes') { EM_Pro::log(sprintf(__('Payment Processing start after include library', 'emp_stripe'))); } Stripe::setApiKey($this->SecretKey); if ($this->debug == 'yes') { EM_Pro::log(sprintf(__('Set Secret Key', 'emp_stripe'))); } try { $amount = $EM_Booking->get_price(false, false, true); if ($this->debug == 'yes') { EM_Pro::log(sprintf(__('Credit Card token create', 'emp_stripe'))); } $token_id = Stripe_Token::create(array("card" => array("number" => $_POST['stripe_card_num'], "exp_month" => $_POST['stripe_exp_date_month'], "exp_year" => $_POST['stripe_exp_date_year'], "cvc" => $_POST['stripe_card_code']))); if ($this->debug == 'yes') { EM_Pro::log(sprintf(__('Token genreated ID : %s', 'emp_stripe'), print_r($token_id->id, true))); } //Email Info $email_customer = get_option('em_' . $this->gateway . '_header_email_customer', 0) ? '1' : '0'; //for later $header_email_receipt = get_option('em_' . $this->gateway . '_header_email_receipt'); $footer_email_receipt = get_option('em_' . $this->gateway . '_footer_email_receipt'); //Order Info $booking_id = $EM_Booking->booking_id; $booking_description = preg_replace('/[^a-zA-Z0-9\\s]/i', "", $EM_Booking->get_event()->event_name); //clean event name $charge = Stripe_Charge::create(array("amount" => $amount * 100, "currency" => get_option('dbem_bookings_currency', 'USD'), "card" => $token_id->id, "metadata" => array("order_id" => $booking_id), "description" => $booking_description)); if ($this->debug == 'yes') { EM_Pro::log(sprintf(__('Return Response from Stripe: %s', 'emp_stripe'), print_r($charge, true))); } if ($token_id->id != '') { if ($charge->paid == true) { if ($this->debug == 'yes') { EM_Pro::log(sprintf(__('Payment Received...', 'emp_stripe'))); } $EM_Booking->booking_meta[$this->gateway] = array('txn_id' => $charge->id, 'amount' => $amount); $this->record_transaction($EM_Booking, $amount, get_option('dbem_bookings_currency', 'USD'), date('Y-m-d H:i:s', current_time('timestamp')), $charge->id, 'Completed', ''); $result = true; } else { if ($this->debug == 'yes') { EM_Pro::log(sprintf(__('Stripe payment failed. Payment declined.', 'emp_stripe'))); } $EM_Booking->add_error('Stripe payment failed. Payment declined.'); $result = false; } } else { if ($this->debug == 'yes') { EM_Pro::log(sprintf(__('Stripe payment failed. Payment declined. Please Check your Admin settings', 'emp_stripe'))); } $EM_Booking->add_error('Stripe payment failed. Payment declined. Please Check your Admin settings'); } //Return transaction_id or false return apply_filters('em_gateway_stripe_capture', $result, $EM_Booking, $this); } catch (Exception $e) { $EM_Booking->add_error(__('Connection error:', 'em-pro') . ': "' . $e->getMessage() . '"'); return false; } }