Current Path : /var/test/www/mainsite/wp-content/plugins/give/includes/gateways/ |
Current File : /var/test/www/mainsite/wp-content/plugins/give/includes/gateways/paypal-standard.php |
<?php /** * PayPal Standard Gateway * * @package Give * @subpackage Gateways * @copyright Copyright (c) 2016, GiveWP * @license https://opensource.org/licenses/gpl-license GNU Public License * @since 1.0 */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Toggle PayPal CC Billing Detail Fieldset. * * @param int $form_id Form ID. * * @return bool * @since 1.8.5 * */ function give_paypal_standard_billing_fields( $form_id ) { if ( give_is_setting_enabled( give_get_option( 'paypal_standard_billing_details' ) ) ) { give_default_cc_address_fields( $form_id ); return true; } return false; } add_action( 'give_paypal_cc_form', 'give_paypal_standard_billing_fields' ); /** * Process PayPal Payment. * * @param array $payment_data Payment data. * * @return void * @since 1.0 * */ function give_process_paypal_payment( $payment_data ) { // Validate nonce. give_validate_nonce( $payment_data['gateway_nonce'], 'give-gateway' ); $payment_id = give_create_payment( $payment_data ); // Check payment. if ( empty( $payment_id ) ) { // Record the error. give_record_gateway_error( __( 'Payment Error', 'give' ), sprintf( /* translators: %s: payment data */ __( 'Payment creation failed before sending donor to PayPal. Payment data: %s', 'give' ), json_encode( $payment_data ) ), $payment_id ); // Problems? Send back. give_send_back_to_checkout( '?payment-mode=' . $payment_data['post_data']['give-gateway'] ); } // Redirect to PayPal. wp_redirect( give_build_paypal_url( $payment_id, $payment_data ) ); exit; } add_action( 'give_gateway_paypal', 'give_process_paypal_payment' ); /** * Listens for a PayPal IPN requests and then sends to the processing function. * * @return void * @since 1.0 */ function give_listen_for_paypal_ipn() { // Regular PayPal IPN. if ( isset( $_GET['give-listener'] ) && 'IPN' === $_GET['give-listener'] ) { /** * Fires while verifying PayPal IPN * * @since 1.0 */ do_action( 'give_verify_paypal_ipn' ); } } add_action( 'init', 'give_listen_for_paypal_ipn' ); /** * Process PayPal IPN * * @return void * @since 1.0 */ function give_process_paypal_ipn() { // Check the request method is POST. if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' !== $_SERVER['REQUEST_METHOD'] ) { return; } // Set initial post data to empty string. $post_data = ''; // Fallback just in case post_max_size is lower than needed. if ( ini_get( 'allow_url_fopen' ) ) { $post_data = file_get_contents( 'php://input' ); } else { // If allow_url_fopen is not enabled, then make sure that post_max_size is large enough. ini_set( 'post_max_size', '12M' ); } // Start the encoded data collection with notification command. $encoded_data = 'cmd=_notify-validate'; // Get current arg separator. $arg_separator = give_get_php_arg_separator_output(); // Verify there is a post_data. if ( $post_data || strlen( $post_data ) > 0 ) { // Append the data. $encoded_data .= $arg_separator . $post_data; } else { // Check if POST is empty. if ( empty( $_POST ) ) { // Nothing to do. return; } else { // Loop through each POST. foreach ( $_POST as $key => $value ) { // Encode the value and append the data. $encoded_data .= $arg_separator . "$key=" . urlencode( $value ); } } } // Convert collected post data to an array. parse_str( $encoded_data, $encoded_data_array ); foreach ( $encoded_data_array as $key => $value ) { if ( false !== strpos( $key, 'amp;' ) ) { $new_key = str_replace( '&', '&', $key ); $new_key = str_replace( 'amp;', '&', $new_key ); unset( $encoded_data_array[ $key ] ); $encoded_data_array[ $new_key ] = $value; } } $api_response = false; // Validate IPN request w/ PayPal if user hasn't disabled this security measure. if ( give_is_setting_enabled( give_get_option( 'paypal_verification' ) ) ) { $remote_post_vars = array( 'method' => 'POST', 'timeout' => 45, 'redirection' => 5, 'httpversion' => '1.1', 'blocking' => true, 'headers' => array( 'host' => 'www.paypal.com', 'connection' => 'close', 'content-type' => 'application/x-www-form-urlencoded', 'post' => '/cgi-bin/webscr HTTP/1.1', ), 'sslverify' => false, 'body' => $encoded_data_array, ); // Validate the IPN. $api_response = wp_remote_post( give_get_paypal_redirect(), $remote_post_vars ); if ( is_wp_error( $api_response ) ) { give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */ __( 'Invalid IPN verification response. IPN data: %s', 'give' ), json_encode( $api_response ) ) ); return; // Something went wrong. } if ( 'VERIFIED' !== $api_response['body'] ) { give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */ __( 'Invalid IPN verification response. IPN data: %s', 'give' ), json_encode( $api_response ) ) ); return; // Response not okay. } }// End if(). // Check if $post_data_array has been populated. if ( ! is_array( $encoded_data_array ) && ! empty( $encoded_data_array ) ) { return; } $defaults = array( 'txn_type' => '', 'payment_status' => '', ); $encoded_data_array = wp_parse_args( $encoded_data_array, $defaults ); $payment_id = isset( $encoded_data_array['custom'] ) ? absint( $encoded_data_array['custom'] ) : 0; $txn_type = $encoded_data_array['txn_type']; // Check for PayPal IPN Notifications and update data based on it. $current_timestamp = current_time( 'timestamp' ); $paypal_ipn_vars = array( 'auth_status' => isset( $api_response['body'] ) ? $api_response['body'] : 'N/A', 'transaction_id' => isset( $encoded_data_array['txn_id'] ) ? $encoded_data_array['txn_id'] : 'N/A', 'payment_id' => $payment_id, ); update_option( 'give_last_paypal_ipn_received', $paypal_ipn_vars, false ); give_insert_payment_note( $payment_id, sprintf( __( 'IPN received on %1$s at %2$s', 'give' ), date_i18n( 'm/d/Y', $current_timestamp ), date_i18n( 'H:i', $current_timestamp ) ) ); give_update_meta( $payment_id, 'give_last_paypal_ipn_received', $current_timestamp ); if ( has_action( 'give_paypal_' . $txn_type ) ) { /** * Fires while processing PayPal IPN $txn_type. * * Allow PayPal IPN types to be processed separately. * * @param array $encoded_data_array Encoded data. * @param int $payment_id Payment id. * * @since 1.0 * */ do_action( "give_paypal_{$txn_type}", $encoded_data_array, $payment_id ); } else { /** * Fires while process PayPal IPN. * * Fallback to web accept just in case the txn_type isn't present. * * @param array $encoded_data_array Encoded data. * @param int $payment_id Payment id. * * @since 1.0 * */ do_action( 'give_paypal_web_accept', $encoded_data_array, $payment_id ); } exit; } add_action( 'give_verify_paypal_ipn', 'give_process_paypal_ipn' ); /** * Process web accept (one time) payment IPNs. * * @param array $data The IPN Data. * @param int $payment_id The payment ID from Give. * * @return void * @since 1.0 * */ function give_process_paypal_web_accept( $data, $payment_id ) { // Only allow through these transaction types. if ( 'web_accept' !== $data['txn_type'] && 'cart' !== $data['txn_type'] && 'refunded' !== strtolower( $data['payment_status'] ) ) { return; } // Need $payment_id to continue. if ( empty( $payment_id ) ) { return; } // Collect donation payment details. $paypal_amount = $data['mc_gross']; $payment_status = strtolower( $data['payment_status'] ); $currency_code = strtolower( $data['mc_currency'] ); $business_email = isset( $data['business'] ) && is_email( $data['business'] ) ? trim( $data['business'] ) : trim( $data['receiver_email'] ); $payment_meta = give_get_payment_meta( $payment_id ); // Must be a PayPal standard IPN. if ( 'paypal' !== give_get_payment_gateway( $payment_id ) ) { return; } // Verify payment recipient. if ( strcasecmp( $business_email, trim( give_get_option( 'paypal_email' ) ) ) !== 0 ) { give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */ __( 'Invalid business email in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id ); give_update_payment_status( $payment_id, 'failed' ); give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid PayPal business email.', 'give' ) ); return; } // Verify payment currency. if ( $currency_code !== strtolower( $payment_meta['currency'] ) ) { give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */ __( 'Invalid currency in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id ); give_update_payment_status( $payment_id, 'failed' ); give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid currency in PayPal IPN.', 'give' ) ); return; } // Process refunds & reversed. if ( 'refunded' === $payment_status || 'reversed' === $payment_status ) { give_process_paypal_refund( $data, $payment_id ); return; } // Only complete payments once. if ( 'publish' === get_post_status( $payment_id ) ) { return; } // Retrieve the total donation amount (before PayPal). $payment_amount = give_donation_amount( $payment_id ); // Check that the donation PP and local db amounts match. if ( number_format( (float) $paypal_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) { // The prices don't match give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */ __( 'Invalid payment amount in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id ); give_update_payment_status( $payment_id, 'failed' ); give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid amount in PayPal IPN.', 'give' ) ); return; } // Process completed donations. if ( 'completed' === $payment_status || give_is_test_mode() ) { give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal transaction ID */ __( 'PayPal Transaction ID: %s', 'give' ), $data['txn_id'] ) ); give_set_payment_transaction_id( $payment_id, $data['txn_id'] ); give_update_payment_status( $payment_id, 'publish' ); } elseif ( 'pending' === $payment_status && isset( $data['pending_reason'] ) ) { // Look for possible pending reasons, such as an eCheck. $note = give_paypal_get_pending_donation_note( $data['pending_reason'] ); if ( ! empty( $note ) ) { give_insert_payment_note( $payment_id, $note ); } } } add_action( 'give_paypal_web_accept', 'give_process_paypal_web_accept', 10, 2 ); /** * Process PayPal IPN Refunds * * @param array $data IPN Data * @param int $payment_id The payment ID. * * @return void * @since 1.0 * */ function give_process_paypal_refund( $data, $payment_id = 0 ) { // Collect payment details. if ( empty( $payment_id ) ) { return; } // Only refund payments once. if ( 'refunded' === get_post_status( $payment_id ) ) { return; } $payment_amount = give_donation_amount( $payment_id ); $refund_amount = $data['payment_gross'] * - 1; if ( number_format( (float) $refund_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) { give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal parent transaction ID */ __( 'Partial PayPal refund processed: %s', 'give' ), $data['parent_txn_id'] ) ); return; // This is a partial refund } give_insert_payment_note( $payment_id, sprintf( /* translators: 1: Paypal parent transaction ID 2. Paypal reason code */ __( 'PayPal Payment #%1$s Refunded for reason: %2$s', 'give' ), $data['parent_txn_id'], $data['reason_code'] ) ); give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal transaction ID */ __( 'PayPal Refund Transaction ID: %s', 'give' ), $data['txn_id'] ) ); give_update_payment_status( $payment_id, 'refunded' ); } /** * Get PayPal Redirect * * @param bool $ssl_check Is SSL? * * @return string * @since 1.0 * */ function give_get_paypal_redirect( $ssl_check = false ) { if ( is_ssl() || ! $ssl_check ) { $protocol = 'https://'; } else { $protocol = 'http://'; } // Check the current payment mode if ( give_is_test_mode() ) { // Test mode $paypal_uri = $protocol . 'www.sandbox.paypal.com/cgi-bin/webscr'; } else { // Live mode $paypal_uri = $protocol . 'www.paypal.com/cgi-bin/webscr'; } return apply_filters( 'give_paypal_uri', $paypal_uri ); } /** * Set the Page Style for offsite PayPal page. * * @return string * @since 1.0 */ function give_get_paypal_page_style() { $page_style = trim( give_get_option( 'paypal_page_style', 'PayPal' ) ); return apply_filters( 'give_paypal_page_style', $page_style ); } /** * PayPal Success Page * * Shows "Donation Processing" message for PayPal payments that are still pending on site return * * @param $content * * @return string * @since 1.0 * */ function give_paypal_success_page_content( $content ) { if ( ! isset( $_GET['payment-id'] ) && ! give_get_purchase_session() ) { return $content; } $payment_id = isset( $_GET['payment-id'] ) ? absint( $_GET['payment-id'] ) : false; if ( ! $payment_id ) { $session = give_get_purchase_session(); $payment_id = give_get_donation_id_by_key( $session['purchase_key'] ); } $payment = get_post( $payment_id ); if ( $payment && 'pending' === $payment->post_status ) { // Payment is still pending so show processing indicator to fix the race condition. ob_start(); give_get_template_part( 'payment', 'processing' ); $content = ob_get_clean(); } return $content; } add_filter( 'give_payment_confirm_paypal', 'give_paypal_success_page_content' ); /** * Given a transaction ID, generate a link to the PayPal transaction ID details * * @param string $transaction_id The Transaction ID * @param int $payment_id The payment ID for this transaction * * @return string A link to the PayPal transaction details * @since 1.0 * */ function give_paypal_link_transaction_id( $transaction_id, $payment_id ) { $paypal_base_url = 'https://history.paypal.com/cgi-bin/webscr?cmd=_history-details-from-hub&id='; $transaction_url = '<a href="' . esc_url( $paypal_base_url . $transaction_id ) . '" target="_blank">' . $transaction_id . '</a>'; return apply_filters( 'give_paypal_link_payment_details_transaction_id', $transaction_url ); } add_filter( 'give_payment_details_transaction_id-paypal', 'give_paypal_link_transaction_id', 10, 2 ); /** * Get pending donation note. * * @param $pending_reason * * @return string * @since 1.6.3 * */ function give_paypal_get_pending_donation_note( $pending_reason ) { $note = ''; switch ( $pending_reason ) { case 'echeck': $note = __( 'Payment made via eCheck and will clear automatically in 5-8 days.', 'give' ); break; case 'address': $note = __( 'Payment requires a confirmed donor address and must be accepted manually through PayPal.', 'give' ); break; case 'intl': $note = __( 'Payment must be accepted manually through PayPal due to international account regulations.', 'give' ); break; case 'multi-currency': $note = __( 'Payment received in non-shop currency and must be accepted manually through PayPal.', 'give' ); break; case 'paymentreview': case 'regulatory_review': $note = __( 'Payment is being reviewed by PayPal staff as high-risk or in possible violation of government regulations.', 'give' ); break; case 'unilateral': $note = __( 'Payment was sent to non-confirmed or non-registered email address.', 'give' ); break; case 'upgrade': $note = __( 'PayPal account must be upgraded before this payment can be accepted.', 'give' ); break; case 'verify': $note = __( 'PayPal account is not verified. Verify account in order to accept this donation.', 'give' ); break; case 'other': $note = __( 'Payment is pending for unknown reasons. Contact PayPal support for assistance.', 'give' ); break; } // End switch(). return apply_filters('give_paypal_get_pending_donation_note', $note); } /** * Build paypal url * * @param int $payment_id Payment ID * @param array $payment_data Array of payment data. * * @return mixed|string */ function give_build_paypal_url( $payment_id, $payment_data ) { // Only send to PayPal if the pending payment is created successfully. $listener_url = add_query_arg( 'give-listener', 'IPN', home_url( 'index.php' ) ); // Get the success url. $return_url = add_query_arg( array( 'payment-confirmation' => 'paypal', 'payment-id' => $payment_id, ), get_permalink( give_get_option( 'success_page' ) ) ); // Get the PayPal redirect uri. $paypal_redirect = trailingslashit( give_get_paypal_redirect() ) . '?'; // Item name. $item_name = give_payment_gateway_item_title( $payment_data ); // Setup PayPal API params. $paypal_args = array( 'business' => give_get_option( 'paypal_email', false ), 'first_name' => $payment_data['user_info']['first_name'], 'last_name' => $payment_data['user_info']['last_name'], 'email' => $payment_data['user_email'], 'invoice' => $payment_data['purchase_key'], 'amount' => $payment_data['price'], 'item_name' => stripslashes( $item_name ), 'no_shipping' => '1', 'shipping' => '0', 'no_note' => '1', 'currency_code' => give_get_currency( $payment_id, $payment_data ), 'charset' => get_bloginfo( 'charset' ), 'custom' => $payment_id, 'rm' => '2', 'return' => $return_url, 'cancel_return' => give_get_failed_transaction_uri( '?payment-id=' . $payment_id ), 'notify_url' => $listener_url, 'page_style' => give_get_paypal_page_style(), 'cbt' => get_bloginfo( 'name' ), 'bn' => 'givewp_SP', ); // Add user address if present. if ( ! empty( $payment_data['user_info']['address'] ) ) { $default_address = array( 'line1' => '', 'line2' => '', 'city' => '', 'state' => '', 'zip' => '', 'country' => '', ); $address = wp_parse_args( $payment_data['user_info']['address'], $default_address ); $paypal_args['address1'] = $address['line1']; $paypal_args['address2'] = $address['line2']; $paypal_args['city'] = $address['city']; $paypal_args['state'] = $address['state']; $paypal_args['zip'] = $address['zip']; $paypal_args['country'] = $address['country']; } // Donations or regular transactions? $paypal_args['cmd'] = give_get_paypal_button_type(); /** * Filter the paypal redirect args. * * @param array $paypal_args PayPal Arguments. * @param array $payment_data Payment Data. * * @since 1.8 */ $paypal_args = apply_filters( 'give_paypal_redirect_args', $paypal_args, $payment_data ); // Build query. $paypal_redirect .= http_build_query( $paypal_args ); // Fix for some sites that encode the entities. $paypal_redirect = str_replace( '&', '&', $paypal_redirect ); return $paypal_redirect; } /** * Get paypal button type. * * @return string * @since 1.8 */ function give_get_paypal_button_type() { // paypal_button_type can be donation or standard. $paypal_button_type = '_donations'; if ( 'standard' === give_get_option( 'paypal_button_type' ) ) { $paypal_button_type = '_xclick'; } return $paypal_button_type; } /** * Update Purchase key for specific gateway. * * @param string $custom_purchase_key * @param string $gateway * @param string $purchase_key * * @return string * @since 2.2.4 * */ function give_paypal_purchase_key( $custom_purchase_key, $gateway, $purchase_key ) { if ( 'paypal' === $gateway ) { $invoice_id_prefix = give_get_option( 'paypal_invoice_prefix', 'GIVE-' ); $custom_purchase_key = $invoice_id_prefix . $purchase_key; } return $custom_purchase_key; } add_filter( 'give_donation_purchase_key', 'give_paypal_purchase_key', 10, 3 ); /** * PayPal Standard Connect button. * * This uses Stripe's Connect button but swaps the link and logo with PayPal's. * * @return string * @since 2.5.0 * */ function give_paypal_connect_button() { // Prepare Stripe Connect URL. $link = admin_url( 'edit.php?post_type=give_forms&page=give-settings&tab=gateways§ion=paypal-standard' ); return sprintf( '<a href="%1$s" id="give-paypal-connect"><span>%2$s</span></a>', esc_url( $link ), esc_html__( 'Connect to PayPal', 'give' ) ); }