woocommerce/classes/gateways/paypal/class-wc-gateway-paypal.php

787 lines
27 KiB
PHP
Raw Normal View History

2012-05-26 16:25:07 +00:00
<?php
2013-02-20 17:14:46 +00:00
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2012-05-26 16:25:07 +00:00
/**
* PayPal Standard Payment Gateway
*
2012-05-26 16:25:07 +00:00
* Provides a PayPal Standard Payment Gateway.
*
* @class WC_Paypal
* @extends WC_Gateway_Paypal
* @version 2.0.0
2012-08-15 18:15:06 +00:00
* @package WooCommerce/Classes/Payment
* @author WooThemes
2012-05-26 16:25:07 +00:00
*/
class WC_Gateway_Paypal extends WC_Payment_Gateway {
2012-11-27 16:22:47 +00:00
2012-09-12 12:36:34 +00:00
var $notify_url;
2012-11-27 16:22:47 +00:00
2012-08-15 18:15:06 +00:00
/**
* Constructor for the gateway.
*
* @access public
* @return void
*/
public function __construct() {
2012-05-26 16:25:07 +00:00
global $woocommerce;
$this->id = 'paypal';
$this->icon = apply_filters( 'woocommerce_paypal_icon', $woocommerce->plugin_url() . '/assets/images/icons/paypal.png' );
$this->has_fields = false;
$this->liveurl = 'https://www.paypal.com/cgi-bin/webscr';
$this->testurl = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
$this->method_title = __( 'PayPal', 'woocommerce' );
$this->notify_url = str_replace( 'https:', 'http:', add_query_arg( 'wc-api', 'WC_Gateway_Paypal', home_url( '/' ) ) );
2012-05-26 16:25:07 +00:00
// Load the settings.
2013-01-02 13:38:33 +00:00
$this->init_form_fields();
2012-05-26 16:25:07 +00:00
$this->init_settings();
2012-05-26 16:25:07 +00:00
// Define user set variables
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
$this->email = $this->get_option( 'email' );
2013-03-11 10:36:51 +00:00
$this->receiver_email = $this->get_option( 'receiver_email', $this->email );
$this->testmode = $this->get_option( 'testmode' );
$this->send_shipping = $this->get_option( 'send_shipping' );
$this->address_override = $this->get_option( 'address_override' );
$this->debug = $this->get_option( 'debug' );
$this->form_submission_method = $this->get_option( 'form_submission_method' ) == 'yes' ? true : false;
$this->page_style = $this->get_option( 'page_style' );
$this->invoice_prefix = $this->get_option( 'invoice_prefix', 'WC-' );
2012-05-26 16:25:07 +00:00
// Logs
if ( 'yes' == $this->debug )
$this->log = $woocommerce->logger();
2012-05-26 16:25:07 +00:00
// Actions
add_action( 'valid-paypal-standard-ipn-request', array( $this, 'successful_request' ) );
add_action( 'woocommerce_receipt_paypal', array( $this, 'receipt_page' ) );
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
2012-11-27 16:22:47 +00:00
2012-09-12 12:36:34 +00:00
// Payment listener/API hook
add_action( 'woocommerce_api_wc_gateway_paypal', array( $this, 'check_ipn_response' ) );
2012-05-26 16:25:07 +00:00
if ( !$this->is_valid_for_use() ) $this->enabled = false;
}
2012-08-15 18:15:06 +00:00
/**
2012-05-26 16:25:07 +00:00
* Check if this gateway is enabled and available in the user's country
2012-08-15 18:15:06 +00:00
*
* @access public
* @return bool
2012-05-26 16:25:07 +00:00
*/
function is_valid_for_use() {
if ( ! in_array( get_woocommerce_currency(), apply_filters( 'woocommerce_paypal_supported_currencies', array( 'AUD', 'BRL', 'CAD', 'MXN', 'NZD', 'HKD', 'SGD', 'USD', 'EUR', 'JPY', 'TRY', 'NOK', 'CZK', 'DKK', 'HUF', 'ILS', 'MYR', 'PHP', 'PLN', 'SEK', 'CHF', 'TWD', 'THB', 'GBP', 'RMB' ) ) ) ) return false;
2012-05-26 16:25:07 +00:00
return true;
}
2012-05-26 16:25:07 +00:00
/**
* Admin Panel Options
2012-05-26 16:25:07 +00:00
* - Options for bits like 'title' and availability on a country-by-country basis
*
* @since 1.0.0
*/
public function admin_options() {
?>
<h3><?php _e( 'PayPal standard', 'woocommerce' ); ?></h3>
<p><?php _e( 'PayPal standard works by sending the user to PayPal to enter their payment information.', 'woocommerce' ); ?></p>
<?php if ( $this->is_valid_for_use() ) : ?>
<table class="form-table">
<?php
2012-05-26 16:25:07 +00:00
// Generate the HTML For the settings form.
$this->generate_settings_html();
?>
</table><!--/.form-table-->
<?php else : ?>
<div class="inline error"><p><strong><?php _e( 'Gateway Disabled', 'woocommerce' ); ?></strong>: <?php _e( 'PayPal does not support your store currency.', 'woocommerce' ); ?></p></div>
<?php
endif;
}
2012-08-15 18:15:06 +00:00
/**
2012-05-26 16:25:07 +00:00
* Initialise Gateway Settings Form Fields
2012-08-15 18:15:06 +00:00
*
* @access public
* @return void
2012-05-26 16:25:07 +00:00
*/
function init_form_fields() {
2012-05-26 16:25:07 +00:00
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable PayPal standard', 'woocommerce' ),
2012-05-26 16:25:07 +00:00
'default' => 'yes'
),
2012-05-26 16:25:07 +00:00
'title' => array(
'title' => __( 'Title', 'woocommerce' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
'default' => __( 'PayPal', 'woocommerce' ),
'desc_tip' => true,
2012-05-26 16:25:07 +00:00
),
'description' => array(
'title' => __( 'Description', 'woocommerce' ),
'type' => 'textarea',
'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce' ),
2012-10-16 14:46:21 +00:00
'default' => __( 'Pay via PayPal; you can pay with your credit card if you don\'t have a PayPal account', 'woocommerce' )
2012-05-26 16:25:07 +00:00
),
'email' => array(
'title' => __( 'PayPal Email', 'woocommerce' ),
'type' => 'email',
'description' => __( 'Please enter your PayPal email address; this is needed in order to take payment.', 'woocommerce' ),
'default' => '',
'desc_tip' => true,
'placeholder' => 'you@youremail.com'
2012-05-26 16:25:07 +00:00
),
2013-03-11 10:36:51 +00:00
'receiver_email' => array(
'title' => __( 'Receiver Email', 'woocommerce' ),
'type' => 'email',
'description' => __( 'If this differs from the email entered above, input your main receiver email for your PayPal account. This is used to validate IPN requests.', 'woocommerce' ),
'default' => '',
'desc_tip' => true,
'placeholder' => 'you@youremail.com'
),
'invoice_prefix' => array(
'title' => __( 'Invoice Prefix', 'woocommerce' ),
'type' => 'text',
2013-03-03 17:07:31 +00:00
'description' => __( 'Please enter a prefix for your invoice numbers. If you use your PayPal account for multiple stores ensure this prefix is unique as PayPal will not allow orders with the same invoice number.', 'woocommerce' ),
'default' => 'WC-',
'desc_tip' => true,
),
'form_submission_method' => array(
'title' => __( 'Submission method', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Use form submission method.', 'woocommerce' ),
'description' => __( 'Enable this to post order data to PayPal via a form instead of using a redirect/querystring.', 'woocommerce' ),
'default' => 'no'
),
'page_style' => array(
'title' => __( 'Page Style', 'woocommerce' ),
'type' => 'text',
'description' => __( 'Optionally enter the name of the page style you wish to use. These are defined within your PayPal account.', 'woocommerce' ),
'default' => '',
'desc_tip' => true,
'placeholder' => __( 'Optional', 'woocommerce' )
),
'shipping' => array(
'title' => __( 'Shipping options', 'woocommerce' ),
'type' => 'title',
'description' => '',
),
2012-05-26 16:25:07 +00:00
'send_shipping' => array(
'title' => __( 'Shipping details', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Send shipping details to PayPal instead of billing.', 'woocommerce' ),
2012-05-26 16:25:07 +00:00
'description' => '',
'description' => __( 'PayPal allows us to send 1 address. If you are using PayPal for shipping labels you may prefer to send the shipping address rather than billing.', 'woocommerce' ),
'default' => 'no'
),
2012-05-26 16:25:07 +00:00
'address_override' => array(
'title' => __( 'Address override', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable "address_override" to prevent address information from being changed.', 'woocommerce' ),
2012-05-26 16:25:07 +00:00
'description' => __( 'PayPal verifies addresses therefore this setting can cause errors (we recommend keeping it disabled).', 'woocommerce' ),
'default' => 'no'
),
'testing' => array(
'title' => __( 'Gateway Testing', 'woocommerce' ),
'type' => 'title',
'description' => '',
2012-05-26 16:25:07 +00:00
),
'testmode' => array(
'title' => __( 'PayPal sandbox', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable PayPal sandbox', 'woocommerce' ),
2012-05-26 16:25:07 +00:00
'default' => 'yes',
'description' => sprintf( __( 'PayPal sandbox can be used to test payments. Sign up for a developer account <a href="%s">here</a>.', 'woocommerce' ), 'https://developer.paypal.com/' ),
),
'debug' => array(
'title' => __( 'Debug Log', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable logging', 'woocommerce' ),
2012-05-26 16:25:07 +00:00
'default' => 'no',
2013-04-17 15:03:23 +00:00
'description' => sprintf( __( 'Log PayPal events, such as IPN requests, inside <code>woocommerce/logs/paypal-%s.txt</code>', 'woocommerce' ), sanitize_file_name( wp_hash( 'paypal' ) ) ),
2012-05-26 16:25:07 +00:00
)
);
2012-08-15 18:15:06 +00:00
}
2012-08-15 18:15:06 +00:00
/**
2012-05-26 16:25:07 +00:00
* Get PayPal Args for passing to PP
2012-08-15 18:15:06 +00:00
*
* @access public
* @param mixed $order
* @return array
*/
2012-05-26 16:25:07 +00:00
function get_paypal_args( $order ) {
global $woocommerce;
2012-05-26 16:25:07 +00:00
$order_id = $order->id;
if ( 'yes' == $this->debug )
$this->log->add( 'paypal', 'Generating payment form for order ' . $order->get_order_number() . '. Notify URL: ' . $this->notify_url );
2012-10-20 14:40:27 +00:00
if ( in_array( $order->billing_country, array( 'US','CA' ) ) ) {
$order->billing_phone = str_replace( array( '( ', '-', ' ', ' )', '.' ), '', $order->billing_phone );
2012-05-26 16:25:07 +00:00
$phone_args = array(
2012-10-20 14:40:27 +00:00
'night_phone_a' => substr( $order->billing_phone, 0, 3 ),
'night_phone_b' => substr( $order->billing_phone, 3, 3 ),
'night_phone_c' => substr( $order->billing_phone, 6, 4 ),
'day_phone_a' => substr( $order->billing_phone, 0, 3 ),
'day_phone_b' => substr( $order->billing_phone, 3, 3 ),
'day_phone_c' => substr( $order->billing_phone, 6, 4 )
2012-05-26 16:25:07 +00:00
);
2012-10-20 14:40:27 +00:00
} else {
2012-05-26 16:25:07 +00:00
$phone_args = array(
'night_phone_b' => $order->billing_phone,
'day_phone_b' => $order->billing_phone
);
2012-10-20 14:40:27 +00:00
}
2012-05-26 16:25:07 +00:00
// PayPal Args
$paypal_args = array_merge(
array(
'cmd' => '_cart',
'business' => $this->email,
'no_note' => 1,
'currency_code' => get_woocommerce_currency(),
'charset' => 'UTF-8',
'rm' => is_ssl() ? 2 : 1,
'upload' => 1,
2012-08-21 12:49:07 +00:00
'return' => add_query_arg( 'utm_nooverride', '1', $this->get_return_url( $order ) ),
2012-05-26 16:25:07 +00:00
'cancel_return' => $order->get_cancel_order_url(),
'page_style' => $this->page_style,
// Order key + ID
'invoice' => $this->invoice_prefix . ltrim( $order->get_order_number(), '#' ),
'custom' => serialize( array( $order_id, $order->order_key ) ),
2012-05-26 16:25:07 +00:00
// IPN
2012-09-12 12:36:34 +00:00
'notify_url' => $this->notify_url,
2012-05-26 16:25:07 +00:00
// Billing Address info
'first_name' => $order->billing_first_name,
'last_name' => $order->billing_last_name,
'company' => $order->billing_company,
'address1' => $order->billing_address_1,
'address2' => $order->billing_address_2,
'city' => $order->billing_city,
'state' => $order->billing_state,
'zip' => $order->billing_postcode,
'country' => $order->billing_country,
'email' => $order->billing_email
),
2012-05-26 16:25:07 +00:00
$phone_args
);
2012-05-26 16:25:07 +00:00
// Shipping
if ( $this->send_shipping=='yes' ) {
$paypal_args['address_override'] = ( $this->address_override == 'yes' ) ? 1 : 0;
2012-05-26 16:25:07 +00:00
$paypal_args['no_shipping'] = 0;
2012-05-26 16:25:07 +00:00
// If we are sending shipping, send shipping address instead of billing
$paypal_args['first_name'] = $order->shipping_first_name;
$paypal_args['last_name'] = $order->shipping_last_name;
$paypal_args['company'] = $order->shipping_company;
$paypal_args['address1'] = $order->shipping_address_1;
$paypal_args['address2'] = $order->shipping_address_2;
$paypal_args['city'] = $order->shipping_city;
$paypal_args['state'] = $order->shipping_state;
$paypal_args['country'] = $order->shipping_country;
$paypal_args['zip'] = $order->shipping_postcode;
} else {
$paypal_args['no_shipping'] = 1;
}
2012-05-26 16:25:07 +00:00
// If prices include tax or have order discounts, send the whole order as a single item
if ( get_option( 'woocommerce_prices_include_tax' ) == 'yes' || $order->get_order_discount() > 0 || ( sizeof( $order->get_items() ) + sizeof( $order->get_fees() ) ) >= 9 ) {
2012-05-26 16:25:07 +00:00
// Discount
$paypal_args['discount_amount_cart'] = $order->get_order_discount();
2012-05-26 16:25:07 +00:00
// Don't pass items - paypal borks tax due to prices including tax. PayPal has no option for tax inclusive pricing sadly. Pass 1 item for the order items overall
$item_names = array();
2012-11-27 16:22:47 +00:00
if ( sizeof( $order->get_items() ) > 0 )
2012-10-20 14:40:27 +00:00
foreach ( $order->get_items() as $item )
2012-11-27 16:22:47 +00:00
if ( $item['qty'] )
2012-10-20 14:40:27 +00:00
$item_names[] = $item['name'] . ' x ' . $item['qty'];
2012-10-20 14:40:27 +00:00
$paypal_args['item_name_1'] = sprintf( __( 'Order %s' , 'woocommerce'), $order->get_order_number() ) . " - " . implode( ', ', $item_names );
2012-05-26 16:25:07 +00:00
$paypal_args['quantity_1'] = 1;
2012-10-20 14:40:27 +00:00
$paypal_args['amount_1'] = number_format( $order->get_total() - $order->get_shipping() - $order->get_shipping_tax() + $order->get_order_discount(), 2, '.', '' );
2012-05-26 16:25:07 +00:00
// Shipping Cost
// No longer using shipping_1 because
// a) paypal ignore it if *any* shipping rules are within paypal
2013-03-03 17:07:31 +00:00
// b) paypal ignore anything over 5 digits, so 999.99 is the max
// $paypal_args['shipping_1'] = number_format( $order->get_shipping() + $order->get_shipping_tax() , 2, '.', '' );
2012-10-20 14:40:27 +00:00
if ( ( $order->get_shipping() + $order->get_shipping_tax() ) > 0 ) {
$paypal_args['item_name_2'] = __( 'Shipping via', 'woocommerce' ) . ' ' . ucwords( $order->shipping_method_title );
2012-05-26 16:25:07 +00:00
$paypal_args['quantity_2'] = '1';
$paypal_args['amount_2'] = number_format( $order->get_shipping() + $order->get_shipping_tax() , 2, '.', '' );
2012-10-20 14:40:27 +00:00
}
2012-10-20 14:40:27 +00:00
} else {
2012-05-26 16:25:07 +00:00
// Tax
$paypal_args['tax_cart'] = $order->get_total_tax();
// Cart Contents
$item_loop = 0;
2012-10-20 14:40:27 +00:00
if ( sizeof( $order->get_items() ) > 0 ) {
foreach ( $order->get_items() as $item ) {
if ( $item['qty'] ) {
2012-11-27 16:22:47 +00:00
2012-10-20 14:40:27 +00:00
$item_loop++;
2012-11-27 16:22:47 +00:00
2012-10-20 14:40:27 +00:00
$product = $order->get_product_from_item( $item );
2012-11-27 16:22:47 +00:00
2012-10-20 14:40:27 +00:00
$item_name = $item['name'];
2012-11-27 16:22:47 +00:00
2012-10-20 14:40:27 +00:00
$item_meta = new WC_Order_Item_Meta( $item['item_meta'] );
if ( $meta = $item_meta->display( true, true ) )
$item_name .= ' ( ' . $meta . ' )';
2012-11-27 16:22:47 +00:00
2012-10-20 14:40:27 +00:00
$paypal_args[ 'item_name_' . $item_loop ] = $item_name;
$paypal_args[ 'quantity_' . $item_loop ] = $item['qty'];
$paypal_args[ 'amount_' . $item_loop ] = $order->get_item_subtotal( $item, false );
2012-11-27 16:22:47 +00:00
if ( $product->get_sku() )
2012-10-20 14:40:27 +00:00
$paypal_args[ 'item_number_' . $item_loop ] = $product->get_sku();
}
}
}
2012-11-27 16:22:47 +00:00
// Discount
if ( $order->get_cart_discount() > 0 )
$paypal_args['discount_amount_cart'] = round( $order->get_cart_discount(), 2 );
2012-11-12 17:15:54 +00:00
// Fees
if ( sizeof( $order->get_fees() ) > 0 ) {
foreach ( $order->get_fees() as $item ) {
$item_loop++;
2012-11-27 16:22:47 +00:00
2012-11-12 17:15:54 +00:00
$paypal_args[ 'item_name_' . $item_loop ] = $item['name'];
$paypal_args[ 'quantity_' . $item_loop ] = 1;
$paypal_args[ 'amount_' . $item_loop ] = $item['line_total'];
}
}
2012-05-26 16:25:07 +00:00
// Shipping Cost item - paypal only allows shipping per item, we want to send shipping for the order
2012-10-20 14:40:27 +00:00
if ( $order->get_shipping() > 0 ) {
2012-05-26 16:25:07 +00:00
$item_loop++;
2012-10-20 14:40:27 +00:00
$paypal_args[ 'item_name_' . $item_loop ] = __( 'Shipping via', 'woocommerce' ) . ' ' . ucwords( $order->shipping_method_title );
$paypal_args[ 'quantity_' . $item_loop ] = '1';
$paypal_args[ 'amount_' . $item_loop ] = number_format( $order->get_shipping(), 2, '.', '' );
}
2012-10-20 14:40:27 +00:00
}
2012-05-26 16:25:07 +00:00
$paypal_args = apply_filters( 'woocommerce_paypal_args', $paypal_args );
2012-05-26 16:25:07 +00:00
return $paypal_args;
}
2012-08-15 18:15:06 +00:00
/**
2012-05-26 16:25:07 +00:00
* Generate the paypal button link
2012-08-15 18:15:06 +00:00
*
* @access public
* @param mixed $order_id
* @return string
*/
2012-05-26 16:25:07 +00:00
function generate_paypal_form( $order_id ) {
global $woocommerce;
2012-05-26 16:25:07 +00:00
$order = new WC_Order( $order_id );
if ( $this->testmode == 'yes' ):
$paypal_adr = $this->testurl . '?test_ipn=1&';
2012-05-26 16:25:07 +00:00
else :
$paypal_adr = $this->liveurl . '?';
2012-05-26 16:25:07 +00:00
endif;
2012-05-26 16:25:07 +00:00
$paypal_args = $this->get_paypal_args( $order );
2012-05-26 16:25:07 +00:00
$paypal_args_array = array();
foreach ($paypal_args as $key => $value) {
$paypal_args_array[] = '<input type="hidden" name="'.esc_attr( $key ).'" value="'.esc_attr( $value ).'" />';
}
2012-10-20 14:40:27 +00:00
$woocommerce->add_inline_js( '
jQuery("body").block({
message: "' . esc_js( __( 'Thank you for your order. We are now redirecting you to PayPal to make payment.', 'woocommerce' ) ) . '",
baseZ: 99999,
overlayCSS:
{
background: "#fff",
opacity: 0.6
2012-05-26 16:25:07 +00:00
},
css: {
padding: "20px",
zindex: "9999999",
textAlign: "center",
color: "#555",
border: "3px solid #aaa",
backgroundColor:"#fff",
2012-05-26 16:25:07 +00:00
cursor: "wait",
lineHeight: "24px",
}
2012-05-26 16:25:07 +00:00
});
jQuery("#submit_paypal_payment_form").click();
2012-10-20 14:40:27 +00:00
' );
2012-05-26 16:25:07 +00:00
return '<form action="'.esc_url( $paypal_adr ).'" method="post" id="paypal_payment_form" target="_top">
2012-10-20 14:40:27 +00:00
' . implode( '', $paypal_args_array) . '
<input type="submit" class="button alt" id="submit_paypal_payment_form" value="' . __( 'Pay via PayPal', 'woocommerce' ) . '" /> <a class="button cancel" href="'.esc_url( $order->get_cancel_order_url() ).'">'.__( 'Cancel order &amp; restore cart', 'woocommerce' ).'</a>
2012-05-26 16:25:07 +00:00
</form>';
2012-05-26 16:25:07 +00:00
}
2012-08-15 18:15:06 +00:00
/**
* Process the payment and return the result
*
* @access public
* @param int $order_id
* @return array
*/
2012-05-26 16:25:07 +00:00
function process_payment( $order_id ) {
2012-05-26 16:25:07 +00:00
$order = new WC_Order( $order_id );
2012-05-26 16:25:07 +00:00
if ( ! $this->form_submission_method ) {
2012-05-26 16:25:07 +00:00
$paypal_args = $this->get_paypal_args( $order );
2012-05-26 16:25:07 +00:00
$paypal_args = http_build_query( $paypal_args, '', '&' );
2012-05-26 16:25:07 +00:00
if ( $this->testmode == 'yes' ):
$paypal_adr = $this->testurl . '?test_ipn=1&';
2012-05-26 16:25:07 +00:00
else :
$paypal_adr = $this->liveurl . '?';
2012-05-26 16:25:07 +00:00
endif;
2012-05-26 16:25:07 +00:00
return array(
'result' => 'success',
'redirect' => $paypal_adr . $paypal_args
);
2012-05-26 16:25:07 +00:00
} else {
2012-05-26 16:25:07 +00:00
return array(
'result' => 'success',
2012-10-20 14:40:27 +00:00
'redirect' => add_query_arg('order', $order->id, add_query_arg('key', $order->order_key, get_permalink(woocommerce_get_page_id('pay' ))))
2012-05-26 16:25:07 +00:00
);
2012-05-26 16:25:07 +00:00
}
2012-05-26 16:25:07 +00:00
}
2012-08-15 18:15:06 +00:00
/**
* Output for the order received page.
*
* @access public
* @return void
*/
2012-05-26 16:25:07 +00:00
function receipt_page( $order ) {
2012-10-16 09:45:33 +00:00
echo '<p>'.__( 'Thank you for your order, please click the button below to pay with PayPal.', 'woocommerce' ).'</p>';
2012-05-26 16:25:07 +00:00
echo $this->generate_paypal_form( $order );
2012-05-26 16:25:07 +00:00
}
2012-05-26 16:25:07 +00:00
/**
* Check PayPal IPN validity
**/
function check_ipn_request_is_valid() {
global $woocommerce;
if ( 'yes' == $this->debug )
2012-09-12 11:08:35 +00:00
$this->log->add( 'paypal', 'Checking IPN response is valid...' );
2012-05-26 16:25:07 +00:00
// Get recieved values from post data
$received_values = array( 'cmd' => '_notify-validate' );
$received_values += stripslashes_deep( $_POST );
2012-05-26 16:25:07 +00:00
// Send back post vars to paypal
$params = array(
2012-05-26 16:25:07 +00:00
'body' => $received_values,
'sslverify' => false,
'timeout' => 60,
2012-05-26 16:25:07 +00:00
'user-agent' => 'WooCommerce/' . $woocommerce->version
);
if ( 'yes' == $this->debug )
$this->log->add( 'paypal', 'IPN Request: ' . print_r( $params, true ) );
2012-05-26 16:25:07 +00:00
// Get url
2012-09-12 11:08:35 +00:00
if ( $this->testmode == 'yes' )
$paypal_adr = $this->testurl;
2012-09-12 11:08:35 +00:00
else
$paypal_adr = $this->liveurl;
2012-05-26 16:25:07 +00:00
// Post back to get a response
$response = wp_remote_post( $paypal_adr, $params );
if ( 'yes' == $this->debug )
2012-09-12 11:08:35 +00:00
$this->log->add( 'paypal', 'IPN Response: ' . print_r( $response, true ) );
2012-05-26 16:25:07 +00:00
// check to see if the request was valid
2012-09-12 11:08:35 +00:00
if ( ! is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 && ( strcmp( $response['body'], "VERIFIED" ) == 0 ) ) {
if ( 'yes' == $this->debug )
2012-09-12 11:08:35 +00:00
$this->log->add( 'paypal', 'Received valid response from PayPal' );
2012-11-27 16:22:47 +00:00
2012-05-26 16:25:07 +00:00
return true;
}
if ( 'yes' == $this->debug ) {
2012-05-26 16:25:07 +00:00
$this->log->add( 'paypal', 'Received invalid response from PayPal' );
2012-09-12 11:08:35 +00:00
if ( is_wp_error( $response ) )
$this->log->add( 'paypal', 'Error response: ' . $response->get_error_message() );
2012-09-12 11:08:35 +00:00
}
2012-05-26 16:25:07 +00:00
return false;
}
2012-08-15 18:15:06 +00:00
2012-05-26 16:25:07 +00:00
/**
* Check for PayPal IPN Response
2012-08-15 18:15:06 +00:00
*
* @access public
* @return void
*/
2012-05-26 16:25:07 +00:00
function check_ipn_response() {
2012-09-12 12:36:34 +00:00
@ob_clean();
if ( ! empty( $_POST ) && $this->check_ipn_request_is_valid() ) {
2012-10-20 14:40:27 +00:00
header( 'HTTP/1.1 200 OK' );
2012-09-12 12:36:34 +00:00
do_action( "valid-paypal-standard-ipn-request", $_POST );
2012-09-12 12:36:34 +00:00
} else {
2012-09-12 12:36:34 +00:00
wp_die( "PayPal IPN Request Failure" );
2012-09-12 12:36:34 +00:00
}
2012-05-26 16:25:07 +00:00
}
2012-08-15 18:15:06 +00:00
2012-05-26 16:25:07 +00:00
/**
* Successful Payment!
2012-08-15 18:15:06 +00:00
*
* @access public
* @param array $posted
* @return void
*/
2012-05-26 16:25:07 +00:00
function successful_request( $posted ) {
2012-07-17 14:36:08 +00:00
global $woocommerce;
$posted = stripslashes_deep( $posted );
2012-05-26 16:25:07 +00:00
// Custom holds post ID
2012-09-12 11:08:35 +00:00
if ( ! empty( $posted['invoice'] ) && ! empty( $posted['custom'] ) ) {
2012-11-27 16:22:47 +00:00
2012-09-12 11:08:35 +00:00
$order = $this->get_paypal_order( $posted );
2012-11-27 16:22:47 +00:00
if ( 'yes' == $this->debug )
$this->log->add( 'paypal', 'Found order #' . $order->id );
2012-09-12 11:08:35 +00:00
// Lowercase returned variables
$posted['payment_status'] = strtolower( $posted['payment_status'] );
$posted['txn_type'] = strtolower( $posted['txn_type'] );
2012-11-27 16:22:47 +00:00
2013-03-27 22:39:55 +00:00
// Sandbox fix
if ( $posted['test_ipn'] == 1 && $posted['payment_status'] == 'pending' )
$posted['payment_status'] = 'completed';
if ( 'yes' == $this->debug )
2012-09-12 11:08:35 +00:00
$this->log->add( 'paypal', 'Payment status: ' . $posted['payment_status'] );
2012-05-26 16:25:07 +00:00
// We are here so lets check status and do actions
2012-09-12 11:08:35 +00:00
switch ( $posted['payment_status'] ) {
2012-05-26 16:25:07 +00:00
case 'completed' :
case 'pending' :
2012-05-26 16:25:07 +00:00
// Check order not already completed
2012-09-12 11:08:35 +00:00
if ( $order->status == 'completed' ) {
if ( 'yes' == $this->debug )
$this->log->add( 'paypal', 'Aborting, Order #' . $order->id . ' is already complete.' );
2012-05-26 16:25:07 +00:00
exit;
2012-09-12 11:08:35 +00:00
}
2012-05-26 16:25:07 +00:00
// Check valid txn_type
2012-09-12 11:08:35 +00:00
$accepted_types = array( 'cart', 'instant', 'express_checkout', 'web_accept', 'masspay', 'send_money' );
if ( ! in_array( $posted['txn_type'], $accepted_types ) ) {
if ( 'yes' == $this->debug )
2012-09-12 11:08:35 +00:00
$this->log->add( 'paypal', 'Aborting, Invalid type:' . $posted['txn_type'] );
2012-05-26 16:25:07 +00:00
exit;
2012-09-12 11:08:35 +00:00
}
2012-11-27 16:22:47 +00:00
2012-09-12 11:08:35 +00:00
// Validate Amount
2012-09-12 11:48:30 +00:00
if ( $order->get_total() != $posted['mc_gross'] ) {
2012-11-27 16:22:47 +00:00
if ( 'yes' == $this->debug )
2012-09-12 11:48:30 +00:00
$this->log->add( 'paypal', 'Payment error: Amounts do not match (gross ' . $posted['mc_gross'] . ')' );
2012-11-27 16:22:47 +00:00
2012-09-12 11:08:35 +00:00
// Put this order on-hold for manual checking
2012-09-12 12:09:54 +00:00
$order->update_status( 'on-hold', sprintf( __( 'Validation error: PayPal amounts do not match (gross %s).', 'woocommerce' ), $posted['mc_gross'] ) );
2012-11-27 16:22:47 +00:00
2012-09-12 11:09:16 +00:00
exit;
2012-11-27 16:22:47 +00:00
}
// Validate Email Address
2013-03-11 10:36:51 +00:00
if ( strcasecmp( trim( $posted['receiver_email'] ), trim( $this->receiver_email ) ) != 0 ) {
if ( 'yes' == $this->debug )
2013-03-11 10:36:51 +00:00
$this->log->add( 'paypal', "IPN Response is for another one: {$posted['receiver_email']} our email is {$this->receiver_email}" );
// Put this order on-hold for manual checking
$order->update_status( 'on-hold', sprintf( __( 'Validation error: PayPal IPN response from a different email address (%s).', 'woocommerce' ), $posted['receiver_email'] ) );
exit;
}
2012-05-26 16:25:07 +00:00
// Store PP Details
if ( ! empty( $posted['payer_email'] ) )
update_post_meta( $order->id, 'Payer PayPal address', $posted['payer_email'] );
if ( ! empty( $posted['txn_id'] ) )
update_post_meta( $order->id, 'Transaction ID', $posted['txn_id'] );
if ( ! empty( $posted['first_name'] ) )
update_post_meta( $order->id, 'Payer first name', $posted['first_name'] );
if ( ! empty( $posted['last_name'] ) )
update_post_meta( $order->id, 'Payer last name', $posted['last_name'] );
if ( ! empty( $posted['payment_type'] ) )
update_post_meta( $order->id, 'Payment type', $posted['payment_type'] );
if ( $posted['payment_status'] == 'completed' ) {
$order->add_order_note( __( 'IPN payment completed', 'woocommerce' ) );
$order->payment_complete();
} else {
$order->update_status( 'on-hold', sprintf( __( 'Payment pending: %s', 'woocommerce' ), $posted['pending_reason'] ) );
}
if ( 'yes' == $this->debug )
2012-09-12 11:08:35 +00:00
$this->log->add( 'paypal', 'Payment complete.' );
2012-05-26 16:25:07 +00:00
break;
case 'denied' :
case 'expired' :
case 'failed' :
case 'voided' :
// Order failed
2012-09-12 11:08:35 +00:00
$order->update_status( 'failed', sprintf( __( 'Payment %s via IPN.', 'woocommerce' ), strtolower( $posted['payment_status'] ) ) );
2012-05-26 16:25:07 +00:00
break;
case "refunded" :
2012-05-26 16:25:07 +00:00
// Only handle full refunds, not partial
2012-09-12 11:08:35 +00:00
if ( $order->get_total() == ( $posted['mc_gross'] * -1 ) ) {
2012-05-26 16:25:07 +00:00
// Mark order as refunded
2012-09-12 11:08:35 +00:00
$order->update_status( 'refunded', sprintf( __( 'Payment %s via IPN.', 'woocommerce' ), strtolower( $posted['payment_status'] ) ) );
2012-07-17 14:36:08 +00:00
$mailer = $woocommerce->mailer();
2012-11-06 11:31:31 +00:00
$message = $mailer->wrap_message(
2012-10-16 09:45:33 +00:00
__( 'Order refunded/reversed', 'woocommerce' ),
sprintf( __( 'Order %s has been marked as refunded - PayPal reason code: %s', 'woocommerce' ), $order->get_order_number(), $posted['reason_code'] )
2012-05-26 16:25:07 +00:00
);
2013-01-10 16:03:56 +00:00
$mailer->send( get_option( 'admin_email' ), sprintf( __( 'Payment for order %s refunded/reversed', 'woocommerce' ), $order->get_order_number() ), $message );
2012-05-26 16:25:07 +00:00
}
2012-05-26 16:25:07 +00:00
break;
case "reversed" :
case "chargeback" :
2012-05-26 16:25:07 +00:00
// Mark order as refunded
2012-10-16 09:45:33 +00:00
$order->update_status( 'refunded', sprintf( __( 'Payment %s via IPN.', 'woocommerce' ), strtolower( $posted['payment_status'] ) ) );
2012-07-17 14:36:08 +00:00
$mailer = $woocommerce->mailer();
2012-11-06 11:31:31 +00:00
$message = $mailer->wrap_message(
2012-10-16 09:45:33 +00:00
__( 'Order refunded/reversed', 'woocommerce' ),
sprintf(__( 'Order %s has been marked as refunded - PayPal reason code: %s', 'woocommerce' ), $order->get_order_number(), $posted['reason_code'] )
2012-05-26 16:25:07 +00:00
);
2013-01-10 16:03:56 +00:00
$mailer->send( get_option( 'admin_email' ), sprintf( __( 'Payment for order %s refunded/reversed', 'woocommerce' ), $order->get_order_number() ), $message );
2012-05-26 16:25:07 +00:00
break;
2012-09-12 11:08:35 +00:00
default :
2012-05-26 16:25:07 +00:00
// No action
break;
2012-09-12 11:08:35 +00:00
}
2012-05-26 16:25:07 +00:00
exit;
}
2012-05-26 16:25:07 +00:00
}
2012-11-27 16:22:47 +00:00
2012-09-12 12:05:53 +00:00
2012-09-12 11:08:35 +00:00
/**
* get_paypal_order function.
2012-11-27 16:22:47 +00:00
*
2012-09-12 11:08:35 +00:00
* @access public
2012-09-12 12:05:53 +00:00
* @param mixed $posted
2012-09-12 11:08:35 +00:00
* @return void
*/
2012-09-12 12:05:53 +00:00
function get_paypal_order( $posted ) {
$custom = maybe_unserialize( $posted['custom'] );
// Backwards comp for IPN requests
if ( is_numeric( $custom ) ) {
$order_id = (int) $custom;
$order_key = $posted['invoice'];
} elseif( is_string( $custom ) ) {
$order_id = (int) str_replace( $this->invoice_prefix, '', $custom );
$order_key = $custom;
2012-09-12 11:08:35 +00:00
} else {
list( $order_id, $order_key ) = $custom;
}
2012-09-12 11:08:35 +00:00
$order = new WC_Order( $order_id );
2012-11-27 16:22:47 +00:00
if ( ! isset( $order->id ) ) {
2012-09-12 11:08:35 +00:00
// We have an invalid $order_id, probably because invoice_prefix has changed
$order_id = woocommerce_get_order_id_by_order_key( $order_key );
$order = new WC_Order( $order_id );
}
2012-11-27 16:22:47 +00:00
2012-09-12 11:08:35 +00:00
// Validate key
if ( $order->order_key !== $order_key ) {
2012-11-27 16:22:47 +00:00
if ( $this->debug=='yes' )
2012-09-12 11:08:35 +00:00
$this->log->add( 'paypal', 'Error: Order Key does not match invoice.' );
exit;
}
2012-11-27 16:22:47 +00:00
2012-09-12 11:08:35 +00:00
return $order;
}
2012-05-26 16:25:07 +00:00
}
class WC_Paypal extends WC_Gateway_Paypal {
public function __construct() {
_deprecated_function( 'WC_Paypal', '1.4', 'WC_Gateway_Paypal' );
parent::__construct();
}
}