PayPal PDT handler Closes #3414

This commit is contained in:
Mike Jolley 2013-08-19 12:28:29 +01:00
parent 759e49a60b
commit 0b8dd0476c
2 changed files with 184 additions and 113 deletions

View File

@ -51,6 +51,7 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
$this->page_style = $this->get_option( 'page_style' );
$this->invoice_prefix = $this->get_option( 'invoice_prefix', 'WC-' );
$this->paymentaction = $this->get_option( 'paymentaction', 'sale' );
$this->identity_token = $this->get_option( 'identity_token', '' );
// Logs
if ( 'yes' == $this->debug )
@ -60,6 +61,7 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
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' ) );
add_action( 'woocommerce_thankyou_paypal', array( $this, 'pdt_return_handler' ) );
// Payment listener/API hook
add_action( 'woocommerce_api_wc_gateway_paypal', array( $this, 'check_ipn_response' ) );
@ -67,7 +69,6 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
if ( !$this->is_valid_for_use() ) $this->enabled = false;
}
/**
* Check if this gateway is enabled and available in the user's country
*
@ -107,7 +108,6 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
endif;
}
/**
* Initialise Gateway Settings Form Fields
*
@ -118,117 +118,123 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable PayPal standard', 'woocommerce' ),
'default' => 'yes'
),
'title' => __( 'Enable/Disable', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable PayPal standard', 'woocommerce' ),
'default' => 'yes'
),
'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,
),
'title' => __( 'Title', 'woocommerce' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
'default' => __( 'PayPal', 'woocommerce' ),
'desc_tip' => true,
),
'description' => array(
'title' => __( 'Description', 'woocommerce' ),
'type' => 'textarea',
'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce' ),
'default' => __( 'Pay via PayPal; you can pay with your credit card if you don\'t have a PayPal account', 'woocommerce' )
),
'title' => __( 'Description', 'woocommerce' ),
'type' => 'textarea',
'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce' ),
'default' => __( 'Pay via PayPal; you can pay with your credit card if you don\'t have a PayPal account', 'woocommerce' )
),
'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'
),
'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'
),
'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'
),
'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'
),
'identity_token' => array(
'title' => __( 'PayPal Identity Token', 'woocommerce' ),
'type' => 'text',
'description' => __( 'Optionally enable "Payment Data Transfer" (Profile > Website Payment Preferences) and then copy your identity token here. This will allow payments to be verified without the need for PayPal IPN.', 'woocommerce' ),
'default' => '',
'desc_tip' => true,
'placeholder' => __( 'Optional', 'woocommerce' )
),
'invoice_prefix' => array(
'title' => __( 'Invoice Prefix', 'woocommerce' ),
'type' => 'text',
'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,
),
'title' => __( 'Invoice Prefix', 'woocommerce' ),
'type' => 'text',
'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,
),
'paymentaction' => array(
'title' => __( 'Payment Action', 'woocommerce' ),
'type' => 'select',
'description' => __( 'Choose whether you wish to capture funds immediately or authorize payment only.', 'woocommerce' ),
'default' => 'sale',
'desc_tip' => true,
'options' => array(
'sale' => __( 'Capture', 'woocommerce' ),
'authorization' => __( 'Authorize', 'woocommerce' )
)
),
'title' => __( 'Payment Action', 'woocommerce' ),
'type' => 'select',
'description' => __( 'Choose whether you wish to capture funds immediately or authorize payment only.', 'woocommerce' ),
'default' => 'sale',
'desc_tip' => true,
'options' => array(
'sale' => __( 'Capture', 'woocommerce' ),
'authorization' => __( 'Authorize', 'woocommerce' )
)
),
'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'
),
'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' )
),
'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' => '',
),
'title' => __( 'Shipping options', 'woocommerce' ),
'type' => 'title',
'description' => '',
),
'send_shipping' => array(
'title' => __( 'Shipping details', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Send shipping details to PayPal instead of billing.', 'woocommerce' ),
'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'
),
'title' => __( 'Shipping details', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Send shipping details to PayPal instead of billing.', 'woocommerce' ),
'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'
),
'address_override' => array(
'title' => __( 'Address override', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable "address_override" to prevent address information from being changed.', 'woocommerce' ),
'description' => __( 'PayPal verifies addresses therefore this setting can cause errors (we recommend keeping it disabled).', 'woocommerce' ),
'default' => 'no'
),
'title' => __( 'Address override', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable "address_override" to prevent address information from being changed.', 'woocommerce' ),
'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' => '',
),
'title' => __( 'Gateway Testing', 'woocommerce' ),
'type' => 'title',
'description' => '',
),
'testmode' => array(
'title' => __( 'PayPal sandbox', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable PayPal sandbox', 'woocommerce' ),
'default' => 'no',
'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/' ),
),
'title' => __( 'PayPal sandbox', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable PayPal sandbox', 'woocommerce' ),
'default' => 'no',
'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' ),
'default' => 'no',
'description' => sprintf( __( 'Log PayPal events, such as IPN requests, inside <code>woocommerce/logs/paypal-%s.txt</code>', 'woocommerce' ), sanitize_file_name( wp_hash( 'paypal' ) ) ),
)
);
'title' => __( 'Debug Log', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable logging', 'woocommerce' ),
'default' => 'no',
'description' => sprintf( __( 'Log PayPal events, such as IPN requests, inside <code>woocommerce/logs/paypal-%s.txt</code>', 'woocommerce' ), sanitize_file_name( wp_hash( 'paypal' ) ) ),
)
);
}
/**
* Get PayPal Args for passing to PP
*
@ -409,7 +415,6 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
return $paypal_args;
}
/**
* Generate the paypal button link
*
@ -472,7 +477,6 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
}
/**
* Process the payment and return the result
*
@ -512,7 +516,6 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
}
/**
* Output for the order received page.
*
@ -524,7 +527,6 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
echo '<p>'.__( 'Thank you - your order is now pending payment. You should be automatically redirected to PayPal to make payment.', 'woocommerce' ).'</p>';
echo $this->generate_paypal_form( $order );
}
/**
@ -581,7 +583,6 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
return false;
}
/**
* Check for PayPal IPN Response
*
@ -606,7 +607,6 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
}
/**
* Successful Payment!
*
@ -622,9 +622,9 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
// Custom holds post ID
if ( ! empty( $posted['invoice'] ) && ! empty( $posted['custom'] ) ) {
$order = $this->get_paypal_order( $posted );
$order = $this->get_paypal_order( $posted['custom'], $posted['invoice'] );
if ( 'yes' == $this->debug )
if ( 'yes' == $this->debug )
$this->log->add( 'paypal', 'Found order #' . $order->id );
// Lowercase returned variables
@ -768,23 +768,93 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
}
/**
* Return handler
*
* Alternative to IPN
*/
public function pdt_return_handler() {
$posted = stripslashes_deep( $_REQUEST );
if ( ! empty( $this->identity_token ) && ! empty( $posted['cm'] ) ) {
$order = $this->get_paypal_order( $posted['cm'] );
if ( $order->status != 'pending' )
return;
$posted['st'] = strtolower( $posted['st'] );
switch ( $posted['st'] ) {
case 'completed' :
// Validate transaction
if ( $this->testmode == 'yes' )
$paypal_adr = $this->testurl;
else
$paypal_adr = $this->liveurl;
$pdt = array(
'body' => array(
'cmd' => '_notify-synch',
'tx' => $posted['tx'],
'at' => $this->identity_token
),
'sslverify' => false,
'timeout' => 60,
'httpversion' => '1.1',
'user-agent' => 'WooCommerce/' . WOOCOMMERCE_VERSION
);
// Post back to get a response
$response = wp_remote_post( $paypal_adr, $pdt );
if ( is_wp_error( $response ) )
return false;
if ( ! strpos( $response['body'], "SUCCESS" ) === 0 )
return false;
// Validate Amount
if ( $order->get_total() != $posted['amt'] ) {
if ( 'yes' == $this->debug )
$this->log->add( 'paypal', 'Payment error: Amounts do not match (amt ' . $posted['amt'] . ')' );
// Put this order on-hold for manual checking
$order->update_status( 'on-hold', sprintf( __( 'Validation error: PayPal amounts do not match (amt %s).', 'woocommerce' ), $posted['amt'] ) );
} else {
// Store PP Details
update_post_meta( $order->id, 'Transaction ID', $posted['tx'] );
$order->add_order_note( __( 'PDT payment completed', 'woocommerce' ) );
$order->payment_complete();
}
break;
}
}
}
/**
* get_paypal_order function.
*
* @access public
* @param mixed $posted
* @return void
* @param string $custom
* @param string $invoice
* @return WC_Order object
*/
function get_paypal_order( $posted ) {
$custom = maybe_unserialize( $posted['custom'] );
private function get_paypal_order( $custom, $invoice = '' ) {
$custom = maybe_unserialize( $custom );
// Backwards comp for IPN requests
if ( is_numeric( $custom ) ) {
$order_id = (int) $custom;
$order_key = $posted['invoice'];
$order_id = (int) $custom;
$order_key = $invoice;
} elseif( is_string( $custom ) ) {
$order_id = (int) str_replace( $this->invoice_prefix, '', $custom );
$order_id = (int) str_replace( $this->invoice_prefix, '', $custom );
$order_key = $custom;
} else {
list( $order_id, $order_key ) = $custom;

View File

@ -177,6 +177,7 @@ Yes you can! Join in on our [GitHub repository](http://github.com/woothemes/wooc
* Feature - Authorize option for paypal standard.
* Feature - Separate options for countries you can ship to and sell to.
* Feature - BACS supports multiple account details.
* Feature - PayPal PDT support (as alternative to IPN).
* Tweak - Added pagination to tax rate screens.
* Tweak - Added filter to check the 'Create account' checkbox on checkout by default.
* Tweak - Update CPT parameters for 'product_variation' and 'shop_coupon' to be no longer public.