WooCommerce PayPal Express Checkout Payment Gateway - Version 1.2.0

Version Description

  • Fix - Prevent conflict with other gateways.
  • Fix - Compatibility with WooCommerce 3.0, including ensuring the customer address is saved correctly.
Download this release

Release Info

Developer woothemes
Plugin Icon 128x128 WooCommerce PayPal Express Checkout Payment Gateway
Version 1.2.0
Comparing to
See all releases

Code changes from version 1.1.2 to 1.2.0

Files changed (31) hide show
  1. assets/css/wc-gateway-ppec-frontend-cart.css +1 -0
  2. assets/js/wc-gateway-ppec-admin-settings.js +0 -177
  3. assets/js/wc-gateway-ppec-admin.js +0 -49
  4. assets/js/wc-gateway-ppec-frontend-checkout.js +0 -47
  5. assets/js/wc-gateway-ppec-frontend-in-context-checkout.js +17 -14
  6. includes/abstracts/abstract-wc-gateway-ppec-paypal-request-handler.php +13 -2
  7. includes/abstracts/abstract-wc-gateway-ppec.php +121 -98
  8. includes/class-wc-gateway-ppec-admin-handler.php +68 -12
  9. includes/class-wc-gateway-ppec-cart-handler.php +29 -269
  10. includes/class-wc-gateway-ppec-checkout-details.php +3 -0
  11. includes/class-wc-gateway-ppec-checkout-handler.php +460 -221
  12. includes/class-wc-gateway-ppec-client.php +781 -40
  13. includes/class-wc-gateway-ppec-gateway-loader.php +24 -2
  14. includes/class-wc-gateway-ppec-ipn-handler.php +47 -34
  15. includes/class-wc-gateway-ppec-ips-handler.php +10 -11
  16. includes/class-wc-gateway-ppec-payment-details.php +2 -1
  17. includes/class-wc-gateway-ppec-plugin.php +22 -1
  18. includes/class-wc-gateway-ppec-session-data.php +77 -19
  19. includes/class-wc-gateway-ppec-settings.php +198 -104
  20. includes/class-wc-gateway-ppec-with-paypal-addons.php +204 -0
  21. includes/class-wc-gateway-ppec-with-paypal-credit.php +0 -22
  22. includes/class-wc-gateway-ppec-with-paypal.php +1 -0
  23. includes/exceptions/class-wc-gateway-ppec-api-exception.php +41 -7
  24. includes/exceptions/class-wc-gateway-ppec-missing-session-exception.php +15 -2
  25. includes/functions.php +16 -6
  26. includes/settings/settings-ppec.php +138 -56
  27. includes/views/admin-settings.php +0 -581
  28. phpcs.xml +10 -0
  29. phpunit.xml +16 -0
  30. readme.txt +11 -2
  31. woocommerce-gateway-paypal-express-checkout.php +6 -6
assets/css/wc-gateway-ppec-frontend-cart.css CHANGED
@@ -12,6 +12,7 @@
12
  display: block;
13
  text-decoration: none !important;
14
  border: 0 !important;
 
15
  }
16
  .wcppec-checkout-buttons__button img {
17
  margin: 0 auto;
12
  display: block;
13
  text-decoration: none !important;
14
  border: 0 !important;
15
+ padding-top: 1em;
16
  }
17
  .wcppec-checkout-buttons__button img {
18
  margin: 0 auto;
assets/js/wc-gateway-ppec-admin-settings.js DELETED
@@ -1,177 +0,0 @@
1
- jQuery(document).ready(function() {
2
-
3
- /**
4
- * Get active environment.
5
- *
6
- * @return {String}
7
- */
8
- function get_active_env() {
9
- return jQuery( '#woo_pp_environment option' ).filter( ':selected' ).val();
10
- }
11
-
12
- /**
13
- * Get row class of active environment.
14
- *
15
- * @return {String}
16
- */
17
- function get_active_env_row_class() {
18
- return 'woo_pp_' + get_active_env();
19
- }
20
-
21
- /**
22
- * Get row class to hide based on active environment.
23
- *
24
- * @return {String}
25
- */
26
- function get_row_class_to_hide() {
27
- if ( 'live' === get_active_env() ) {
28
- return 'woo_pp_sandbox';
29
- }
30
-
31
- return 'woo_pp_live';
32
- }
33
-
34
- /**
35
- * Get API style of active environment.
36
- *
37
- * @return {String}
38
- */
39
- function get_active_env_api_style() {
40
- return jQuery( '#' + get_active_env_row_class() + '_api_style option' ).filter( ':selected' ).val();
41
- }
42
-
43
- /**
44
- * Get API style class to hide based on active API style.
45
- *
46
- * @return {String}
47
- */
48
- function get_api_style_class_to_hide() {
49
- var api_style_to_hide = 'signature';
50
-
51
- if ( 'signature' === get_active_env_api_style() ) {
52
- api_style_to_hide = 'certificate';
53
- }
54
-
55
- return get_active_env_row_class() + '_' + api_style_to_hide;
56
- }
57
-
58
- /**
59
- * Checks whether IPS is enabled or not.
60
- *
61
- * @return {Bool}
62
- */
63
- function is_ips_enabled() {
64
- return jQuery( '.ips-enabled' ).length > 0;
65
- }
66
-
67
- /**
68
- * Hide API credential fields if IPS is enabled.
69
- */
70
- function maybe_hide_api_credential_fields() {
71
- if ( is_ips_enabled() ) {
72
- jQuery( '.api-credential-row' ).hide();
73
- }
74
- }
75
-
76
- /**
77
- * Event handler to toggle API credential fields.
78
- *
79
- * @param {Object} e
80
- */
81
- function toggle_api_credential_fields_handler( e ) {
82
- var a = jQuery( e.target ),
83
- isHidden = jQuery( e.target ).hasClass( 'api-credential-fields-hidden' );
84
-
85
- if ( isHidden ) {
86
- jQuery( '.' + get_active_env_row_class() + '.api-credential-row' ).show();
87
- jQuery( '.' + get_active_env_row_class() + '_' + get_active_env_api_style() + '.api-credential-row' ).show();
88
-
89
- a.text( a.data( 'hide-text' ) );
90
- } else {
91
- jQuery( '.' + get_active_env_row_class() + '.api-credential-row' ).hide();
92
- jQuery( '.' + get_active_env_row_class() + '_' + get_active_env_api_style() + '.api-credential-row' ).hide();
93
-
94
- a.text( a.data( 'show-text' ) );
95
- }
96
-
97
- a.toggleClass( 'api-credential-fields-hidden' );
98
-
99
- e.preventDefault();
100
- }
101
-
102
- jQuery( '.toggle-api-credential-fields' ).click( toggle_api_credential_fields_handler );
103
-
104
- var env_to_hide = get_row_class_to_hide();
105
-
106
- jQuery( '.' + env_to_hide ).hide();
107
- jQuery( '.' + env_to_hide + '_signature' ).hide();
108
- jQuery( '.' + env_to_hide + '_certificate' ).hide();
109
-
110
- jQuery( '.' + get_api_style_class_to_hide() ).hide();
111
-
112
- maybe_hide_api_credential_fields();
113
-
114
- jQuery( '#woo_pp_environment' ).change(function() {
115
- var env = jQuery( '#woo_pp_environment option' ).filter( ':selected' ).val();
116
- var env_to_hide = '';
117
- var env_to_show = '';
118
- if ( 'live' == env ) {
119
- env_to_hide = 'sandbox';
120
- env_to_show = 'live';
121
- } else {
122
- env_to_hide = 'live';
123
- env_to_show = 'sandbox';
124
- }
125
-
126
- jQuery( '.woo_pp_' + env_to_hide ).hide();
127
- jQuery( '.woo_pp_' + env_to_hide + '_signature' ).hide();
128
- jQuery( '.woo_pp_' + env_to_hide + '_certificate' ).hide();
129
-
130
- jQuery( '.woo_pp_' + env_to_show ).show();
131
-
132
- var style = jQuery( '#woo_pp_' + env_to_show + '_api_style option' ).filter( ':selected' ).val();
133
- var style_to_hide = '';
134
- if ( 'signature' == style ) {
135
- style_to_hide = 'certificate';
136
- } else {
137
- style_to_hide = 'signature';
138
- }
139
-
140
- jQuery( '.woo_pp_' + env_to_show + '_' + style_to_hide ).hide();
141
- jQuery( '.woo_pp_' + env_to_show + '_' + style ).show();
142
-
143
- maybe_hide_api_credential_fields();
144
-
145
- var apiCredentialsToggler = jQuery( '.toggle-api-credential-fields' );
146
- apiCredentialsToggler.addClass( 'api-credential-fields-hidden' ).text( apiCredentialsToggler.data( 'show-text' ) );
147
- });
148
-
149
- jQuery( '#woo_pp_live_api_style' ).change(function() {
150
- var style = jQuery( '#woo_pp_live_api_style option' ).filter( ':selected' ).val();
151
- var style_to_hide = '';
152
-
153
- if ( 'signature' == style ) {
154
- style_to_hide = 'certificate';
155
- } else {
156
- style_to_hide = 'signature';
157
- }
158
-
159
- jQuery( '.woo_pp_live_' + style_to_hide ).hide();
160
- jQuery( '.woo_pp_live_' + style ).show();
161
- });
162
-
163
- jQuery( '#woo_pp_sandbox_api_style' ).change(function() {
164
- var style = jQuery( '#woo_pp_sandbox_api_style option' ).filter( ':selected' ).val();
165
- var style_to_hide = '';
166
-
167
- if ( 'signature' == style ) {
168
- style_to_hide = 'certificate';
169
- } else {
170
- style_to_hide = 'signature';
171
- }
172
-
173
- jQuery( '.woo_pp_sandbox_' + style_to_hide ).hide();
174
- jQuery( '.woo_pp_sandbox_' + style ).show();
175
- });
176
-
177
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/js/wc-gateway-ppec-admin.js DELETED
@@ -1,49 +0,0 @@
1
- /*globals jQuery, wc_ppec_settings */
2
- ( function( $ ) {
3
-
4
- function disable_paypal_standard_section_link() {
5
- var sections = $( '.woocommerce .subsubsub li' );
6
-
7
- if ( ! sections.length ) {
8
- return;
9
- }
10
-
11
- $.each( sections, function() {
12
- var section = $( this ),
13
- a = $( 'a', this );
14
-
15
- if ( ! a.length ) {
16
- return;
17
- }
18
-
19
- if ( 'wc_gateway_paypal' === get_gateway_slug( a.attr( 'href' ) ) ) {
20
- a.attr( 'href', '#' ).css({'opacity': 0.3, 'color': 'black', 'cursor': 'default'});
21
- }
22
- } );
23
- }
24
-
25
- function get_gateway_slug( href ) {
26
- var parsed_url = document.createElement('a'),
27
- qs,
28
- slug;
29
-
30
- parsed_url.href = href;
31
- qs = parsed_url.search.substr(1).split('&');
32
-
33
- qs.forEach( function( el ) {
34
-
35
- var kv = el.split( '=' );
36
-
37
- if ( 2 === kv.length && 'section' === kv[0] ) {
38
- slug = kv[1];
39
- }
40
- } );
41
-
42
- return slug;
43
- }
44
-
45
- if ( wc_ppec_settings && wc_ppec_settings.enabled ) {
46
- disable_paypal_standard_section_link();
47
- }
48
-
49
- } )( jQuery );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/js/wc-gateway-ppec-frontend-checkout.js DELETED
@@ -1,47 +0,0 @@
1
- /**
2
- * globals jQuery, wc_ppec, window.
3
- *
4
- * This script only enqueued when buyer start checkout from checkout. In this
5
- * case buyer needs to fill billing fields then proceed to login through PayPal.
6
- */
7
-
8
- var woo_pp_icc_started = false;
9
- window.paypalCheckoutReady = function() {
10
- paypal.checkout.setup( wc_ppec.payer_id, {
11
- container: 'woo_pp_icc_container'
12
- } );
13
- };
14
-
15
- jQuery( "form.checkout" ).submit(function() {
16
- if ( jQuery( '#payment_method_ppec_paypal, #payment_method_ppec_paypal_credit' ).is( ':checked' ) ) {
17
- woo_pp_icc_started = true;
18
- paypal.checkout.initXO();
19
- }
20
- } );
21
-
22
- jQuery( document ).ajaxComplete( function( event, xhr, settings ) {
23
- if( ! woo_pp_icc_started ) {
24
- return;
25
- }
26
-
27
- var resp = jQuery.parseJSON( xhr.responseText );
28
- if ( ! resp ) {
29
- return;
30
- }
31
-
32
- if( 'success' !== resp.result ) {
33
- paypal.checkout.closeFlow();
34
- woo_pp_icc_started = false;
35
- }
36
- } );
37
-
38
- jQuery( document ).ajaxError(function() {
39
- if( woo_pp_icc_started ) {
40
- paypal.checkout.closeFlow();
41
- woo_pp_icc_started = false;
42
- }
43
- } );
44
-
45
- function woo_pp_checkout_callback( url ) {
46
- paypal.checkout.startFlow( decodeURI( url ) );
47
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/js/wc-gateway-ppec-frontend-in-context-checkout.js CHANGED
@@ -1,20 +1,23 @@
1
  ;(function ( $, window, document ) {
 
2
 
3
- window.paypalCheckoutReady = function() {
4
- paypal.checkout.setup(
5
- wc_ppec_context.payer_id,
6
- {
7
- environment: wc_ppec_context.environment,
8
- button: 'woo_pp_ec_button',
9
- locale: wc_ppec_context.locale,
 
 
 
 
 
10
  }
11
- );
12
  }
13
 
14
- $( document ).on( 'click', '#woo_pp_ec_button', function( event ) {
15
- event.preventDefault();
16
- paypal.checkout.initXO();
17
- paypal.checkout.startFlow( wc_ppec_context.start_flow );
18
- } );
19
-
20
  })( jQuery, window, document );
1
  ;(function ( $, window, document ) {
2
+ 'use strict';
3
 
4
+ var $wc_ppec = {
5
+ init: function() {
6
+ window.paypalCheckoutReady = function() {
7
+ paypal.checkout.setup(
8
+ wc_ppec_context.payer_id,
9
+ {
10
+ environment: wc_ppec_context.environment,
11
+ button: ['woo_pp_ec_button', 'woo_pp_ppc_button'],
12
+ locale: wc_ppec_context.locale,
13
+ container: ['woo_pp_ec_button', 'woo_pp_ppc_button']
14
+ }
15
+ );
16
  }
17
+ }
18
  }
19
 
20
+ if ( wc_ppec_context.show_modal ) {
21
+ $wc_ppec.init();
22
+ }
 
 
 
23
  })( jQuery, window, document );
includes/abstracts/abstract-wc-gateway-ppec-paypal-request-handler.php CHANGED
@@ -50,7 +50,14 @@ abstract class WC_Gateway_PPEC_PayPal_Request_Handler {
50
  $order_id = wc_get_order_id_by_order_key( $order_key );
51
  $order = wc_get_order( $order_id );
52
  }
53
- if ( ! $order || $order->order_key !== $order_key ) {
 
 
 
 
 
 
 
54
  wc_gateway_ppec_log( sprintf( '%s: %s', __FUNCTION__, 'Error: Order Keys do not match.' ) );
55
  return false;
56
  }
@@ -77,7 +84,11 @@ abstract class WC_Gateway_PPEC_PayPal_Request_Handler {
77
  */
78
  protected function payment_on_hold( $order, $reason = '' ) {
79
  $order->update_status( 'on-hold', $reason );
80
- wc_reduce_stock_levels( $order_id );
 
 
 
 
81
  WC()->cart->empty_cart();
82
  }
83
  }
50
  $order_id = wc_get_order_id_by_order_key( $order_key );
51
  $order = wc_get_order( $order_id );
52
  }
53
+
54
+ if ( $order ) {
55
+ $order_key_from_order = version_compare( WC_VERSION, '3.0', '<' ) ? $order->order_key : $order->get_order_key();
56
+ } else {
57
+ $order_key_from_order = '';
58
+ }
59
+
60
+ if ( ! $order || $order_key_from_order !== $order_key ) {
61
  wc_gateway_ppec_log( sprintf( '%s: %s', __FUNCTION__, 'Error: Order Keys do not match.' ) );
62
  return false;
63
  }
84
  */
85
  protected function payment_on_hold( $order, $reason = '' ) {
86
  $order->update_status( 'on-hold', $reason );
87
+ if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
88
+ $order->reduce_order_stock();
89
+ } else {
90
+ wc_reduce_stock_levels( $order->get_id() );
91
+ }
92
  WC()->cart->empty_cart();
93
  }
94
  }
includes/abstracts/abstract-wc-gateway-ppec.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  if ( ! defined( 'ABSPATH' ) ) {
4
- exit; // Exit if accessed directly
5
  }
6
 
7
  /**
@@ -61,10 +61,7 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
61
 
62
  // Change gateway name if session is active
63
  if ( ! is_admin() ) {
64
- $session = WC()->session->get( 'paypal' );
65
- $checkout = wc_gateway_ppec()->checkout;
66
-
67
- if ( ! $checkout->has_active_session() || ! $session->checkout_completed ) {
68
  $this->title = $this->get_option( 'title' );
69
  $this->description = $this->get_option( 'description' );
70
  }
@@ -79,48 +76,61 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
79
  }
80
 
81
  /**
82
- * Process payments
 
 
 
 
83
  */
84
  public function process_payment( $order_id ) {
85
  $checkout = wc_gateway_ppec()->checkout;
86
  $order = wc_get_order( $order_id );
87
  $session = WC()->session->get( 'paypal' );
88
 
89
- // Redirect them over to PayPal if they have no current session (this is for PayPal Mark).
90
- if ( ! $checkout->has_active_session() || ! $session->checkout_completed ) {
 
91
  try {
92
  return array(
93
  'result' => 'success',
94
  'redirect' => $checkout->start_checkout_from_checkout( $order_id ),
95
  );
96
- } catch( PayPal_API_Exception $e ) {
97
- wc_gateway_ppec_format_paypal_api_exception( $e->errors );
98
  }
99
  } else {
100
  try {
101
  // Get details
102
- $checkout_details = $checkout->getCheckoutDetails( $session->token );
103
 
104
- // Store addresses given by PayPal
105
- $order->set_address( $checkout->get_mapped_billing_address( $checkout_details ), 'billing' );
106
- $order->set_address( $checkout->get_mapped_shipping_address( $checkout_details ), 'shipping' );
 
 
 
 
107
 
108
  // Complete the payment now.
109
- $checkout->do_payment( $order, $session->token, $session->payerID );
110
 
111
  // Clear Cart
112
  WC()->cart->empty_cart();
113
 
114
  return array(
115
  'result' => 'success',
116
- 'redirect' => $this->get_return_url( $order )
117
  );
118
- } catch( PayPal_Missing_Session_Exception $e ) {
119
- // For some reason, our session data is missing. Generally, if we've made it this far, this shouldn't happen.
 
 
120
  wc_add_notice( __( 'Sorry, an error occurred while trying to process your payment. Please try again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
121
- } catch( PayPal_API_Exception $e ) {
122
- // Did we get a 10486 or 10422 back from PayPal? If so, this means we need to send the buyer back over to
123
- // PayPal to have them pick out a new funding method.
 
 
124
  $error_codes = wp_list_pluck( $e->errors, 'error_code' );
125
 
126
  if ( in_array( '10486', $error_codes ) || in_array( '10422', $error_codes ) ) {
@@ -131,10 +141,10 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
131
 
132
  return array(
133
  'result' => 'success',
134
- 'redirect' => wc_gateway_ppec()->settings->get_paypal_redirect_url( $session->token, true )
135
  );
136
  } else {
137
- wc_gateway_ppec_format_paypal_api_exception( $e->errors );
138
  }
139
  }
140
  }
@@ -150,7 +160,7 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
150
  return __( 'No API certificate on file.', 'woocommerce-gateway-paypal-express-checkout' );
151
  }
152
 
153
- $cert = @openssl_x509_read( $cert_string );
154
  $out = '';
155
 
156
  if ( false !== $cert ) {
@@ -182,7 +192,7 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
182
  * Do some additonal validation before saving options via the API.
183
  */
184
  public function process_admin_options() {
185
- // Validate logo
186
  $logo_image_url = wc_clean( $_POST['woocommerce_ppec_paypal_logo_image_url'] );
187
 
188
  if ( ! empty( $logo_image_url ) && ! preg_match( '/https?:\/\/[a-zA-Z0-9][a-zA-Z0-9.-]+[a-zA-Z0-9](\/[a-zA-Z0-9.\/?&%#]*)?/', $logo_image_url ) ) {
@@ -217,7 +227,7 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
217
 
218
  parent::process_admin_options();
219
 
220
- // Validate credentials
221
  $this->validate_active_credentials();
222
  }
223
 
@@ -225,7 +235,7 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
225
  * Validate the provided credentials.
226
  */
227
  protected function validate_active_credentials() {
228
- $settings = wc_gateway_ppec()->settings->load_settings( true );
229
  $creds = $settings->get_active_api_credentials();
230
 
231
  $username = $creds->get_username();
@@ -245,20 +255,19 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
245
  $payer_id = wc_gateway_ppec()->client->test_api_credentials( $creds, $settings->get_environment() );
246
 
247
  if ( ! $payer_id ) {
248
- WC_Admin_Settings::add_error( sprintf( __( 'Error: The %s credentials you provided are not valid. Please double-check that you entered them correctly and try again.', 'woocommerce-gateway-paypal-express-checkout' ), __( $settings->get_environment(), 'woocommerce-gateway-paypal-express-checkout' ) ) );
249
  return false;
250
  }
 
251
 
252
- } catch( PayPal_API_Exception $ex ) {
253
- WC_Admin_Settings::add_error( sprintf( __( 'An error occurred while trying to validate your %s API credentials. Unable to verify that your API credentials are correct.', 'woocommerce-gateway-paypal-express-checkout' ), __( $settings->get_environment(), 'woocommerce-gateway-paypal-express-checkout' ) ) );
254
  }
255
-
256
  } elseif ( is_a( $creds, 'WC_Gateway_PPEC_Client_Credential_Certificate' ) && $creds->get_certificate() ) {
257
 
258
- $cert = @openssl_x509_read( $creds->get_certificate() );
259
 
260
  if ( false === $cert ) {
261
- WC_Admin_Settings::add_error( sprintf( __( 'Error: The %s API certificate is not valid.', 'woocommerce-gateway-paypal-express-checkout' ), __( $settings->get_environment(), 'woocommerce-gateway-paypal-express-checkout' ) ) );
262
  return false;
263
  }
264
 
@@ -266,7 +275,7 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
266
  $valid_until = $cert_info['validTo_time_t'];
267
 
268
  if ( $valid_until < time() ) {
269
- WC_Admin_Settings::add_error( sprintf( __( 'Error: The %s API certificate has expired.', 'woocommerce-gateway-paypal-express-checkout' ), __( $settings->get_environment(), 'woocommerce-gateway-paypal-express-checkout' ) ) );
270
  return false;
271
  }
272
 
@@ -280,17 +289,16 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
280
  $payer_id = wc_gateway_ppec()->client->test_api_credentials( $creds, $settings->get_environment() );
281
 
282
  if ( ! $payer_id ) {
283
- WC_Admin_Settings::add_error( sprintf( __( 'Error: The %s credentials you provided are not valid. Please double-check that you entered them correctly and try again.', 'woocommerce-gateway-paypal-express-checkout' ), __( $settings->get_environment(), 'woocommerce-gateway-paypal-express-checkout' ) ) );
284
  return false;
285
  }
286
-
287
- } catch( PayPal_API_Exception $ex ) {
288
- WC_Admin_Settings::add_error( sprintf( __( 'An error occurred while trying to validate your %s API credentials. Unable to verify that your API credentials are correct.', 'woocommerce-gateway-paypal-express-checkout' ), __( $settings->get_environment(), 'woocommerce-gateway-paypal-express-checkout' ) ) );
289
  }
290
 
291
  } else {
292
 
293
- WC_Admin_Settings::add_error( sprintf( __( 'Error: You must provide a %s API signature or certificate.', 'woocommerce-gateway-paypal-express-checkout' ), __( $settings->get_environment(), 'woocommerce-gateway-paypal-express-checkout' ) ) );
294
  return false;
295
  }
296
 
@@ -301,7 +309,7 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
301
 
302
  try {
303
  $is_account_enabled_for_billing_address = wc_gateway_ppec()->client->test_for_billing_address_enabled( $creds, $settings->get_environment() );
304
- } catch( PayPal_API_Exception $ex ) {
305
  $is_account_enabled_for_billing_address = false;
306
  }
307
 
@@ -315,7 +323,13 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
315
  }
316
 
317
  /**
318
- * Refunds.
 
 
 
 
 
 
319
  */
320
  public function process_refund( $order_id, $amount = null, $reason = '' ) {
321
  $order = wc_get_order( $order_id );
@@ -328,106 +342,102 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
328
  // loop through each transaction to compile list of txns that are able to be refunded
329
  // process refunds against each txn in the list until full amount of refund is reached
330
  // first loop through, try to find a transaction that equals the refund amount being requested
331
- $txnData = get_post_meta( $order_id, '_woo_pp_txnData', true );
332
- $didRefund = false;
 
333
 
334
- foreach ( $txnData['refundable_txns'] as $key => $value ) {
335
- $refundableAmount = $value['amount'] - $value['refunded_amount'];
336
 
337
- if ( $amount == $refundableAmount ) {
338
- if ( 0 == $value['refunded_amount'] ) {
339
- $refundType = 'Full';
340
- } else {
341
- $refundType = 'Partial';
342
- }
343
 
344
  try {
345
- $refundTxnID = WC_Gateway_PPEC_Refund::refund_order( $order, $amount, $refundType, $reason, $order->get_order_currency() );
346
- $txnData['refundable_txns'][ $key ]['refunded_amount'] += $amount;
347
- $order->add_order_note( sprintf( __( 'PayPal refund completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $refundTxnID ) );
348
- update_post_meta( $order_id, '_woo_pp_txnData', $txnData );
 
 
 
 
349
 
350
  return true;
351
 
352
- } catch( PayPal_API_Exception $e ) {
353
- foreach ( $e->errors as $error ) {
354
- $final_output .= sprintf( __( 'Error: %1$s - %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $error->error_code, $error->long_message );
355
- }
356
-
357
- return new WP_Error( 'paypal_refund_error', $final_output );
358
  }
359
  }
360
  }
361
 
362
- foreach ( $txnData['refundable_txns'] as $key => $value ) {
363
- $refundableAmount = $value['amount'] - $value['refunded_amount'];
364
 
365
- if ( $amount < $refundableAmount ) {
366
 
367
  try {
368
- $refundTxnID = WC_Gateway_PPEC_Refund::refund_order( $order, $amount, 'Partial', $reason, $order->get_order_currency() );
369
- $txnData['refundable_txns'][ $key ]['refunded_amount'] += $amount;
370
- $order->add_order_note( sprintf( __( 'PayPal refund completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $refundTxnID ) );
371
- update_post_meta( $order_id, '_woo_pp_txnData', $txnData );
 
 
 
 
372
 
373
  return true;
374
 
375
- } catch( PayPal_API_Exception $e ) {
376
- foreach ( $e->errors as $error ) {
377
- $final_output .= sprintf( __( 'Error: %1$s - %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $error->error_code, $error->long_message );
378
- }
379
-
380
- return new WP_Error( 'paypal_refund_error', $final_output );
381
  }
382
 
383
  }
384
  }
385
 
386
- $totalRefundableAmount = 0;
387
- foreach ( $txnData['refundable_txns'] as $key => $value ) {
388
- $refundableAmount = $value['amount'] - $value['refunded_amount'];
389
- $totalRefundableAmount += $refundableAmount;
390
  }
391
 
392
- if ( $totalRefundableAmount < $amount ) {
393
- if ( 0 == $totalRefundableAmount ) {
394
  return new WP_Error( 'paypal_refund_error', __( 'Refund Error: All transactions have been fully refunded. There is no amount left to refund', 'woocommerce-gateway-paypal-express-checkout' ) );
395
  } else {
396
- return new WP_Error( 'paypal_refund_error', sprintf( __( 'Refund Error: The requested refund amount is too large. The refund amount must be less than or equal to %s.', 'woocommerce-gateway-paypal-express-checkout' ), html_entity_decode( get_woocommerce_currency_symbol() ) . $totalRefundableAmount ) );
397
  }
398
  } else {
399
  $total_to_refund = $amount;
400
 
401
- foreach ( $txnData['refundable_txns'] as $key => $value ) {
402
- $refundableAmount = $value['amount'] - $value['refunded_amount'];
403
 
404
- if ( $refundableAmount > $total_to_refund ) {
405
  $amount_to_refund = $total_to_refund;
406
  } else {
407
- $amount_to_refund = $refundableAmount;
408
  }
409
 
410
  if ( 0 < $amount_to_refund ) {
 
411
  if ( 0 == $value['refunded_amount'] && $amount_to_refund == $value['amount'] ) {
412
- $refundType = 'Full';
413
- } else {
414
- $refundType = 'Partial';
415
  }
416
 
417
  try {
418
- $refundTxnID = WC_Gateway_PPEC_Refund::refund_order( $order, $amount_to_refund, $refundType, $reason, $order->get_order_currency() );
419
  $total_to_refund -= $amount_to_refund;
420
- $txnData['refundable_txns'][ $key ]['refunded_amount'] += $amount_to_refund;
421
- $order->add_order_note( sprintf( __( 'PayPal refund completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $refundTxnID ) );
422
- update_post_meta( $order_id, '_woo_pp_txnData', $txnData );
423
-
424
- return true;
425
- } catch( PayPal_API_Exception $e ) {
426
- foreach ( $e->errors as $error ) {
427
- $final_output .= sprintf( __( 'Error: %1$s - %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $error->error_code, $error->long_message );
428
  }
429
 
430
- return new WP_Error( 'paypal_refund_error', $final_output );
 
 
431
  }
432
  }
433
  }
@@ -457,4 +467,17 @@ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
457
  public function is_available() {
458
  return 'yes' === $this->enabled;
459
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
460
  }
1
  <?php
2
 
3
  if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly.
5
  }
6
 
7
  /**
61
 
62
  // Change gateway name if session is active
63
  if ( ! is_admin() ) {
64
+ if ( wc_gateway_ppec()->checkout->is_started_from_checkout_page() ) {
 
 
 
65
  $this->title = $this->get_option( 'title' );
66
  $this->description = $this->get_option( 'description' );
67
  }
76
  }
77
 
78
  /**
79
+ * Process payments.
80
+ *
81
+ * @param int $order_id Order ID
82
+ *
83
+ * @return array
84
  */
85
  public function process_payment( $order_id ) {
86
  $checkout = wc_gateway_ppec()->checkout;
87
  $order = wc_get_order( $order_id );
88
  $session = WC()->session->get( 'paypal' );
89
 
90
+ // Redirect them over to PayPal if they have no current session (this
91
+ // is for PayPal Mark).
92
+ if ( $checkout->is_started_from_checkout_page() ) {
93
  try {
94
  return array(
95
  'result' => 'success',
96
  'redirect' => $checkout->start_checkout_from_checkout( $order_id ),
97
  );
98
+ } catch ( PayPal_API_Exception $e ) {
99
+ wc_add_notice( $e->getMessage(), 'error' );
100
  }
101
  } else {
102
  try {
103
  // Get details
104
+ $checkout_details = $checkout->get_checkout_details( $session->token );
105
 
106
+ $checkout_context = array(
107
+ 'start_from' => 'checkout',
108
+ 'order_id' => $order_id,
109
+ );
110
+ if ( $checkout->needs_billing_agreement_creation( $checkout_context ) ) {
111
+ $checkout->create_billing_agreement( $order, $checkout_details );
112
+ }
113
 
114
  // Complete the payment now.
115
+ $checkout->do_payment( $order, $session->token, $session->payer_id );
116
 
117
  // Clear Cart
118
  WC()->cart->empty_cart();
119
 
120
  return array(
121
  'result' => 'success',
122
+ 'redirect' => $this->get_return_url( $order ),
123
  );
124
+ } catch ( PayPal_Missing_Session_Exception $e ) {
125
+
126
+ // For some reason, our session data is missing. Generally,
127
+ // if we've made it this far, this shouldn't happen.
128
  wc_add_notice( __( 'Sorry, an error occurred while trying to process your payment. Please try again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
129
+ } catch ( PayPal_API_Exception $e ) {
130
+
131
+ // Did we get a 10486 or 10422 back from PayPal? If so, this
132
+ // means we need to send the buyer back over to PayPal to have
133
+ // them pick out a new funding method.
134
  $error_codes = wp_list_pluck( $e->errors, 'error_code' );
135
 
136
  if ( in_array( '10486', $error_codes ) || in_array( '10422', $error_codes ) ) {
141
 
142
  return array(
143
  'result' => 'success',
144
+ 'redirect' => wc_gateway_ppec()->settings->get_paypal_redirect_url( $session->token, true ),
145
  );
146
  } else {
147
+ wc_add_notice( $e->getMessage(), 'error' );
148
  }
149
  }
150
  }
160
  return __( 'No API certificate on file.', 'woocommerce-gateway-paypal-express-checkout' );
161
  }
162
 
163
+ $cert = @openssl_x509_read( $cert_string ); // @codingStandardsIgnoreLine
164
  $out = '';
165
 
166
  if ( false !== $cert ) {
192
  * Do some additonal validation before saving options via the API.
193
  */
194
  public function process_admin_options() {
195
+ // Validate logo.
196
  $logo_image_url = wc_clean( $_POST['woocommerce_ppec_paypal_logo_image_url'] );
197
 
198
  if ( ! empty( $logo_image_url ) && ! preg_match( '/https?:\/\/[a-zA-Z0-9][a-zA-Z0-9.-]+[a-zA-Z0-9](\/[a-zA-Z0-9.\/?&%#]*)?/', $logo_image_url ) ) {
227
 
228
  parent::process_admin_options();
229
 
230
+ // Validate credentials.
231
  $this->validate_active_credentials();
232
  }
233
 
235
  * Validate the provided credentials.
236
  */
237
  protected function validate_active_credentials() {
238
+ $settings = wc_gateway_ppec()->settings->load( true );
239
  $creds = $settings->get_active_api_credentials();
240
 
241
  $username = $creds->get_username();
255
  $payer_id = wc_gateway_ppec()->client->test_api_credentials( $creds, $settings->get_environment() );
256
 
257
  if ( ! $payer_id ) {
258
+ WC_Admin_Settings::add_error( __( 'Error: The API credentials you provided are not valid. Please double-check that you entered them correctly and try again.', 'woocommerce-gateway-paypal-express-checkout' ) );
259
  return false;
260
  }
261
+ } catch ( PayPal_API_Exception $ex ) {
262
 
263
+ WC_Admin_Settings::add_error( __( 'An error occurred while trying to validate your API credentials. Unable to verify that your API credentials are correct.', 'woocommerce-gateway-paypal-express-checkout' ) );
 
264
  }
 
265
  } elseif ( is_a( $creds, 'WC_Gateway_PPEC_Client_Credential_Certificate' ) && $creds->get_certificate() ) {
266
 
267
+ $cert = @openssl_x509_read( $creds->get_certificate() ); // @codingStandardsIgnoreLine
268
 
269
  if ( false === $cert ) {
270
+ WC_Admin_Settings::add_error( __( 'Error: The API certificate is not valid.', 'woocommerce-gateway-paypal-express-checkout' ) );
271
  return false;
272
  }
273
 
275
  $valid_until = $cert_info['validTo_time_t'];
276
 
277
  if ( $valid_until < time() ) {
278
+ WC_Admin_Settings::add_error( __( 'Error: The API certificate has expired.', 'woocommerce-gateway-paypal-express-checkout' ) );
279
  return false;
280
  }
281
 
289
  $payer_id = wc_gateway_ppec()->client->test_api_credentials( $creds, $settings->get_environment() );
290
 
291
  if ( ! $payer_id ) {
292
+ WC_Admin_Settings::add_error( __( 'Error: The API credentials you provided are not valid. Please double-check that you entered them correctly and try again.', 'woocommerce-gateway-paypal-express-checkout' ) );
293
  return false;
294
  }
295
+ } catch ( PayPal_API_Exception $ex ) {
296
+ WC_Admin_Settings::add_error( __( 'An error occurred while trying to validate your API credentials. Unable to verify that your API credentials are correct.', 'woocommerce-gateway-paypal-express-checkout' ) );
 
297
  }
298
 
299
  } else {
300
 
301
+ WC_Admin_Settings::add_error( __( 'Error: You must provide API signature or certificate.', 'woocommerce-gateway-paypal-express-checkout' ) );
302
  return false;
303
  }
304
 
309
 
310
  try {
311
  $is_account_enabled_for_billing_address = wc_gateway_ppec()->client->test_for_billing_address_enabled( $creds, $settings->get_environment() );
312
+ } catch ( PayPal_API_Exception $ex ) {
313
  $is_account_enabled_for_billing_address = false;
314
  }
315
 
323
  }
324
 
325
  /**
326
+ * Process refund.
327
+ *
328
+ * @param int $order_id Order ID
329
+ * @param float $amount Order amount
330
+ * @param string $reason Refund reason
331
+ *
332
+ * @return boolean True or false based on success, or a WP_Error object.
333
  */
334
  public function process_refund( $order_id, $amount = null, $reason = '' ) {
335
  $order = wc_get_order( $order_id );
342
  // loop through each transaction to compile list of txns that are able to be refunded
343
  // process refunds against each txn in the list until full amount of refund is reached
344
  // first loop through, try to find a transaction that equals the refund amount being requested
345
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
346
+ $txn_data = $old_wc ? get_post_meta( $order_id, '_woo_pp_txnData', true ) : $order->get_meta( '_woo_pp_txnData', true );
347
+ $order_currency = $old_wc ? $order->order_currency : $order->get_currency();
348
 
349
+ foreach ( $txn_data['refundable_txns'] as $key => $value ) {
350
+ $refundable_amount = $value['amount'] - $value['refunded_amount'];
351
 
352
+ if ( $amount == $refundable_amount ) {
353
+ $refund_type = ( 0 == $value['refunded_amount'] ) ? 'Full' : 'Partial';
 
 
 
 
354
 
355
  try {
356
+ $refund_txn_id = WC_Gateway_PPEC_Refund::refund_order( $order, $amount, $refund_type, $reason, $order_currency );
357
+ $txn_data['refundable_txns'][ $key ]['refunded_amount'] += $amount;
358
+ $order->add_order_note( sprintf( __( 'PayPal refund completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $refund_txn_id ) );
359
+ if ( $old_wc ) {
360
+ update_post_meta( $order_id, '_woo_pp_txnData', $txn_data );
361
+ } else {
362
+ $order->update_meta_data( '_woo_pp_txnData', $txn_data );
363
+ }
364
 
365
  return true;
366
 
367
+ } catch ( PayPal_API_Exception $e ) {
368
+ return new WP_Error( 'paypal_refund_error', $e->getMessage() );
 
 
 
 
369
  }
370
  }
371
  }
372
 
373
+ foreach ( $txn_data['refundable_txns'] as $key => $value ) {
374
+ $refundable_amount = $value['amount'] - $value['refunded_amount'];
375
 
376
+ if ( $amount < $refundable_amount ) {
377
 
378
  try {
379
+ $refund_txn_id = WC_Gateway_PPEC_Refund::refund_order( $order, $amount, 'Partial', $reason, $order_currency );
380
+ $txn_data['refundable_txns'][ $key ]['refunded_amount'] += $amount;
381
+ $order->add_order_note( sprintf( __( 'PayPal refund completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $refund_txn_id ) );
382
+ if ( $old_wc ) {
383
+ update_post_meta( $order_id, '_woo_pp_txnData', $txn_data );
384
+ } else {
385
+ $order->update_meta_data( '_woo_pp_txnData', $txn_data );
386
+ }
387
 
388
  return true;
389
 
390
+ } catch ( PayPal_API_Exception $e ) {
391
+ return new WP_Error( 'paypal_refund_error', $e->getMessage() );
 
 
 
 
392
  }
393
 
394
  }
395
  }
396
 
397
+ $total_refundable_amount = 0;
398
+ foreach ( $txn_data['refundable_txns'] as $key => $value ) {
399
+ $refundable_amount = $value['amount'] - $value['refunded_amount'];
400
+ $total_refundable_amount += $refundable_amount;
401
  }
402
 
403
+ if ( $total_refundable_amount < $amount ) {
404
+ if ( 0 == $total_refundable_amount ) {
405
  return new WP_Error( 'paypal_refund_error', __( 'Refund Error: All transactions have been fully refunded. There is no amount left to refund', 'woocommerce-gateway-paypal-express-checkout' ) );
406
  } else {
407
+ return new WP_Error( 'paypal_refund_error', sprintf( __( 'Refund Error: The requested refund amount is too large. The refund amount must be less than or equal to %s.', 'woocommerce-gateway-paypal-express-checkout' ), html_entity_decode( get_woocommerce_currency_symbol() ) . $total_refundable_amount ) );
408
  }
409
  } else {
410
  $total_to_refund = $amount;
411
 
412
+ foreach ( $txn_data['refundable_txns'] as $key => $value ) {
413
+ $refundable_amount = $value['amount'] - $value['refunded_amount'];
414
 
415
+ if ( $refundable_amount > $total_to_refund ) {
416
  $amount_to_refund = $total_to_refund;
417
  } else {
418
+ $amount_to_refund = $refundable_amount;
419
  }
420
 
421
  if ( 0 < $amount_to_refund ) {
422
+ $refund_type = 'Partial';
423
  if ( 0 == $value['refunded_amount'] && $amount_to_refund == $value['amount'] ) {
424
+ $refund_type = 'Full';
 
 
425
  }
426
 
427
  try {
428
+ $refund_txn_id = WC_Gateway_PPEC_Refund::refund_order( $order, $amount_to_refund, $refund_type, $reason, $order_currency );
429
  $total_to_refund -= $amount_to_refund;
430
+ $txn_data['refundable_txns'][ $key ]['refunded_amount'] += $amount_to_refund;
431
+ $order->add_order_note( sprintf( __( 'PayPal refund completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $refund_txn_id ) );
432
+ if ( $old_wc ) {
433
+ update_post_meta( $order_id, '_woo_pp_txnData', $txn_data );
434
+ } else {
435
+ $order->update_meta_data( '_woo_pp_txnData', $txn_data );
 
 
436
  }
437
 
438
+ return true;
439
+ } catch ( PayPal_API_Exception $e ) {
440
+ return new WP_Error( 'paypal_refund_error', $e->getMessage() );
441
  }
442
  }
443
  }
467
  public function is_available() {
468
  return 'yes' === $this->enabled;
469
  }
470
+
471
+ /**
472
+ * Whether PayPal credit is supported.
473
+ *
474
+ * @since 1.2.0
475
+ *
476
+ * @return bool Returns true if PayPal credit is supported
477
+ */
478
+ public function is_credit_supported() {
479
+ $base = wc_get_base_location();
480
+
481
+ return 'US' === $base['country'];
482
+ }
483
  }
includes/class-wc-gateway-ppec-admin-handler.php CHANGED
@@ -28,6 +28,7 @@ class WC_Gateway_PPEC_Admin_Handler {
28
  add_action( 'woocommerce_order_action_ppec_capture_charge', array( $this, 'maybe_capture_charge' ) );
29
 
30
  add_action( 'load-woocommerce_page_wc-settings', array( $this, 'maybe_redirect_to_ppec_settings' ) );
 
31
  }
32
 
33
  public function add_capture_charge_order_action( $actions ) {
@@ -37,8 +38,13 @@ class WC_Gateway_PPEC_Admin_Handler {
37
 
38
  $order = wc_get_order( $_REQUEST['post'] );
39
 
 
 
 
 
 
40
  // bail if the order wasn't paid for with this gateway
41
- if ( 'ppec_paypal' !== $order->payment_method || 'pending' !== get_post_meta( $order->id, '_paypal_status', true ) ) {
42
  return $actions;
43
  }
44
 
@@ -106,7 +112,7 @@ class WC_Gateway_PPEC_Admin_Handler {
106
  // current section. (Note: The option will be empty if it has never been enabled)
107
 
108
  $simplify_commerce_options = get_option( 'woocommerce_simplify_commerce_settings', array() );
109
- if ( empty( $simplify_commerce_options ) || ( "no" === $simplify_commerce_options['enabled'] ) ) {
110
  if ( 'wc_gateway_simplify_commerce' !== $current_section ) {
111
  $sections_to_remove[] = 'wc_gateway_simplify_commerce';
112
  }
@@ -115,8 +121,8 @@ class WC_Gateway_PPEC_Admin_Handler {
115
  }
116
  }
117
 
118
- foreach( $sections_to_remove as $section_to_remove ) {
119
- unset( $sections[$section_to_remove] );
120
  }
121
 
122
  return $sections;
@@ -128,7 +134,8 @@ class WC_Gateway_PPEC_Admin_Handler {
128
  $order = wc_get_order( $order );
129
  }
130
 
131
- $this->capture_payment( $order->id );
 
132
 
133
  return true;
134
  }
@@ -139,15 +146,20 @@ class WC_Gateway_PPEC_Admin_Handler {
139
  * @param int $order_id
140
  */
141
  public function capture_payment( $order_id ) {
142
- $order = wc_get_order( $order_id );
 
143
 
144
- if ( 'ppec_paypal' === $order->payment_method ) {
145
- $trans_id = get_post_meta( $order_id, '_transaction_id', true );
 
 
146
  $trans_details = wc_gateway_ppec()->client->get_transaction_details( array( 'TRANSACTIONID' => $trans_id ) );
147
 
148
  if ( $trans_id && $this->is_authorized_only( $trans_details ) ) {
 
 
149
  $params['AUTHORIZATIONID'] = $trans_id;
150
- $params['AMT'] = floatval( $order->order_total );
151
  $params['COMPLETETYPE'] = 'Complete';
152
 
153
  $result = wc_gateway_ppec()->client->do_express_checkout_capture( $params );
@@ -183,9 +195,12 @@ class WC_Gateway_PPEC_Admin_Handler {
183
  */
184
  public function cancel_payment( $order_id ) {
185
  $order = wc_get_order( $order_id );
 
 
186
 
187
- if ( 'ppec_paypal' === $order->payment_method ) {
188
- $trans_id = get_post_meta( $order_id, '_transaction_id', true );
 
189
  $trans_details = wc_gateway_ppec()->client->get_transaction_details( array( 'TRANSACTIONID' => $trans_id ) );
190
 
191
  if ( $trans_id && $this->is_authorized_only( $trans_details ) ) {
@@ -196,7 +211,7 @@ class WC_Gateway_PPEC_Admin_Handler {
196
  if ( is_wp_error( $result ) ) {
197
  $order->add_order_note( __( 'Unable to void charge!', 'woocommerce-gateway-paypal-express-checkout' ) . ' ' . $result->get_error_message() );
198
  } else {
199
- $order->add_order_note( sprintf( __( 'PayPal Express Checkout charge voided (Charge ID: %s)', 'woocommerce-gateway-paypal-express-checkout' ), $trans_id) );
200
  }
201
  }
202
  }
@@ -235,4 +250,45 @@ class WC_Gateway_PPEC_Admin_Handler {
235
  wp_safe_redirect( $redirect );
236
  }
237
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
  }
28
  add_action( 'woocommerce_order_action_ppec_capture_charge', array( $this, 'maybe_capture_charge' ) );
29
 
30
  add_action( 'load-woocommerce_page_wc-settings', array( $this, 'maybe_redirect_to_ppec_settings' ) );
31
+ add_action( 'load-woocommerce_page_wc-settings', array( $this, 'maybe_reset_api_credentials' ) );
32
  }
33
 
34
  public function add_capture_charge_order_action( $actions ) {
38
 
39
  $order = wc_get_order( $_REQUEST['post'] );
40
 
41
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
42
+ $order_id = $old_wc ? $order->id : $order->get_id();
43
+ $payment_method = $old_wc ? $order->payment_method : $order->get_payment_method();
44
+ $paypal_status = $old_wc ? get_post_meta( $order_id, '_paypal_status', true ) : $order->get_meta( '_paypal_status', true );
45
+
46
  // bail if the order wasn't paid for with this gateway
47
+ if ( 'ppec_paypal' !== $payment_method || 'pending' !== $paypal_status ) {
48
  return $actions;
49
  }
50
 
112
  // current section. (Note: The option will be empty if it has never been enabled)
113
 
114
  $simplify_commerce_options = get_option( 'woocommerce_simplify_commerce_settings', array() );
115
+ if ( empty( $simplify_commerce_options ) || ( 'no' === $simplify_commerce_options['enabled'] ) ) {
116
  if ( 'wc_gateway_simplify_commerce' !== $current_section ) {
117
  $sections_to_remove[] = 'wc_gateway_simplify_commerce';
118
  }
121
  }
122
  }
123
 
124
+ foreach ( $sections_to_remove as $section_to_remove ) {
125
+ unset( $sections[ $section_to_remove ] );
126
  }
127
 
128
  return $sections;
134
  $order = wc_get_order( $order );
135
  }
136
 
137
+ $order_id = version_compare( WC_VERSION, '3.0', '<' ) ? $order->id : $order->get_id();
138
+ $this->capture_payment( $order_id );
139
 
140
  return true;
141
  }
146
  * @param int $order_id
147
  */
148
  public function capture_payment( $order_id ) {
149
+ $order = wc_get_order( $order_id );
150
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
151
 
152
+ $payment_method = $old_wc ? $order->payment_method : $order->get_payment_method();
153
+ if ( 'ppec_paypal' === $payment_method ) {
154
+
155
+ $trans_id = $old_wc ? get_post_meta( $order_id, '_transaction_id', true ) : $order->get_meta( '_transaction_id', true );
156
  $trans_details = wc_gateway_ppec()->client->get_transaction_details( array( 'TRANSACTIONID' => $trans_id ) );
157
 
158
  if ( $trans_id && $this->is_authorized_only( $trans_details ) ) {
159
+ $order_total = $old_wc ? $order->order_total : $order->get_total();
160
+
161
  $params['AUTHORIZATIONID'] = $trans_id;
162
+ $params['AMT'] = floatval( $order_total );
163
  $params['COMPLETETYPE'] = 'Complete';
164
 
165
  $result = wc_gateway_ppec()->client->do_express_checkout_capture( $params );
195
  */
196
  public function cancel_payment( $order_id ) {
197
  $order = wc_get_order( $order_id );
198
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
199
+ $payment_method = $old_wc ? $order->payment_method : $order->get_payment_method();
200
 
201
+ if ( 'ppec_paypal' === $payment_method ) {
202
+
203
+ $trans_id = $old_wc ? get_post_meta( $order_id, '_transaction_id', true ) : $order->get_meta( '_transaction_id', true );
204
  $trans_details = wc_gateway_ppec()->client->get_transaction_details( array( 'TRANSACTIONID' => $trans_id ) );
205
 
206
  if ( $trans_id && $this->is_authorized_only( $trans_details ) ) {
211
  if ( is_wp_error( $result ) ) {
212
  $order->add_order_note( __( 'Unable to void charge!', 'woocommerce-gateway-paypal-express-checkout' ) . ' ' . $result->get_error_message() );
213
  } else {
214
+ $order->add_order_note( sprintf( __( 'PayPal Express Checkout charge voided (Charge ID: %s)', 'woocommerce-gateway-paypal-express-checkout' ), $trans_id ) );
215
  }
216
  }
217
  }
250
  wp_safe_redirect( $redirect );
251
  }
252
  }
253
+
254
+ /**
255
+ * Reset API credentials if merchant clicked the reset credential link.
256
+ *
257
+ * When API credentials empty, the connect button will be displayed again,
258
+ * allowing merchant to reconnect with other account.
259
+ *
260
+ * When WooCommerce Branding is active, this handler may not be invoked as
261
+ * screen ID may evaluates to something else.
262
+ *
263
+ * @since 1.2.0
264
+ */
265
+ public function maybe_reset_api_credentials() {
266
+ if ( empty( $_GET['reset_ppec_api_credentials'] ) ) {
267
+ return;
268
+ }
269
+
270
+ if ( empty( $_GET['reset_nonce'] ) || ! wp_verify_nonce( $_GET['reset_nonce'], 'reset_ppec_api_credentials' ) ) {
271
+ return;
272
+ }
273
+
274
+ $settings = wc_gateway_ppec()->settings;
275
+ $env = $settings->_environment;
276
+ if ( ! empty( $_GET['environment'] ) ) {
277
+ $env = $_GET['environment'];
278
+ }
279
+ $prefix = 'sandbox' === $env ? 'sandbox_' : '';
280
+
281
+ foreach ( array( 'api_username', 'api_password', 'api_signature', 'api_certificate' ) as $key ) {
282
+ $key = $prefix . $key;
283
+ $settings->{$key} = '';
284
+ }
285
+
286
+ // Save environment too as when it switches to another env and merchant
287
+ // click the reset they'd expect to save the environment too.
288
+ $settings->environment = 'sandbox' === $env ? 'sandbox' : 'live';
289
+
290
+ $settings->save();
291
+
292
+ wp_safe_redirect( wc_gateway_ppec()->get_admin_setting_link() );
293
+ }
294
  }
includes/class-wc-gateway-ppec-cart-handler.php CHANGED
@@ -11,28 +11,6 @@ if ( ! defined( 'ABSPATH' ) ) {
11
  */
12
  class WC_Gateway_PPEC_Cart_Handler {
13
 
14
- /**
15
- * TODO rename this to underscore var names
16
- */
17
- protected $orderTotal;
18
- protected $orderTax;
19
- protected $shipping;
20
- protected $insurance;
21
- protected $handling;
22
- protected $items;
23
- protected $totalItemAmount;
24
- protected $currency;
25
- protected $custom;
26
- protected $invoiceNumber;
27
- protected $shipDiscountAmount;
28
-
29
- /**
30
- * Currencies that support 0 decimal places -- "zero decimal place" currencies
31
- *
32
- * @var array
33
- */
34
- protected $zdp_currencies = array( 'HUF', 'JPY', 'TWD' );
35
-
36
  /**
37
  * Constructor.
38
  */
@@ -47,6 +25,9 @@ class WC_Gateway_PPEC_Cart_Handler {
47
  add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
48
  }
49
 
 
 
 
50
  public function before_cart_totals() {
51
  // If there then call start_checkout() else do nothing so page loads as normal.
52
  if ( ! empty( $_GET['startcheckout'] ) && 'true' === $_GET['startcheckout'] ) {
@@ -57,12 +38,17 @@ class WC_Gateway_PPEC_Cart_Handler {
57
  }
58
 
59
  /**
60
- * Display paypal button on the cart page
61
  */
62
  public function display_paypal_button() {
 
63
  $gateways = WC()->payment_gateways->get_available_payment_gateways();
64
  $settings = wc_gateway_ppec()->settings;
65
 
 
 
 
 
66
  if ( ! isset( $gateways['ppec_paypal'] ) ) {
67
  return;
68
  }
@@ -76,8 +62,14 @@ class WC_Gateway_PPEC_Cart_Handler {
76
  <?php endif; ?>
77
 
78
  <a href="<?php echo esc_url( add_query_arg( array( 'startcheckout' => 'true' ), wc_get_page_permalink( 'cart' ) ) ); ?>" id="woo_pp_ec_button" class="wcppec-checkout-buttons__button">
79
- <img src="<?php echo esc_url( 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-' . $settings->button_size . '.png' ); ?>" alt="<?php _e( 'Check out with PayPal', 'woocommerce-gateway-paypal-express-checkout' ); ?>" style="width: auto; height: auto;">
80
  </a>
 
 
 
 
 
 
81
  </div>
82
  <?php
83
  }
@@ -86,9 +78,10 @@ class WC_Gateway_PPEC_Cart_Handler {
86
  * Display paypal button on the cart widget
87
  */
88
  public function display_mini_paypal_button() {
 
89
  $gateways = WC()->payment_gateways->get_available_payment_gateways();
90
- $settings = wc_gateway_ppec()->settings;
91
 
 
92
  if ( ! isset( $gateways['ppec_paypal'] ) ) {
93
  return;
94
  }
@@ -121,263 +114,30 @@ class WC_Gateway_PPEC_Cart_Handler {
121
  'environment' => $settings->get_environment(),
122
  'locale' => $settings->get_paypal_locale(),
123
  'start_flow' => esc_url( add_query_arg( array( 'startcheckout' => 'true' ), wc_get_page_permalink( 'cart' ) ) ),
 
124
  )
125
  );
126
  }
127
  }
128
 
129
  /**
130
- * Load cart details.
131
  */
132
  public function loadCartDetails() {
133
-
134
- $this->totalItemAmount = 0;
135
- $this->items = array();
136
-
137
- // load all cart items into an array
138
- $roundedPayPalTotal = 0;
139
-
140
- $is_zdp_currency = in_array( get_woocommerce_currency(), $this->zdp_currencies );
141
- if ( $is_zdp_currency ) {
142
- $decimals = 0;
143
- } else {
144
- $decimals = 2;
145
- }
146
-
147
- $discounts = round( WC()->cart->get_cart_discount_total(), $decimals );
148
- foreach ( WC()->cart->cart_contents as $cart_item_key => $values ) {
149
- $amount = round( $values['line_subtotal'] / $values['quantity'] , $decimals );
150
- $item = array(
151
- 'name' => $values['data']->post->post_title,
152
- 'description' => $values['data']->post->post_content,
153
- 'quantity' => $values['quantity'],
154
- 'amount' => $amount,
155
- );
156
-
157
- $this->items[] = $item;
158
-
159
- $roundedPayPalTotal += round( $amount * $values['quantity'], $decimals );
160
- }
161
-
162
- $this->orderTax = round( WC()->cart->tax_total + WC()->cart->shipping_tax_total, $decimals );
163
- $this->shipping = round( WC()->cart->shipping_total, $decimals );
164
- $this->totalItemAmount = round( WC()->cart->cart_contents_total, $decimals ) + $discounts;
165
- $this->orderTotal = round( $this->totalItemAmount + $this->orderTax + $this->shipping, $decimals );
166
-
167
- // need to compare WC totals with what PayPal will calculate to see if they match
168
- // if they do not match, check to see what the merchant would like to do
169
- // options are to remove line items or add a line item to adjust for the difference
170
- if ( $this->totalItemAmount != $roundedPayPalTotal ) {
171
- if ( 'add' === wc_gateway_ppec()->settings->get_subtotal_mismatch_behavior() ) {
172
- // ...
173
- // Add line item to make up different between WooCommerce calculations and PayPal calculations
174
- $cartItemAmountDifference = $this->totalItemAmount - $roundedPayPalTotal;
175
-
176
- $modifyLineItem = array(
177
- 'name' => 'Line Item Amount Offset',
178
- 'description' => 'Adjust cart calculation discrepancy',
179
- 'quantity' => 1,
180
- 'amount' => round( $cartItemAmountDifference, $decimals )
181
- );
182
-
183
- $this->items[] = $modifyLineItem;
184
- $this->totalItemAmount += $modifyLineItem[ 'amount' ];
185
- $this->orderTotal += $modifyLineItem[ 'amount' ];
186
-
187
- } else {
188
- // ...
189
- // Omit line items altogether
190
- unset($this->items);
191
- }
192
-
193
- }
194
-
195
- // enter discount shenanigans. item total cannot be 0 so make modifications accordingly
196
- if ( $this->totalItemAmount == $discounts ) {
197
- // ...
198
- // Omit line items altogether
199
- unset($this->items);
200
- $this->shipDiscountAmount = 0;
201
- $this->totalItemAmount -= $discounts;
202
- $this->orderTotal -= $discounts;
203
- } else {
204
- // Build PayPal_Cart object as normal
205
- if ( $discounts > 0 ) {
206
- $discLineItem = array(
207
- 'name' => 'Discount',
208
- 'description' => 'Discount Amount',
209
- 'quantity' => 1,
210
- 'amount' => '-' . $discounts
211
- );
212
-
213
- $this->items[] = $discLineItem;
214
- }
215
-
216
- $this->shipDiscountAmount = 0;
217
- $this->totalItemAmount -= $discounts;
218
- $this->orderTotal -= $discounts;
219
- }
220
-
221
- // If the totals don't line up, adjust the tax to make it work (cause it's probably a tax mismatch).
222
- $wooOrderTotal = round( WC()->cart->total, $decimals );
223
- if( $wooOrderTotal != $this->orderTotal ) {
224
- $this->orderTax += $wooOrderTotal - $this->orderTotal;
225
- $this->orderTotal = $wooOrderTotal;
226
- }
227
-
228
- $this->orderTax = round( $this->orderTax, $decimals );
229
-
230
- // after all of the discount shenanigans, load up the other standard variables
231
- $this->insurance = 0;
232
- $this->handling = 0;
233
- $this->currency = get_woocommerce_currency();
234
- $this->custom = '';
235
- $this->invoiceNumber = '';
236
-
237
- if ( ! is_numeric( $this->shipping ) ) {
238
- $this->shipping = 0;
239
- }
240
  }
241
 
 
 
 
242
  public function loadOrderDetails( $order_id ) {
243
-
244
- $order = wc_get_order( $order_id );
245
- $this->totalItemAmount = 0;
246
- $this->items = array();
247
-
248
- // load all cart items into an array
249
- $roundedPayPalTotal = 0;
250
-
251
- $is_zdp_currency = in_array( get_woocommerce_currency(), $this->zdp_currencies );
252
- if ( $is_zdp_currency ) {
253
- $decimals = 0;
254
- } else {
255
- $decimals = 2;
256
- }
257
-
258
- $discounts = round( $order->get_total_discount(), $decimals );
259
- foreach ( $order->get_items() as $cart_item_key => $values ) {
260
- $amount = round( $values['line_subtotal'] / $values['qty'] , $decimals );
261
- $item = array(
262
- 'name' => $values['name'],
263
- 'quantity' => $values['qty'],
264
- 'amount' => $amount,
265
- );
266
-
267
- $this->items[] = $item;
268
-
269
- $roundedPayPalTotal += round( $amount * $values['qty'], $decimals );
270
- }
271
-
272
- $this->orderTax = round( $order->get_total_tax(), $decimals );
273
- $this->shipping = round( $order->get_total_shipping(), $decimals );
274
- // if ( $order->get_shipping_tax() != 0 ) {
275
- // $this->shipping += round( $order->get_shipping_tax(), $decimals );
276
- // }
277
- $this->totalItemAmount = round( $order->get_subtotal(), $decimals );
278
- $this->orderTotal = round( $this->totalItemAmount + $this->orderTax + $this->shipping, $decimals );
279
-
280
- // need to compare WC totals with what PayPal will calculate to see if they match
281
- // if they do not match, check to see what the merchant would like to do
282
- // options are to remove line items or add a line item to adjust for the difference
283
- if ( $this->totalItemAmount != $roundedPayPalTotal ) {
284
- if ( 'add' === wc_gateway_ppec()->settings->get_subtotal_mismatch_behavior() ) {
285
- // ...
286
- // Add line item to make up different between WooCommerce calculations and PayPal calculations
287
- $cartItemAmountDifference = $this->totalItemAmount - $roundedPayPalTotal;
288
-
289
- $modifyLineItem = array(
290
- 'name' => 'Line Item Amount Offset',
291
- 'description' => 'Adjust cart calculation discrepancy',
292
- 'quantity' => 1,
293
- 'amount' => round( $cartItemAmountDifference, $decimals )
294
- );
295
-
296
- $this->items[] = $modifyLineItem;
297
-
298
- } else {
299
- // ...
300
- // Omit line items altogether
301
- unset($this->items);
302
- }
303
-
304
- }
305
-
306
- // enter discount shenanigans. item total cannot be 0 so make modifications accordingly
307
- if ( $this->totalItemAmount == $discounts ) {
308
- // Omit line items altogether
309
- unset($this->items);
310
- $this->shipDiscountAmount = 0;
311
- $this->totalItemAmount -= $discounts;
312
- $this->orderTotal -= $discounts;
313
- } else {
314
- // Build PayPal_Cart object as normal
315
- if ( $discounts > 0 ) {
316
- $discLineItem = array(
317
- 'name' => 'Discount',
318
- 'description' => 'Discount Amount',
319
- 'quantity' => 1,
320
- 'amount' => '-' . $discounts
321
- );
322
-
323
- $this->items[] = $discLineItem;
324
- $this->totalItemAmount -= $discounts;
325
- $this->orderTotal -= $discounts;
326
- }
327
-
328
- $this->shipDiscountAmount = 0;
329
- }
330
-
331
- // If the totals don't line up, adjust the tax to make it work (cause it's probably a tax mismatch).
332
- $wooOrderTotal = round( $order->get_total(), $decimals );
333
- if( $wooOrderTotal != $this->orderTotal ) {
334
- $this->orderTax += $wooOrderTotal - $this->orderTotal;
335
- $this->orderTotal = $wooOrderTotal;
336
- }
337
-
338
- $this->orderTax = round( $this->orderTax, $decimals );
339
-
340
- // after all of the discount shenanigans, load up the other standard variables
341
- $this->insurance = 0;
342
- $this->handling = 0;
343
- $this->currency = get_woocommerce_currency();
344
- $this->custom = '';
345
- $this->invoiceNumber = '';
346
-
347
- if ( ! is_numeric( $this->shipping ) ) {
348
- $this->shipping = 0;
349
- }
350
  }
351
 
 
 
 
352
  public function setECParams() {
353
- $stdParams = array (
354
- 'PAYMENTREQUEST_0_AMT' => $this->orderTotal,
355
- 'PAYMENTREQUEST_0_CURRENCYCODE' => $this->currency,
356
- 'PAYMENTREQUEST_0_ITEMAMT' => $this->totalItemAmount,
357
- 'PAYMENTREQUEST_0_SHIPPINGAMT' => $this->shipping,
358
- 'PAYMENTREQUEST_0_INSURANCEAMT' => $this->insurance,
359
- 'PAYMENTREQUEST_0_HANDLINGAMT' => $this->handling,
360
- 'PAYMENTREQUEST_0_TAXAMT' => $this->orderTax,
361
- 'PAYMENTREQUEST_0_CUSTOM' => $this->custom,
362
- 'PAYMENTREQUEST_0_INVNUM' => $this->invoiceNumber,
363
- 'PAYMENTREQUEST_0_SHIPDISCAMT' => $this->shipDiscountAmount,
364
- 'NOSHIPPING' => WC()->cart->needs_shipping() ? 0 : 1,
365
- );
366
-
367
- if ( ! empty( $this->items ) ) {
368
- $count = 0;
369
- foreach ( $this->items as $line_item_key => $values ) {
370
- $lineItemParams = array(
371
- 'L_PAYMENTREQUEST_0_NAME' . $count => $values['name'],
372
- 'L_PAYMENTREQUEST_0_DESC' . $count => ! empty( $values['description'] ) ? strip_tags( $values['description'] ) : '',
373
- 'L_PAYMENTREQUEST_0_QTY' . $count => $values['quantity'],
374
- 'L_PAYMENTREQUEST_0_AMT' . $count => $values['amount']
375
- );
376
-
377
- $stdParams = array_merge( $stdParams, $lineItemParams );
378
- $count++;
379
- }
380
- }
381
- return $stdParams;
382
  }
383
  }
11
  */
12
  class WC_Gateway_PPEC_Cart_Handler {
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  /**
15
  * Constructor.
16
  */
25
  add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
26
  }
27
 
28
+ /**
29
+ * Start checkout handler when cart is loaded.
30
+ */
31
  public function before_cart_totals() {
32
  // If there then call start_checkout() else do nothing so page loads as normal.
33
  if ( ! empty( $_GET['startcheckout'] ) && 'true' === $_GET['startcheckout'] ) {
38
  }
39
 
40
  /**
41
+ * Display paypal button on the cart page.
42
  */
43
  public function display_paypal_button() {
44
+
45
  $gateways = WC()->payment_gateways->get_available_payment_gateways();
46
  $settings = wc_gateway_ppec()->settings;
47
 
48
+ $express_checkout_img_url = apply_filters( 'woocommerce_paypal_express_checkout_button_img_url', sprintf( 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-%s.png', $settings->button_size ) );
49
+ $paypal_credit_img_url = apply_filters( 'woocommerce_paypal_express_checkout_credit_button_img_url', sprintf( 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/ppcredit-logo-%s.png', $settings->button_size ) );
50
+
51
+ // billing details on checkout page to calculate shipping costs
52
  if ( ! isset( $gateways['ppec_paypal'] ) ) {
53
  return;
54
  }
62
  <?php endif; ?>
63
 
64
  <a href="<?php echo esc_url( add_query_arg( array( 'startcheckout' => 'true' ), wc_get_page_permalink( 'cart' ) ) ); ?>" id="woo_pp_ec_button" class="wcppec-checkout-buttons__button">
65
+ <img src="<?php echo esc_url( $express_checkout_img_url ); ?>" alt="<?php _e( 'Check out with PayPal', 'woocommerce-gateway-paypal-express-checkout' ); ?>" style="width: auto; height: auto;">
66
  </a>
67
+
68
+ <?php if ( $settings->is_credit_enabled() ) : ?>
69
+ <a href="<?php echo esc_url( add_query_arg( array( 'startcheckout' => 'true', 'use-ppc' => 'true' ), wc_get_page_permalink( 'cart' ) ) ); ?>" id="woo_pp_ppc_button" class="wcppec-checkout-buttons__button">
70
+ <img src="<?php echo esc_url( $paypal_credit_img_url ); ?>" alt="<?php _e( 'Pay with PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' ); ?>" style="width: auto; height: auto;">
71
+ </a>
72
+ <?php endif; ?>
73
  </div>
74
  <?php
75
  }
78
  * Display paypal button on the cart widget
79
  */
80
  public function display_mini_paypal_button() {
81
+
82
  $gateways = WC()->payment_gateways->get_available_payment_gateways();
 
83
 
84
+ // billing details on checkout page to calculate shipping costs
85
  if ( ! isset( $gateways['ppec_paypal'] ) ) {
86
  return;
87
  }
114
  'environment' => $settings->get_environment(),
115
  'locale' => $settings->get_paypal_locale(),
116
  'start_flow' => esc_url( add_query_arg( array( 'startcheckout' => 'true' ), wc_get_page_permalink( 'cart' ) ) ),
117
+ 'show_modal' => apply_filters( 'woocommerce_paypal_express_checkout_show_cart_modal', true ),
118
  )
119
  );
120
  }
121
  }
122
 
123
  /**
124
+ * @deprecated
125
  */
126
  public function loadCartDetails() {
127
+ _deprecated_function( __METHOD__, '1.2.0', '' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  }
129
 
130
+ /**
131
+ * @deprecated
132
+ */
133
  public function loadOrderDetails( $order_id ) {
134
+ _deprecated_function( __METHOD__, '1.2.0', '' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  }
136
 
137
+ /**
138
+ * @deprecated
139
+ */
140
  public function setECParams() {
141
+ _deprecated_function( __METHOD__, '1.2.0', '' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  }
143
  }
includes/class-wc-gateway-ppec-checkout-details.php CHANGED
@@ -13,6 +13,7 @@ $includes_path = wc_gateway_ppec()->includes_path;
13
  require_once( $includes_path . 'class-wc-gateway-ppec-address.php' );
14
 
15
  class PayPal_Checkout_Details {
 
16
  public $token = false;
17
  public $custom = false;
18
  public $invnum = false;
@@ -47,6 +48,8 @@ class PayPal_Checkout_Details {
47
  public $payments = false;
48
 
49
  public function loadFromGetECResponse( $getECResponse ) {
 
 
50
  $map = array(
51
  'TOKEN' => 'token',
52
  'CUSTOM' => 'custom',
13
  require_once( $includes_path . 'class-wc-gateway-ppec-address.php' );
14
 
15
  class PayPal_Checkout_Details {
16
+ public $raw_response = array();
17
  public $token = false;
18
  public $custom = false;
19
  public $invnum = false;
48
  public $payments = false;
49
 
50
  public function loadFromGetECResponse( $getECResponse ) {
51
+ $this->raw_response = $getECResponse;
52
+
53
  $map = array(
54
  'TOKEN' => 'token',
55
  'CUSTOM' => 'custom',
includes/class-wc-gateway-ppec-checkout-handler.php CHANGED
@@ -24,16 +24,21 @@ require_once( $includes_path . 'class-wc-gateway-ppec-address.php' );
24
 
25
  class WC_Gateway_PPEC_Checkout_Handler {
26
 
27
- // $_shippingAddress can be a single PayPal_Address object, or an array of PayPal_Address objects
28
- // (for the purposes of doing parallel payments).
29
- protected $_shippingAddress;
 
 
 
 
 
30
 
31
  public function __construct() {
32
- $this->_shippingAddress = false;
33
-
34
  add_action( 'init', array( $this, 'init' ) );
35
  add_filter( 'the_title', array( $this, 'endpoint_page_titles' ) );
36
  add_action( 'woocommerce_checkout_init', array( $this, 'checkout_init' ) );
 
 
37
 
38
  add_action( 'wp', array( $this, 'maybe_return_from_paypal' ) );
39
  add_action( 'wp', array( $this, 'maybe_cancel_checkout_with_paypal' ) );
@@ -41,6 +46,8 @@ class WC_Gateway_PPEC_Checkout_Handler {
41
 
42
  add_action( 'woocommerce_available_payment_gateways', array( $this, 'maybe_disable_other_gateways' ) );
43
  add_action( 'woocommerce_review_order_after_submit', array( $this, 'maybe_render_cancel_link' ) );
 
 
44
  }
45
 
46
  /**
@@ -70,32 +77,122 @@ class WC_Gateway_PPEC_Checkout_Handler {
70
  }
71
 
72
  /**
73
- * Prepare billing and shipping details if there's active sesssion during checkout.
 
 
 
 
74
  *
75
  * @param WC_Checkout $checkout
76
  */
77
  function checkout_init( $checkout ) {
78
- global $wp_query, $wp;
 
 
79
 
80
- if ( $this->has_active_session() ) {
81
- // We don't neeed billing and shipping to confirm a paypal order.
82
- $checkout->checkout_fields['billing'] = array();
83
- $checkout->checkout_fields['shipping'] = array();
 
 
 
 
 
 
 
84
 
85
- remove_action( 'woocommerce_checkout_billing', array( $checkout, 'checkout_form_billing' ) );
86
- remove_action( 'woocommerce_checkout_shipping', array( $checkout, 'checkout_form_shipping' ) );
87
- add_action( 'woocommerce_checkout_billing', array( $this, 'paypal_billing_details' ) );
88
- add_action( 'woocommerce_checkout_shipping', array( $this, 'paypal_shipping_details' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
 
 
90
  }
91
 
92
  /**
93
- * Show billing information.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  */
95
  public function paypal_billing_details() {
96
  $session = WC()->session->get( 'paypal' );
97
  $token = isset( $_GET['token'] ) ? $_GET['token'] : $session->token;
98
- $checkout_details = $this->getCheckoutDetails( $token );
 
 
 
 
 
99
  ?>
100
  <h3><?php _e( 'Billing details', 'woocommerce-gateway-paypal-express-checkout' ); ?></h3>
101
  <ul>
@@ -117,14 +214,66 @@ class WC_Gateway_PPEC_Checkout_Handler {
117
  }
118
 
119
  /**
120
- * Show shipping information.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  */
122
  public function paypal_shipping_details() {
123
  $session = WC()->session->get( 'paypal' );
124
  $token = isset( $_GET['token'] ) ? $_GET['token'] : $session->token;
125
- $checkout_details = $this->getCheckoutDetails( $token );
126
 
127
- if ( ! $session->needs_shipping ) {
 
 
 
 
 
 
 
128
  return;
129
  }
130
  ?>
@@ -133,8 +282,16 @@ class WC_Gateway_PPEC_Checkout_Handler {
133
  echo WC()->countries->get_formatted_address( $this->get_mapped_shipping_address( $checkout_details ) );
134
  }
135
 
 
 
 
 
 
 
 
136
  /**
137
  * Map PayPal billing address to WC shipping address
 
138
  * @param object $checkout_details
139
  * @return array
140
  */
@@ -142,6 +299,7 @@ class WC_Gateway_PPEC_Checkout_Handler {
142
  if ( empty( $checkout_details->payer_details ) ) {
143
  return array();
144
  }
 
145
  return array(
146
  'first_name' => $checkout_details->payer_details->first_name,
147
  'last_name' => $checkout_details->payer_details->last_name,
@@ -158,14 +316,16 @@ class WC_Gateway_PPEC_Checkout_Handler {
158
  }
159
 
160
  /**
161
- * Map PayPal shipping address to WC shipping address
162
- * @param object $checkout_details
 
163
  * @return array
164
  */
165
  public function get_mapped_shipping_address( $checkout_details ) {
166
  if ( empty( $checkout_details->payments[0] ) || empty( $checkout_details->payments[0]->shipping_address ) ) {
167
  return array();
168
  }
 
169
  $name = explode( ' ', $checkout_details->payments[0]->shipping_address->getName() );
170
  $first_name = array_shift( $name );
171
  $last_name = implode( ' ', $name );
@@ -190,34 +350,36 @@ class WC_Gateway_PPEC_Checkout_Handler {
190
  return;
191
  }
192
 
193
- $token = $_GET['token'];
194
- $payer_id = $_GET['PayerID'];
195
- $session = WC()->session->get( 'paypal' );
 
196
 
197
  if ( empty( $session ) || $this->session_has_expired( $token ) ) {
198
  wc_add_notice( __( 'Your PayPal checkout session has expired. Please check out again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
199
  return;
200
  }
201
 
202
- // Store values in session
203
  $session->checkout_completed = true;
204
- $session->payerID = $payer_id;
205
  WC()->session->set( 'paypal', $session );
206
 
207
  try {
208
- $checkout_details = $this->getCheckoutDetails( $token );
209
-
210
  // If commit was true, take payment right now
211
  if ( 'order' === $session->source && $session->order_id ) {
 
212
 
213
  // Get order
214
  $order = wc_get_order( $session->order_id );
215
 
216
- // Store address given by PayPal
217
- $order->set_address( $this->get_mapped_shipping_address( $checkout_details ), 'shipping' );
 
 
218
 
219
  // Complete the payment now.
220
- $this->do_payment( $order, $session->token, $session->payerID );
221
 
222
  // Clear Cart
223
  WC()->cart->empty_cart();
@@ -226,12 +388,12 @@ class WC_Gateway_PPEC_Checkout_Handler {
226
  wp_redirect( $order->get_checkout_order_received_url() );
227
  exit;
228
  }
229
- } catch( PayPal_API_Exception $e ) {
230
  wc_add_notice( __( 'Sorry, an error occurred while trying to retrieve your information from PayPal. Please try again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
231
  $this->maybe_clear_session_data();
232
  wp_safe_redirect( wc_get_page_permalink( 'cart' ) );
233
  exit;
234
- } catch( PayPal_Missing_Session_Exception $e ) {
235
  wc_add_notice( __( 'Your PayPal checkout session has expired. Please check out again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
236
  $this->maybe_clear_session_data();
237
  wp_safe_redirect( wc_get_page_permalink( 'cart' ) );
@@ -278,17 +440,24 @@ class WC_Gateway_PPEC_Checkout_Handler {
278
  if ( $this->has_active_session() ) {
279
  printf(
280
  '<a href="%s" class="wc-gateway-ppec-cancel">%s</a>',
281
- esc_url( add_query_arg( 'wc-gateway-ppec-clear-session', true, WC()->cart->get_cart_url() ) ),
282
  esc_html__( 'Cancel', 'woocommerce-gateway-paypal-express-checkout' )
283
  );
284
  }
285
  }
286
 
 
 
 
 
 
287
  public function maybe_cancel_checkout_with_paypal() {
288
  if ( is_cart() && ! empty( $_GET['wc-gateway-ppec-clear-session'] ) ) {
289
  $this->maybe_clear_session_data();
290
- if ( ! wc_has_notice( __( 'You have cancelled Checkout with PayPal. Please try to process your order again.', 'woocommerce-gateway-paypal-express-checkout' ), 'notice' ) ) {
291
- wc_add_notice( __( 'You have cancelled Checkout with PayPal. Please try to process your order again.', 'woocommerce-gateway-paypal-express-checkout' ), 'notice' );
 
 
292
  }
293
  }
294
  }
@@ -327,151 +496,81 @@ class WC_Gateway_PPEC_Checkout_Handler {
327
  * @return bool Returns true if PPEC session exists and still valid
328
  */
329
  public function has_active_session() {
 
 
 
 
330
  $session = WC()->session->paypal;
331
- return ( is_a( $session, 'WC_Gateway_PPEC_Session_Data' ) && $session->payerID && $session->expiry_time > time() );
332
  }
333
 
334
  /**
335
- * Get token from session.
336
- *
337
- * @since 1.0.0
338
- *
339
- * @return string Token from session
340
  */
341
  public function get_token_from_session() {
342
- $token = '';
343
- $session = WC()->session->paypal;
344
-
345
- if ( is_a( $session, 'WC_Gateway_PPEC_Session_Data' ) && $session->token ) {
346
- $token = $session->token;
347
- }
348
-
349
- return $token;
350
  }
351
 
 
 
 
352
  public function setShippingAddress( $address ) {
353
- if ( is_a( $address, 'PayPal_Address' ) ) {
354
- $this->_shippingAddress = $address;
355
- }
356
- if ( is_array( $address ) ) {
357
- // Check each of the elements to make sure they're all PayPal_Address objects as well
358
- foreach ( $address as $index => $value ) {
359
- if ( ! is_a( $value, 'PayPal_Address' ) ) {
360
- return;
361
- }
362
- // And also check to make sure we're not exceeding the maximum number of parallel
363
- // payments PayPal will allow
364
- if ( ! is_int( $index ) || $value > 9 ) {
365
- return;
366
- }
367
- }
368
-
369
- $this->_shippingAddress = $address;
370
- }
371
  }
372
 
 
 
 
373
  public function getSetExpressCheckoutParameters() {
374
- // First off, get the cart parameters
375
- $params = wc_gateway_ppec()->cart->setECParams();
376
-
377
- if ( false !== $this->_shippingAddress ) {
378
- if ( is_array( $this->_shippingAddress ) ) {
379
- foreach ( $this->_shippingAddress as $index => $value ) {
380
- $params = array_merge( $params, $value->getAddressParams( 'PAYMENTREQUEST_' . $index . '_SHIPTO' ) );
381
- }
382
- } else {
383
- $params = array_merge( $params, $this->_shippingAddress->getAddressParams( 'PAYMENTREQUEST_0_SHIPTO' ) );
384
- }
385
- }
386
-
387
- return $params;
388
  }
389
 
 
 
 
390
  public function getDoExpressCheckoutParameters( $token, $payer_id ) {
391
- $params = wc_gateway_ppec()->cart->setECParams();
392
-
393
- if ( false !== $this->_shippingAddress ) {
394
- if ( is_array( $this->_shippingAddress ) ) {
395
- foreach ( $this->_shippingAddress as $index => $value ) {
396
- $params = array_merge( $params, $value->getAddressParams( 'PAYMENTREQUEST_' . $index . '_SHIPTO' ) );
397
- }
398
- } else {
399
- $params = array_merge( $params, $this->_shippingAddress->getAddressParams( 'PAYMENTREQUEST_0_SHIPTO' ) );
400
- }
401
- }
402
-
403
- $params['TOKEN'] = $token;
404
- $params['PAYERID'] = $payer_id;
405
-
406
- return $params;
407
  }
408
 
409
  /**
410
- * Whether PayPal response indicates an okay message.
411
- *
412
- * @param array $response Response from PayPal
413
- *
414
- * @return bool True if it's okay
415
  */
416
  protected function is_success( $response ) {
417
- return (
418
- isset( $response['ACK'] )
419
- &&
420
- in_array( $response['ACK'], array( 'Success', 'SuccessWithWarning' ) )
421
- );
422
- }
423
 
424
- /**
425
- * Get return URL.
426
- *
427
- * The URL to return from express checkout.
428
- *
429
- * @return string Return URL
430
- */
431
- protected function get_return_url() {
432
- return add_query_arg( 'woo-paypal-return', 'true', WC()->cart->get_checkout_url() );
433
  }
434
 
435
  /**
436
- * Get cancel URL.
437
  *
438
- * The URL to return when canceling the express checkout.
 
439
  *
440
- * @return string Cancel URL
441
  */
442
- protected function get_cancel_url() {
443
- return add_query_arg( 'woo-paypal-cancel', 'true', WC()->cart->get_cart_url() );
444
- }
445
-
446
  public function start_checkout_from_cart() {
447
-
448
- wc_gateway_ppec()->cart->loadCartDetails();
449
-
450
- $settings = wc_gateway_ppec()->settings;
451
-
452
- $params = array_merge(
453
- $settings->get_set_express_checkout_shortcut_params(),
454
- $this->getSetExpressCheckoutParameters()
455
  );
 
456
 
457
- $brand_name = get_bloginfo( 'name', 'display' );
458
- if ( ! empty( $brand_name ) ) {
459
- $brand_name = substr( $brand_name, 0, 127 );
460
- $params['BRANDNAME'] = $brand_name;
461
- }
462
-
463
- $params['RETURNURL'] = $this->get_return_url();
464
- $params['CANCELURL'] = $this->get_cancel_url();
465
-
466
- $response = wc_gateway_ppec()->client->set_express_checkout( $params );
467
- if ( $this->is_success( $response ) ) {
468
- // Save some data to the session.
469
  WC()->session->paypal = new WC_Gateway_PPEC_Session_Data(
470
  array(
471
- 'token' => $response['TOKEN'],
472
- 'source' => 'cart',
473
- 'needs_shipping' => WC()->cart->needs_shipping(),
474
- 'expires_in' => $settings->get_token_session_length()
475
  )
476
  );
477
 
@@ -481,64 +580,34 @@ class WC_Gateway_PPEC_Checkout_Handler {
481
  }
482
  }
483
 
 
 
 
 
 
 
 
 
 
 
484
  public function start_checkout_from_checkout( $order_id ) {
485
-
486
- wc_gateway_ppec()->cart->loadOrderDetails( $order_id );
487
-
488
- $settings = wc_gateway_ppec()->settings;
489
-
490
- //new wc order > get address from that order > new pp address > assign address from order to new pp address > $this->setShippingAddress(pp address object)
491
- $getAddress = wc_get_order( $order_id );
492
- $shipAddressName = $getAddress->shipping_first_name . ' ' . $getAddress->shipping_last_name;
493
-
494
-
495
- $shipAddress = new PayPal_Address;
496
- $shipAddress->setName($shipAddressName);
497
- $shipAddress->setStreet1($getAddress->shipping_address_1);
498
- $shipAddress->setStreet2($getAddress->shipping_address_2);
499
- $shipAddress->setCity($getAddress->shipping_city);
500
- $shipAddress->setState($getAddress->shipping_state);
501
- $shipAddress->setZip($getAddress->shipping_postcode);
502
-
503
- // In case merchant only expects domestic shipping and hides shipping
504
- // country, fallback to base country.
505
- //
506
- // @see https://github.com/woothemes/woocommerce-gateway-paypal-express-checkout/issues/139
507
- $shipping_country = $getAddress->shipping_country;
508
- if ( empty( $shipping_country ) ) {
509
- $shipping_country = WC()->countries->get_base_country();
510
- }
511
- $shipAddress->setCountry( $shipping_country );
512
-
513
- $this->setShippingAddress( $shipAddress );
514
-
515
- // Do we also need to grab the phone number and pass it through?
516
-
517
- $params = array_merge(
518
- $settings->get_set_express_checkout_mark_params(),
519
- $this->getSetExpressCheckoutParameters()
520
  );
 
521
 
522
- $brand_name = get_bloginfo( 'name', 'display' );
523
- if ( ! empty( $brand_name ) ) {
524
- $brand_name = substr( $brand_name, 0, 127 );
525
- $params['BRANDNAME'] = $brand_name;
526
- }
527
-
528
- $params['RETURNURL'] = $this->get_return_url();
529
- $params['CANCELURL'] = $this->get_cancel_url();
530
- $params['ADDROVERRIDE'] = '1';
531
- $response = wc_gateway_ppec()->client->set_express_checkout( $params );
532
-
533
- if ( $this->is_success( $response ) ) {
534
- // Save some data to the session.
535
  WC()->session->paypal = new WC_Gateway_PPEC_Session_Data(
536
  array(
537
- 'token' => $response['TOKEN'],
538
- 'source' => 'order',
539
- 'order_id' => $order_id,
540
- 'needs_shipping' => WC()->cart->needs_shipping(),
541
- 'expires_in' => $settings->get_token_session_length()
542
  )
543
  );
544
 
@@ -546,50 +615,137 @@ class WC_Gateway_PPEC_Checkout_Handler {
546
  } else {
547
  throw new PayPal_API_Exception( $response );
548
  }
 
 
 
 
 
 
 
 
 
 
 
549
 
 
 
 
 
 
550
  }
551
 
 
 
 
552
  public function getCheckoutDetails( $token = false ) {
553
- if ( false === $token ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
  $token = $_GET['token'];
555
  }
556
 
557
- $response = wc_gateway_ppec()->client->get_express_checkout_details( $token );
 
558
 
559
- if ( $this->is_success( $response ) ) {
560
  $checkout_details = new PayPal_Checkout_Details();
561
  $checkout_details->loadFromGetECResponse( $response );
 
 
562
  return $checkout_details;
563
  } else {
564
  throw new PayPal_API_Exception( $response );
565
  }
566
  }
567
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
568
  /**
569
  * Complete a payment that has been authorized via PPEC.
570
  */
571
- public function do_payment( $order, $token, $payerID ) {
572
  $settings = wc_gateway_ppec()->settings;
573
  $session_data = WC()->session->get( 'paypal', null );
574
 
575
- if ( ! $order || null === $session_data || $this->session_has_expired( $token ) || empty( $payerID ) ) {
576
  throw new PayPal_Missing_Session_Exception();
577
  }
578
 
579
- // Ensure details are set
580
- wc_gateway_ppec()->cart->loadOrderDetails( $order->id );
581
-
582
- // Generate params to send to paypal, then do request
583
- $response = wc_gateway_ppec()->client->do_express_checkout_payment( array_merge(
584
- $this->getDoExpressCheckoutParameters( $token, $payerID ),
585
- $settings->get_do_express_checkout_params( $order )
586
  ) );
587
 
588
- if ( $this->is_success( $response ) ) {
 
 
589
  $payment_details = new PayPal_Payment_Details();
590
  $payment_details->loadFromDoECResponse( $response );
591
 
592
- $meta = get_post_meta( $order->id, '_woo_pp_txnData', true );
593
  if ( ! empty( $meta ) ) {
594
  $txnData = $meta;
595
  } else {
@@ -616,7 +772,11 @@ class WC_Gateway_PPEC_Checkout_Handler {
616
 
617
  $txnData['txn_type'] = $paymentAction;
618
 
619
- update_post_meta( $order->id, '_woo_pp_txnData', $txnData );
 
 
 
 
620
 
621
  // Payment was taken so clear session
622
  $this->maybe_clear_session_data();
@@ -633,8 +793,14 @@ class WC_Gateway_PPEC_Checkout_Handler {
633
  */
634
  public function handle_payment_response( $order, $payment ) {
635
  // Store meta data to order
636
- update_post_meta( $order->id, '_paypal_status', strtolower( $payment->payment_status ) );
637
- update_post_meta( $order->id, '_transaction_id', $payment->transaction_id );
 
 
 
 
 
 
638
 
639
  // Handle $payment response
640
  if ( 'completed' === strtolower( $payment->payment_status ) ) {
@@ -645,7 +811,80 @@ class WC_Gateway_PPEC_Checkout_Handler {
645
  } else {
646
  $order->update_status( 'on-hold', sprintf( __( 'Payment pending (%s).', 'woocommerce-gateway-paypal-express-checkout' ), $payment->pending_reason ) );
647
  }
648
- $order->reduce_order_stock();
 
 
 
 
649
  }
650
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
651
  }
24
 
25
  class WC_Gateway_PPEC_Checkout_Handler {
26
 
27
+ /**
28
+ * Cached result from self::get_checkout_defails.
29
+ *
30
+ * @since 1.2.0
31
+ *
32
+ * @var PayPal_Checkout_Details
33
+ */
34
+ protected $_checkout_details;
35
 
36
  public function __construct() {
 
 
37
  add_action( 'init', array( $this, 'init' ) );
38
  add_filter( 'the_title', array( $this, 'endpoint_page_titles' ) );
39
  add_action( 'woocommerce_checkout_init', array( $this, 'checkout_init' ) );
40
+ add_filter( 'woocommerce_billing_fields', array( $this, 'filter_billing_fields' ) );
41
+ add_action( 'woocommerce_checkout_process', array( $this, 'copy_checkout_details_to_post' ) );
42
 
43
  add_action( 'wp', array( $this, 'maybe_return_from_paypal' ) );
44
  add_action( 'wp', array( $this, 'maybe_cancel_checkout_with_paypal' ) );
46
 
47
  add_action( 'woocommerce_available_payment_gateways', array( $this, 'maybe_disable_other_gateways' ) );
48
  add_action( 'woocommerce_review_order_after_submit', array( $this, 'maybe_render_cancel_link' ) );
49
+
50
+ add_action( 'woocommerce_cart_shipping_packages', array( $this, 'maybe_add_shipping_information' ) );
51
  }
52
 
53
  /**
77
  }
78
 
79
  /**
80
+ * If there's an active PayPal session during checkout (e.g. if the customer started checkout
81
+ * with PayPal from the cart), import billing and shipping details from PayPal using the
82
+ * token we have for the customer.
83
+ *
84
+ * Hooked to the woocommerce_checkout_init action
85
  *
86
  * @param WC_Checkout $checkout
87
  */
88
  function checkout_init( $checkout ) {
89
+ if ( ! $this->has_active_session() ) {
90
+ return;
91
+ }
92
 
93
+ // Since we've removed the billing and shipping checkout fields, we should also remove the
94
+ // billing and shipping portion of the checkout form
95
+ remove_action( 'woocommerce_checkout_billing', array( $checkout, 'checkout_form_billing' ) );
96
+ remove_action( 'woocommerce_checkout_shipping', array( $checkout, 'checkout_form_shipping' ) );
97
+
98
+ // Lastly, let's add back in 1) displaying customer details from PayPal, 2) allow for
99
+ // account registration and 3) shipping details from PayPal
100
+ add_action( 'woocommerce_checkout_billing', array( $this, 'paypal_billing_details' ) );
101
+ add_action( 'woocommerce_checkout_billing', array( $this, 'account_registration' ) );
102
+ add_action( 'woocommerce_checkout_shipping', array( $this, 'paypal_shipping_details' ) );
103
+ }
104
 
105
+ /**
106
+ * Since PayPal doesn't always give us the phone number for the buyer, we need to make
107
+ * that field not required. And if the cart doesn't need shipping at all, don't require
108
+ * the address fields either (this is unique to PPEC)
109
+ *
110
+ * @since 1.2.0
111
+ * @param $billing_fields array
112
+ *
113
+ * @return array
114
+ */
115
+ public function filter_billing_fields( $billing_fields ) {
116
+ if ( array_key_exists( 'billing_phone', $billing_fields ) ) {
117
+ $billing_fields['billing_phone']['required'] = false;
118
+ };
119
+
120
+ if ( ! WC()->cart->needs_shipping() ) {
121
+ $not_required_fields = array( 'billing_address_1', 'billing_city', 'billing_state', 'billing_postcode' );
122
+ foreach ( $not_required_fields as $not_required_field ) {
123
+ if ( array_key_exists( $not_required_field, $billing_fields ) ) {
124
+ $billing_fields[ $not_required_field ]['required'] = false;
125
+ }
126
+ }
127
  }
128
+
129
+ return $billing_fields;
130
  }
131
 
132
  /**
133
+ * When an active session is present, gets (from PayPal) the buyer details
134
+ * and replaces the appropriate checkout fields in $_POST
135
+ *
136
+ * Hooked to woocommerce_checkout_process
137
+ *
138
+ * @since 1.2.0
139
+ */
140
+ public function copy_checkout_details_to_post() {
141
+ if ( ! $this->has_active_session() ) {
142
+ return;
143
+ }
144
+
145
+ // Make sure the selected payment method is ppec_paypal
146
+ if ( ! isset( $_POST['payment_method'] ) || ( 'ppec_paypal' !== $_POST['payment_method'] ) ) {
147
+ return;
148
+ }
149
+
150
+ // Get the buyer details from PayPal
151
+ try {
152
+ $session = WC()->session->get( 'paypal' );
153
+ $token = isset( $_GET['token'] ) ? $_GET['token'] : $session->token;
154
+ $checkout_details = $this->get_checkout_details( $token );
155
+ } catch ( PayPal_API_Exception $e ) {
156
+ wc_add_notice( $e->getMessage(), 'error' );
157
+ return;
158
+ }
159
+
160
+ $shipping_details = $this->get_mapped_shipping_address( $checkout_details );
161
+ foreach( $shipping_details as $key => $value ) {
162
+ $_POST['shipping_' . $key] = $value;
163
+ }
164
+
165
+ $billing_details = $this->get_mapped_billing_address( $checkout_details );
166
+ // If the billing address is empty, copy address from shipping
167
+ if ( empty( $billing_details['address_1'] ) ) {
168
+ $copyable_keys = array( 'address_1', 'address_2', 'city', 'state', 'postcode', 'country' );
169
+ foreach ( $copyable_keys as $copyable_key ) {
170
+ if ( array_key_exists( $copyable_key, $shipping_details ) ) {
171
+ $billing_details[ $copyable_key ] = $shipping_details[ $copyable_key ];
172
+ }
173
+ }
174
+ }
175
+ foreach( $billing_details as $key => $value ) {
176
+ $_POST['billing_' . $key] = $value;
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Show billing information obtained from PayPal. This replaces the billing fields
182
+ * that the customer would ordinarily fill in. Should only happen if we have an active
183
+ * session (e.g. if the customer started checkout with PayPal from their cart.)
184
+ *
185
+ * Is hooked to woocommerce_checkout_billing action by checkout_init
186
  */
187
  public function paypal_billing_details() {
188
  $session = WC()->session->get( 'paypal' );
189
  $token = isset( $_GET['token'] ) ? $_GET['token'] : $session->token;
190
+ try {
191
+ $checkout_details = $this->get_checkout_details( $token );
192
+ } catch ( PayPal_API_Exception $e ) {
193
+ wc_add_notice( $e->getMessage(), 'error' );
194
+ return;
195
+ }
196
  ?>
197
  <h3><?php _e( 'Billing details', 'woocommerce-gateway-paypal-express-checkout' ); ?></h3>
198
  <ul>
214
  }
215
 
216
  /**
217
+ * If there is an active session (e.g. the customer initiated checkout from the cart), since we
218
+ * removed the checkout_form_billing action, we need to put a registration form back in to
219
+ * allow the customer to create an account.
220
+ *
221
+ * Is hooked to woocommerce_checkout_billing action by checkout_init
222
+ * @since 1.2.0
223
+ */
224
+ public function account_registration() {
225
+ $checkout = WC()->checkout();
226
+
227
+ if ( ! is_user_logged_in() && $checkout->enable_signup ) {
228
+
229
+ if ( $checkout->enable_guest_checkout ) {
230
+ ?>
231
+ <p class="form-row form-row-wide create-account">
232
+ <input class="input-checkbox" id="createaccount" <?php checked( ( true === $checkout->get_value( 'createaccount' ) || ( true === apply_filters( 'woocommerce_create_account_default_checked', false ) ) ), true) ?> type="checkbox" name="createaccount" value="1" /> <label for="createaccount" class="checkbox"><?php _e( 'Create an account?', '' ); ?></label>
233
+ </p>
234
+ <?php
235
+ }
236
+
237
+ if ( ! empty( $checkout->checkout_fields['account'] ) ) {
238
+ ?>
239
+ <div class="create-account">
240
+
241
+ <p><?php _e( 'Create an account by entering the information below. If you are a returning customer please login at the top of the page.', 'woocommerce' ); ?></p>
242
+
243
+ <?php foreach ( $checkout->checkout_fields['account'] as $key => $field ) : ?>
244
+
245
+ <?php woocommerce_form_field( $key, $field, $checkout->get_value( $key ) ); ?>
246
+
247
+ <?php endforeach; ?>
248
+
249
+ <div class="clear"></div>
250
+
251
+ </div>
252
+ <?php
253
+ }
254
+
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Show shipping information obtained from PayPal. This replaces the shipping fields
260
+ * that the customer would ordinarily fill in. Should only happen if we have an active
261
+ * session (e.g. if the customer started checkout with PayPal from their cart.)
262
+ *
263
+ * Is hooked to woocommerce_checkout_shipping action by checkout_init
264
  */
265
  public function paypal_shipping_details() {
266
  $session = WC()->session->get( 'paypal' );
267
  $token = isset( $_GET['token'] ) ? $_GET['token'] : $session->token;
 
268
 
269
+ try {
270
+ $checkout_details = $this->get_checkout_details( $token );
271
+ } catch ( PayPal_API_Exception $e ) {
272
+ wc_add_notice( $e->getMessage(), 'error' );
273
+ return;
274
+ }
275
+
276
+ if ( ! WC()->cart->needs_shipping() ) {
277
  return;
278
  }
279
  ?>
282
  echo WC()->countries->get_formatted_address( $this->get_mapped_shipping_address( $checkout_details ) );
283
  }
284
 
285
+ /**
286
+ * @deprecated 1.2.0
287
+ */
288
+ public function after_checkout_validation( $posted_checkout ) {
289
+ _deprecated_function( 'after_checkout_validation', '1.2.0', '' );
290
+ }
291
+
292
  /**
293
  * Map PayPal billing address to WC shipping address
294
+ * NOTE: Not all PayPal_Checkout_Payer_Details objects include a billing address
295
  * @param object $checkout_details
296
  * @return array
297
  */
299
  if ( empty( $checkout_details->payer_details ) ) {
300
  return array();
301
  }
302
+
303
  return array(
304
  'first_name' => $checkout_details->payer_details->first_name,
305
  'last_name' => $checkout_details->payer_details->last_name,
316
  }
317
 
318
  /**
319
+ * Map PayPal shipping address to WC shipping address.
320
+ *
321
+ * @param object $checkout_details Checkout details
322
  * @return array
323
  */
324
  public function get_mapped_shipping_address( $checkout_details ) {
325
  if ( empty( $checkout_details->payments[0] ) || empty( $checkout_details->payments[0]->shipping_address ) ) {
326
  return array();
327
  }
328
+
329
  $name = explode( ' ', $checkout_details->payments[0]->shipping_address->getName() );
330
  $first_name = array_shift( $name );
331
  $last_name = implode( ' ', $name );
350
  return;
351
  }
352
 
353
+ $token = $_GET['token'];
354
+ $payer_id = $_GET['PayerID'];
355
+ $create_billing_agreement = ! empty( $_GET['create-billing-agreement'] );
356
+ $session = WC()->session->get( 'paypal' );
357
 
358
  if ( empty( $session ) || $this->session_has_expired( $token ) ) {
359
  wc_add_notice( __( 'Your PayPal checkout session has expired. Please check out again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
360
  return;
361
  }
362
 
363
+ // Store values in session.
364
  $session->checkout_completed = true;
365
+ $session->payer_id = $payer_id;
366
  WC()->session->set( 'paypal', $session );
367
 
368
  try {
 
 
369
  // If commit was true, take payment right now
370
  if ( 'order' === $session->source && $session->order_id ) {
371
+ $checkout_details = $this->get_checkout_details( $token );
372
 
373
  // Get order
374
  $order = wc_get_order( $session->order_id );
375
 
376
+ // Maybe create billing agreement.
377
+ if ( $create_billing_agreement ) {
378
+ $this->create_billing_agreement( $order, $checkout_details );
379
+ }
380
 
381
  // Complete the payment now.
382
+ $this->do_payment( $order, $session->token, $session->payer_id );
383
 
384
  // Clear Cart
385
  WC()->cart->empty_cart();
388
  wp_redirect( $order->get_checkout_order_received_url() );
389
  exit;
390
  }
391
+ } catch ( PayPal_API_Exception $e ) {
392
  wc_add_notice( __( 'Sorry, an error occurred while trying to retrieve your information from PayPal. Please try again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
393
  $this->maybe_clear_session_data();
394
  wp_safe_redirect( wc_get_page_permalink( 'cart' ) );
395
  exit;
396
+ } catch ( PayPal_Missing_Session_Exception $e ) {
397
  wc_add_notice( __( 'Your PayPal checkout session has expired. Please check out again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
398
  $this->maybe_clear_session_data();
399
  wp_safe_redirect( wc_get_page_permalink( 'cart' ) );
440
  if ( $this->has_active_session() ) {
441
  printf(
442
  '<a href="%s" class="wc-gateway-ppec-cancel">%s</a>',
443
+ esc_url( add_query_arg( 'wc-gateway-ppec-clear-session', true, wc_get_cart_url() ) ),
444
  esc_html__( 'Cancel', 'woocommerce-gateway-paypal-express-checkout' )
445
  );
446
  }
447
  }
448
 
449
+ /**
450
+ * Buyer cancels checkout with PayPal.
451
+ *
452
+ * Clears the session data and display notice.
453
+ */
454
  public function maybe_cancel_checkout_with_paypal() {
455
  if ( is_cart() && ! empty( $_GET['wc-gateway-ppec-clear-session'] ) ) {
456
  $this->maybe_clear_session_data();
457
+
458
+ $notice = __( 'You have cancelled Checkout with PayPal. Please try to process your order again.', 'woocommerce-gateway-paypal-express-checkout' );
459
+ if ( ! wc_has_notice( $notice, 'notice' ) ) {
460
+ wc_add_notice( $notice, 'notice' );
461
  }
462
  }
463
  }
496
  * @return bool Returns true if PPEC session exists and still valid
497
  */
498
  public function has_active_session() {
499
+ if ( ! WC()->session ) {
500
+ return false;
501
+ }
502
+
503
  $session = WC()->session->paypal;
504
+ return ( is_a( $session, 'WC_Gateway_PPEC_Session_Data' ) && $session->payer_id && $session->expiry_time > time() );
505
  }
506
 
507
  /**
508
+ * @deprecated
 
 
 
 
509
  */
510
  public function get_token_from_session() {
511
+ _deprecated_function( __METHOD__, '1.2.0', '' );
 
 
 
 
 
 
 
512
  }
513
 
514
+ /**
515
+ * @deprecated
516
+ */
517
  public function setShippingAddress( $address ) {
518
+ _deprecated_function( __METHOD__, '1.2.0', '' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
519
  }
520
 
521
+ /**
522
+ * @deprecated
523
+ */
524
  public function getSetExpressCheckoutParameters() {
525
+ // No replacement because WC_Gateway_PPEC_Client::get_set_express_checkout_params
526
+ // needs context from where the buyer starts checking out.
527
+ _deprecated_function( __METHOD__, '1.2.0', '' );
 
 
 
 
 
 
 
 
 
 
 
528
  }
529
 
530
+ /**
531
+ * @deprecated
532
+ */
533
  public function getDoExpressCheckoutParameters( $token, $payer_id ) {
534
+ // No replacement because WC_Gateway_PPEC_Client::get_do_express_checkout_params
535
+ // needs order_id to return properly.
536
+ _deprecated_function( __METHOD__, '1.2.0', '' );
 
 
 
 
 
 
 
 
 
 
 
 
 
537
  }
538
 
539
  /**
540
+ * @deprecated
 
 
 
 
541
  */
542
  protected function is_success( $response ) {
543
+ _deprecated_function( __METHOD__, '1.2.0', 'WC_Gateway_PPEC_Client::response_has_success_status' );
 
 
 
 
 
544
 
545
+ $client = wc_gateway_ppec()->client;
546
+ return $client->response_has_success_status( $response );
 
 
 
 
 
 
 
547
  }
548
 
549
  /**
550
+ * Handler when buyer is checking out from cart page.
551
  *
552
+ * @todo This methods looks similar to start_checkout_from_checkout. Please
553
+ * refactor by merging them.
554
  *
555
+ * @throws PayPal_API_Exception
556
  */
 
 
 
 
557
  public function start_checkout_from_cart() {
558
+ $settings = wc_gateway_ppec()->settings;
559
+ $client = wc_gateway_ppec()->client;
560
+ $context_args = array(
561
+ 'start_from' => 'cart',
 
 
 
 
562
  );
563
+ $context_args['create_billing_agreement'] = $this->needs_billing_agreement_creation( $context_args );
564
 
565
+ $params = $client->get_set_express_checkout_params( $context_args );
566
+ $response = $client->set_express_checkout( $params );
567
+ if ( $client->response_has_success_status( $response ) ) {
 
 
 
 
 
 
 
 
 
568
  WC()->session->paypal = new WC_Gateway_PPEC_Session_Data(
569
  array(
570
+ 'token' => $response['TOKEN'],
571
+ 'source' => 'cart',
572
+ 'expires_in' => $settings->get_token_session_length(),
573
+ 'use_paypal_credit' => wc_gateway_ppec_is_using_credit(),
574
  )
575
  );
576
 
580
  }
581
  }
582
 
583
+ /**
584
+ * Handler when buyer is checking out from checkout page.
585
+ *
586
+ * @todo This methods looks similar to start_checkout_from_cart. Please
587
+ * refactor by merging them.
588
+ *
589
+ * @throws PayPal_API_Exception
590
+ *
591
+ * @param int $order_id Order ID
592
+ */
593
  public function start_checkout_from_checkout( $order_id ) {
594
+ $settings = wc_gateway_ppec()->settings;
595
+ $client = wc_gateway_ppec()->client;
596
+ $context_args = array(
597
+ 'start_from' => 'checkout',
598
+ 'order_id' => $order_id,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
599
  );
600
+ $context_args['create_billing_agreement'] = $this->needs_billing_agreement_creation( $context_args );
601
 
602
+ $params = $client->get_set_express_checkout_params( $context_args );
603
+ $response = $client->set_express_checkout( $params );
604
+ if ( $client->response_has_success_status( $response ) ) {
 
 
 
 
 
 
 
 
 
 
605
  WC()->session->paypal = new WC_Gateway_PPEC_Session_Data(
606
  array(
607
+ 'token' => $response['TOKEN'],
608
+ 'source' => 'order',
609
+ 'order_id' => $order_id,
610
+ 'expires_in' => $settings->get_token_session_length()
 
611
  )
612
  );
613
 
615
  } else {
616
  throw new PayPal_API_Exception( $response );
617
  }
618
+ }
619
+
620
+ /**
621
+ * Checks whether buyer checkout from checkout page.
622
+ *
623
+ * @since 1.2.0
624
+ *
625
+ * @return bool Returns true if buyer checkout from checkout page
626
+ */
627
+ public function is_started_from_checkout_page() {
628
+ $session = WC()->session->get( 'paypal' );
629
 
630
+ return (
631
+ ! $this->has_active_session()
632
+ ||
633
+ ! $session->checkout_completed
634
+ );
635
  }
636
 
637
+ /**
638
+ * @deprecated
639
+ */
640
  public function getCheckoutDetails( $token = false ) {
641
+ _deprecated_function( __METHOD__, '1.2.0', 'WC_Gateway_PPEC_Checkout_Handler::get_checkout_details' );
642
+ return $this->get_checkout_details( $token );
643
+ }
644
+
645
+ /**
646
+ * Get checkout details from token.
647
+ *
648
+ * @since 1.2.0
649
+ *
650
+ * @throws \Exception
651
+ *
652
+ * @param bool|string $token Express Checkout token
653
+ */
654
+ public function get_checkout_details( $token = false ) {
655
+ if ( is_a( $this->_checkout_details, 'PayPal_Checkout_Details' ) ) {
656
+ return $this->_checkout_details;
657
+ }
658
+
659
+ if ( false === $token && ! empty( $_GET['token'] ) ) {
660
  $token = $_GET['token'];
661
  }
662
 
663
+ $client = wc_gateway_ppec()->client;
664
+ $response = $client->get_express_checkout_details( $token );
665
 
666
+ if ( $client->response_has_success_status( $response ) ) {
667
  $checkout_details = new PayPal_Checkout_Details();
668
  $checkout_details->loadFromGetECResponse( $response );
669
+
670
+ $this->_checkout_details = $checkout_details;
671
  return $checkout_details;
672
  } else {
673
  throw new PayPal_API_Exception( $response );
674
  }
675
  }
676
 
677
+ /**
678
+ * Creates billing agreement and stores the billing agreement ID in order's
679
+ * meta and subscriptions meta.
680
+ *
681
+ * @since 1.2.0
682
+ *
683
+ * @throws \Exception
684
+ *
685
+ * @param WC_Order $order Order object
686
+ * @param PayPal_Checkout_Details $checkout_details Checkout details
687
+ */
688
+ public function create_billing_agreement( $order, $checkout_details ) {
689
+ if ( 1 !== intval( $checkout_details->billing_agreement_accepted ) ) {
690
+ throw new PayPal_API_Exception( $checkout_details->raw_response );
691
+ }
692
+
693
+ $client = wc_gateway_ppec()->client;
694
+ $resp = $client->create_billing_agreement( $checkout_details->token );
695
+
696
+ if ( ! $client->response_has_success_status( $resp ) || empty( $resp['BILLINGAGREEMENTID'] ) ) {
697
+ throw new PayPal_API_Exception( $resp );
698
+ }
699
+
700
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
701
+ $order_id = $old_wc ? $order->id : $order->get_id();
702
+ if ( $old_wc ) {
703
+ update_post_meta( $order_id, '_ppec_billing_agreement_id', $resp['BILLINGAGREEMENTID'] );
704
+ } else {
705
+ $order->update_meta_data( '_ppec_billing_agreement_id', $resp['BILLINGAGREEMENTID'] );
706
+ }
707
+
708
+ $subscriptions = array();
709
+ if ( function_exists( 'wcs_order_contains_subscription' ) && wcs_order_contains_subscription( $order_id ) ) {
710
+ $subscriptions = wcs_get_subscriptions_for_order( $order_id );
711
+ } elseif ( function_exists( 'wcs_order_contains_renewal' ) && wcs_order_contains_renewal( $order_id ) ) {
712
+ $subscriptions = wcs_get_subscriptions_for_renewal_order( $order_id );
713
+ }
714
+
715
+ $billing_agreement_id = $old_wc ? get_post_meta( $order_id, '_ppec_billing_agreement_id', true ) : $order->get_meta( '_ppec_billing_agreement_id', true );
716
+
717
+ foreach ( $subscriptions as $subscription ) {
718
+ update_post_meta( $subscription->id, '_ppec_billing_agreement_id', $billing_agreement_id );
719
+ }
720
+ }
721
+
722
  /**
723
  * Complete a payment that has been authorized via PPEC.
724
  */
725
+ public function do_payment( $order, $token, $payer_id ) {
726
  $settings = wc_gateway_ppec()->settings;
727
  $session_data = WC()->session->get( 'paypal', null );
728
 
729
+ if ( ! $order || null === $session_data || $this->session_has_expired( $token ) || empty( $payer_id ) ) {
730
  throw new PayPal_Missing_Session_Exception();
731
  }
732
 
733
+ $client = wc_gateway_ppec()->client;
734
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
735
+ $order_id = $old_wc ? $order->id : $order->get_id();
736
+ $params = $client->get_do_express_checkout_params( array(
737
+ 'order_id' => $order_id,
738
+ 'token' => $token,
739
+ 'payer_id' => $payer_id,
740
  ) );
741
 
742
+ $response = $client->do_express_checkout_payment( $params );
743
+
744
+ if ( $client->response_has_success_status( $response ) ) {
745
  $payment_details = new PayPal_Payment_Details();
746
  $payment_details->loadFromDoECResponse( $response );
747
 
748
+ $meta = $old_wc ? get_post_meta( $order_id, '_woo_pp_txnData', true ) : $order->get_meta( '_woo_pp_txnData', true );
749
  if ( ! empty( $meta ) ) {
750
  $txnData = $meta;
751
  } else {
772
 
773
  $txnData['txn_type'] = $paymentAction;
774
 
775
+ if ( $old_wc ) {
776
+ update_post_meta( $order_id, '_woo_pp_txnData', $txnData );
777
+ } else {
778
+ $order->update_meta_data( '_woo_pp_txnData', $txnData );
779
+ }
780
 
781
  // Payment was taken so clear session
782
  $this->maybe_clear_session_data();
793
  */
794
  public function handle_payment_response( $order, $payment ) {
795
  // Store meta data to order
796
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
797
+ if ( $old_wc ) {
798
+ update_post_meta( $order->id, '_paypal_status', strtolower( $payment->payment_status ) );
799
+ update_post_meta( $order->id, '_transaction_id', $payment->transaction_id );
800
+ } else {
801
+ $order->update_meta_data( '_paypal_status', strtolower( $payment->payment_status ) );
802
+ $order->update_meta_data( '_transaction_id', $payment->transaction_id );
803
+ }
804
 
805
  // Handle $payment response
806
  if ( 'completed' === strtolower( $payment->payment_status ) ) {
811
  } else {
812
  $order->update_status( 'on-hold', sprintf( __( 'Payment pending (%s).', 'woocommerce-gateway-paypal-express-checkout' ), $payment->pending_reason ) );
813
  }
814
+ if ( $old_wc ) {
815
+ $order->reduce_order_stock();
816
+ } else {
817
+ wc_reduce_stock_levels( $order->get_id() );
818
+ }
819
  }
820
  }
821
+
822
+ /**
823
+ * This function filter the packages adding shipping information from PayPal on the checkout page
824
+ * after the user is authenticated by PayPal.
825
+ *
826
+ * @since 1.9.13 Introduced
827
+ * @param array $packages
828
+ *
829
+ * @return mixed
830
+ */
831
+ public function maybe_add_shipping_information( $packages ) {
832
+ if ( empty( $_GET['woo-paypal-return'] ) || empty( $_GET['token'] ) || empty( $_GET['PayerID'] ) ) {
833
+ return $packages;
834
+ }
835
+ // Shipping details from PayPal
836
+
837
+ try {
838
+ $checkout_details = $this->get_checkout_details( wc_clean( $_GET['token'] ) );
839
+ } catch ( PayPal_API_Exception $e ) {
840
+ return $packages;
841
+ }
842
+
843
+
844
+ $destination = $this->get_mapped_shipping_address( $checkout_details );
845
+
846
+ $packages[0]['destination']['country'] = $destination['country'];
847
+ $packages[0]['destination']['state'] = $destination['state'];
848
+ $packages[0]['destination']['postcode'] = $destination['postcode'];
849
+ $packages[0]['destination']['city'] = $destination['city'];
850
+ $packages[0]['destination']['address'] = $destination['address_1'];
851
+ $packages[0]['destination']['address_2'] = $destination['address_2'];
852
+
853
+ return $packages;
854
+ }
855
+
856
+ /**
857
+ * Checks whether checkout needs billing agreement creation.
858
+ *
859
+ * @since 1.2.0
860
+ *
861
+ * @param array $args {
862
+ * Context args to retrieve SetExpressCheckout parameters.
863
+ *
864
+ * @type string $start_from Start from 'cart' or 'checkout'.
865
+ * @type int $order_id Order ID if $start_from is 'checkout'.
866
+ * }
867
+ *
868
+ * @return bool Returns true if billing agreement is needed in the purchase
869
+ */
870
+ public function needs_billing_agreement_creation( $args ) {
871
+ $needs_billing_agreement = false;
872
+ switch ( $args['start_from'] ) {
873
+ case 'cart':
874
+ if ( class_exists( 'WC_Subscriptions_Cart' ) ) {
875
+ $needs_billing_agreement = WC_Subscriptions_Cart::cart_contains_subscription();
876
+ }
877
+ break;
878
+ case 'checkout':
879
+ if ( function_exists( 'wcs_order_contains_subscription' ) ) {
880
+ $needs_billing_agreement = wcs_order_contains_subscription( $args['order_id'] );
881
+ }
882
+ if ( function_exists( 'wcs_order_contains_renewal' ) ) {
883
+ $needs_billing_agreement = ( $needs_billing_agreement || wcs_order_contains_renewal( $args['order_id'] ) );
884
+ }
885
+ break;
886
+ }
887
+
888
+ return $needs_billing_agreement;
889
+ }
890
  }
includes/class-wc-gateway-ppec-client.php CHANGED
@@ -98,7 +98,6 @@ class WC_Gateway_PPEC_Client {
98
  public function get_endpoint() {
99
  return sprintf(
100
  'https://%s%s.paypal.com/nvp',
101
-
102
  $this->_credential->get_endpoint_subdomain(),
103
  'sandbox' === $this->_environment ? '.sandbox' : ''
104
  );
@@ -114,23 +113,10 @@ class WC_Gateway_PPEC_Client {
114
  */
115
  protected function _request( array $params ) {
116
  try {
117
- wc_gateway_ppec_log( sprintf( '%s: trying to make a request to PayPal with params: %s', __METHOD__, print_r( $params, true ) ) );
118
-
119
- // Make sure $_credential and $_environment have been configured.
120
- if ( ! $this->_credential ) {
121
- throw new Exception( __( 'Missing credential', 'woocommerce-gateway-paypal-express-checkout' ), self::INVALID_CREDENTIAL_ERROR );
122
- }
123
-
124
- if ( ! is_a( $this->_credential, 'WC_Gateway_PPEC_Client_Credential' ) ) {
125
- throw new Exception( __( 'Invalid credential object', 'woocommerce-gateway-paypal-express-checkout' ), self::INVALID_CREDENTIAL_ERROR );
126
- }
127
-
128
- if ( ! in_array( $this->_environment, array( 'live', 'sandbox' ) ) ) {
129
- throw new Exception( __( 'Invalid environment', 'woocommerce-gateway-paypal-express-checkout' ), self::INVALID_ENVIRONMENT_ERROR );
130
- }
131
 
132
  // First, add in the necessary credential parameters.
133
- $body = array_merge( $params, $this->_credential->get_request_params() );
134
  $args = array(
135
  'method' => 'POST',
136
  'body' => $body,
@@ -142,28 +128,11 @@ class WC_Gateway_PPEC_Client {
142
  // For cURL transport.
143
  add_action( 'http_api_curl', array( $this->_credential, 'configure_curl' ), 10, 3 );
144
 
145
- wc_gateway_ppec_log( sprintf( '%s: remote request to %s with args: %s', __METHOD__, $this->get_endpoint(), print_r( $args, true ) ) );
146
 
147
  $resp = wp_safe_remote_post( $this->get_endpoint(), $args );
148
 
149
- wc_gateway_ppec_log( sprintf( '%s: response from remote request to %s: %s', __METHOD__, $this->get_endpoint(), print_r( $resp, true ) ) );
150
-
151
- if ( is_wp_error( $resp ) ) {
152
- throw new Exception( sprintf( __( 'An error occurred while trying to connect to PayPal: %s', 'woocommerce-gateway-paypal-express-checkout' ), $resp->get_error_message() ), self::REQUEST_ERROR );
153
- }
154
-
155
- parse_str( wp_remote_retrieve_body( $resp ), $result );
156
-
157
- if ( ! array_key_exists( 'ACK', $result ) ) {
158
- throw new Exception( __( 'Malformed response received from PayPal', 'woocommerce-gateway-paypal-express-checkout' ), self::REQUEST_ERROR );
159
- }
160
-
161
- wc_gateway_ppec_log( sprintf( '%s: acknowleged response body: %s', __METHOD__, print_r( $result, true ) ) );
162
-
163
- remove_action( 'http_api_curl', array( $this->_credential, 'configure_curl' ), 10 );
164
-
165
- // Let the caller deals with the response.
166
- return $result;
167
 
168
  } catch ( Exception $e ) {
169
 
@@ -175,15 +144,64 @@ class WC_Gateway_PPEC_Client {
175
  'L_ERRORCODE0' => $e->getCode(),
176
  'L_SHORTMESSAGE0' => 'Error in ' . __METHOD__,
177
  'L_LONGMESSAGE0' => $e->getMessage(),
178
- 'L_SEVERITYCODE0' => 'Error'
179
  );
180
 
181
- wc_gateway_ppec_log( sprintf( '%s: exception is thrown while trying to make a request to PayPal: %s', __METHOD__, $e->getMessage() ) );
182
  wc_gateway_ppec_log( sprintf( '%s: returns error: %s', __METHOD__, print_r( $error, true ) ) );
183
 
184
  return $error;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
 
 
186
  }
 
 
 
 
 
 
187
  }
188
 
189
  /**
@@ -201,12 +219,532 @@ class WC_Gateway_PPEC_Client {
201
  return $this->_request( $params );
202
  }
203
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  /**
205
  * Get details from a given token.
206
  *
207
  * @see https://developer.paypal.com/docs/classic/api/merchant/GetExpressCheckoutDetails_API_Operation_NVP/
208
  *
209
- * @param array $params NVP params
210
  * @return array NVP response
211
  */
212
  public function get_express_checkout_details( $token ) {
@@ -229,7 +767,7 @@ class WC_Gateway_PPEC_Client {
229
  * @param array $params NVP params
230
  * @return array NVP response
231
  */
232
- public function do_express_checkout_payment( $params ) {
233
  $params['METHOD'] = 'DoExpressCheckoutPayment';
234
  $params['VERSION'] = self::API_VERSION;
235
  $params['BUTTONSOURCE'] = 'WooThemes_EC';
@@ -237,6 +775,192 @@ class WC_Gateway_PPEC_Client {
237
  return $this->_request( $params );
238
  }
239
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  public function do_express_checkout_capture( $params ) {
241
  $params['METHOD'] = 'DoCapture';
242
  $params['VERSION'] = self::API_VERSION;
@@ -330,7 +1054,7 @@ class WC_Gateway_PPEC_Client {
330
  'RETURNURL' => home_url( '/' ),
331
  'CANCELURL' => home_url( '/' ),
332
  'REQBILLINGADDRESS' => '1',
333
- 'AMT' => '1.00'
334
  );
335
  $result = $this->set_express_checkout( $req );
336
 
@@ -353,4 +1077,21 @@ class WC_Gateway_PPEC_Client {
353
 
354
  return true;
355
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356
  }
98
  public function get_endpoint() {
99
  return sprintf(
100
  'https://%s%s.paypal.com/nvp',
 
101
  $this->_credential->get_endpoint_subdomain(),
102
  'sandbox' === $this->_environment ? '.sandbox' : ''
103
  );
113
  */
114
  protected function _request( array $params ) {
115
  try {
116
+ $this->_validate_request();
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
  // First, add in the necessary credential parameters.
119
+ $body = apply_filters( 'woocommerce_paypal_express_checkout_request_body', array_merge( $params, $this->_credential->get_request_params() ) );
120
  $args = array(
121
  'method' => 'POST',
122
  'body' => $body,
128
  // For cURL transport.
129
  add_action( 'http_api_curl', array( $this->_credential, 'configure_curl' ), 10, 3 );
130
 
131
+ wc_gateway_ppec_log( sprintf( '%s: remote request to %s with params: %s', __METHOD__, $this->get_endpoint(), print_r( $body, true ) ) );
132
 
133
  $resp = wp_safe_remote_post( $this->get_endpoint(), $args );
134
 
135
+ return $this->_process_response( $resp );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
  } catch ( Exception $e ) {
138
 
144
  'L_ERRORCODE0' => $e->getCode(),
145
  'L_SHORTMESSAGE0' => 'Error in ' . __METHOD__,
146
  'L_LONGMESSAGE0' => $e->getMessage(),
147
+ 'L_SEVERITYCODE0' => 'Error',
148
  );
149
 
 
150
  wc_gateway_ppec_log( sprintf( '%s: returns error: %s', __METHOD__, print_r( $error, true ) ) );
151
 
152
  return $error;
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Validate request.
158
+ *
159
+ * @since 1.2.0
160
+ *
161
+ * @throws \Exception
162
+ */
163
+ protected function _validate_request() {
164
+ // Make sure $_credential and $_environment have been configured.
165
+ if ( ! $this->_credential ) {
166
+ throw new Exception( __( 'Missing credential', 'woocommerce-gateway-paypal-express-checkout' ), self::INVALID_CREDENTIAL_ERROR );
167
+ }
168
+
169
+ if ( ! is_a( $this->_credential, 'WC_Gateway_PPEC_Client_Credential' ) ) {
170
+ throw new Exception( __( 'Invalid credential object', 'woocommerce-gateway-paypal-express-checkout' ), self::INVALID_CREDENTIAL_ERROR );
171
+ }
172
+
173
+ if ( ! in_array( $this->_environment, array( 'live', 'sandbox' ) ) ) {
174
+ throw new Exception( __( 'Invalid environment', 'woocommerce-gateway-paypal-express-checkout' ), self::INVALID_ENVIRONMENT_ERROR );
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Process response from API.
180
+ *
181
+ * @since 1.2.0
182
+ *
183
+ * @throws \Exception
184
+ *
185
+ * @param WP_Error|array Response from remote API
186
+ *
187
+ * @return array
188
+ */
189
+ protected function _process_response( $response ) {
190
+ if ( is_wp_error( $response ) ) {
191
+ throw new Exception( sprintf( __( 'An error occurred while trying to connect to PayPal: %s', 'woocommerce-gateway-paypal-express-checkout' ), $response->get_error_message() ), self::REQUEST_ERROR );
192
+ }
193
+
194
+ parse_str( wp_remote_retrieve_body( $response ), $result );
195
 
196
+ if ( ! array_key_exists( 'ACK', $result ) ) {
197
+ throw new Exception( __( 'Malformed response received from PayPal', 'woocommerce-gateway-paypal-express-checkout' ), self::REQUEST_ERROR );
198
  }
199
+
200
+ wc_gateway_ppec_log( sprintf( '%s: acknowleged response body: %s', __METHOD__, print_r( $result, true ) ) );
201
+
202
+ remove_action( 'http_api_curl', array( $this->_credential, 'configure_curl' ), 10 );
203
+
204
+ return $result;
205
  }
206
 
207
  /**
219
  return $this->_request( $params );
220
  }
221
 
222
+ /**
223
+ * Get params for SetExpressCheckout call.
224
+ *
225
+ * @since 1.2.0
226
+ *
227
+ * @param array $args {
228
+ * Context args to retrieve SetExpressCheckout parameters.
229
+ *
230
+ * @type string $start_from Start from 'cart' or 'checkout'.
231
+ * @type int $order_id Order ID if $start_from is 'checkout'.
232
+ * @type bool $create_billing_agreement Whether billing agreement creation
233
+ * is needed after returned from PayPal.
234
+ * }
235
+ *
236
+ * @return array Params for SetExpressCheckout call
237
+ */
238
+ public function get_set_express_checkout_params( array $args ) {
239
+ $args = wp_parse_args(
240
+ $args,
241
+ array(
242
+ 'start_from' => 'cart',
243
+ 'order_id' => '',
244
+ 'create_billing_agreement' => false,
245
+ )
246
+ );
247
+
248
+ $settings = wc_gateway_ppec()->settings;
249
+
250
+ $params = array();
251
+ $params['LOGOIMG'] = $settings->logo_image_url;
252
+ $params['HDRIMG'] = $settings->header_image_url;
253
+ $params['PAGESTYLE'] = $settings->page_style;
254
+ $params['BRANDNAME'] = $settings->get_brand_name();
255
+ $params['RETURNURL'] = $this->_get_return_url( $args );
256
+ $params['CANCELURL'] = $this->_get_cancel_url();
257
+
258
+ if ( wc_gateway_ppec_is_using_credit() ) {
259
+ $params['USERSELECTEDFUNDINGSOURCE'] = 'Finance';
260
+ }
261
+
262
+ if ( 'checkout' === $args['start_from'] ) {
263
+ $params['ADDROVERRIDE'] = '1';
264
+ }
265
+
266
+ if ( in_array( $settings->landing_page, array( 'Billing', 'Login' ) ) ) {
267
+ $params['LANDINGPAGE'] = $settings->landing_page;
268
+ }
269
+
270
+ if ( apply_filters( 'woocommerce_paypal_express_checkout_allow_guests', true ) ) {
271
+ $params['SOLUTIONTYPE'] = 'Sole';
272
+ }
273
+
274
+ if ( 'yes' === $settings->require_billing ) {
275
+ $params['REQBILLINGADDRESS'] = '1';
276
+ }
277
+
278
+ $params['PAYMENTREQUEST_0_PAYMENTACTION'] = $settings->get_paymentaction();
279
+ if ( 'yes' === $settings->instant_payments && 'sale' === $settings->get_paymentaction() ) {
280
+ $params['PAYMENTREQUEST_0_ALLOWEDPAYMENTMETHOD'] = 'InstantPaymentOnly';
281
+ }
282
+
283
+ $params['PAYMENTREQUEST_0_INSURANCEAMT'] = 0;
284
+ $params['PAYMENTREQUEST_0_HANDLINGAMT'] = 0;
285
+ $params['PAYMENTREQUEST_0_CUSTOM'] = '';
286
+ $params['PAYMENTREQUEST_0_INVNUM'] = '';
287
+ $params['PAYMENTREQUEST_0_CURRENCYCODE'] = get_woocommerce_currency();
288
+
289
+ switch ( $args['start_from'] ) {
290
+ case 'checkout':
291
+ $details = $this->_get_details_from_order( $args['order_id'] );
292
+ break;
293
+ case 'cart':
294
+ $details = $this->_get_details_from_cart();
295
+ break;
296
+ }
297
+
298
+ $params = array_merge(
299
+ $params,
300
+ array(
301
+ 'PAYMENTREQUEST_0_AMT' => $details['order_total'],
302
+ 'PAYMENTREQUEST_0_ITEMAMT' => $details['total_item_amount'],
303
+ 'PAYMENTREQUEST_0_SHIPPINGAMT' => $details['shipping'],
304
+ 'PAYMENTREQUEST_0_TAXAMT' => $details['order_tax'],
305
+ 'PAYMENTREQUEST_0_SHIPDISCAMT' => $details['ship_discount_amount'],
306
+ 'NOSHIPPING' => WC()->cart->needs_shipping() ? 0 : 1,
307
+ )
308
+ );
309
+
310
+ if ( $args['create_billing_agreement'] ) {
311
+ $params['L_BILLINGTYPE0'] = 'MerchantInitiatedBillingSingleAgreement';
312
+ $params['L_BILLINGAGREEMENTDESCRIPTION0'] = $this->_get_billing_agreement_description();
313
+ $params['L_BILLINGAGREEMENTCUSTOM0'] = '';
314
+ }
315
+
316
+ if ( ! empty( $details['shipping_address'] ) ) {
317
+ $params = array_merge(
318
+ $params,
319
+ $details['shipping_address']->getAddressParams( 'PAYMENTREQUEST_0_SHIPTO' )
320
+ );
321
+ }
322
+
323
+ if ( ! empty( $details['items'] ) ) {
324
+ $count = 0;
325
+ foreach ( $details['items'] as $line_item_key => $values ) {
326
+ $line_item_params = array(
327
+ 'L_PAYMENTREQUEST_0_NAME' . $count => $values['name'],
328
+ 'L_PAYMENTREQUEST_0_DESC' . $count => ! empty( $values['description'] ) ? strip_tags( $values['description'] ) : '',
329
+ 'L_PAYMENTREQUEST_0_QTY' . $count => $values['quantity'],
330
+ 'L_PAYMENTREQUEST_0_AMT' . $count => $values['amount'],
331
+ );
332
+
333
+ $params = array_merge( $params, $line_item_params );
334
+ $count++;
335
+ }
336
+ }
337
+
338
+ return $params;
339
+ }
340
+
341
+ /**
342
+ * Get return URL.
343
+ *
344
+ * The URL to return from express checkout.
345
+ *
346
+ * @since 1.2.0
347
+ *
348
+ * @param array $context_args {
349
+ * Context args to retrieve SetExpressCheckout parameters.
350
+ *
351
+ * @type string $start_from Start from 'cart' or 'checkout'.
352
+ * @type int $order_id Order ID if $start_from is 'checkout'.
353
+ * @type bool $create_billing_agreement Whether billing agreement creation
354
+ * is needed after returned from PayPal.
355
+ * }
356
+ *
357
+ * @return string Return URL
358
+ */
359
+ protected function _get_return_url( array $context_args ) {
360
+ $query_args = array(
361
+ 'woo-paypal-return' => 'true',
362
+ );
363
+ if ( $context_args['create_billing_agreement'] ) {
364
+ $query_args['create-billing-agreement'] = 'true';
365
+ }
366
+
367
+ return add_query_arg( $query_args, wc_get_checkout_url() );
368
+ }
369
+
370
+ /**
371
+ * Get cancel URL.
372
+ *
373
+ * The URL to return when canceling the express checkout.
374
+ *
375
+ * @since 1.2.0
376
+ *
377
+ * @return string Cancel URL
378
+ */
379
+ protected function _get_cancel_url() {
380
+ return add_query_arg( 'woo-paypal-cancel', 'true', wc_get_cart_url() );
381
+ }
382
+
383
+ /**
384
+ * Get billing agreement description to be passed to PayPal.
385
+ *
386
+ * @since 1.2.0
387
+ *
388
+ * @return string Billing agreement description
389
+ */
390
+ protected function _get_billing_agreement_description() {
391
+ /* translators: placeholder is blogname */
392
+ $description = sprintf( _x( 'Orders with %s', 'data sent to PayPal', 'woocommerce-subscriptions' ), get_bloginfo( 'name' ) );
393
+
394
+ if ( strlen( $description ) > 127 ) {
395
+ $description = substr( $description, 0, 124 ) . '...';
396
+ }
397
+
398
+ return html_entity_decode( $description, ENT_NOQUOTES, 'UTF-8' );
399
+ }
400
+
401
+ /**
402
+ * Get extra line item when for subtotal mismatch.
403
+ *
404
+ * @since 1.2.0
405
+ *
406
+ * @param float $amount Item's amount
407
+ *
408
+ * @return array Line item
409
+ */
410
+ protected function _get_extra_offset_line_item( $amount ) {
411
+ return array(
412
+ 'name' => 'Line Item Amount Offset',
413
+ 'description' => 'Adjust cart calculation discrepancy',
414
+ 'quantity' => 1,
415
+ 'amount' => $amount,
416
+ );
417
+ }
418
+
419
+ /**
420
+ * Get extra line item when for discount.
421
+ *
422
+ * @since 1.2.0
423
+ *
424
+ * @param float $amount Item's amount
425
+ *
426
+ * @return array Line item
427
+ */
428
+ protected function _get_extra_discount_line_item( $amount ) {
429
+ return array(
430
+ 'name' => 'Discount',
431
+ 'description' => 'Discount Amount',
432
+ 'quantity' => 1,
433
+ 'amount' => '-' . $amount,
434
+ );
435
+ }
436
+
437
+ /**
438
+ * Get details, not params to be passed in PayPal API request, from cart contents.
439
+ *
440
+ * This is the details when buyer is checking out from cart page.
441
+ *
442
+ * @since 1.2.0
443
+ *
444
+ * @return array Order details
445
+ */
446
+ protected function _get_details_from_cart() {
447
+ $settings = wc_gateway_ppec()->settings;
448
+
449
+ $decimals = $settings->get_number_of_decimal_digits();
450
+ $discounts = round( WC()->cart->get_cart_discount_total(), $decimals );
451
+ $rounded_total = $this->_get_rounded_total_in_cart();
452
+
453
+ $details = array(
454
+ 'total_item_amount' => round( WC()->cart->cart_contents_total, $decimals ) + $discounts,
455
+ 'order_tax' => round( WC()->cart->tax_total + WC()->cart->shipping_tax_total, $decimals ),
456
+ 'shipping' => round( WC()->cart->shipping_total, $decimals ),
457
+ 'items' => $this->_get_paypal_line_items_from_cart(),
458
+ );
459
+
460
+ $details['order_total'] = round(
461
+ $details['total_item_amount'] + $details['order_tax'] + $details['shipping'],
462
+ $decimals
463
+ );
464
+
465
+ // Compare WC totals with what PayPal will calculate to see if they match.
466
+ // if they do not match, check to see what the merchant would like to do.
467
+ // Options are to remove line items or add a line item to adjust for
468
+ // the difference.
469
+ if ( $details['total_item_amount'] != $rounded_total ) {
470
+ if ( 'add' === $settings->get_subtotal_mismatch_behavior() ) {
471
+ // Add line item to make up different between WooCommerce
472
+ // calculations and PayPal calculations.
473
+ $diff = round( $details['total_item_amount'] - $rounded_total, $decimals );
474
+ if ( $diff != 0 ) {
475
+ $extra_line_item = $this->_get_extra_offset_line_item( $diff );
476
+
477
+ $details['items'][] = $extra_line_item;
478
+ $details['total_item_amount'] += $extra_line_item['amount'];
479
+ $details['order_total'] += $extra_line_item['amount'];
480
+ }
481
+ } else {
482
+ // Omit line items altogether.
483
+ unset( $details['items'] );
484
+ }
485
+ }
486
+
487
+ // Enter discount shenanigans. Item total cannot be 0 so make modifications
488
+ // accordingly.
489
+ if ( $details['total_item_amount'] == $discounts ) {
490
+ // Omit line items altogether.
491
+ unset( $details['items'] );
492
+ $details['ship_discount_amount'] = 0;
493
+ $details['total_item_amount'] -= $discounts;
494
+ $details['order_total'] -= $discounts;
495
+ } else {
496
+ if ( $discounts > 0 ) {
497
+ $details['items'][] = $this->_get_extra_offset_line_item( $discounts );
498
+ }
499
+
500
+ $details['ship_discount_amount'] = 0;
501
+ $details['total_item_amount'] -= $discounts;
502
+ $details['order_total'] -= $discounts;
503
+ }
504
+
505
+ // If the totals don't line up, adjust the tax to make it work (it's
506
+ // probably a tax mismatch).
507
+ $wc_order_total = round( WC()->cart->total, $decimals );
508
+ if ( $wc_order_total != $details['order_total'] ) {
509
+ $details['order_tax'] += $wc_order_total - $details['order_total'];
510
+ $details['order_total'] = $wc_order_total;
511
+ }
512
+ $details['order_tax'] = round( $details['order_tax'], $decimals );
513
+
514
+ if ( ! is_numeric( $details['shipping'] ) ) {
515
+ $details['shipping'] = 0;
516
+ }
517
+
518
+ return $details;
519
+ }
520
+
521
+ /**
522
+ * Get line items from cart contents.
523
+ *
524
+ * @since 1.2.0
525
+ *
526
+ * @return array Line items
527
+ */
528
+ protected function _get_paypal_line_items_from_cart() {
529
+ $settings = wc_gateway_ppec()->settings;
530
+ $decimals = $settings->get_number_of_decimal_digits();
531
+
532
+ $items = array();
533
+ foreach ( WC()->cart->cart_contents as $cart_item_key => $values ) {
534
+ $amount = round( $values['line_subtotal'] / $values['quantity'] , $decimals );
535
+
536
+ if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
537
+ $name = $values['data']->post->post_title;
538
+ $description = $values['data']->post->post_content;
539
+ } else {
540
+ $product = $values['data'];
541
+ $name = $product->get_name();
542
+ $description = $product->get_description();
543
+ }
544
+
545
+ $item = array(
546
+ 'name' => $name,
547
+ 'description' => $description,
548
+ 'quantity' => $values['quantity'],
549
+ 'amount' => $amount,
550
+ );
551
+
552
+ $items[] = $item;
553
+ }
554
+
555
+ return $items;
556
+ }
557
+
558
+ /**
559
+ * Get rounded total of items in cart.
560
+ *
561
+ * @since 1.2.0
562
+ *
563
+ * @return float Rounded total in cart
564
+ */
565
+ protected function _get_rounded_total_in_cart() {
566
+ $settings = wc_gateway_ppec()->settings;
567
+ $decimals = $settings->get_number_of_decimal_digits();
568
+
569
+ $rounded_total = 0;
570
+ foreach ( WC()->cart->cart_contents as $cart_item_key => $values ) {
571
+ $amount = round( $values['line_subtotal'] / $values['quantity'] , $decimals );
572
+ $rounded_total += round( $amount * $values['quantity'], $decimals );
573
+ }
574
+
575
+ return $rounded_total;
576
+ }
577
+
578
+ /**
579
+ * Get details from given order_id.
580
+ *
581
+ * This is the details when buyer is checking out from checkout page.
582
+ *
583
+ * @since 1.2.0
584
+ *
585
+ * @param int $order_id Order ID
586
+ *
587
+ * @return array Order details
588
+ */
589
+ protected function _get_details_from_order( $order_id ) {
590
+ $order = wc_get_order( $order_id );
591
+ $settings = wc_gateway_ppec()->settings;
592
+
593
+ $decimals = $settings->is_currency_supports_zero_decimal() ? 0 : 2;
594
+ $discounts = round( $order->get_total_discount(), $decimals );
595
+ $rounded_total = $this->_get_rounded_total_in_order( $order );
596
+
597
+ $details = array(
598
+ 'order_tax' => round( $order->get_total_tax(), $decimals ),
599
+ 'shipping' => round( ( version_compare( WC_VERSION, '3.0', '<' ) ? $order->get_total_shipping() : $order->get_shipping_total() ), $decimals ),
600
+ 'total_item_amount' => round( $order->get_subtotal(), $decimals ),
601
+ 'items' => $this->_get_paypal_line_items_from_order( $order ),
602
+ );
603
+
604
+ $details['order_total'] = round( $details['total_item_amount'] + $details['order_tax'] + $details['shipping'], $decimals );
605
+
606
+ // Compare WC totals with what PayPal will calculate to see if they match.
607
+ // if they do not match, check to see what the merchant would like to do.
608
+ // Options are to remove line items or add a line item to adjust for
609
+ // the difference.
610
+ if ( $details['total_item_amount'] != $rounded_total ) {
611
+ if ( 'add' === $settings->get_subtotal_mismatch_behavior() ) {
612
+ // Add line item to make up different between WooCommerce
613
+ // calculations and PayPal calculations.
614
+ $diff = round( $details['total_item_amount'] - $rounded_total, $decimals );
615
+
616
+ $details['items'][] = $this->_get_extra_offset_line_item( $diff );
617
+
618
+ } else {
619
+ // Omit line items altogether.
620
+ unset( $details['items'] );
621
+ }
622
+ }
623
+
624
+ // Enter discount shenanigans. Item total cannot be 0 so make modifications
625
+ // accordingly.
626
+ if ( $details['total_item_amount'] == $discounts ) {
627
+ // Omit line items altogether.
628
+ unset( $details['items'] );
629
+ $details['ship_discount_amount'] = 0;
630
+ $details['total_item_amount'] -= $discounts;
631
+ $details['order_total'] -= $discounts;
632
+ } else {
633
+ if ( $discounts > 0 ) {
634
+ $details['items'][] = $this->_get_extra_discount_line_item( $discounts );
635
+
636
+ $details['total_item_amount'] -= $discounts;
637
+ $details['order_total'] -= $discounts;
638
+ }
639
+
640
+ $details['ship_discount_amount'] = 0;
641
+ }
642
+
643
+ // If the totals don't line up, adjust the tax to make it work (it's
644
+ // probably a tax mismatch).
645
+ $wc_order_total = round( $order->get_total(), $decimals );
646
+ if ( $wc_order_total != $details['order_total'] ) {
647
+ $details['order_tax'] += $wc_order_total - $details['order_total'];
648
+ $details['order_total'] = $wc_order_total;
649
+ }
650
+ $details['order_tax'] = round( $details['order_tax'], $decimals );
651
+
652
+ if ( ! is_numeric( $details['shipping'] ) ) {
653
+ $details['shipping'] = 0;
654
+ }
655
+
656
+ // PayPal shipping address from order.
657
+ $shipping_address = new PayPal_Address;
658
+
659
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
660
+ $shipping_first_name = $old_wc ? $order->shipping_first_name : $order->get_shipping_first_name();
661
+ $shipping_last_name = $old_wc ? $order->shipping_last_name : $order->get_shipping_last_name();
662
+ $shipping_address_1 = $old_wc ? $order->shipping_address_1 : $order->get_shipping_address_1();
663
+ $shipping_address_2 = $old_wc ? $order->shipping_address_2 : $order->get_shipping_address_2();
664
+ $shipping_city = $old_wc ? $order->shipping_city : $order->get_shipping_city();
665
+ $shipping_state = $old_wc ? $order->shipping_state : $order->get_shipping_state();
666
+ $shipping_postcode = $old_wc ? $order->shipping_postcode : $order->get_shipping_postcode();
667
+ $shipping_country = $old_wc ? $order->shipping_country : $order->get_shipping_country();
668
+
669
+ $shipping_address->setName( $shipping_first_name . ' ' . $shipping_last_name );
670
+ $shipping_address->setStreet1( $shipping_address_1 );
671
+ $shipping_address->setStreet2( $shipping_address_2 );
672
+ $shipping_address->setCity( $shipping_city );
673
+ $shipping_address->setState( $shipping_state );
674
+ $shipping_address->setZip( $shipping_postcode );
675
+
676
+ // In case merchant only expects domestic shipping and hides shipping
677
+ // country, fallback to base country.
678
+ //
679
+ // @see https://github.com/woothemes/woocommerce-gateway-paypal-express-checkout/issues/139
680
+ if ( empty( $shipping_country ) ) {
681
+ $shipping_country = WC()->countries->get_base_country();
682
+ }
683
+ $shipping_address->setCountry( $shipping_country );
684
+
685
+ $details['shipping_address'] = $shipping_address;
686
+
687
+ return $details;
688
+ }
689
+
690
+ /**
691
+ * Get line items from given order.
692
+ *
693
+ * @since 1.2.0
694
+ *
695
+ * @param int|WC_Order $order Order ID or order object
696
+ *
697
+ * @return array Line items
698
+ */
699
+ protected function _get_paypal_line_items_from_order( $order ) {
700
+ $settings = wc_gateway_ppec()->settings;
701
+ $decimals = $settings->get_number_of_decimal_digits();
702
+ $order = wc_get_order( $order );
703
+
704
+ $items = array();
705
+ foreach ( $order->get_items() as $cart_item_key => $values ) {
706
+ $amount = round( $values['line_subtotal'] / $values['qty'] , $decimals );
707
+ $item = array(
708
+ 'name' => $values['name'],
709
+ 'quantity' => $values['qty'],
710
+ 'amount' => $amount,
711
+ );
712
+
713
+ $items[] = $item;
714
+ }
715
+
716
+ return $items;
717
+ }
718
+
719
+ /**
720
+ * Get rounded total of a given order.
721
+ *
722
+ * @since 1.2.0
723
+ *
724
+ * @param int|WC_Order Order ID or order object
725
+ *
726
+ * @return float
727
+ */
728
+ protected function _get_rounded_total_in_order( $order ) {
729
+ $settings = wc_gateway_ppec()->settings;
730
+ $decimals = $settings->get_number_of_decimal_digits();
731
+ $order = wc_get_order( $order );
732
+
733
+ $rounded_total = 0;
734
+ foreach ( $order->get_items() as $cart_item_key => $values ) {
735
+ $amount = round( $values['line_subtotal'] / $values['qty'] , $decimals );
736
+ $rounded_total += round( $amount * $values['qty'], $decimals );
737
+ }
738
+
739
+ return $rounded_total;
740
+ }
741
+
742
  /**
743
  * Get details from a given token.
744
  *
745
  * @see https://developer.paypal.com/docs/classic/api/merchant/GetExpressCheckoutDetails_API_Operation_NVP/
746
  *
747
+ * @param string $token Token from SetExpressCheckout response
748
  * @return array NVP response
749
  */
750
  public function get_express_checkout_details( $token ) {
767
  * @param array $params NVP params
768
  * @return array NVP response
769
  */
770
+ public function do_express_checkout_payment( array $params ) {
771
  $params['METHOD'] = 'DoExpressCheckoutPayment';
772
  $params['VERSION'] = self::API_VERSION;
773
  $params['BUTTONSOURCE'] = 'WooThemes_EC';
775
  return $this->_request( $params );
776
  }
777
 
778
+ /**
779
+ * Get params for DoExpressCheckoutPayment call.
780
+ *
781
+ * @since 1.2.0
782
+ *
783
+ * @param array $args Args
784
+ *
785
+ * @return array Params for DoExpressCheckoutPayment call
786
+ */
787
+ public function get_do_express_checkout_params( array $args ) {
788
+ $settings = wc_gateway_ppec()->settings;
789
+ $order = wc_get_order( $args['order_id'] );
790
+
791
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
792
+ $order_id = $old_wc ? $order->id : $order->get_id();
793
+ $details = $this->_get_details_from_order( $order_id );
794
+ $order_key = $old_wc ? $order->order_key : $order->get_order_key();
795
+
796
+ $params = array(
797
+ 'TOKEN' => $args['token'],
798
+ 'PAYERID' => $args['payer_id'],
799
+ 'PAYMENTREQUEST_0_AMT' => $details['order_total'],
800
+ 'PAYMENTREQUEST_0_ITEMAMT' => $details['total_item_amount'],
801
+ 'PAYMENTREQUEST_0_SHIPPINGAMT' => $details['shipping'],
802
+ 'PAYMENTREQUEST_0_TAXAMT' => $details['order_tax'],
803
+ 'PAYMENTREQUEST_0_SHIPDISCAMT' => $details['ship_discount_amount'],
804
+ 'PAYMENTREQUEST_0_INSURANCEAMT' => 0,
805
+ 'PAYMENTREQUEST_0_HANDLINGAMT' => 0,
806
+ 'PAYMENTREQUEST_0_CURRENCYCODE' => get_woocommerce_currency(),
807
+ 'PAYMENTREQUEST_0_NOTIFYURL' => WC()->api_request_url( 'WC_Gateway_PPEC' ),
808
+ 'PAYMENTREQUEST_0_PAYMENTACTION' => $settings->get_paymentaction(),
809
+ 'PAYMENTREQUEST_0_INVNUM' => $settings->invoice_prefix . $order->get_order_number(),
810
+ 'PAYMENTREQUEST_0_CUSTOM' => json_encode( array(
811
+ 'order_id' => $order_id,
812
+ 'order_key' => $order_key,
813
+ ) ),
814
+ 'NOSHIPPING' => WC()->cart->needs_shipping() ? 0 : 1,
815
+ );
816
+
817
+ if ( WC()->cart->needs_shipping() && ! empty( $details['shipping_address'] ) ) {
818
+ $params = array_merge(
819
+ $params,
820
+ $details['shipping_address']->getAddressParams( 'PAYMENTREQUEST_0_SHIPTO' )
821
+ );
822
+ }
823
+
824
+ if ( ! empty( $details['items'] ) ) {
825
+ $count = 0;
826
+ foreach ( $details['items'] as $line_item_key => $values ) {
827
+ $line_item_params = array(
828
+ 'L_PAYMENTREQUEST_0_NAME' . $count => $values['name'],
829
+ 'L_PAYMENTREQUEST_0_DESC' . $count => ! empty( $values['description'] ) ? strip_tags( $values['description'] ) : '',
830
+ 'L_PAYMENTREQUEST_0_QTY' . $count => $values['quantity'],
831
+ 'L_PAYMENTREQUEST_0_AMT' . $count => $values['amount'],
832
+ );
833
+
834
+ $params = array_merge( $params, $line_item_params );
835
+ $count++;
836
+ }
837
+ }
838
+
839
+ return $params;
840
+ }
841
+
842
+ /**
843
+ * Creates a billing agreement with a PayPal account holder.
844
+ *
845
+ * Used for subscription products in the purchase.
846
+ *
847
+ * @see https://developer.paypal.com/docs/classic/api/merchant/CreateBillingAgreement_API_Operation_NVP/
848
+ *
849
+ * @since 1.2.0
850
+ *
851
+ * @param string $token Token from SetExpressCheckout response
852
+ */
853
+ public function create_billing_agreement( $token ) {
854
+ $params = array(
855
+ 'METHOD' => 'CreateBillingAgreement',
856
+ 'VERSION' => self::API_VERSION,
857
+ 'TOKEN' => $token,
858
+ );
859
+
860
+ return $this->_request( $params );
861
+ }
862
+
863
+ /**
864
+ * Updates or deletes a billing agreement.
865
+ *
866
+ * @see https://developer.paypal.com/docs/classic/api/merchant/BAUpdate_API_Operation_NVP/
867
+ *
868
+ * @since 1.2.0
869
+ *
870
+ * @param string $billing_agreement_id Billing agreement ID
871
+ */
872
+ public function update_billing_agreement( $billing_agreement_id ) {
873
+ $params = array(
874
+ 'METHOD' => 'BillAgreementUpdate',
875
+ 'VERSION' => self::API_VERSION,
876
+ 'REFERENCEID' => $billing_agreement_id,
877
+ );
878
+ }
879
+
880
+ /**
881
+ * Processes a payment from a buyer's account, which is identified by a
882
+ * previous transaction
883
+ *
884
+ * @see https://developer.paypal.com/docs/classic/api/merchant/DoReferenceTransaction_API_Operation_NVP/
885
+ *
886
+ * @since 1.2.0
887
+ *
888
+ * @param array $params NVP params
889
+ * @return array NVP response
890
+ */
891
+ public function do_reference_transaction( array $params ) {
892
+ $params['METHOD'] = 'DoReferenceTransaction';
893
+ $params['VERSION'] = self::API_VERSION;
894
+ $params['BUTTONSOURCE'] = 'WooThemes_EC';
895
+
896
+ return $this->_request( $params );
897
+ }
898
+
899
+ /**
900
+ * Get params for DoReferenceTransaction call.
901
+ *
902
+ * @since 1.2.0
903
+ *
904
+ * @param array $args Args
905
+ *
906
+ * @return array Params for DoReferenceTransaction call
907
+ */
908
+ public function get_do_reference_transaction_params( array $args ) {
909
+ $settings = wc_gateway_ppec()->settings;
910
+ $order = wc_get_order( $args['order_id'] );
911
+
912
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
913
+ $order_id = $old_wc ? $order->id : $order->get_id();
914
+ $details = $this->_get_details_from_order( $order_id );
915
+ $order_key = $old_wc ? $order->order_key : $order->get_order_key();
916
+
917
+ $params = array(
918
+ 'REFERENCEID' => $args['reference_id'],
919
+ 'AMT' => $args['amount'],
920
+ 'ITEMAMT' => $details['total_item_amount'],
921
+ 'SHIPPINGAMT' => $details['shipping'],
922
+ 'TAXAMT' => $details['order_tax'],
923
+ 'SHIPDISCAMT' => $details['ship_discount_amount'],
924
+ 'INSURANCEAMT' => 0,
925
+ 'HANDLINGAMT' => 0,
926
+ 'CURRENCYCODE' => get_woocommerce_currency(),
927
+ 'NOTIFYURL' => WC()->api_request_url( 'WC_Gateway_PPEC' ),
928
+ 'PAYMENTACTION' => $settings->get_paymentaction(),
929
+ 'INVNUM' => $settings->invoice_prefix . $order->get_order_number(),
930
+ 'CUSTOM' => json_encode( array(
931
+ 'order_id' => $order_id,
932
+ 'order_key' => $order_key,
933
+ ) ),
934
+ );
935
+
936
+ if ( ! empty( $details['shipping_address'] ) ) {
937
+ $params = array_merge(
938
+ $params,
939
+ $details['shipping_address']->getAddressParams( 'SHIPTO' )
940
+ );
941
+
942
+ $params['SHIPTOCOUNTRY'] = $params['SHIPTOCOUNTRYCODE'];
943
+ unset( $params['SHIPTOCOUNTRYCODE'] );
944
+ }
945
+
946
+ if ( ! empty( $details['items'] ) ) {
947
+ $count = 0;
948
+ foreach ( $details['items'] as $line_item_key => $values ) {
949
+ $line_item_params = array(
950
+ 'L_NAME' . $count => $values['name'],
951
+ 'L_DESC' . $count => ! empty( $values['description'] ) ? strip_tags( $values['description'] ) : '',
952
+ 'L_QTY' . $count => $values['quantity'],
953
+ 'L_AMT' . $count => $values['amount'],
954
+ );
955
+
956
+ $params = array_merge( $params, $line_item_params );
957
+ $count++;
958
+ }
959
+ }
960
+
961
+ return $params;
962
+ }
963
+
964
  public function do_express_checkout_capture( $params ) {
965
  $params['METHOD'] = 'DoCapture';
966
  $params['VERSION'] = self::API_VERSION;
1054
  'RETURNURL' => home_url( '/' ),
1055
  'CANCELURL' => home_url( '/' ),
1056
  'REQBILLINGADDRESS' => '1',
1057
+ 'AMT' => '1.00',
1058
  );
1059
  $result = $this->set_express_checkout( $req );
1060
 
1077
 
1078
  return true;
1079
  }
1080
+
1081
+ /**
1082
+ * Checks whether response indicates a successful operation.
1083
+ *
1084
+ * @since 1.2.0
1085
+ *
1086
+ * @param array $response NVP response
1087
+ *
1088
+ * @return bool Returns true if response indicates a successful operation
1089
+ */
1090
+ public function response_has_success_status( $response ) {
1091
+ return (
1092
+ isset( $response['ACK'] )
1093
+ &&
1094
+ in_array( $response['ACK'], array( 'Success', 'SuccessWithWarning' ) )
1095
+ );
1096
+ }
1097
  }
includes/class-wc-gateway-ppec-gateway-loader.php CHANGED
@@ -17,16 +17,38 @@ class WC_Gateway_PPEC_Gateway_Loader {
17
 
18
  require_once( $includes_path . 'class-wc-gateway-ppec-refund.php' );
19
  require_once( $includes_path . 'abstracts/abstract-wc-gateway-ppec.php' );
 
20
  require_once( $includes_path . 'class-wc-gateway-ppec-with-paypal.php' );
 
21
 
22
  add_filter( 'woocommerce_payment_gateways', array( $this, 'payment_gateways' ) );
23
  }
24
 
25
  /**
26
- * Init gateways
 
 
 
 
27
  */
28
  public function payment_gateways( $methods ) {
29
- $methods[] = 'WC_Gateway_PPEC_With_PayPal';
 
 
 
 
 
30
  return $methods;
31
  }
 
 
 
 
 
 
 
 
 
 
 
32
  }
17
 
18
  require_once( $includes_path . 'class-wc-gateway-ppec-refund.php' );
19
  require_once( $includes_path . 'abstracts/abstract-wc-gateway-ppec.php' );
20
+
21
  require_once( $includes_path . 'class-wc-gateway-ppec-with-paypal.php' );
22
+ require_once( $includes_path . 'class-wc-gateway-ppec-with-paypal-addons.php' );
23
 
24
  add_filter( 'woocommerce_payment_gateways', array( $this, 'payment_gateways' ) );
25
  }
26
 
27
  /**
28
+ * Register the PPEC payment methods.
29
+ *
30
+ * @param array $methods Payment methods.
31
+ *
32
+ * @return array Payment methods
33
  */
34
  public function payment_gateways( $methods ) {
35
+ if ( $this->can_use_addons() ) {
36
+ $methods[] = 'WC_Gateway_PPEC_With_PayPal_Addons';
37
+ } else {
38
+ $methods[] = 'WC_Gateway_PPEC_With_PayPal';
39
+ }
40
+
41
  return $methods;
42
  }
43
+
44
+ /**
45
+ * Checks whether gateway addons can be used.
46
+ *
47
+ * @since 1.2.0
48
+ *
49
+ * @return bool Returns true if gateway addons can be used
50
+ */
51
+ public function can_use_addons() {
52
+ return ( class_exists( 'WC_Subscriptions_Order' ) && function_exists( 'wcs_create_renewal_order' ) );
53
+ }
54
  }
includes/class-wc-gateway-ppec-ipn-handler.php CHANGED
@@ -5,7 +5,7 @@ if ( ! defined( 'ABSPATH' ) ) {
5
  }
6
 
7
  /**
8
- * WC_Gateway_PPEC_Cart_Handler handles button display in the cart.
9
  *
10
  * @see https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNImplementation/
11
  * @since 1.1.2
@@ -25,7 +25,6 @@ class WC_Gateway_PPEC_IPN_Handler extends WC_Gateway_PPEC_PayPal_Request_Handler
25
  */
26
  public function check_request() {
27
  try {
28
-
29
  if ( empty( $_POST ) ) {
30
  throw new Exception( esc_html__( 'Empty POST data.', 'woocommerce-gateway-paypal-express-checkout' ) );
31
  }
@@ -38,11 +37,8 @@ class WC_Gateway_PPEC_IPN_Handler extends WC_Gateway_PPEC_PayPal_Request_Handler
38
  wc_gateway_ppec_log( 'IPN request is NOT valid according to PayPal.' );
39
  throw new Exception( esc_html__( 'Invalid IPN request.' , 'woocommerce-gateway-paypal-express-checkout' ) );
40
  }
41
-
42
  } catch ( Exception $e ) {
43
-
44
  wp_die( $e->getMessage(), esc_html__( 'PayPal IPN Request Failure', 'woocommerce-gateway-paypal-express-checkout' ), array( 'response' => 500 ) );
45
-
46
  }
47
  }
48
 
@@ -97,7 +93,6 @@ class WC_Gateway_PPEC_IPN_Handler extends WC_Gateway_PPEC_PayPal_Request_Handler
97
  */
98
  public function handle_valid_ipn( $posted_data ) {
99
  if ( ! empty( $posted_data['custom'] ) && ( $order = $this->get_paypal_order( $posted_data['custom'] ) ) ) {
100
-
101
  // Lowercase returned variables.
102
  $posted_data['payment_status'] = strtolower( $posted_data['payment_status'] );
103
 
@@ -106,13 +101,13 @@ class WC_Gateway_PPEC_IPN_Handler extends WC_Gateway_PPEC_PayPal_Request_Handler
106
  $posted_data['payment_status'] = 'completed';
107
  }
108
 
109
- wc_gateway_ppec_log( 'Found order #' . $order->id );
 
110
  wc_gateway_ppec_log( 'Payment status: ' . $posted_data['payment_status'] );
111
 
112
  if ( method_exists( $this, 'payment_status_' . $posted_data['payment_status'] ) ) {
113
  call_user_func( array( $this, 'payment_status_' . $posted_data['payment_status'] ), $order, $posted_data );
114
  }
115
-
116
  } else {
117
  wc_gateway_ppec_log( sprintf( '%s: %s', __FUNCTION__, 'No order data being passed' ) );
118
  }
@@ -138,8 +133,11 @@ class WC_Gateway_PPEC_IPN_Handler extends WC_Gateway_PPEC_PayPal_Request_Handler
138
  * @param string $currency Currency
139
  */
140
  protected function validate_currency( $order, $currency ) {
141
- if ( $order->order_currency !== $currency ) {
142
- wc_gateway_ppec_log( 'Payment error: Currencies do not match (sent "' . $order->order_currency . '" | returned "' . $currency . '")' );
 
 
 
143
  // Put this order on-hold for manual checking.
144
  $order->update_status( 'on-hold', sprintf( __( 'Validation error: PayPal currencies do not match (code %s).', 'woocommerce-gateway-paypal-express-checkout' ), $currency ) );
145
  exit;
@@ -168,8 +166,11 @@ class WC_Gateway_PPEC_IPN_Handler extends WC_Gateway_PPEC_PayPal_Request_Handler
168
  * @param array $posted_data Posted data
169
  */
170
  protected function payment_status_completed( $order, $posted_data ) {
 
 
 
171
  if ( $order->has_status( array( 'processing', 'completed' ) ) ) {
172
- wc_gateway_ppec_log( 'Aborting, Order #' . $order->id . ' is already complete.' );
173
  exit;
174
  }
175
 
@@ -182,7 +183,12 @@ class WC_Gateway_PPEC_IPN_Handler extends WC_Gateway_PPEC_PayPal_Request_Handler
182
  $this->payment_complete( $order, ( ! empty( $posted_data['txn_id'] ) ? wc_clean( $posted_data['txn_id'] ) : '' ), __( 'IPN payment completed', 'woocommerce-gateway-paypal-express-checkout' ) );
183
  if ( ! empty( $posted_data['mc_fee'] ) ) {
184
  // Log paypal transaction fee.
185
- update_post_meta( $order->id, 'PayPal Transaction Fee', wc_clean( $posted_data['mc_fee'] ) );
 
 
 
 
 
186
  }
187
  } else {
188
  if ( 'authorization' === $posted_data['pending_reason'] ) {
@@ -251,12 +257,13 @@ class WC_Gateway_PPEC_IPN_Handler extends WC_Gateway_PPEC_PayPal_Request_Handler
251
  */
252
  protected function payment_status_refunded( $order, $posted_data ) {
253
  // Only handle full refunds, not partial.
 
254
  if ( $order->get_total() == ( $posted_data['mc_gross'] * -1 ) ) {
255
  // Mark order as refunded.
256
  $order->update_status( 'refunded', sprintf( __( 'Payment %s via IPN.', 'woocommerce-gateway-paypal-express-checkout' ), strtolower( $posted_data['payment_status'] ) ) );
257
  $this->send_ipn_email_notification(
258
- sprintf( __( 'Payment for order %s refunded', 'woocommerce-gateway-paypal-express-checkout' ), '<a class="link" href="' . esc_url( admin_url( 'post.php?post=' . $order->id . '&action=edit' ) ) . '">' . $order->get_order_number() . '</a>' ),
259
- sprintf( __( 'Order #%s has been marked as refunded - PayPal reason code: %s', 'woocommerce-gateway-paypal-express-checkout' ), $order->get_order_number(), $posted_data['reason_code'] )
260
  );
261
  }
262
  }
@@ -268,10 +275,11 @@ class WC_Gateway_PPEC_IPN_Handler extends WC_Gateway_PPEC_PayPal_Request_Handler
268
  * @param array $posted_data Posted data
269
  */
270
  protected function payment_status_reversed( $order, $posted_data ) {
 
271
  $order->update_status( 'on-hold', sprintf( __( 'Payment %s via IPN.', 'woocommerce-gateway-paypal-express-checkout' ), wc_clean( $posted_data['payment_status'] ) ) );
272
  $this->send_ipn_email_notification(
273
- sprintf( __( 'Payment for order %s reversed', 'woocommerce-gateway-paypal-express-checkout' ), '<a class="link" href="' . esc_url( admin_url( 'post.php?post=' . $order->id . '&action=edit' ) ) . '">' . $order->get_order_number() . '</a>' ),
274
- sprintf( __( 'Order #%s has been marked on-hold due to a reversal - PayPal reason code: %s', 'woocommerce-gateway-paypal-express-checkout' ), $order->get_order_number(), wc_clean( $posted_data['reason_code'] ) )
275
  );
276
  }
277
 
@@ -282,9 +290,10 @@ class WC_Gateway_PPEC_IPN_Handler extends WC_Gateway_PPEC_PayPal_Request_Handler
282
  * @param array $posted_data Posted data
283
  */
284
  protected function payment_status_canceled_reversal( $order, $posted_data ) {
 
285
  $this->send_ipn_email_notification(
286
  sprintf( __( 'Reversal cancelled for order #%s', 'woocommerce-gateway-paypal-express-checkout' ), $order->get_order_number() ),
287
- sprintf( __( 'Order #%s has had a reversal cancelled. Please check the status of payment and update the order status accordingly here: %s', 'woocommerce-gateway-paypal-express-checkout' ), $order->get_order_number(), esc_url( admin_url( 'post.php?post=' . $order->id . '&action=edit' ) ) )
288
  );
289
  }
290
 
@@ -295,23 +304,27 @@ class WC_Gateway_PPEC_IPN_Handler extends WC_Gateway_PPEC_PayPal_Request_Handler
295
  * @param array $posted_data Posted data
296
  */
297
  protected function save_paypal_meta_data( $order, $posted_data ) {
298
- if ( ! empty( $posted_data['payer_email'] ) ) {
299
- update_post_meta( $order->id, 'Payer PayPal address', wc_clean( $posted_data['payer_email'] ) );
300
- }
301
- if ( ! empty( $posted_data['first_name'] ) ) {
302
- update_post_meta( $order->id, 'Payer first name', wc_clean( $posted_data['first_name'] ) );
303
- }
304
- if ( ! empty( $posted_data['last_name'] ) ) {
305
- update_post_meta( $order->id, 'Payer last name', wc_clean( $posted_data['last_name'] ) );
306
- }
307
- if ( ! empty( $posted_data['payment_type'] ) ) {
308
- update_post_meta( $order->id, 'Payment type', wc_clean( $posted_data['payment_type'] ) );
309
- }
310
- if ( ! empty( $posted_data['txn_id'] ) ) {
311
- update_post_meta( $order->id, '_transaction_id', wc_clean( $posted_data['txn_id'] ) );
312
- }
313
- if ( ! empty( $posted_data['payment_status'] ) ) {
314
- update_post_meta( $order->id, '_paypal_status', wc_clean( $posted_data['payment_status'] ) );
 
 
 
 
315
  }
316
  }
317
 
5
  }
6
 
7
  /**
8
+ * PayPal Instant Payment Notification handler.
9
  *
10
  * @see https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNImplementation/
11
  * @since 1.1.2
25
  */
26
  public function check_request() {
27
  try {
 
28
  if ( empty( $_POST ) ) {
29
  throw new Exception( esc_html__( 'Empty POST data.', 'woocommerce-gateway-paypal-express-checkout' ) );
30
  }
37
  wc_gateway_ppec_log( 'IPN request is NOT valid according to PayPal.' );
38
  throw new Exception( esc_html__( 'Invalid IPN request.' , 'woocommerce-gateway-paypal-express-checkout' ) );
39
  }
 
40
  } catch ( Exception $e ) {
 
41
  wp_die( $e->getMessage(), esc_html__( 'PayPal IPN Request Failure', 'woocommerce-gateway-paypal-express-checkout' ), array( 'response' => 500 ) );
 
42
  }
43
  }
44
 
93
  */
94
  public function handle_valid_ipn( $posted_data ) {
95
  if ( ! empty( $posted_data['custom'] ) && ( $order = $this->get_paypal_order( $posted_data['custom'] ) ) ) {
 
96
  // Lowercase returned variables.
97
  $posted_data['payment_status'] = strtolower( $posted_data['payment_status'] );
98
 
101
  $posted_data['payment_status'] = 'completed';
102
  }
103
 
104
+ $order_id = version_compare( WC_VERSION, '3.0', '<' ) ? $order->id : $order->get_id();
105
+ wc_gateway_ppec_log( 'Found order #' . $order_id );
106
  wc_gateway_ppec_log( 'Payment status: ' . $posted_data['payment_status'] );
107
 
108
  if ( method_exists( $this, 'payment_status_' . $posted_data['payment_status'] ) ) {
109
  call_user_func( array( $this, 'payment_status_' . $posted_data['payment_status'] ), $order, $posted_data );
110
  }
 
111
  } else {
112
  wc_gateway_ppec_log( sprintf( '%s: %s', __FUNCTION__, 'No order data being passed' ) );
113
  }
133
  * @param string $currency Currency
134
  */
135
  protected function validate_currency( $order, $currency ) {
136
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
137
+ $order_currency = $old_wc ? $order->order_currency : $order->get_currency();
138
+
139
+ if ( $order_currency !== $currency ) {
140
+ wc_gateway_ppec_log( 'Payment error: Currencies do not match (sent "' . $order_currency . '" | returned "' . $currency . '")' );
141
  // Put this order on-hold for manual checking.
142
  $order->update_status( 'on-hold', sprintf( __( 'Validation error: PayPal currencies do not match (code %s).', 'woocommerce-gateway-paypal-express-checkout' ), $currency ) );
143
  exit;
166
  * @param array $posted_data Posted data
167
  */
168
  protected function payment_status_completed( $order, $posted_data ) {
169
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
170
+ $order_id = $old_wc ? $order->id : $order->get_id();
171
+
172
  if ( $order->has_status( array( 'processing', 'completed' ) ) ) {
173
+ wc_gateway_ppec_log( 'Aborting, Order #' . $order_id . ' is already complete.' );
174
  exit;
175
  }
176
 
183
  $this->payment_complete( $order, ( ! empty( $posted_data['txn_id'] ) ? wc_clean( $posted_data['txn_id'] ) : '' ), __( 'IPN payment completed', 'woocommerce-gateway-paypal-express-checkout' ) );
184
  if ( ! empty( $posted_data['mc_fee'] ) ) {
185
  // Log paypal transaction fee.
186
+ $transaction_fee = wc_clean( $posted_data['mc_fee'] );
187
+ if ( $old_wc ) {
188
+ update_post_meta( $order_id, 'PayPal Transaction Fee', $transaction_fee );
189
+ } else {
190
+ $order->update_meta_data( 'PayPal Transaction Fee', $transaction_fee );
191
+ }
192
  }
193
  } else {
194
  if ( 'authorization' === $posted_data['pending_reason'] ) {
257
  */
258
  protected function payment_status_refunded( $order, $posted_data ) {
259
  // Only handle full refunds, not partial.
260
+ $order_id = version_compare( WC_VERSION, '3.0', '<' ) ? $order->id : $order->get_id();
261
  if ( $order->get_total() == ( $posted_data['mc_gross'] * -1 ) ) {
262
  // Mark order as refunded.
263
  $order->update_status( 'refunded', sprintf( __( 'Payment %s via IPN.', 'woocommerce-gateway-paypal-express-checkout' ), strtolower( $posted_data['payment_status'] ) ) );
264
  $this->send_ipn_email_notification(
265
+ sprintf( __( 'Payment for order %s refunded', 'woocommerce-gateway-paypal-express-checkout' ), '<a class="link" href="' . esc_url( admin_url( 'post.php?post=' . $order_id . '&action=edit' ) ) . '">' . $order->get_order_number() . '</a>' ),
266
+ sprintf( __( 'Order #%1$s has been marked as refunded - PayPal reason code: %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $order->get_order_number(), $posted_data['reason_code'] )
267
  );
268
  }
269
  }
275
  * @param array $posted_data Posted data
276
  */
277
  protected function payment_status_reversed( $order, $posted_data ) {
278
+ $order_id = version_compare( WC_VERSION, '3.0', '<' ) ? $order->id : $order->get_id();
279
  $order->update_status( 'on-hold', sprintf( __( 'Payment %s via IPN.', 'woocommerce-gateway-paypal-express-checkout' ), wc_clean( $posted_data['payment_status'] ) ) );
280
  $this->send_ipn_email_notification(
281
+ sprintf( __( 'Payment for order %s reversed', 'woocommerce-gateway-paypal-express-checkout' ), '<a class="link" href="' . esc_url( admin_url( 'post.php?post=' . $order_id . '&action=edit' ) ) . '">' . $order->get_order_number() . '</a>' ),
282
+ sprintf( __( 'Order #%1$s has been marked on-hold due to a reversal - PayPal reason code: %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $order->get_order_number(), wc_clean( $posted_data['reason_code'] ) )
283
  );
284
  }
285
 
290
  * @param array $posted_data Posted data
291
  */
292
  protected function payment_status_canceled_reversal( $order, $posted_data ) {
293
+ $order_id = version_compare( WC_VERSION, '3.0', '<' ) ? $order->id : $order->get_id();
294
  $this->send_ipn_email_notification(
295
  sprintf( __( 'Reversal cancelled for order #%s', 'woocommerce-gateway-paypal-express-checkout' ), $order->get_order_number() ),
296
+ sprintf( __( 'Order #%1$s has had a reversal cancelled. Please check the status of payment and update the order status accordingly here: %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $order->get_order_number(), esc_url( admin_url( 'post.php?post=' . $order_id . '&action=edit' ) ) )
297
  );
298
  }
299
 
304
  * @param array $posted_data Posted data
305
  */
306
  protected function save_paypal_meta_data( $order, $posted_data ) {
307
+
308
+ // A map of PayPal $POST keys to order meta keys
309
+ $mapped_keys = array(
310
+ 'payer_email' => 'Payer PayPal address',
311
+ 'first_name' => 'Payer first name',
312
+ 'last_name' => 'Payer last name',
313
+ 'payment_type' => 'Payment type',
314
+ 'txn_id' => '_transaction_id',
315
+ 'payment_status' => '_paypal_status'
316
+ );
317
+
318
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
319
+ foreach ( $mapped_keys as $post_key => $meta_key ) {
320
+ if ( ! empty( $posted_data[ $post_key ] ) ) {
321
+ $value = wc_clean( $posted_data[ $post_key ] );
322
+ if ( $old_wc ) {
323
+ update_post_meta( $order->id, $meta_key, $value );
324
+ } else {
325
+ $order->update_meta_data( $meta_key, $value );
326
+ }
327
+ }
328
  }
329
  }
330
 
includes/class-wc-gateway-ppec-ips-handler.php CHANGED
@@ -1,12 +1,12 @@
1
  <?php
2
- /**
3
- * PayPal Express Integrated PayPal Signup Handler.
4
- */
5
 
6
  if ( ! defined( 'ABSPATH' ) ) {
7
  exit; // Exit if accessed directly
8
  }
9
 
 
 
 
10
  class WC_Gateway_PPEC_IPS_Handler {
11
 
12
  const MIDDLEWARE_BASE_URL = 'https://connect.woocommerce.com';
@@ -16,6 +16,7 @@ class WC_Gateway_PPEC_IPS_Handler {
16
  *
17
  * @var array
18
  */
 
19
  private $_supported_countries = array(
20
  'AL', 'DZ', 'AO', 'AI', 'AG', 'AR', 'AM', 'AW', 'AU', 'AT', 'AZ', 'BS',
21
  'BH', 'BB', 'BE', 'BZ', 'BJ', 'BM', 'BT', 'BO', 'BA', 'BW', 'VG', 'BN',
@@ -34,6 +35,7 @@ class WC_Gateway_PPEC_IPS_Handler {
34
  'TG', 'TO', 'TT', 'TN', 'TR', 'TM', 'TC', 'TV', 'UG', 'UA', 'AE', 'GB',
35
  'TZ', 'US', 'UY', 'VU', 'VE', 'VN', 'WF', 'YE', 'ZM',
36
  );
 
37
 
38
  /**
39
  * Get merchant redirect URL for IPS.
@@ -107,9 +109,7 @@ class WC_Gateway_PPEC_IPS_Handler {
107
  */
108
  protected function _redirect_with_messages( $error_msg ) {
109
  if ( ! is_array( $error_msg ) ) {
110
- $error_msgs = array( array(
111
- 'error' => $error_msg
112
- ) );
113
  } else {
114
  $error_msgs = $error_msg;
115
  }
@@ -136,7 +136,7 @@ class WC_Gateway_PPEC_IPS_Handler {
136
  $env = in_array( $_GET['env'], array( 'live', 'sandbox' ) ) ? $_GET['env'] : 'live';
137
 
138
  // Verify the nonce.
139
- if ( ! wp_verify_nonce( $_GET['wc_ppec_ips_admin_nonce'], 'wc_ppec_ips') ) {
140
  wp_die( __( 'Invalid connection request', 'woocommerce-gateway-paypal-express-checkout' ) );
141
  }
142
 
@@ -172,15 +172,14 @@ class WC_Gateway_PPEC_IPS_Handler {
172
  if ( ! $payer_id ) {
173
  $this->_redirect_with_messages( __( 'Easy Setup was able to obtain your API credentials, but was unable to verify that they work correctly. Please make sure your PayPal account is set up properly and try Easy Setup again.', 'woocommerce-gateway-paypal-express-checkout' ) );
174
  }
175
-
176
- } catch( PayPal_API_Exception $ex ) {
177
  $error_msgs[] = array(
178
- 'warning' => __( 'Easy Setup was able to obtain your API credentials, but an error occurred while trying to verify that they work correctly. Please try Easy Setup again.', 'woocommerce-gateway-paypal-express-checkout' )
179
  );
180
  }
181
 
182
  $error_msgs[] = array(
183
- 'success' => __( 'Success! Your PayPal account has been set up successfully.', 'woocommerce-gateway-paypal-express-checkout' )
184
  );
185
 
186
  if ( ! empty( $error_msgs ) ) {
1
  <?php
 
 
 
2
 
3
  if ( ! defined( 'ABSPATH' ) ) {
4
  exit; // Exit if accessed directly
5
  }
6
 
7
+ /**
8
+ * PayPal Express Integrated PayPal Signup Handler.
9
+ */
10
  class WC_Gateway_PPEC_IPS_Handler {
11
 
12
  const MIDDLEWARE_BASE_URL = 'https://connect.woocommerce.com';
16
  *
17
  * @var array
18
  */
19
+ // @codingStandardsIgnoreStart
20
  private $_supported_countries = array(
21
  'AL', 'DZ', 'AO', 'AI', 'AG', 'AR', 'AM', 'AW', 'AU', 'AT', 'AZ', 'BS',
22
  'BH', 'BB', 'BE', 'BZ', 'BJ', 'BM', 'BT', 'BO', 'BA', 'BW', 'VG', 'BN',
35
  'TG', 'TO', 'TT', 'TN', 'TR', 'TM', 'TC', 'TV', 'UG', 'UA', 'AE', 'GB',
36
  'TZ', 'US', 'UY', 'VU', 'VE', 'VN', 'WF', 'YE', 'ZM',
37
  );
38
+ // @codingStandardsIgnoreEnd
39
 
40
  /**
41
  * Get merchant redirect URL for IPS.
109
  */
110
  protected function _redirect_with_messages( $error_msg ) {
111
  if ( ! is_array( $error_msg ) ) {
112
+ $error_msgs = array( array( 'error' => $error_msg ) );
 
 
113
  } else {
114
  $error_msgs = $error_msg;
115
  }
136
  $env = in_array( $_GET['env'], array( 'live', 'sandbox' ) ) ? $_GET['env'] : 'live';
137
 
138
  // Verify the nonce.
139
+ if ( ! wp_verify_nonce( $_GET['wc_ppec_ips_admin_nonce'], 'wc_ppec_ips' ) ) {
140
  wp_die( __( 'Invalid connection request', 'woocommerce-gateway-paypal-express-checkout' ) );
141
  }
142
 
172
  if ( ! $payer_id ) {
173
  $this->_redirect_with_messages( __( 'Easy Setup was able to obtain your API credentials, but was unable to verify that they work correctly. Please make sure your PayPal account is set up properly and try Easy Setup again.', 'woocommerce-gateway-paypal-express-checkout' ) );
174
  }
175
+ } catch ( PayPal_API_Exception $ex ) {
 
176
  $error_msgs[] = array(
177
+ 'warning' => __( 'Easy Setup was able to obtain your API credentials, but an error occurred while trying to verify that they work correctly. Please try Easy Setup again.', 'woocommerce-gateway-paypal-express-checkout' ),
178
  );
179
  }
180
 
181
  $error_msgs[] = array(
182
+ 'success' => __( 'Success! Your PayPal account has been set up successfully.', 'woocommerce-gateway-paypal-express-checkout' ),
183
  );
184
 
185
  if ( ! empty( $error_msgs ) ) {
includes/class-wc-gateway-ppec-payment-details.php CHANGED
@@ -32,7 +32,8 @@ class PayPal_Payment_Details {
32
 
33
  foreach ( $doECResponse as $index => $value ) {
34
  if ( array_key_exists( $index, $map ) ) {
35
- $this->$map[ $index ] = $value;
 
36
  }
37
  // Figure out the highest payment number
38
  if ( preg_match( '/^PAYMENTINFO_(\d)_(TRANSACTIONID|EBAYITEMAUCTIONTXNID|PARENTTRANSACTIONID|RECEIPTID|TRANSACTIONTYPE|PAYMENTTYPE|EXPECTEDECHECKCLEARDATE|ORDERTIME|AMT|CURRENCYCODE|FEEAMT|SETTLEAMT|TAXAMT|EXCHANGERATE|PAYMENTSTATUS|PENDINGREASON|REASONCODE|HOLDDECISION|SHIPPINGMETHOD|PROTECTIONELIGIBILITY|PROTECTIONELIGIBILITYTYPE|RECEIPTREFERENCENUMBER|SHIPPINGAMT|HANDLINGAMT|PAYMENTREQUESTID|INSTRUMENTCATEGORY|INSTRUMENTID|OFFERCODE|OFFERTRACKINGID|SHORTMESSAGE|LONGMESSAGE|ERRORCODE|SEVERITYCODE|ACK|SELLERPAYPALACCOUNTID|SECUREMERCHANTACCOUNTID|SELLERID|SELLERUSERNAME|SELLERREGISTRATIONDATE)$/', $index, $matches ) ) {
32
 
33
  foreach ( $doECResponse as $index => $value ) {
34
  if ( array_key_exists( $index, $map ) ) {
35
+ $key = $map[ $index ];
36
+ $this->$key = $value;
37
  }
38
  // Figure out the highest payment number
39
  if ( preg_match( '/^PAYMENTINFO_(\d)_(TRANSACTIONID|EBAYITEMAUCTIONTXNID|PARENTTRANSACTIONID|RECEIPTID|TRANSACTIONTYPE|PAYMENTTYPE|EXPECTEDECHECKCLEARDATE|ORDERTIME|AMT|CURRENCYCODE|FEEAMT|SETTLEAMT|TAXAMT|EXCHANGERATE|PAYMENTSTATUS|PENDINGREASON|REASONCODE|HOLDDECISION|SHIPPINGMETHOD|PROTECTIONELIGIBILITY|PROTECTIONELIGIBILITYTYPE|RECEIPTREFERENCENUMBER|SHIPPINGAMT|HANDLINGAMT|PAYMENTREQUESTID|INSTRUMENTCATEGORY|INSTRUMENTID|OFFERCODE|OFFERTRACKINGID|SHORTMESSAGE|LONGMESSAGE|ERRORCODE|SEVERITYCODE|ACK|SELLERPAYPALACCOUNTID|SECUREMERCHANTACCOUNTID|SELLERID|SELLERUSERNAME|SELLERREGISTRATIONDATE)$/', $index, $matches ) ) {
includes/class-wc-gateway-ppec-plugin.php CHANGED
@@ -145,6 +145,8 @@ class WC_Gateway_PPEC_Plugin {
145
  add_action( 'plugins_loaded', array( $this, 'bootstrap' ) );
146
  add_filter( 'allowed_redirect_hosts' , array( $this, 'whitelist_paypal_domains_for_redirect' ) );
147
  add_action( 'init', array( $this, 'load_plugin_textdomain' ) );
 
 
148
  }
149
 
150
  public function bootstrap() {
@@ -243,7 +245,7 @@ class WC_Gateway_PPEC_Plugin {
243
  $credential = $this->settings->get_active_api_credentials();
244
  if ( ! is_a( $credential, 'WC_Gateway_PPEC_Client_Credential' ) || '' === $credential->get_username() ) {
245
  $setting_link = $this->get_admin_setting_link();
246
- throw new Exception( __( 'PayPal Express Checkout is almost ready. To get started, <a href="' . $setting_link . '">connect your PayPal account</a>.', 'woocommerce-gateway-paypal-express-checkout' ), self::NOT_CONNECTED );
247
  }
248
  }
249
 
@@ -348,4 +350,23 @@ class WC_Gateway_PPEC_Plugin {
348
  public function load_plugin_textdomain() {
349
  load_plugin_textdomain( 'woocommerce-gateway-paypal-express-checkout', false, plugin_basename( $this->plugin_path ) . '/languages' );
350
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  }
145
  add_action( 'plugins_loaded', array( $this, 'bootstrap' ) );
146
  add_filter( 'allowed_redirect_hosts' , array( $this, 'whitelist_paypal_domains_for_redirect' ) );
147
  add_action( 'init', array( $this, 'load_plugin_textdomain' ) );
148
+
149
+ add_filter( 'plugin_action_links_' . plugin_basename( $this->file ), array( $this, 'plugin_action_links' ) );
150
  }
151
 
152
  public function bootstrap() {
245
  $credential = $this->settings->get_active_api_credentials();
246
  if ( ! is_a( $credential, 'WC_Gateway_PPEC_Client_Credential' ) || '' === $credential->get_username() ) {
247
  $setting_link = $this->get_admin_setting_link();
248
+ throw new Exception( sprintf( __( 'PayPal Express Checkout is almost ready. To get started, <a href="%s">connect your PayPal account</a>.', 'woocommerce-gateway-paypal-express-checkout' ), esc_url( $setting_link ) ), self::NOT_CONNECTED );
249
  }
250
  }
251
 
350
  public function load_plugin_textdomain() {
351
  load_plugin_textdomain( 'woocommerce-gateway-paypal-express-checkout', false, plugin_basename( $this->plugin_path ) . '/languages' );
352
  }
353
+
354
+ /**
355
+ * Add relevant links to plugins page.
356
+ *
357
+ * @since 1.2.0
358
+ *
359
+ * @param array $links Plugin action links
360
+ *
361
+ * @return array Plugin action links
362
+ */
363
+ public function plugin_action_links( $links ) {
364
+ $setting_url = $this->get_admin_setting_link();
365
+
366
+ $plugin_links = array(
367
+ '<a href="' . esc_url( $setting_url ) . '">' . esc_html__( 'Settings', 'woocommerce-gateway-paypal-express-checkout' ) . '</a>',
368
+ '<a href="https://docs.woocommerce.com/document/paypal-express-checkout/">' . esc_html__( 'Docs', 'woocommerce-gateway-paypal-express-checkout' ) . '</a>',
369
+ );
370
+ return array_merge( $plugin_links, $links );
371
+ }
372
  }
includes/class-wc-gateway-ppec-session-data.php CHANGED
@@ -4,34 +4,92 @@ if ( ! defined( 'ABSPATH' ) ) {
4
  exit; // Exit if accessed directly
5
  }
6
 
 
 
 
7
  class WC_Gateway_PPEC_Session_Data {
8
 
9
- public $source = false; // 'cart' or 'order'
10
- public $order_id = false; // if $source is 'order', this should be the order ID
11
- public $checkout_details = false; // Will be populated with the GetEC details later
12
- public $needs_shipping = false; // True if a shipping address is required for this transaction, false otherwise
13
- public $checkout_completed = false; // True if the buyer has just returned from PayPal and we should select PayPal as the payment method
14
- public $token = false; // The EC token
15
- public $payerID = false; // The buyer's payer ID, once they come back from PayPal
16
- public $expiry_time = false; // The time at which the token will expire
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  /**
19
- * Constructor
20
- * @param array $args
 
21
  */
22
  public function __construct( $args = array() ) {
23
  $args = wp_parse_args( $args, array(
24
- 'token' => '',
25
- 'source' => 'cart',
26
- 'order_id' => false,
27
- 'needs_shipping' => true,
28
- 'expires_in' => 10800,
29
  ) );
30
 
31
- $this->token = $args['token'];
32
- $this->source = $args['source'];
33
- $this->needs_shipping = $args['needs_shipping'];
34
- $this->expiry_time = time() + $args['expires_in'];
35
 
36
  if ( 'order' === $this->source ) {
37
  $this->order_id = $args['order_id'];
4
  exit; // Exit if accessed directly
5
  }
6
 
7
+ /**
8
+ * PayPal Express Checkout session wrapper.
9
+ */
10
  class WC_Gateway_PPEC_Session_Data {
11
 
12
+ /**
13
+ * Source where the session is set from. Valid value is either 'cart' or
14
+ * 'order'.
15
+ *
16
+ * If source is 'cart' then buyer starts it from cart page, otherwise it
17
+ * starts from checkout page or flow that has order created (e.g. from cart
18
+ * then the process_payment encountered error).
19
+ *
20
+ * @var string
21
+ */
22
+ public $source;
23
+
24
+ /**
25
+ * WooCommerce Order ID.
26
+ *
27
+ * If self::$source is 'order', this must be set to order ID.
28
+ *
29
+ * @var int
30
+ */
31
+ public $order_id;
32
+
33
+ /**
34
+ * Whether the buyer has returned from PayPal.
35
+ *
36
+ * If checkout_completed is true PPEC should be selected as the payment
37
+ * method.
38
+ *
39
+ * @var bool
40
+ */
41
+ public $checkout_completed = false;
42
+
43
+ /**
44
+ * Express checkout token.
45
+ *
46
+ * @var string
47
+ */
48
+ public $token;
49
+
50
+ /**
51
+ * The buyer's payer ID.
52
+ *
53
+ * Retrieved after buyer comes back from PayPal in-context dialog.
54
+ *
55
+ * @var string
56
+ */
57
+ public $payer_id;
58
+
59
+ /**
60
+ * How long the token will expires (in seconds).
61
+ *
62
+ * @var int
63
+ */
64
+ public $expiry_time;
65
+
66
+ /**
67
+ * Whether the buyer is checking out with PayPal Credit.
68
+ *
69
+ * @since 1.2.0
70
+ *
71
+ * @var bool
72
+ */
73
+ public $use_paypal_credit;
74
 
75
  /**
76
+ * Constructor.
77
+ *
78
+ * @param array $args Arguments for session data
79
  */
80
  public function __construct( $args = array() ) {
81
  $args = wp_parse_args( $args, array(
82
+ 'token' => '',
83
+ 'source' => 'cart',
84
+ 'order_id' => false,
85
+ 'expires_in' => 10800,
86
+ 'use_paypal_credit' => false,
87
  ) );
88
 
89
+ $this->token = $args['token'];
90
+ $this->source = $args['source'];
91
+ $this->expiry_time = time() + $args['expires_in'];
92
+ $this->use_paypal_credit = $args['use_paypal_credit'];
93
 
94
  if ( 'order' === $this->source ) {
95
  $this->order_id = $args['order_id'];
includes/class-wc-gateway-ppec-settings.php CHANGED
@@ -9,19 +9,58 @@ if ( ! defined( 'ABSPATH' ) ) {
9
  */
10
  class WC_Gateway_PPEC_Settings {
11
 
12
- protected $_settings = array();
13
- protected $_supportedLocale = array(
14
- 'da_DK', 'de_DE', 'en_AU', 'en_GB', 'en_US', 'es_ES', 'fr_CA', 'fr_FR',
15
- 'he_IL', 'id_ID', 'it_IT', 'ja_JP', 'nl_NL', 'no_NO', 'pl_PL', 'pt_BR',
16
- 'pt_PT', 'ru_RU', 'sv_SE', 'th_TH', 'tr_TR', 'zh_CN', 'zh_HK', 'zh_TW',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  );
18
 
19
  /**
20
  * Flag to indicate setting has been loaded from DB.
 
21
  * @var bool
22
  */
23
  private $_is_setting_loaded = false;
24
 
 
 
 
 
 
 
25
  public function __get( $key ) {
26
  if ( array_key_exists( $key, $this->_settings ) ) {
27
  return $this->_settings[ $key ];
@@ -34,17 +73,19 @@ class WC_Gateway_PPEC_Settings {
34
  }
35
 
36
  public function __construct() {
37
- $this->load_settings();
38
  }
39
 
40
  /**
41
  * Load settings from DB.
42
  *
43
- * @param bool $force_reload Force reload, ignore
 
 
44
  *
45
  * @return WC_Gateway_PPEC_Settings Instance of WC_Gateway_PPEC_Settings
46
  */
47
- public function load_settings( $force_reload = false ) {
48
  if ( $this->_is_setting_loaded && ! $force_reload ) {
49
  return $this;
50
  }
@@ -54,51 +95,79 @@ class WC_Gateway_PPEC_Settings {
54
  }
55
 
56
  /**
57
- * Get API credentials for the live envionment.
58
- * @return object
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  */
60
  public function get_live_api_credentials() {
61
  if ( $this->api_signature ) {
62
  return new WC_Gateway_PPEC_Client_Credential_Signature( $this->api_username, $this->api_password, $this->api_signature, $this->api_subject );
63
- } else {
64
- return new WC_Gateway_PPEC_Client_Credential_Certificate( $this->api_username, $this->api_password, $this->api_certificate, $this->api_subject );
65
  }
 
 
 
66
  }
67
 
68
  /**
69
- * Get API credentials for the live envionment.
70
- * @return object.
 
71
  */
72
  public function get_sandbox_api_credentials() {
73
  if ( $this->sandbox_api_signature ) {
74
  return new WC_Gateway_PPEC_Client_Credential_Signature( $this->sandbox_api_username, $this->sandbox_api_password, $this->sandbox_api_signature, $this->sandbox_api_subject );
75
- } else {
76
- return new WC_Gateway_PPEC_Client_Credential_Certificate( $this->sandbox_api_username, $this->sandbox_api_password, $this->sandbox_api_certificate, $this->sandbox_api_subject );
77
  }
 
 
78
  }
79
 
80
  /**
81
  * Get API credentials for the current envionment.
82
- * @return object|false if invalid
 
83
  */
84
  public function get_active_api_credentials() {
85
- if ( 'live' === $this->get_environment() ) {
86
- return $this->get_live_api_credentials();
87
- } else {
88
- return $this->get_sandbox_api_credentials();
89
- }
90
  }
91
 
 
 
 
 
 
 
 
 
 
 
 
92
  public function get_paypal_redirect_url( $token, $commit = false ) {
93
  $url = 'https://www.';
94
 
95
- if ( $this->environment !== 'live' ) {
96
  $url .= 'sandbox.';
97
  }
98
 
99
- $url .= 'paypal.com/';
100
- $url .= 'checkoutnow?';
101
- $url .= 'token=' . urlencode( $token );
102
 
103
  if ( $commit ) {
104
  $url .= '&useraction=commit';
@@ -108,71 +177,18 @@ class WC_Gateway_PPEC_Settings {
108
  }
109
 
110
  public function get_set_express_checkout_shortcut_params( $buckets = 1 ) {
111
- $params = array();
112
-
113
- if ( $this->logo_image_url ) {
114
- $params['LOGOIMG'] = $this->logo_image_url;
115
- }
116
-
117
- if ( apply_filters( 'woocommerce_paypal_express_checkout_allow_guests', true ) ) {
118
- $params['SOLUTIONTYPE'] = 'Sole';
119
- }
120
-
121
- if ( ! is_array( $buckets ) ) {
122
- $numBuckets = $buckets;
123
- $buckets = array();
124
- for ( $i = 0; $i < $numBuckets; $i++ ) {
125
- $buckets[] = $i;
126
- }
127
- }
128
 
129
- if ( 'yes' === $this->require_billing ) {
130
- $params['REQBILLINGADDRESS'] = '1';
131
- }
132
-
133
- foreach ( $buckets as $bucketNum ) {
134
- $params[ 'PAYMENTREQUEST_' . $bucketNum . '_PAYMENTACTION' ] = $this->get_paymentaction();
135
-
136
- if ( 'yes' === $this->instant_payments && 'sale' === $this->get_paymentaction() ) {
137
- $params[ 'PAYMENTREQUEST_' . $bucketNum . '_ALLOWEDPAYMENTMETHOD' ] = 'InstantPaymentOnly';
138
- }
139
- }
140
-
141
- return $params;
142
  }
143
 
144
  public function get_set_express_checkout_mark_params( $buckets = 1 ) {
145
- $params = array();
146
-
147
- if ( $this->logo_image_url ) {
148
- $params['LOGOIMG'] = $this->logo_image_url;
149
- }
150
-
151
- if ( false === apply_filters( 'woocommerce_paypal_express_checkout_allow_guests', true ) ) {
152
- $params['SOLUTIONTYPE'] = 'Sole';
153
- }
154
-
155
- if ( ! is_array( $buckets ) ) {
156
- $numBuckets = $buckets;
157
- $buckets = array();
158
- for ( $i = 0; $i < $numBuckets; $i++ ) {
159
- $buckets[] = $i;
160
- }
161
- }
162
-
163
- if ( 'yes' === $this->require_billing ) {
164
- $params['REQBILLINGADDRESS'] = '1';
165
- }
166
-
167
- foreach ( $buckets as $bucketNum ) {
168
- $params[ 'PAYMENTREQUEST_' . $bucketNum . '_PAYMENTACTION' ] = $this->get_paymentaction();
169
 
170
- if ( 'yes' === $this->instant_payments && 'sale' === $this->get_paymentaction() ) {
171
- $params[ 'PAYMENTREQUEST_' . $bucketNum . '_ALLOWEDPAYMENTMETHOD' ] = 'InstantPaymentOnly';
172
- }
173
- }
174
-
175
- return $params;
176
  }
177
 
178
  /**
@@ -188,25 +204,26 @@ class WC_Gateway_PPEC_Settings {
188
  public function get_do_express_checkout_params( WC_Order $order, $buckets = 1 ) {
189
  $params = array();
190
  if ( ! is_array( $buckets ) ) {
191
- $numBuckets = $buckets;
192
  $buckets = array();
193
- for ( $i = 0; $i < $numBuckets; $i++ ) {
194
  $buckets[] = $i;
195
  }
196
  }
197
 
198
- foreach ( $buckets as $bucketNum ) {
199
- $params[ 'PAYMENTREQUEST_' . $bucketNum . '_NOTIFYURL' ] = WC()->api_request_url( 'WC_Gateway_PPEC' );
200
- $params[ 'PAYMENTREQUEST_' . $bucketNum . '_PAYMENTACTION' ] = $this->get_paymentaction();
201
- $params[ 'PAYMENTREQUEST_' . $bucketNum . '_INVNUM' ] = $this->invoice_prefix . $order->get_order_number();
202
- $params[ 'PAYMENTREQUEST_' . $bucketNum . '_CUSTOM' ] = json_encode( array( 'order_id' => $order->id, 'order_key' => $order->order_key ) );
203
  }
204
 
205
  return $params;
206
  }
207
 
208
  /**
209
- * Is PPEC enabled
 
210
  * @return bool
211
  */
212
  public function is_enabled() {
@@ -214,7 +231,8 @@ class WC_Gateway_PPEC_Settings {
214
  }
215
 
216
  /**
217
- * Is logging enabled
 
218
  * @return bool
219
  */
220
  public function is_logging_enabled() {
@@ -222,36 +240,41 @@ class WC_Gateway_PPEC_Settings {
222
  }
223
 
224
  /**
225
- * Payment action
 
226
  * @return string
227
  */
228
  public function get_paymentaction() {
229
- return $this->paymentaction === 'authorization' ? 'authorization' : 'sale';
230
  }
231
 
232
  /**
233
- * Payment action
 
234
  * @return string
235
  */
236
  public function get_environment() {
237
- return $this->environment === 'sandbox' ? 'sandbox' : 'live';
238
  }
239
 
240
  /**
241
- * Subtotal mismatches
 
242
  * @return string
243
  */
244
  public function get_subtotal_mismatch_behavior() {
245
- return $this->subtotal_mismatch_behavior === 'drop' ? 'drop' : 'add';
246
  }
247
 
248
  /**
249
  * Get session length.
 
 
 
250
  * @return int
251
  */
252
  public function get_token_session_length() {
253
- // Really, we should map this to a merchant-configurable setting, but for now, we'll just set it to the default (3 hours).
254
- return 10800;
255
  }
256
 
257
  /**
@@ -271,13 +294,84 @@ class WC_Gateway_PPEC_Settings {
271
 
272
  /**
273
  * Get locale for PayPal.
 
274
  * @return string
275
  */
276
  public function get_paypal_locale() {
277
  $locale = get_locale();
278
- if ( ! in_array( $locale, $this->_supportedLocale ) ) {
279
  $locale = 'en_US';
280
  }
281
  return $locale;
282
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  }
9
  */
10
  class WC_Gateway_PPEC_Settings {
11
 
12
+ /**
13
+ * Setting values from get_option.
14
+ *
15
+ * @var array
16
+ */
17
+ protected $_settings = array();
18
+
19
+ /**
20
+ * List of locales supported by PayPal.
21
+ *
22
+ * @var array
23
+ */
24
+ protected $_supported_locales = array(
25
+ 'da_DK',
26
+ 'de_DE',
27
+ 'en_AU',
28
+ 'en_GB',
29
+ 'en_US',
30
+ 'es_ES',
31
+ 'fr_CA',
32
+ 'fr_FR',
33
+ 'he_IL',
34
+ 'id_ID',
35
+ 'it_IT',
36
+ 'ja_JP',
37
+ 'nl_NL',
38
+ 'no_NO',
39
+ 'pl_PL',
40
+ 'pt_BR',
41
+ 'pt_PT',
42
+ 'ru_RU',
43
+ 'sv_SE',
44
+ 'th_TH',
45
+ 'tr_TR',
46
+ 'zh_CN',
47
+ 'zh_HK',
48
+ 'zh_TW',
49
  );
50
 
51
  /**
52
  * Flag to indicate setting has been loaded from DB.
53
+ *
54
  * @var bool
55
  */
56
  private $_is_setting_loaded = false;
57
 
58
+ public function __set( $key, $value ) {
59
+ if ( array_key_exists( $key, $this->_settings ) ) {
60
+ $this->_settings[ $key ] = $value;
61
+ }
62
+ }
63
+
64
  public function __get( $key ) {
65
  if ( array_key_exists( $key, $this->_settings ) ) {
66
  return $this->_settings[ $key ];
73
  }
74
 
75
  public function __construct() {
76
+ $this->load();
77
  }
78
 
79
  /**
80
  * Load settings from DB.
81
  *
82
+ * @since 1.2.0
83
+ *
84
+ * @param bool $force_reload Force reload settings
85
  *
86
  * @return WC_Gateway_PPEC_Settings Instance of WC_Gateway_PPEC_Settings
87
  */
88
+ public function load( $force_reload = false ) {
89
  if ( $this->_is_setting_loaded && ! $force_reload ) {
90
  return $this;
91
  }
95
  }
96
 
97
  /**
98
+ * Load settings from DB.
99
+ *
100
+ * @deprecated
101
+ */
102
+ public function load_settings( $force_reload = false ) {
103
+ _deprecated_function( __METHOD__, '1.2.0', 'WC_Gateway_PPEC_Settings::load' );
104
+ return $this->load( $force_reload );
105
+ }
106
+
107
+ /**
108
+ * Save current settings.
109
+ *
110
+ * @since 1.2.0
111
+ */
112
+ public function save() {
113
+ update_option( 'woocommerce_ppec_paypal_settings', $this->_settings );
114
+ }
115
+
116
+ /**
117
+ * Get API credentials for live envionment.
118
+ *
119
+ * @return WC_Gateway_PPEC_Client_Credential_Signature|WC_Gateway_PPEC_Client_Credential_Certificate
120
  */
121
  public function get_live_api_credentials() {
122
  if ( $this->api_signature ) {
123
  return new WC_Gateway_PPEC_Client_Credential_Signature( $this->api_username, $this->api_password, $this->api_signature, $this->api_subject );
 
 
124
  }
125
+
126
+ return new WC_Gateway_PPEC_Client_Credential_Certificate( $this->api_username, $this->api_password, $this->api_certificate, $this->api_subject );
127
+
128
  }
129
 
130
  /**
131
+ * Get API credentials for sandbox envionment.
132
+ *
133
+ * @return WC_Gateway_PPEC_Client_Credential_Signature|WC_Gateway_PPEC_Client_Credential_Certificate
134
  */
135
  public function get_sandbox_api_credentials() {
136
  if ( $this->sandbox_api_signature ) {
137
  return new WC_Gateway_PPEC_Client_Credential_Signature( $this->sandbox_api_username, $this->sandbox_api_password, $this->sandbox_api_signature, $this->sandbox_api_subject );
 
 
138
  }
139
+
140
+ return new WC_Gateway_PPEC_Client_Credential_Certificate( $this->sandbox_api_username, $this->sandbox_api_password, $this->sandbox_api_certificate, $this->sandbox_api_subject );
141
  }
142
 
143
  /**
144
  * Get API credentials for the current envionment.
145
+ *
146
+ * @return object
147
  */
148
  public function get_active_api_credentials() {
149
+ return 'live' === $this->get_environment() ? $this->get_live_api_credentials() : $this->get_sandbox_api_credentials();
 
 
 
 
150
  }
151
 
152
+ /**
153
+ * Get PayPal redirect URL.
154
+ *
155
+ * @param string $token Token
156
+ * @param bool $commit If set to true, 'useraction' parameter will be set
157
+ * to 'commit' which makes PayPal sets the button text
158
+ * to **Pay Now** ont the PayPal _Review your information_
159
+ * page.
160
+ *
161
+ * @return string PayPal redirect URL
162
+ */
163
  public function get_paypal_redirect_url( $token, $commit = false ) {
164
  $url = 'https://www.';
165
 
166
+ if ( 'live' !== $this->environment ) {
167
  $url .= 'sandbox.';
168
  }
169
 
170
+ $url .= 'paypal.com/checkoutnow?token=' . urlencode( $token );
 
 
171
 
172
  if ( $commit ) {
173
  $url .= '&useraction=commit';
177
  }
178
 
179
  public function get_set_express_checkout_shortcut_params( $buckets = 1 ) {
180
+ _deprecated_function( __METHOD__, '1.2.0', 'WC_Gateway_PPEC_Client::get_set_express_checkout_params' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
 
182
+ return wc_gateway_ppec()->client->get_set_express_checkout_params( array( 'start_from' => 'cart' ) );
 
 
 
 
 
 
 
 
 
 
 
 
183
  }
184
 
185
  public function get_set_express_checkout_mark_params( $buckets = 1 ) {
186
+ _deprecated_function( __METHOD__, '1.2.0', 'WC_Gateway_PPEC_Client::get_set_express_checkout_params' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
 
188
+ // Still missing order_id in args.
189
+ return wc_gateway_ppec()->client->get_set_express_checkout_params( array(
190
+ 'start_from' => 'checkout',
191
+ ) );
 
 
192
  }
193
 
194
  /**
204
  public function get_do_express_checkout_params( WC_Order $order, $buckets = 1 ) {
205
  $params = array();
206
  if ( ! is_array( $buckets ) ) {
207
+ $num_buckets = $buckets;
208
  $buckets = array();
209
+ for ( $i = 0; $i < $num_buckets; $i++ ) {
210
  $buckets[] = $i;
211
  }
212
  }
213
 
214
+ foreach ( $buckets as $bucket_num ) {
215
+ $params[ 'PAYMENTREQUEST_' . $bucket_num . '_NOTIFYURL' ] = WC()->api_request_url( 'WC_Gateway_PPEC' );
216
+ $params[ 'PAYMENTREQUEST_' . $bucket_num . '_PAYMENTACTION' ] = $this->get_paymentaction();
217
+ $params[ 'PAYMENTREQUEST_' . $bucket_num . '_INVNUM' ] = $this->invoice_prefix . $order->get_order_number();
218
+ $params[ 'PAYMENTREQUEST_' . $bucket_num . '_CUSTOM' ] = json_encode( array( 'order_id' => $order->id, 'order_key' => $order->order_key ) );
219
  }
220
 
221
  return $params;
222
  }
223
 
224
  /**
225
+ * Is PPEC enabled.
226
+ *
227
  * @return bool
228
  */
229
  public function is_enabled() {
231
  }
232
 
233
  /**
234
+ * Is logging enabled.
235
+ *
236
  * @return bool
237
  */
238
  public function is_logging_enabled() {
240
  }
241
 
242
  /**
243
+ * Get payment action from setting.
244
+ *
245
  * @return string
246
  */
247
  public function get_paymentaction() {
248
+ return 'authorization' === $this->paymentaction ? 'authorization' : 'sale';
249
  }
250
 
251
  /**
252
+ * Get active environment from setting.
253
+ *
254
  * @return string
255
  */
256
  public function get_environment() {
257
+ return 'sandbox' === $this->environment ? 'sandbox' : 'live';
258
  }
259
 
260
  /**
261
+ * Subtotal mismatches.
262
+ *
263
  * @return string
264
  */
265
  public function get_subtotal_mismatch_behavior() {
266
+ return 'drop' === $this->subtotal_mismatch_behavior ? 'drop' : 'add';
267
  }
268
 
269
  /**
270
  * Get session length.
271
+ *
272
+ * @todo Map this to a merchant-configurable setting
273
+ *
274
  * @return int
275
  */
276
  public function get_token_session_length() {
277
+ return 10800; // 3h
 
278
  }
279
 
280
  /**
294
 
295
  /**
296
  * Get locale for PayPal.
297
+ *
298
  * @return string
299
  */
300
  public function get_paypal_locale() {
301
  $locale = get_locale();
302
+ if ( ! in_array( $locale, $this->_supported_locales ) ) {
303
  $locale = 'en_US';
304
  }
305
  return $locale;
306
  }
307
+
308
+ /**
309
+ * Get brand name form settings.
310
+ *
311
+ * Default to site's name if brand_name in settings empty.
312
+ *
313
+ * @since 1.2.0
314
+ *
315
+ * @return string
316
+ */
317
+ public function get_brand_name() {
318
+ $brand_name = $this->brand_name ? $this->brand_name : get_bloginfo( 'name', 'display' );
319
+
320
+ /**
321
+ * Character length and limitations for this parameter is 127 single-byte
322
+ * alphanumeric characters.
323
+ *
324
+ * @see https://developer.paypal.com/docs/classic/api/merchant/SetExpressCheckout_API_Operation_NVP/
325
+ */
326
+ if ( ! empty( $brand_name ) ) {
327
+ $brand_name = substr( $brand_name, 0, 127 );
328
+ }
329
+
330
+ /**
331
+ * Filters the brand name in PayPal hosted checkout pages.
332
+ *
333
+ * @since 1.2.0
334
+ *
335
+ * @param string Brand name
336
+ */
337
+ return apply_filters( 'woocommerce_paypal_express_checkout_get_brand_name', $brand_name );
338
+ }
339
+
340
+ /**
341
+ * Checks whether PayPal Credit is enabled.
342
+ *
343
+ * @since 1.2.0
344
+ *
345
+ * @return bool Returns true if PayPal Credit is enabled and supported
346
+ */
347
+ public function is_credit_enabled() {
348
+ $gateways = WC()->payment_gateways->get_available_payment_gateways();
349
+ if ( ! isset( $gateways['ppec_paypal'] ) ) {
350
+ return false;
351
+ }
352
+
353
+ return 'yes' === $this->credit_enabled && $gateways['ppec_paypal']->is_credit_supported();
354
+ }
355
+
356
+ /**
357
+ * Checks if currency in setting supports 0 decimal places.
358
+ *
359
+ * @since 1.2.0
360
+ *
361
+ * @return bool Returns true if currency supports 0 decimal places
362
+ */
363
+ public function is_currency_supports_zero_decimal() {
364
+ return in_array( get_woocommerce_currency(), array( 'HUF', 'JPY', 'TWD' ) );
365
+ }
366
+
367
+ /**
368
+ * Get number of digits after the decimal point.
369
+ *
370
+ * @since 1.2.0
371
+ *
372
+ * @return int Number of digits after the decimal point. Either 2 or 0
373
+ */
374
+ public function get_number_of_decimal_digits() {
375
+ return $this->is_currency_supports_zero_decimal() ? 0 : 2;
376
+ }
377
  }
includes/class-wc-gateway-ppec-with-paypal-addons.php ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ class WC_Gateway_PPEC_With_PayPal_Addons extends WC_Gateway_PPEC_With_PayPal {
8
+
9
+ public function __construct() {
10
+ parent::__construct();
11
+
12
+ $this->supports = array_merge(
13
+ $this->supports,
14
+ array(
15
+ 'subscriptions',
16
+ 'subscription_cancellation',
17
+ 'subscription_reactivation',
18
+ 'subscription_suspension',
19
+ 'multiple_subscriptions',
20
+ 'subscription_payment_method_change_customer',
21
+ 'subscription_payment_method_change_admin',
22
+ 'subscription_amount_changes',
23
+ 'subscription_date_changes',
24
+ )
25
+ );
26
+
27
+ $this->_maybe_register_callback_in_subscriptions();
28
+ }
29
+
30
+ /**
31
+ * Maybe register callback in WooCommerce Subscription hooks.
32
+ *
33
+ * @since 1.2.0
34
+ */
35
+ protected function _maybe_register_callback_in_subscriptions() {
36
+ if ( ! class_exists( 'WC_Subscriptions_Order' ) ) {
37
+ return;
38
+ }
39
+
40
+ add_action( 'woocommerce_scheduled_subscription_payment_' . $this->id, array( $this, 'scheduled_subscription_payment' ), 10, 2 );
41
+ add_action( 'woocommerce_subscription_failing_payment_method_' . $this->id, array( $this, 'update_failing_payment_method' ) );
42
+ }
43
+
44
+ /**
45
+ * Checks whether order is part of subscription.
46
+ *
47
+ * @since 1.2.0
48
+ *
49
+ * @param int $order_id Order ID
50
+ *
51
+ * @return bool Returns true if order is part of subscription
52
+ */
53
+ public function is_subscription( $order_id ) {
54
+ return ( function_exists( 'wcs_order_contains_subscription' ) && ( wcs_order_contains_subscription( $order_id ) || wcs_is_subscription( $order_id ) || wcs_order_contains_renewal( $order_id ) ) );
55
+ }
56
+
57
+ /**
58
+ * Process payment.
59
+ *
60
+ * @since 1.2.0
61
+ *
62
+ * @param int $order_id Order ID
63
+ *
64
+ * @return array
65
+ */
66
+ public function process_payment( $order_id ) {
67
+ if ( $this->is_subscription( $order_id ) ) {
68
+ return $this->process_subscription( $order_id );
69
+ }
70
+
71
+ return parent::process_payment( $order_id );
72
+ }
73
+
74
+ /**
75
+ * Process initial subscription.
76
+ *
77
+ * @since 1.2.0
78
+ *
79
+ * @param int $order_id Order ID
80
+ *
81
+ * @return array
82
+ */
83
+ public function process_subscription( $order_id ) {
84
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
85
+ $resp = parent::process_payment( $order_id );
86
+ $order = wc_get_order( $order_id );
87
+
88
+ $subscriptions = array();
89
+ if ( function_exists( 'wcs_order_contains_subscription' ) && wcs_order_contains_subscription( $order_id ) ) {
90
+ $subscriptions = wcs_get_subscriptions_for_order( $order_id );
91
+ } elseif ( function_exists( 'wcs_order_contains_renewal' ) && wcs_order_contains_renewal( $order_id ) ) {
92
+ $subscriptions = wcs_get_subscriptions_for_renewal_order( $order_id );
93
+ }
94
+
95
+ $billing_agreement_id = $old_wc ? get_post_meta( $order_id, '_ppec_billing_agreement_id', true ) : $order->get_meta( '_ppec_billing_agreement_id', true );
96
+
97
+ // Shipping / billing addresses and billing agreement were not copied
98
+ // because it's not available during subscription creation.
99
+ foreach ( $subscriptions as $subscription ) {
100
+ wcs_copy_order_address( $order, $subscription );
101
+ update_post_meta( $subscription->id, '_ppec_billing_agreement_id', $billing_agreement_id );
102
+ }
103
+
104
+ return $resp;
105
+ }
106
+
107
+ /**
108
+ * Process scheduled subscription payment.
109
+ *
110
+ * @since 1.2.0
111
+ *
112
+ * @param float $amount Subscription amount
113
+ * @param int|WC_Order $order Order ID or order object
114
+ */
115
+ public function scheduled_subscription_payment( $amount, $order ) {
116
+ $old_wc = version_compare( WC_VERSION, '3.0', '<' );
117
+ $order = wc_get_order( $order );
118
+ $order_id = $old_wc ? $order->id : $order->get_id();
119
+ $billing_agreement_id = $old_wc ? get_post_meta( $order_id, '_ppec_billing_agreement_id', true ) : $order->get_meta( '_ppec_billing_agreement_id', true );
120
+
121
+ if ( empty( $billing_agreement_id ) ) {
122
+ wc_gateway_ppec_log( sprintf( '%s: Could not found billing agreement. Skip reference transaction', __METHOD__ ) );
123
+ return;
124
+ }
125
+
126
+ if ( 0 == $amount ) {
127
+ $order->payment_complete();
128
+ return;
129
+ }
130
+
131
+ $client = wc_gateway_ppec()->client;
132
+ $params = $client->get_do_reference_transaction_params( array(
133
+ 'reference_id' => $billing_agreement_id,
134
+ 'amount' => $amount,
135
+ 'order_id' => $order_id,
136
+ ) );
137
+
138
+ $resp = $client->do_reference_transaction( $params );
139
+
140
+ $this->_process_reference_transaction_response( $order, $resp );
141
+ }
142
+
143
+ /**
144
+ * Process reference transaction response used when creating payment for
145
+ * scheduled subscription.
146
+ *
147
+ * @since 1.2.0
148
+ *
149
+ * @param WC_Order $order Order object
150
+ * @param array $response Response from DoReferenceTransaction
151
+ */
152
+ protected function _process_reference_transaction_response( $order, $response ) {
153
+ $client = wc_gateway_ppec()->client;
154
+
155
+ try {
156
+ if ( ! $client->response_has_success_status( $response ) ) {
157
+ throw new Exception( __( 'PayPal API error', 'woocommerce-gateway-paypal-express-checkout' ) );
158
+ }
159
+
160
+ $status = ! empty( $response['PAYMENTSTATUS'] ) ? $response['PAYMENTSTATUS'] : '';
161
+
162
+ switch ( $status ) {
163
+ case 'Pending':
164
+ /* translators: placeholder is pending reason from PayPal API. */
165
+ $order_note = sprintf( __( 'PayPal transaction held: %s', 'woocommerce-gateway-paypal-express-checkout' ), $response['PENDINGREASON'] );
166
+ if ( ! $order->has_status( 'on-hold' ) ) {
167
+ $order->update_status( 'on-hold', $order_note );
168
+ } else {
169
+ $order->add_order_note( $order_note );
170
+ }
171
+ break;
172
+ case 'Completed':
173
+ case 'Processed':
174
+ case 'In-Progress':
175
+ $transaction_id = $response['TRANSACTIONID'];
176
+ $order->add_order_note( sprintf( __( 'PayPal payment approved (ID: %s)', 'woocommerce-gateway-paypal-express-checkout' ), $transaction_id ) );
177
+ $order->payment_complete( $transaction_id );
178
+ break;
179
+ default:
180
+ throw new Exception( __( 'PayPal payment declined', 'woocommerce-gateway-paypal-express-checkout' ) );
181
+ }
182
+
183
+ } catch ( Exception $e ) {
184
+ $order->update_status( 'failed', $e->getMessage() );
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Update billing agreement ID for a subscription after using PPEC to complete
190
+ * a payment to make up for an automatic renewal payment which previously
191
+ * failed.
192
+ *
193
+ * @since 1.2.0
194
+ *
195
+ * @param WC_Subscription $subscription The subscription for which the failing
196
+ * payment method relates
197
+ * @param WC_Order $renewal_order The order which recorded the successful
198
+ * payment (to make up for the failed
199
+ * automatic payment)
200
+ */
201
+ public function update_failing_payment_method( $subscription, $renewal_order ) {
202
+ update_post_meta( $subscription->id, '_ppec_billing_agreement_id', $renewal_order->ppec_billing_agreement_id );
203
+ }
204
+ }
includes/class-wc-gateway-ppec-with-paypal-credit.php DELETED
@@ -1,22 +0,0 @@
1
- <?php
2
-
3
- if ( ! defined( 'ABSPATH' ) ) {
4
- exit; // Exit if accessed directly
5
- }
6
-
7
- class WC_Gateway_PPEC_With_PayPal_Credit extends WC_Gateway_PPEC {
8
- public function __construct() {
9
-
10
- $this->id = 'ppec_paypal_credit';
11
-
12
- parent::__construct();
13
-
14
- $settings = wc_gateway_ppec()->settings->loadSettings();
15
-
16
- $this->icon = 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/ppc-acceptance-' . $settings->markSize . '.png';
17
- $this->enabled = $settings->ppcEnabled ? 'yes' : 'no';
18
- $this->title = __( 'PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' );
19
- $this->description = __( 'Make checkout quick and easy for your buyers, and give them an easy way to finance their purchases at the same time.', 'woocommerce-gateway-paypal-express-checkout' );
20
- }
21
- }
22
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/class-wc-gateway-ppec-with-paypal.php CHANGED
@@ -7,6 +7,7 @@ if ( ! defined( 'ABSPATH' ) ) {
7
  class WC_Gateway_PPEC_With_PayPal extends WC_Gateway_PPEC {
8
  public function __construct() {
9
  $this->id = 'ppec_paypal';
 
10
  parent::__construct();
11
 
12
  if ( $this->is_available() ) {
7
  class WC_Gateway_PPEC_With_PayPal extends WC_Gateway_PPEC {
8
  public function __construct() {
9
  $this->id = 'ppec_paypal';
10
+
11
  parent::__construct();
12
 
13
  if ( $this->is_available() ) {
includes/exceptions/class-wc-gateway-ppec-api-exception.php CHANGED
@@ -4,15 +4,40 @@ if ( ! defined( 'ABSPATH' ) ) {
4
  exit; // Exit if accessed directly
5
  }
6
 
 
 
 
7
  class PayPal_API_Exception extends Exception {
 
 
 
 
 
 
8
  public $errors;
 
 
 
 
 
 
 
 
 
 
9
  public $correlation_id;
10
 
11
- // This constructor takes the API response received from PayPal, parses out the errors in the response,
12
- // then places those errors into the $errors property. It also captures correlation ID and places that
13
- // in the $correlation_id property.
 
 
 
 
 
 
14
  public function __construct( $response ) {
15
- parent::__construct( 'An error occurred while calling the PayPal API.' );
16
 
17
  $errors = array();
18
  foreach ( $response as $index => $value ) {
@@ -29,11 +54,20 @@ class PayPal_API_Exception extends Exception {
29
  }
30
  }
31
 
32
- $error_objects = array();
 
33
  foreach ( $errors as $value ) {
34
- $error_objects[] = new PayPal_API_Error( $value['code'], $value['message'], $value['long'], $value['severity'] );
 
 
 
 
 
 
 
 
35
  }
36
 
37
- $this->errors = $error_objects;
38
  }
39
  }
4
  exit; // Exit if accessed directly
5
  }
6
 
7
+ /**
8
+ * PayPal API Exception.
9
+ */
10
  class PayPal_API_Exception extends Exception {
11
+
12
+ /**
13
+ * List of errors from PayPal API.
14
+ *
15
+ * @var array
16
+ */
17
  public $errors;
18
+
19
+ /**
20
+ * Unique identifier of PayPal transaction.
21
+ *
22
+ * This identifies the PayPal application that processed the request and
23
+ * must be provided to Merchant Technical Support if you need their assistance
24
+ * with a specific transaction.
25
+ *
26
+ * @var string
27
+ */
28
  public $correlation_id;
29
 
30
+ /**
31
+ * Constructor.
32
+ *
33
+ * This constructor takes the API response received from PayPal, parses out the
34
+ * errors in the response, then places those errors into the $errors property.
35
+ * It also captures correlation ID and places that in the $correlation_id property.
36
+ *
37
+ * @param array $response Response from PayPal API
38
+ */
39
  public function __construct( $response ) {
40
+ parent::__construct( __( 'An error occurred while calling the PayPal API.', 'woocommerce-gateway-paypal-express-checkout' ) );
41
 
42
  $errors = array();
43
  foreach ( $response as $index => $value ) {
54
  }
55
  }
56
 
57
+ $this->errors = array();
58
+ $error_messages = array();
59
  foreach ( $errors as $value ) {
60
+ $error = new PayPal_API_Error( $value['code'], $value['message'], $value['long'], $value['severity'] );
61
+ $this->errors[] = $error;
62
+
63
+ /* translators: placeholders are error code and message from PayPal */
64
+ $error_messages[] = sprintf( __( 'PayPal error (%1$s): %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $error->error_code, $error->maptoBuyerFriendlyError() );
65
+ }
66
+
67
+ if ( empty( $error_messages ) ) {
68
+ $error_messages[] = __( 'An error occurred while calling the PayPal API.', 'woocommerce-gateway-paypal-express-checkout' );
69
  }
70
 
71
+ $this->message = implode( PHP_EOL, $error_messages );
72
  }
73
  }
includes/exceptions/class-wc-gateway-ppec-missing-session-exception.php CHANGED
@@ -4,8 +4,21 @@ if ( ! defined( 'ABSPATH' ) ) {
4
  exit; // Exit if accessed directly
5
  }
6
 
 
 
 
7
  class PayPal_Missing_Session_Exception extends Exception {
8
- public function __construct() {
9
- parent::__construct( 'The buyer\'s session information could not be found.' );
 
 
 
 
 
 
 
 
 
 
10
  }
11
  }
4
  exit; // Exit if accessed directly
5
  }
6
 
7
+ /**
8
+ * Missing session exception.
9
+ */
10
  class PayPal_Missing_Session_Exception extends Exception {
11
+
12
+ /**
13
+ * Constructor.
14
+ *
15
+ * @param string $message Exception message
16
+ */
17
+ public function __construct( $message = '' ) {
18
+ if ( empty( $message ) ) {
19
+ $message = __( 'The buyer\'s session information could not be found.', 'woocommerce-gateway-paypal-express-checkout' );
20
+ }
21
+
22
+ parent::__construct( $message );
23
  }
24
  }
includes/functions.php CHANGED
@@ -8,7 +8,7 @@ function woo_pp_start_checkout() {
8
  wp_safe_redirect( $redirect_url );
9
  exit;
10
  } catch( PayPal_API_Exception $e ) {
11
- wc_gateway_ppec_format_paypal_api_exception( $e->errors );
12
 
13
  $redirect_url = WC()->cart->get_cart_url();
14
  $settings = wc_gateway_ppec()->settings;
@@ -37,12 +37,11 @@ function woo_pp_start_checkout() {
37
  }
38
  }
39
 
 
 
 
40
  function wc_gateway_ppec_format_paypal_api_exception( $errors ) {
41
- $error_strings = array();
42
- foreach ( $errors as $error ) {
43
- $error_strings[] = $error->maptoBuyerFriendlyError();
44
- }
45
- wc_add_notice( __( 'Payment error:', 'woocommerce-gateway-paypal-express-checkout' ) . '<ul><li>' . implode( '</li><li>', $error_strings ) . '</li></ul>', 'error' );
46
  }
47
 
48
  /**
@@ -68,3 +67,14 @@ function wc_gateway_ppec_log( $message ) {
68
  error_log( $message );
69
  }
70
  }
 
 
 
 
 
 
 
 
 
 
 
8
  wp_safe_redirect( $redirect_url );
9
  exit;
10
  } catch( PayPal_API_Exception $e ) {
11
+ wc_add_notice( $e->getMessage(), 'error' );
12
 
13
  $redirect_url = WC()->cart->get_cart_url();
14
  $settings = wc_gateway_ppec()->settings;
37
  }
38
  }
39
 
40
+ /**
41
+ * @deprecated
42
+ */
43
  function wc_gateway_ppec_format_paypal_api_exception( $errors ) {
44
+ _deprecated_function( 'wc_gateway_ppec_format_paypal_api_exception', '1.2.0', '' );
 
 
 
 
45
  }
46
 
47
  /**
67
  error_log( $message );
68
  }
69
  }
70
+
71
+ /**
72
+ * Checks whether buyer is checking out with PayPal Credit.
73
+ *
74
+ * @since 1.2.0
75
+ *
76
+ * @return bool Returns true if buyer is checking out with PayPal Credit
77
+ */
78
+ function wc_gateway_ppec_is_using_credit() {
79
+ return ! empty( $_GET['use-ppc'] ) && 'true' === $_GET['use-ppc'];
80
+ }
includes/settings/settings-ppec.php CHANGED
@@ -15,14 +15,37 @@ if ( $enable_ips && $needs_creds ) {
15
  $ips_button = '<a href="' . esc_url( wc_gateway_ppec()->ips->get_signup_url( 'live' ) ) . '" class="button button-primary">' . __( 'Setup or link an existing PayPal account', 'woocommerce-gateway-paypal-express-checkout' ) . '</a>';
16
  $api_creds_text = sprintf( __( '%s or <a href="#" class="ppec-toggle-settings">click here to toggle manual API credential input</a>.', 'woocommerce-gateway-paypal-express-checkout' ), $ips_button );
17
  } else {
18
- $api_creds_text = '';
 
 
 
 
 
 
 
 
 
19
  }
20
 
21
  if ( $enable_ips && $needs_sandbox_creds ) {
22
  $sandbox_ips_button = '<a href="' . esc_url( wc_gateway_ppec()->ips->get_signup_url( 'sandbox' ) ) . '" class="button button-primary">' . __( 'Setup or link an existing PayPal Sandbox account', 'woocommerce-gateway-paypal-express-checkout' ) . '</a>';
23
  $sandbox_api_creds_text = sprintf( __( '%s or <a href="#" class="ppec-toggle-sandbox-settings">click here to toggle manual API credential input</a>.', 'woocommerce-gateway-paypal-express-checkout' ), $sandbox_ips_button );
24
  } else {
25
- $sandbox_api_creds_text = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  }
27
 
28
  wc_enqueue_js( "
@@ -71,13 +94,15 @@ wc_enqueue_js( "
71
  }).change();
72
 
73
  if ( enable_toggle ) {
74
- $( document ).on( 'click', '.ppec-toggle-settings', function() {
75
- $( ppec_live_fields ).closest( 'tr' ).toggle();
 
76
  } );
77
  }
78
  if ( enable_sandbox_toggle ) {
79
- $( document ).on( 'click', '.ppec-toggle-sandbox-settings', function() {
80
- $( ppec_sandbox_fields ).closest( 'tr' ).toggle();
 
81
  } );
82
  }
83
  });
@@ -93,41 +118,9 @@ return array(
93
  'label' => __( 'Enable PayPal Express Checkout', 'woocommerce-gateway-paypal-express-checkout' ),
94
  'description' => __( 'This enables PayPal Express Checkout which allows customers to checkout directly via PayPal from your cart page.', 'woocommerce-gateway-paypal-express-checkout' ),
95
  'desc_tip' => true,
96
- 'default' => 'yes'
97
- ),
98
- 'button_size' => array(
99
- 'title' => __( 'Button Size', 'woocommerce-gateway-paypal-express-checkout' ),
100
- 'type' => 'select',
101
- 'class' => 'wc-enhanced-select',
102
- 'description' => __( 'PayPal offers different sizes of the "PayPal Checkout" buttons, allowing you to select a size that best fits your site\'s theme. This setting will allow you to choose which size button(s) appear on your cart page.', 'woocommerce-gateway-paypal-express-checkout' ),
103
- 'default' => 'large',
104
- 'desc_tip' => true,
105
- 'options' => array(
106
- 'small' => __( 'Small', 'woocommerce-gateway-paypal-express-checkout' ),
107
- 'medium' => __( 'Medium', 'woocommerce-gateway-paypal-express-checkout' ),
108
- 'large' => __( 'Large', 'woocommerce-gateway-paypal-express-checkout' )
109
- )
110
- ),
111
- 'environment' => array(
112
- 'title' => __( 'Environment', 'woocommerce-gateway-paypal-express-checkout' ),
113
- 'type' => 'select',
114
- 'class' => 'wc-enhanced-select',
115
- 'description' => __( 'This setting specifies whether you will process live transactions, or whether you will process simulated transactions using the PayPal Sandbox.', 'woocommerce-gateway-paypal-express-checkout' ),
116
- 'default' => 'live',
117
- 'desc_tip' => true,
118
- 'options' => array(
119
- 'live' => __( 'Live', 'woocommerce-gateway-paypal-express-checkout' ),
120
- 'sandbox' => __( 'Sandbox', 'woocommerce-gateway-paypal-express-checkout' ),
121
- )
122
- ),
123
- 'mark_enabled' => array(
124
- 'title' => __( 'PayPal Mark', 'woocommerce-gateway-paypal-express-checkout' ),
125
- 'type' => 'checkbox',
126
- 'label' => __( 'Enable the PayPal Mark on regular checkout', 'woocommerce-gateway-paypal-express-checkout' ),
127
- 'description' => __( 'This enables the PayPal mark, which can be shown on regular WooCommerce checkout to use PayPal Express Checkout like a regular WooCommerce gateway.', 'woocommerce-gateway-paypal-express-checkout' ),
128
- 'desc_tip' => true,
129
- 'default' => 'no'
130
  ),
 
131
  'title' => array(
132
  'title' => __( 'Title', 'woocommerce-gateway-paypal-express-checkout' ),
133
  'type' => 'text',
@@ -140,7 +133,25 @@ return array(
140
  'type' => 'text',
141
  'desc_tip' => true,
142
  'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce-gateway-paypal-express-checkout' ),
143
- 'default' => __( 'Pay using either your PayPal account or credit card. All credit card payments will be processed by PayPal.', 'woocommerce-gateway-paypal-express-checkout' )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  ),
145
 
146
  'api_credentials' => array(
@@ -168,7 +179,7 @@ return array(
168
  'description' => __( 'Get your API credentials from PayPal.', 'woocommerce-gateway-paypal-express-checkout' ),
169
  'default' => '',
170
  'desc_tip' => true,
171
- 'placeholder' => __( 'Optional if you provide a certificate below', 'woocommerce-gateway-paypal-express-checkout' )
172
  ),
173
  'api_certificate' => array(
174
  'title' => __( 'Live API Certificate', 'woocommerce-gateway-paypal-express-checkout' ),
@@ -182,7 +193,7 @@ return array(
182
  'description' => __( 'If you\'re processing transactions on behalf of someone else\'s PayPal account, enter their email address or Secure Merchant Account ID (also known as a Payer ID) here. Generally, you must have API permissions in place with the other account in order to process anything other than "sale" transactions for them.', 'woocommerce-gateway-paypal-express-checkout' ),
183
  'default' => '',
184
  'desc_tip' => true,
185
- 'placeholder' => __( 'Optional', 'woocommerce-gateway-paypal-express-checkout' )
186
  ),
187
 
188
  'sandbox_api_credentials' => array(
@@ -224,7 +235,86 @@ return array(
224
  'description' => __( 'If you\'re processing transactions on behalf of someone else\'s PayPal account, enter their email address or Secure Merchant Account ID (also known as a Payer ID) here. Generally, you must have API permissions in place with the other account in order to process anything other than "sale" transactions for them.', 'woocommerce-gateway-paypal-express-checkout' ),
225
  'default' => '',
226
  'desc_tip' => true,
227
- 'placeholder' => __( 'Optional', 'woocommerce-gateway-paypal-express-checkout' )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  ),
229
 
230
  'advanced' => array(
@@ -253,7 +343,7 @@ return array(
253
  'label' => __( 'Require Billing Address', 'woocommerce-gateway-paypal-express-checkout' ),
254
  'default' => 'no',
255
  'desc_tip' => true,
256
- 'description' => __( 'PayPal does not share buyer billing details with you. However, there are times when you must collect the buyer billing address to fulfill an essential business function (such as determining whether you must charge the buyer tax). Enable this function to collect the address before payment is taken.', 'woocommerce-gateway-paypal-express-checkout' ),
257
  ),
258
  'paymentaction' => array(
259
  'title' => __( 'Payment Action', 'woocommerce-gateway-paypal-express-checkout' ),
@@ -264,8 +354,8 @@ return array(
264
  'desc_tip' => true,
265
  'options' => array(
266
  'sale' => __( 'Sale', 'woocommerce-gateway-paypal-express-checkout' ),
267
- 'authorization' => __( 'Authorize', 'woocommerce-gateway-paypal-express-checkout' )
268
- )
269
  ),
270
  'instant_payments' => array(
271
  'title' => __( 'Instant Payments', 'woocommerce-gateway-paypal-express-checkout' ),
@@ -275,14 +365,6 @@ return array(
275
  'desc_tip' => true,
276
  'description' => __( 'If you enable this setting, PayPal will be instructed not to allow the buyer to use funding sources that take additional time to complete (for example, eChecks). Instead, the buyer will be required to use an instant funding source, such as an instant transfer, a credit/debit card, or PayPal Credit.', 'woocommerce-gateway-paypal-express-checkout' ),
277
  ),
278
- 'logo_image_url' => array(
279
- 'title' => __( 'Logo Image URL', 'woocommerce-gateway-paypal-express-checkout' ),
280
- 'type' => 'text',
281
- 'description' => __( 'If you want PayPal to co-brand the checkout page with your logo, enter the URL of your logo image here.<br/>The image must be no larger than 190x60, GIF, PNG, or JPG format, and should be served over HTTPS.', 'woocommerce-gateway-paypal-express-checkout' ),
282
- 'default' => '',
283
- 'desc_tip' => true,
284
- 'placeholder' => __( 'Optional', 'woocommerce-gateway-paypal-express-checkout' ),
285
- ),
286
  'subtotal_mismatch_behavior' => array(
287
  'title' => __( 'Subtotal Mismatch Behavior', 'woocommerce-gateway-paypal-express-checkout' ),
288
  'type' => 'select',
@@ -293,6 +375,6 @@ return array(
293
  'options' => array(
294
  'add' => __( 'Add another line item', 'woocommerce-gateway-paypal-express-checkout' ),
295
  'drop' => __( 'Do not send line items to PayPal', 'woocommerce-gateway-paypal-express-checkout' ),
296
- )
297
  ),
298
  );
15
  $ips_button = '<a href="' . esc_url( wc_gateway_ppec()->ips->get_signup_url( 'live' ) ) . '" class="button button-primary">' . __( 'Setup or link an existing PayPal account', 'woocommerce-gateway-paypal-express-checkout' ) . '</a>';
16
  $api_creds_text = sprintf( __( '%s or <a href="#" class="ppec-toggle-settings">click here to toggle manual API credential input</a>.', 'woocommerce-gateway-paypal-express-checkout' ), $ips_button );
17
  } else {
18
+ $reset_link = add_query_arg(
19
+ array(
20
+ 'reset_ppec_api_credentials' => 'true',
21
+ 'environment' => 'live',
22
+ 'reset_nonce' => wp_create_nonce( 'reset_ppec_api_credentials' ),
23
+ ),
24
+ wc_gateway_ppec()->get_admin_setting_link()
25
+ );
26
+
27
+ $api_creds_text = sprintf( __( 'To reset current credentials and use other account <a href="%1$s" title="%2$s">click here</a>.', 'woocommerce-gateway-paypal-express-checkout' ), $reset_link, __( 'Reset current credentials', 'woocommerce-gateway-paypal-express-checkout' ) );
28
  }
29
 
30
  if ( $enable_ips && $needs_sandbox_creds ) {
31
  $sandbox_ips_button = '<a href="' . esc_url( wc_gateway_ppec()->ips->get_signup_url( 'sandbox' ) ) . '" class="button button-primary">' . __( 'Setup or link an existing PayPal Sandbox account', 'woocommerce-gateway-paypal-express-checkout' ) . '</a>';
32
  $sandbox_api_creds_text = sprintf( __( '%s or <a href="#" class="ppec-toggle-sandbox-settings">click here to toggle manual API credential input</a>.', 'woocommerce-gateway-paypal-express-checkout' ), $sandbox_ips_button );
33
  } else {
34
+ $reset_link = add_query_arg(
35
+ array(
36
+ 'reset_ppec_api_credentials' => 'true',
37
+ 'environment' => 'sandbox',
38
+ 'reset_nonce' => wp_create_nonce( 'reset_ppec_api_credentials' ),
39
+ ),
40
+ wc_gateway_ppec()->get_admin_setting_link()
41
+ );
42
+
43
+ $sandbox_api_creds_text = sprintf( __( 'Your account setting is set to sandbox, no real charging takes place. To accept live payments, switch your environment to live and connect your PayPal account. To reset current credentials and use other sandbox account <a href="%1$s" title="%2$s">click here</a>.', 'woocommerce-gateway-paypal-express-checkout' ), $reset_link, __( 'Reset current sandbox credentials', 'woocommerce-gateway-paypal-express-checkout' ) );
44
+ }
45
+
46
+ $credit_enabled_label = __( 'Enable PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' );
47
+ if ( ! $this->is_credit_supported() ) {
48
+ $credit_enabled_label .= '<p><em>' . __( 'This option is disabled. Currently PayPal Credit only available for U.S. merchants.', 'woocommerce-gateway-paypal-express-checkout' ) . '</em></p>';
49
  }
50
 
51
  wc_enqueue_js( "
94
  }).change();
95
 
96
  if ( enable_toggle ) {
97
+ $( document ).on( 'click', '.ppec-toggle-settings', function( e ) {
98
+ $( ppec_live_fields ).closest( 'tr' ).toggle( 'fast' );
99
+ e.preventDefault();
100
  } );
101
  }
102
  if ( enable_sandbox_toggle ) {
103
+ $( document ).on( 'click', '.ppec-toggle-sandbox-settings', function( e ) {
104
+ $( ppec_sandbox_fields ).closest( 'tr' ).toggle( 'fast' );
105
+ e.preventDefault();
106
  } );
107
  }
108
  });
118
  'label' => __( 'Enable PayPal Express Checkout', 'woocommerce-gateway-paypal-express-checkout' ),
119
  'description' => __( 'This enables PayPal Express Checkout which allows customers to checkout directly via PayPal from your cart page.', 'woocommerce-gateway-paypal-express-checkout' ),
120
  'desc_tip' => true,
121
+ 'default' => 'yes',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  ),
123
+
124
  'title' => array(
125
  'title' => __( 'Title', 'woocommerce-gateway-paypal-express-checkout' ),
126
  'type' => 'text',
133
  'type' => 'text',
134
  'desc_tip' => true,
135
  'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce-gateway-paypal-express-checkout' ),
136
+ 'default' => __( 'Pay via PayPal; you can pay with your credit card if you don\'t have a PayPal account.', 'woocommerce-gateway-paypal-express-checkout' ),
137
+ ),
138
+
139
+ 'account_settings' => array(
140
+ 'title' => __( 'Account Settings', 'woocommerce-gateway-paypal-express-checkout' ),
141
+ 'type' => 'title',
142
+ 'description' => '',
143
+ ),
144
+ 'environment' => array(
145
+ 'title' => __( 'Environment', 'woocommerce-gateway-paypal-express-checkout' ),
146
+ 'type' => 'select',
147
+ 'class' => 'wc-enhanced-select',
148
+ 'description' => __( 'This setting specifies whether you will process live transactions, or whether you will process simulated transactions using the PayPal Sandbox.', 'woocommerce-gateway-paypal-express-checkout' ),
149
+ 'default' => 'live',
150
+ 'desc_tip' => true,
151
+ 'options' => array(
152
+ 'live' => __( 'Live', 'woocommerce-gateway-paypal-express-checkout' ),
153
+ 'sandbox' => __( 'Sandbox', 'woocommerce-gateway-paypal-express-checkout' ),
154
+ ),
155
  ),
156
 
157
  'api_credentials' => array(
179
  'description' => __( 'Get your API credentials from PayPal.', 'woocommerce-gateway-paypal-express-checkout' ),
180
  'default' => '',
181
  'desc_tip' => true,
182
+ 'placeholder' => __( 'Optional if you provide a certificate below', 'woocommerce-gateway-paypal-express-checkout' ),
183
  ),
184
  'api_certificate' => array(
185
  'title' => __( 'Live API Certificate', 'woocommerce-gateway-paypal-express-checkout' ),
193
  'description' => __( 'If you\'re processing transactions on behalf of someone else\'s PayPal account, enter their email address or Secure Merchant Account ID (also known as a Payer ID) here. Generally, you must have API permissions in place with the other account in order to process anything other than "sale" transactions for them.', 'woocommerce-gateway-paypal-express-checkout' ),
194
  'default' => '',
195
  'desc_tip' => true,
196
+ 'placeholder' => __( 'Optional', 'woocommerce-gateway-paypal-express-checkout' ),
197
  ),
198
 
199
  'sandbox_api_credentials' => array(
235
  'description' => __( 'If you\'re processing transactions on behalf of someone else\'s PayPal account, enter their email address or Secure Merchant Account ID (also known as a Payer ID) here. Generally, you must have API permissions in place with the other account in order to process anything other than "sale" transactions for them.', 'woocommerce-gateway-paypal-express-checkout' ),
236
  'default' => '',
237
  'desc_tip' => true,
238
+ 'placeholder' => __( 'Optional', 'woocommerce-gateway-paypal-express-checkout' ),
239
+ ),
240
+
241
+ 'display_settings' => array(
242
+ 'title' => __( 'Display Settings', 'woocommerce-gateway-paypal-express-checkout' ),
243
+ 'type' => 'title',
244
+ 'description' => __( 'Customize the appearance of Express Checkout in your store.', 'woocommerce-gateway-paypal-express-checkout' ),
245
+ ),
246
+ 'brand_name' => array(
247
+ 'title' => __( 'Brand Name', 'woocommerce-gateway-paypal-express-checkout' ),
248
+ 'type' => 'text',
249
+ 'description' => __( 'A label that overrides the business name in the PayPal account on the PayPal hosted checkout pages.', 'woocommerce-gateway-paypal-express-checkout' ),
250
+ 'default' => get_bloginfo( 'name', 'display' ),
251
+ 'desc_tip' => true,
252
+ ),
253
+ 'button_size' => array(
254
+ 'title' => __( 'Button Size', 'woocommerce-gateway-paypal-express-checkout' ),
255
+ 'type' => 'select',
256
+ 'class' => 'wc-enhanced-select',
257
+ 'description' => __( 'PayPal offers different sizes of the "PayPal Checkout" buttons, allowing you to select a size that best fits your site\'s theme. This setting will allow you to choose which size button(s) appear on your cart page.', 'woocommerce-gateway-paypal-express-checkout' ),
258
+ 'default' => 'large',
259
+ 'desc_tip' => true,
260
+ 'options' => array(
261
+ 'small' => __( 'Small', 'woocommerce-gateway-paypal-express-checkout' ),
262
+ 'medium' => __( 'Medium', 'woocommerce-gateway-paypal-express-checkout' ),
263
+ 'large' => __( 'Large', 'woocommerce-gateway-paypal-express-checkout' ),
264
+ ),
265
+ ),
266
+ 'mark_enabled' => array(
267
+ 'title' => __( 'PayPal Mark', 'woocommerce-gateway-paypal-express-checkout' ),
268
+ 'type' => 'checkbox',
269
+ 'label' => __( 'Enable the PayPal Mark on regular checkout', 'woocommerce-gateway-paypal-express-checkout' ),
270
+ 'description' => __( 'This enables the PayPal mark, which can be shown on regular WooCommerce checkout to use PayPal Express Checkout like a regular WooCommerce gateway.', 'woocommerce-gateway-paypal-express-checkout' ),
271
+ 'desc_tip' => true,
272
+ 'default' => 'no',
273
+ ),
274
+ 'logo_image_url' => array(
275
+ 'title' => __( 'Logo Image (190×60)', 'woocommerce-gateway-paypal-express-checkout' ),
276
+ 'type' => 'text',
277
+ 'description' => __( 'If you want PayPal to co-brand the checkout page with your logo, enter the URL of your logo image here.<br/>The image must be no larger than 190x60, GIF, PNG, or JPG format, and should be served over HTTPS.', 'woocommerce-gateway-paypal-express-checkout' ),
278
+ 'default' => '',
279
+ 'desc_tip' => true,
280
+ 'placeholder' => __( 'Optional', 'woocommerce-gateway-paypal-express-checkout' ),
281
+ ),
282
+ 'header_image_url' => array(
283
+ 'title' => __( 'Header Image (750×90)', 'woocommerce-gateway-paypal-express-checkout' ),
284
+ 'type' => 'text',
285
+ 'description' => __( 'If you want PayPal to co-brand the checkout page with your header, enter the URL of your header image here.<br/>The image must be no larger than 750x90, GIF, PNG, or JPG format, and should be served over HTTPS.', 'woocommerce-gateway-paypal-express-checkout' ),
286
+ 'default' => '',
287
+ 'desc_tip' => true,
288
+ 'placeholder' => __( 'Optional', 'woocommerce-gateway-paypal-express-checkout' ),
289
+ ),
290
+ 'page_style' => array(
291
+ 'title' => __( 'Page Style', 'woocommerce-gateway-paypal-express-checkout' ),
292
+ 'type' => 'text',
293
+ 'description' => __( 'Optionally enter the name of the page style you wish to use. These are defined within your PayPal account.', 'woocommerce-gateway-paypal-express-checkout' ),
294
+ 'default' => '',
295
+ 'desc_tip' => true,
296
+ 'placeholder' => __( 'Optional', 'woocommerce-gateway-paypal-express-checkout' ),
297
+ ),
298
+ 'landing_page' => array(
299
+ 'title' => __( 'Landing Page', 'woocommerce-gateway-paypal-express-checkout' ),
300
+ 'type' => 'select',
301
+ 'class' => 'wc-enhanced-select',
302
+ 'description' => __( 'Type of PayPal page to display.', 'woocommerce-gateway-paypal-express-checkout' ),
303
+ 'default' => 'Login',
304
+ 'desc_tip' => true,
305
+ 'options' => array(
306
+ 'Billing' => _x( 'Billing (Non-PayPal account)', 'Type of PayPal page', 'woocommerce-gateway-paypal-express-checkout' ),
307
+ 'Login' => _x( 'Login (PayPal account login)', 'Type of PayPal page', 'woocommerce-gateway-paypal-express-checkout' ),
308
+ ),
309
+ ),
310
+ 'credit_enabled' => array(
311
+ 'title' => __( 'Enable PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' ),
312
+ 'type' => 'checkbox',
313
+ 'label' => $credit_enabled_label,
314
+ 'disabled' => ! $this->is_credit_supported(),
315
+ 'default' => 'no',
316
+ 'desc_tip' => true,
317
+ 'description' => __( 'This enables PayPal Credit, which displays a PayPal Credit button next to the Express Checkout button. PayPal Express Checkout lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button shows up next to the PayPal Button, and lets customers pay quickly with PayPal Credit®.', 'woocommerce-gateway-paypal-express-checkout' ),
318
  ),
319
 
320
  'advanced' => array(
343
  'label' => __( 'Require Billing Address', 'woocommerce-gateway-paypal-express-checkout' ),
344
  'default' => 'no',
345
  'desc_tip' => true,
346
+ 'description' => __( 'PayPal does not share buyer billing details with you. However, there are times when you must collect the buyer billing address to fulfill an essential business function (such as determining whether you must charge the buyer tax). Enable this function to collect the address before payment is taken. To enable this functionality on your PayPal account, please call PayPal customer support.', 'woocommerce-gateway-paypal-express-checkout' ),
347
  ),
348
  'paymentaction' => array(
349
  'title' => __( 'Payment Action', 'woocommerce-gateway-paypal-express-checkout' ),
354
  'desc_tip' => true,
355
  'options' => array(
356
  'sale' => __( 'Sale', 'woocommerce-gateway-paypal-express-checkout' ),
357
+ 'authorization' => __( 'Authorize', 'woocommerce-gateway-paypal-express-checkout' ),
358
+ ),
359
  ),
360
  'instant_payments' => array(
361
  'title' => __( 'Instant Payments', 'woocommerce-gateway-paypal-express-checkout' ),
365
  'desc_tip' => true,
366
  'description' => __( 'If you enable this setting, PayPal will be instructed not to allow the buyer to use funding sources that take additional time to complete (for example, eChecks). Instead, the buyer will be required to use an instant funding source, such as an instant transfer, a credit/debit card, or PayPal Credit.', 'woocommerce-gateway-paypal-express-checkout' ),
367
  ),
 
 
 
 
 
 
 
 
368
  'subtotal_mismatch_behavior' => array(
369
  'title' => __( 'Subtotal Mismatch Behavior', 'woocommerce-gateway-paypal-express-checkout' ),
370
  'type' => 'select',
375
  'options' => array(
376
  'add' => __( 'Add another line item', 'woocommerce-gateway-paypal-express-checkout' ),
377
  'drop' => __( 'Do not send line items to PayPal', 'woocommerce-gateway-paypal-express-checkout' ),
378
+ ),
379
  ),
380
  );
includes/views/admin-settings.php DELETED
@@ -1,581 +0,0 @@
1
- <?php
2
-
3
- if ( ! defined( 'ABSPATH' ) ) {
4
- exit; // Exit if accessed directly
5
- }
6
-
7
- ?><h3><?php _e( 'PayPal Configuration', 'woocommerce-gateway-paypal-express-checkout' ); ?></h3>
8
- <?php
9
- if ( $live_cert ) {
10
- ?>
11
- <input type="hidden" name="woo_pp_live_api_cert_string" value="<?php echo esc_attr( base64_encode( $live_cert ) ); ?>">
12
- <?php
13
- }
14
-
15
- if ( $sb_cert ) {
16
- ?>
17
- <input type="hidden" name="woo_pp_sandbox_api_cert_string" value="<?php echo esc_attr( base64_encode( $sb_cert ) ); ?>">
18
- <?php
19
- }
20
-
21
- ?>
22
-
23
- <table class="form-table ppec-settings<?php echo $enable_ips ? ' ips-enabled' : ''; ?>">
24
- <tr>
25
- <th>
26
- <label for="woo_pp_enabled"><?php _e( 'Enable/Disable', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
27
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_enabled_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
28
- </th>
29
- <td>
30
- <div id="woo_pp_enabled_help" style="display: none;">
31
- <p>
32
- <h2><?php _e( 'Enable PayPal Express Checkout', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
33
- <?php _e( '<p>If this setting is enabled, buyers will be allowed to pay for their purchases using PayPal Express Checkout.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
34
- </p>
35
- </div>
36
- <fieldset>
37
- <legend class="screen-reader-text"><span><?php _e( 'Enable PayPal Express Checkout', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
38
- <input type="checkbox" name="woo_pp_enabled" id="woo_pp_enabled" value="true"<?php checked( $enabled ); ?>>
39
- <label for="woo_pp_enabled"><?php _e( 'Enable PayPal Express Checkout', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
40
- </fieldset>
41
- </td>
42
- </tr>
43
-
44
- <?php /* defer ppc for next release
45
- <?php if ( 'US' === WC()->countries->get_base_country() ) : ?>
46
- <tr>
47
- <th>
48
- <label for="woo_pp_ppc_enabled"><?php _e( 'PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
49
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_ppc_enabled_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
50
- </th>
51
- <td>
52
- <div id="woo_pp_ppc_enabled_help" style="display: none;">
53
- <p>
54
- <h2><?php _e( 'Enable PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
55
- <?php _e( '<p>PayPal Credit allows you a convenient way to offer financing to your customers, without exposing your business to the additional risk typically involved with seller financing. Offer your buyers a convenient way to finance their purchases with a single click!</p><p>If this setting is enabled, the PayPal Credit button will be shown to buyers on the shopping cart page:</p><div style="text-align: center;"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/ppcredit-logo-small.png" width="148" height="26" alt="PayPal Credit button"></div><p>When buyers click on this button, they are taken to PayPal and invited to sign up for PayPal Credit (if they are not already signed up) and to pay for their purchase using PayPal Credit. The transaction appears no differently to you than a normal PayPal transaction &mdash; you still receive the proceeds from the transaction immediately, as you normally would.</p><p><strong>Note:</strong> PayPal recommends that you enable this option. However, PayPal Credit is available primarily to users in the United States; if most of your buyers come from outside the United States, you may want to disable this option.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
56
- </p>
57
- </div>
58
- <fieldset>
59
- <legend class="screen-reader-text"><span><?php _e( 'Enable PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
60
- <input type="checkbox" name="woo_pp_ppc_enabled" id="woo_pp_ppc_enabled" value="true"<?php checked( $ppc_enabled ); ?>>
61
- <label for="woo_pp_ppc_enabled"><?php _e( 'Enable PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
62
- </fieldset>
63
- </td>
64
- </tr>
65
- <?php endif; ?>
66
- */ ?>
67
-
68
- <tr>
69
- <th>
70
- <label for="woo_pp_environment"><?php _e( 'Environment', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
71
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_environment_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
72
- </th>
73
- <td>
74
- <div id="woo_pp_environment_help" style="display: none;">
75
- <p>
76
- <h2><?php _e( 'Environment', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
77
- <?php _e( '<p>This setting specifies whether you will process live transactions, or whether you will process simulated transactions using the PayPal Sandbox.</p><ul style="list-style: disc inside;"><li>If you set this to <strong>Live</strong>, your site will process live transactions using the live PayPal site.</li><li>If you set this to <strong>Sandbox</strong>, transactions will be simulated using the PayPal Sandbox.</li></ul><p>To get started with the PayPal Sandbox, go to <a href="https://developer.paypal.com" target="_blank">https://developer.paypal.com</a>.</p><p><strong>Note:</strong> The PayPal Sandbox is completely isolated from the live site. If you have a PayPal account onthe live PayPal site, it does not necessarily mean that you have an account on the PayPal Sandbox, and vice-versa. For this reason, we maintain two separate sets of API credentials for you &mdash; one set for the live site, and one set for the Sandbox.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
78
- </p>
79
- </div>
80
- <fieldset>
81
- <legend class="screen-reader-text"><span><?php _e( 'Environment', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
82
- <select name="woo_pp_environment" id="woo_pp_environment">
83
- <option value="live"<?php selected( $environment, 'live' ) ?>><?php _e( 'Live', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
84
- <option value="sandbox"<?php selected( $environment, 'sandbox' ) ?>><?php _e( 'Sandbox', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
85
- </select>
86
- </fieldset>
87
- </td>
88
- </tr>
89
- <?php if ( $enable_ips ) { ?>
90
- <tr class="woo_pp_live">
91
- <th>
92
- <?php _e( 'Easy Setup', 'woocommerce-gateway-paypal-express-checkout' ); ?>
93
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_easy_setup_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
94
- </th>
95
- <td>
96
- <div id="woo_pp_easy_setup_help" style="display: none;">
97
- <p>
98
- <h2><?php _e( 'Easy Setup', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
99
- <?php _e( '<p>Easy Setup allows you to set up your PayPal account and get API credentials all in one easy process. Just click on the link and follow the steps provided. You\'ll have your PayPal account up and running in seconds!</p><p><strong>Note:</strong> If you get an error message on PayPal saying that credentials already exist for your account, just come back to the settings page and click the "(Click here if you need certificate credentials)" link.</p><p>If you know that your account already has API certificate credentials, just click the "(Click here if you need certificate credentials)" link instead.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
100
- </p>
101
- </div>
102
-
103
- <a href="<?php echo esc_url( wc_gateway_ppec()->ips->get_signup_url( 'live' ) ); ?>" class="button button-primary"><?php _e( 'Click Here to Set Up Your PayPal Account', 'woocommerce-gateway-paypal-express-checkout' ); ?></a>
104
- <?php
105
- /* Disable certificate-style until middleware support it. Maybe in v1.1.
106
- <a href="<?php echo esc_url( $ips_url ); ?>&amp;mode=certificate&amp;env=live" class="button"><?php _e( 'Click here if you need certificate credentials', 'woocommerce-gateway-paypal-express-checkout' ); ?></a>
107
- */ ?>
108
-
109
- <br>
110
- <a href="#" class="toggle-api-credential-fields api-credential-fields-hidden" style="display: inline-block; margin-top: 10px;" data-hide-text="<?php _e( 'Hide credential fields', 'woocommerce-gateway-paypal-express-checkout' ); ?>" data-show-text="<?php _e( 'Show credential fields', 'woocommerce-gateway-paypal-express-checkout' ); ?>"><?php _e( 'Show credential fields', 'woocommerce-gateway-paypal-express-checkout' ); ?></a>
111
- </td>
112
- </tr>
113
- <?php } ?>
114
- <tr class="woo_pp_live api-credential-row">
115
- <th>
116
- <label for="woo_pp_live_api_style"><?php _e( 'Live API credential type', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
117
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_live_api_style_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
118
- </th>
119
- <td>
120
- <div id="woo_pp_live_api_style_help" style="display: none;">
121
- <p>
122
- <h2><?php _e( 'Live API credential type', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
123
- <?php _e( '<p>To process PayPal transactions using Express Checkout, you must have a PayPal Business account and a set of API credentials from PayPal. If you\'ve processed PayPal transactions in the past on another site (not including eBay), you may already have API credentials from PayPal. Otherwise, you will need to request a set from PayPal.</p><p>When you request your API credentials, PayPal gives you have the choice of selecting between an API signature or an API certificate. This setting allows you to specify which type of credentials you have.</p><p>If you already have API credentials from PayPal, simply select which type you have.</p><p>If you aren\'t sure whether or not you have API credentials, follow these steps:<ol style="list-style: decimal outside;"><li>Log in to your PayPal account at <a href="https://www.paypal.com" target="_blank">https://www.paypal.com</a>.</li><li>The next few steps will differ depending on your account settings.<ul style="list-style: disc outside; margin-left: 15px;"><li>Do you see a set of tabs at the top of the page that read <strong>Money</strong>, <strong>Transactions</strong>, <strong>Customers</strong>, <strong>Tools</strong>, and <strong>More</strong>? If so:<ol style="list-style: lower-roman outside;"><li>Click the Business Profile button. (It\'s in the upper-right corner of the page, immediately to the left of the <strong>Log Out</strong> button.)</li><li>Click <strong>Profile and settings</strong>.</li><li>On the left-hand side of the page, click <strong>My selling tools</strong>.</li><li>Locate <strong>API access</strong> in the list of settings. Click the <strong>Update</strong> link immediately to the right of it.</li></ol></li><li>Look in your browser\'s address bar. Does the URL start with <strong>https://paypalmanager.paypal.com/</strong>? If so:<ol style="list-style: lower-roman outside;"><li>Click <strong>Profile</strong>. (It will be in the row of links underneath the <strong>My Account</strong> tab.)</li><li>Click <strong>Request API credentials</strong>. (It will be in the <strong>Account information</strong> section.)</li><li>Click <strong>Set up PayPal API credentials and permissions</strong>. (It will be in the <strong>Option 1 - PayPal API</strong> box.)</li></ol></li><li>Otherwise, follow these steps:<ol style="list-style: lower-roman outside;"><li>Under <strong>Profile</strong>, click <strong>My Selling Tools</strong>.</li><li>Locate <strong>API access</strong> in the list of settings. Click the <strong>Update</strong> link immediately to the right of it.</li></ol></li></ul></li><li>Look in the <strong>Option 2</strong> box. This box will have a link in it that says <strong>View API Signature</strong>, <strong>View API Certificate</strong>, or <strong>Request API credentials</strong>.<ul style="list-style: disc outside; margin-left: 15px;"><li>If the link says <strong>View API Signature</strong> or <strong>View API Certificate</strong>, you already have PayPal API credentials. Click on the link to view them.</li><li>If the link says <strong>Request API credentials</strong>, you do not yet have API credentials. Click the link to request your API credentials. (<strong>Note:</strong> If you are requesting a new set of API credentials, we recommend that you request an API certificate.)</li></ul></li><li>Once you have your API credentials, simply copy them into the spaces provided.</li></ol></p><p><strong>Note:</strong> PayPal only allows you to have one set of credentials at a time. If you request an API signature, then later discover you need an API certificate (or vice-versa), you will have to delete your existing credentials before requesting a new set.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
124
- </p>
125
- </div>
126
- <fieldset>
127
- <legend class="screen-reader-text"><span><?php _e( 'Live API credential type', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
128
- <select name="woo_pp_live_api_style" id="woo_pp_live_api_style">
129
- <option value="signature"<?php selected( $live_style, 'signature' ); ?>><?php _e( 'API signature', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
130
- <option value="certificate"<?php selected( $live_style, 'certificate' ); ?>><?php _e( 'API certificate', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
131
- </select>
132
- </fieldset>
133
- </td>
134
- </tr>
135
- <tr class="woo_pp_live api-credential-row">
136
- <th>
137
- <label for="woo_pp_live_api_username"><?php _e( 'Live API username', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
138
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_live_api_username_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
139
- </th>
140
- <td>
141
- <div id="woo_pp_live_api_username_help" style="display: none;">
142
- <p>
143
- <h2><?php _e( 'Live API username', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
144
- <?php _e( '<p>Enter the API username provided to you by PayPal.</p><p><strong>Note:</strong> This value is generated for you by PayPal. You must use the username that they give you. This is <strong>not</strong> the same as the email address you use to log in to <a href="https://www.paypal.com" target="_blank">www.paypal.com</a>.</p><p>For help on retrieving your API credentials, click on the help for the <strong>Live API credential type</strong> setting.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
145
- </p>
146
- </div>
147
- <fieldset>
148
- <legend class="screen-reader-text"><span><?php _e( 'Live API username', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
149
- <input type="text" name="woo_pp_live_api_username" id="woo_pp_live_api_username" size="40" value="<?php echo esc_attr( $live_api_username ); ?>">
150
- </fieldset>
151
- </td>
152
- </tr>
153
- <tr class="woo_pp_live api-credential-row">
154
- <th>
155
- <label for="woo_pp_live_api_password"><?php _e( 'Live API password', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
156
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_live_api_password_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
157
- </th>
158
- <td>
159
- <div id="woo_pp_live_api_password_help" style="display: none;">
160
- <p>
161
- <h2><?php _e( 'Live API password', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
162
- <?php _e( '<p>Enter the API password provided to you by PayPal.</p><p><strong>Note:</strong> This value is generated for you by PayPal. You must use the password that they give you. This is <strong>not</strong> the same as the password you use to log in to <a href="https://www.paypal.com" target="_blank">www.paypal.com</a>.</p><p>For help on retrieving your API credentials, click on the help for the <strong>Live API credential type</strong> setting.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
163
- </p>
164
- </div>
165
- <fieldset>
166
- <legend class="screen-reader-text"><span><?php _e( 'Live API password' ); ?></span></legend>
167
- <input type="password" name="woo_pp_live_api_password" id="woo_pp_live_api_password" size="40" value="<?php echo esc_attr( $live_api_pass ); ?>">
168
- </fieldset>
169
- </td>
170
- </tr>
171
- <tr class="woo_pp_live_signature api-credential-row">
172
- <th>
173
- <label for="woo_pp_live_api_signature"><?php _e( 'Live API signature', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
174
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_live_api_signature_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
175
- </th>
176
- <td>
177
- <div id="woo_pp_live_api_signature_help" style="display: none;">
178
- <p>
179
- <h2><?php _e( 'Live API signature', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
180
- <?php _e( '<p>Enter the API signature provided to you by PayPal.</p><p><strong>Note:</strong> This value is generated for you by PayPal. You must use the signature that they give you. This is <strong>not</strong> the same as the email address or password that you use to log in to <a href="https://www.paypal.com" target="_blank">www.paypal.com</a>.</p><p>For help on retrieving your API credentials, click on the help for the <strong>Live API credential type</strong> setting.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
181
- </p>
182
- </div>
183
- <fieldset>
184
- <legend class="screen-reader-text"><span><?php _e( 'Live API signature', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
185
- <input type="password" name="woo_pp_live_api_signature" id="woo_pp_live_api_signature" size="40" value="<?php echo esc_attr( $live_api_sig ); ?>">
186
- </fieldset>
187
- </td>
188
- </tr>
189
- <tr class="woo_pp_live_certificate api-credential-row">
190
- <th>
191
- <label for="woo_pp_live_api_certificate"><?php _e( 'Live API certificate', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
192
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_live_api_certificate_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
193
- </th>
194
- <td>
195
- <div id="woo_pp_live_api_certificate_help" style="display: none;">
196
- <p>
197
- <h2><?php _e( 'Live API certificate', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
198
- <?php _e( '<p>When you request API credentials from PayPal, and you choose the API certificate option, PayPal provides you with the certificate as a file. This file is typically called <strong>cert_key_pem.txt</strong>. Upload the file using this setting.</p><p><strong>Note:</strong> Upload the file exactly as it was provided to you by PayPal. The name of the file doesn\'t matter, but you must not modify the contents of the file.</p><p>For help on retrieving your API credentials, click on the help for the <strong>Live API credential type</strong> setting.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
199
- </p>
200
- </div>
201
- <fieldset>
202
- <legend class="screen-reader-text"><span><?php _e( 'Live API certificate', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
203
- <p><strong><?php _e( 'Certificate status:', 'woocommerce-gateway-paypal-express-checkout' ); ?></strong> <?php echo $live_cert_info; ?></p>
204
- <p><span style="font-style: italic;"><?php _e( 'Upload a new certificate:', 'woocommerce-gateway-paypal-express-checkout' ); ?></span> <input type="file" name="woo_pp_live_api_certificate" id="woo_pp_live_api_certificate">
205
- </fieldset>
206
- </td>
207
- </tr>
208
- <tr class="woo_pp_live api-credential-row">
209
- <th>
210
- <label for="woo_pp_live_subject">Live subject</label>
211
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_live_subject_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
212
- </th>
213
- <td>
214
- <div id="woo_pp_live_subject_help" style="display: none;">
215
- <p>
216
- <h2><?php _e( 'Live subject', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
217
- <?php _e( '<p>If you\'re processing transactions on behalf of someone else\'s PayPal account, enter their email address or Secure Merchant Account ID (also known as a Payer ID) here. Generally, you must have API permissions in place with the other account in order to process anything other than "sale" transactions for them.</p><p>Most people won\'t need to use this setting. If you\'re not sure what to put here, leave it blank.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
218
- </p>
219
- </div>
220
- <fieldset>
221
- <legend class="screen-reader-text"><span><?php _e( 'Live subject', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
222
- <input type="text" name="woo_pp_live_subject" size="40" value="<?php echo esc_attr( $live_subject ); ?>">
223
- </fieldset>
224
- </td>
225
- </tr>
226
- <?php if ( $enable_ips ) { ?>
227
- <tr class="woo_pp_sandbox">
228
- <th>
229
- <?php _e( 'Easy Setup', 'woocommerce-gateway-paypal-express-checkout' ); ?>
230
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_easy_setup_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
231
- </th>
232
- <td>
233
- <a href="<?php echo esc_url( wc_gateway_ppec()->ips->get_signup_url( 'sandbox' ) ); ?>" class="button button-primary"><?php _e( 'Click Here to Set Up Your PayPal Account', 'woocommerce-gateway-paypal-express-checkout' ); ?></a>
234
- <?php
235
- /* Disable certificate-style until middleware support it. Maybe in v1.1.
236
- <a href="<?php echo esc_url( $ips_url ); ?>&amp;mode=certificate&amp;env=sandbox" class="button"><?php _e( 'Click here if you need certificate credentials', 'woocommerce-gateway-paypal-express-checkout' ); ?></a>
237
- */ ?>
238
-
239
- <br>
240
- <a href="#" class="toggle-api-credential-fields api-credential-fields-hidden" style="display: inline-block; margin-top: 10px;" data-hide-text="<?php _e( 'Hide credential fields', 'woocommerce-gateway-paypal-express-checkout' ); ?>" data-show-text="<?php _e( 'Show credential fields', 'woocommerce-gateway-paypal-express-checkout' ); ?>"><?php _e( 'Show credential fields', 'woocommerce-gateway-paypal-express-checkout' ); ?></a>
241
- </td>
242
- </tr>
243
- <?php } ?>
244
- <tr class="woo_pp_sandbox api-credential-row">
245
- <th>
246
- <label for="woo_pp_sandbox_api_style"><?php _e( 'Sandbox API credential type', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
247
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_sandbox_api_style_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
248
- </th>
249
- <td>
250
- <div id="woo_pp_sandbox_api_style_help" style="display: none;">
251
- <p>
252
- <h2><?php _e( 'Sandbox API credential type', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
253
- <?php _e( '<p>This setting allows you to specify whether the API credentials for your Sandbox account contain an API signature or an API certificate.</p><p>To process PayPal transactions on the PayPal Sandbox using Express Checkout, you must:</p><ul style="list-style: disc outside; margin-left: 20px;"><li>Have a live PayPal account (it doesn\'t matter if you have a Personal or Business account)</li><li>Sign in to <a href="https://developer.paypal.com" target="_blank">http://developer.paypal.com</a> using the email address and password from your live PayPal account.</li><li>Create at least one PayPal Business account and one PayPal Personal account on the Sandbox. The Business account will represent you, as the merchant, and the Personal account will represent your buyer. (<strong>Note:</strong> PayPal will usually create a Personal and a Business account on the Sandbox for you the first time you log in.)</li></ul><p>Typically, when you create a PayPal Business account on the Sandbox, PayPal will generate a set of credentials for you. These credentials will usually have an API signature, so most people can select <strong>API signature</strong> here.</p><p>To retrieve the API credentials for your Sandbox account, follow these steps:</p><ol style="list-style: decimal outside;"><li>Go to <a href="https://developer.paypal.com" target="_blank">https://developer.paypal.com</a> and sign in using the email address and password from your live PayPal account.</li><li>Click <strong>Dashboard</strong>.</li><li>Under <strong>Sandbox</strong>, click <strong>Accounts</strong>.</li><li>In the list of accounts, click on the email address of your Business account. (If you do not have a business account, click <strong>Create Account</strong> to create a new account.)</li><li>Click the <strong>Profile</strong> link that appears underneath the account\'s email address.</li><li>Click the <strong>API credentials</strong> tab.</li><li>Copy and paste the API credentials into the spaces provided.</li></ol><p><strong>Note:</strong> If you see a username and password, but not a signature, the account has an API certificate attached to it. To retrieve the API certificate, follow these steps:<ol style="list-style: decimal outside;"><li>Log in to the account at <a href="https://www.sandbox.paypal.com" target="_blank">https://www.sandbox.paypal.com</a>.</li><li>The next few steps will differ depending on your account settings.<ul style="list-style: disc outside; margin-left: 15px;"><li>Do you see a set of tabs at the top of the page that read <strong>Money</strong>, <strong>Transactions</strong>, <strong>Customers</strong>, <strong>Tools</strong>, and <strong>More</strong>? If so:<ol style="list-style: lower-roman outside;"><li>Click the Business Profile button. (It\'s in the upper-right corner of the page, immediately to the left of the <strong>Log Out</strong> button.)</li><li>Click <strong>Profile and settings</strong>.</li><li>On the left-hand side of the page, click <strong>My selling tools</strong>.</li><li>Locate <strong>API access</strong> in the list of settings. Click the <strong>Update</strong> link immediately to the right of it.</li></ol></li><li>Look in your browser\'s address bar. Does the URL start with <strong>https://paypalmanager.sandbox.paypal.com/</strong>? If so:<ol style="list-style: lower-roman outside;"><li>Click <strong>Profile</strong>. (It will be in the row of links underneath the <strong>My Account</strong> tab.)</li><li>Click <strong>Request API credentials</strong>. (It will be in the <strong>Account information</strong> section.)</li><li>Click <strong>Set up PayPal API credentials and permissions</strong>. (It will be in the <strong>Option 1 - PayPal API</strong> box.)</li></ol></li><li>Otherwise, follow these steps:<ol style="list-style: lower-roman outside;"><li>Under <strong>Profile</strong>, click <strong>My Selling Tools</strong>.</li><li>Locate <strong>API access</strong> in the list of settings. Click the <strong>Update</strong> link immediately to the right of it.</li></ol></li></ul></li><li>Click <strong>View API certificate</strong>. (It will be in the <strong>Option 2</strong> box, on the right-hand side of the page.)</li><li>Click <strong>Download Certificate</strong>. Your API certificate will be downloaded to your computer.</li></ol></p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
254
- </p>
255
- </div>
256
- <fieldset>
257
- <legend class="screen-reader-text"><span><?php _e( 'Sandbox API credential type', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
258
- <select name="woo_pp_sandbox_api_style" id="woo_pp_sandbox_api_style">
259
- <option value="signature"<?php selected( $sb_style, 'signature' ); ?>><?php _e( 'API signature', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
260
- <option value="certificate"<?php selected( $sb_style, 'certificate' ); ?>><?php _e( 'API certificate', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
261
- </select>
262
- </fieldset>
263
- </td>
264
- </tr>
265
- <tr class="woo_pp_sandbox api-credential-row">
266
- <th>
267
- <label for="woo_pp_sandbox_api_username"><?php _e( 'Sandbox API username', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
268
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_sandbox_api_username_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
269
- </th>
270
- <td>
271
- <div id="woo_pp_sandbox_api_username_help" style="display: none;">
272
- <p>
273
- <h2><?php _e( 'Sandbox API username', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
274
- <?php _e( '<p>Enter the API username provided to you by PayPal.</p><p><strong>Note:</strong> This value is generated for you by PayPal. You must use the username that they give you. This is <strong>not</strong> the same as the email address you use to log in to <a href="https://developer.paypal.com" target="_blank">developer.paypal.com</a>, <a href="https://www.sandbox.paypal.com" target="_blank">www.sandbox.paypal.com</a> or <a href="https://www.paypal.com" target="_blank">www.paypal.com</a>.</p><p>For help on retrieving your API credentials, click on the help for the <strong>Sandbox API credential type</strong> setting.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
275
- </p>
276
- </div>
277
- <fieldset>
278
- <legend class="screen-reader-text"><span><?php _e( 'Sandbox API username', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
279
- <input type="text" name="woo_pp_sandbox_api_username" id="woo_pp_sandbox_api_username" size="40" value="<?php echo esc_attr( $sb_api_username ); ?>">
280
- </fieldset>
281
- </td>
282
- </tr>
283
- <tr class="woo_pp_sandbox api-credential-row">
284
- <th>
285
- <label for="woo_pp_sandbox_api_password"><?php _e( 'Sandbox API password', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
286
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_sandbox_api_password_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
287
- </th>
288
- <td>
289
- <div id="woo_pp_sandbox_api_password_help" style="display: none;">
290
- <p>
291
- <h2><?php _e( 'Sandbox API password', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
292
- <?php _e( '<p>Enter the API password provided to you by PayPal.</p><p><strong>Note:</strong> This value is generated for you by PayPal. You must use the password that they give you. This is <strong>not</strong> the same as the password you use to log in to <a href="https://developer.paypal.com" target="_blank">developer.paypal.com</a>, <a href="https://www.sandbox.paypal.com" target="_blank">www.sandbox.paypal.com</a> or <a href="https://www.paypal.com" target="_blank">www.paypal.com</a>.</p><p>For help on retrieving your API credentials, click on the help for the <strong>Sandbox API credential type</strong> setting.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
293
- </p>
294
- </div>
295
- <fieldset>
296
- <legend class="screen-reader-text"><span><?php _e( 'Sandbox API password', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
297
- <input type="password" name="woo_pp_sandbox_api_password" id="woo_pp_sandbox_api_password" size="40" value="<?php echo esc_attr( $sb_api_pass ); ?>">
298
- </fieldset>
299
- </td>
300
- </tr>
301
- <tr class="woo_pp_sandbox_signature api-credential-row">
302
- <th>
303
- <label for="woo_pp_sandbox_api_signature"><?php _e( 'Sandbox API signature', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
304
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_sandbox_api_signature_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
305
- </th>
306
- <td>
307
- <div id="woo_pp_sandbox_api_signature_help" style="display: none;">
308
- <p>
309
- <h2><?php _e( 'Sandbox API signature', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
310
- <?php _e( '<p>Enter the API signature provided to you by PayPal.</p><p><strong>Note:</strong> This value is generated for you by PayPal. You must use the signature that they give you. This is <strong>not</strong> the same as the email address or password that you use to log in to <a href="https://developer.paypal.com" target="_blank">developer.paypal.com</a>, <a href="https://www.sandbox.paypal.com" target="_blank">www.sandbox.paypal.com</a> or <a href="https://www.paypal.com" target="_blank">www.paypal.com</a>.</p><p>For help on retrieving your API credentials, click on the help for the <strong>Sandbox API credential type</strong> setting.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
311
- </p>
312
- </div>
313
- <fieldset>
314
- <legend class="screen-reader-text"><span><?php _e( 'Sandbox API signature', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
315
- <input type="password" name="woo_pp_sandbox_api_signature" id="woo_pp_sandbox_api_signature" size="40" value="<?php echo esc_attr( $sb_api_sig ); ?>">
316
- </fieldset>
317
- </td>
318
- </tr>
319
- <tr class="woo_pp_sandbox_certificate api-credential-row">
320
- <th>
321
- <label for="woo_pp_sandbox_api_certificate"><?php _e( 'Sandbox API certificate', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
322
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_sandbox_api_certificate_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
323
- </th>
324
- <td>
325
- <div id="woo_pp_sandbox_api_certificate_help" style="display: none;">
326
- <p>
327
- <h2><?php _e( 'Sandbox API certificate', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
328
- <?php _e( '<p>When you request API credentials from PayPal, and you choose the API certificate option, PayPal provides you with the certificate as a file. This file is typically called <strong>cert_key_pem.txt</strong>. Upload the file using this setting.</p><p><strong>Note:</strong> Upload the file exactly as it was provided to you by PayPal. The name of the file doesn\'t matter, but you must not modify the contents of the file.</p><p>For help on retrieving your API credentials, click on the help for the <strong>Sandbox API credential type</strong> setting.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
329
- </p>
330
- </div>
331
- <fieldset>
332
- <legend class="screen-reader-text"><span><?php _e( 'Sandbox API certificate', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
333
- <p><strong><?php _e( 'Certificate status:', 'woocommerce-gateway-paypal-express-checkout' ); ?></strong> <?php echo $sb_cert_info; ?></p>
334
- <p><span style="font-style: italic;"><?php _e( 'Upload a new certificate:', 'woocommerce-gateway-paypal-express-checkout' ); ?></span> <input type="file" name="woo_pp_sandbox_api_certificate" id="woo_pp_sandbox_api_certificate"></p>
335
- </fieldset>
336
- </td>
337
- </tr>
338
- <tr class="woo_pp_sandbox api-credential-row">
339
- <th>
340
- <label for="woo_pp_sandbox_subject"><?php _e( 'Sandbox subject', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
341
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_sandbox_subject_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
342
- </th>
343
- <td>
344
- <div id="woo_pp_sandbox_subject_help" style="display: none;">
345
- <p>
346
- <h2><?php _e( 'Sandbox subject', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
347
- <?php _e( '<p>If you\'re processing transactions on behalf of another PayPal account, enter the email address or Secure Merchant Account ID (also known as a Payer ID) of the other account here. Generally, you must have API permissions in place with the other account in order to process anything other than "sale" transactions for them.</p><p>Most people won\'t need to use this setting. If you\'re not sure what to put here, leave it blank.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
348
- </p>
349
- </div>
350
- <fieldset>
351
- <legend class="screen-reader-text"><span><?php _e( 'Sandbox subject', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
352
- <input type="text" name="woo_pp_sandbox_subject" id="woo_pp_sandbox_subject" size="40" value="<?php echo esc_attr( $sb_subject ); ?>">
353
- </fieldset>
354
- </td>
355
- </tr>
356
- <tr>
357
- <th>
358
- <label for="woo_pp_enable_in_context_checkout"><?php _e( 'In-Context Checkout', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
359
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_enable_in_context_checkout_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
360
- </th>
361
- <td>
362
- <div id="woo_pp_enable_in_context_checkout_help" style="display: none;">
363
- <p>
364
- <h2><?php _e( 'Enable In-Context Checkout', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
365
- <?php _e( '<p>PayPal offers a new in-context checkout experience, which allows you to show the PayPal checkout in a minibrowser on top of your checkout. This experience can help to improve conversion on your store by reassuring buyers that they have not left your store.</p><p>More information on in-context checkout is available from <a href="https://developer.paypal.com/docs/classic/express-checkout/in-context/">the PayPal Developer Portal</a>.</p><p>If you want to use the new in-context checkout, enable this setting.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
366
-
367
- <img src="<?php echo esc_url( wc_gateway_ppec()->plugin_url . 'assets/img/in-context-composite.png' ); ?>" width="378" height"299">
368
- </p>
369
- </div>
370
- <fieldset>
371
- <legend class="screen-reader-text"><span><?php _e( 'Enable In-Context Checkout', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
372
- <input type="checkbox" name="woo_pp_icc_enabled" id="woo_pp_enable_in_context_checkout"<?php checked( $icc_enabled ); ?> value="true">
373
- <label for="woo_pp_enable_in_context_checkout"><?php _e( 'Enable In-Context Checkout', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
374
- </fieldset>
375
- </td>
376
- </tr>
377
- <tr>
378
- <th>
379
- <label for="woo_pp_enable_logging"><?php _e( 'Enable logging', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
380
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_enable_logging_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
381
- </th>
382
- <td>
383
- <div id="woo_pp_enable_logging_help" style="display: none;">
384
- <p>
385
- <h2><?php _e( 'Enable Logging', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
386
- <?php _e( '<p>If this setting is enabled, some informations will be logged to a log file. The log is accessible via WooCommerce &gt; System Status &gt; Logs. From the dropdown, select filename with prefix <code>wc_gateway_ppec</code></p> then click View.', 'woocommerce-gateway-paypal-express-checkout' ); ?>
387
- </p>
388
- </div>
389
- <fieldset>
390
- <legend class="screen-reader-text"><span><?php _e( 'Enable logging', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
391
- <input type="checkbox" name="woo_pp_logging_enabled" id="woo_pp_enable_logging"<?php checked( $logging_enabled ); ?> value="true">
392
- <label for="woo_pp_enable_logging"><?php _e( 'Enable logging', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
393
- </fieldset>
394
- </td>
395
- </tr>
396
- <tr>
397
- <th>
398
- <label for="woo_pp_button_size"><?php _e( 'Button size', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
399
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_button_size_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
400
- </th>
401
- <td>
402
- <div id="woo_pp_button_size_help" style="display: none;">
403
- <p>
404
- <h2><?php _e( 'Button size', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
405
- <?php _e( '<p>PayPal offers different sizes of the "PayPal Checkout" and "PayPal Credit" buttons, allowing you to select a size that best fits your site\'s theme. This setting will allow you to choose which size button(s) appear on your cart page:</p><table><tr><th style="text-align: right;">Small:</th><td style="padding: 3px;"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-small.png"></td><td style="padding: 3px;"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/ppcredit-logo-small.png"></td></tr><tr><th style="text-align: right;">Medium:</th><td style="padding: 3px;"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-medium.png"></td><td style="padding: 3px;"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/ppcredit-logo-medium.png"></td></tr><tr><th style="text-align: right;">Large:</th><td style="padding: 3px;"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-large.png"></td><td style="padding: 3px;"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/ppcredit-logo-large.png"></td></tr></table>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
406
- </p>
407
- </div>
408
- <fieldset>
409
- <legend class="screen-reader-text"><span><?php _e( 'Button size', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
410
- <select name="woo_pp_button_size">
411
- <option value="<?php echo WC_Gateway_PPEC_Settings::buttonSizeSmall; ?>"<?php selected( $button_size, WC_Gateway_PPEC_Settings::buttonSizeSmall ); ?>><?php _e( 'Small', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
412
- <option value="<?php echo WC_Gateway_PPEC_Settings::buttonSizeMedium; ?>"<?php selected( $button_size, WC_Gateway_PPEC_Settings::buttonSizeMedium ); ?>><?php _e( 'Medium', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
413
- </select>
414
- </fieldset>
415
- </td>
416
- </tr>
417
- <tr>
418
- <th>
419
- <label for="woo_pp_mark_size"><?php _e( 'Mark size', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
420
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_mark_size_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
421
- </th>
422
- <td>
423
- <div id="woo_pp_mark_size_help" style="display: none;">
424
- <p>
425
- <h2><?php _e( 'Mark size', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
426
- <?php _e( '<p>PayPal offers different sizes of the PayPal and PayPal Credit logos, allowing you to select a size that best fits your site\'s theme. This setting will allow you to choose which size logo(s) appear in the list of payment methods in the checkout:</p><table><tr><th style="text-align: right;">Small:</th><td style="padding: 3px;"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png"></td><td style="padding: 3px;"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/ppc-acceptance-small.png"></td></tr><tr><th style="text-align: right;">Medium:</th><td style="padding: 3px;"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-medium.png"></td><td style="padding: 3px;"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/ppc-acceptance-medium.png"></td></tr><tr><th style="text-align: right;">Large:</th><td style="padding: 3px;"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-large.png"></td><td style="padding: 3px;"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/ppc-acceptance-large.png"></td></tr></table>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
427
- </p>
428
- </div>
429
- <fieldset>
430
- <legend class="screen-reader-text"><span><?php _e( 'Mark size', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
431
- <select name="woo_pp_mark_size">
432
- <option value="<?php echo WC_Gateway_PPEC_Settings::markSizeSmall; ?>"<?php selected( $mark_size, WC_Gateway_PPEC_Settings::markSizeSmall ); ?>><?php _e( 'Small', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
433
- <option value="<?php echo WC_Gateway_PPEC_Settings::markSizeMedium; ?>"<?php selected( $mark_size, WC_Gateway_PPEC_Settings::markSizeMedium ); ?>><?php _e( 'Medium', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
434
- <option value="<?php echo WC_Gateway_PPEC_Settings::markSizeLarge; ?>"<?php selected( $mark_size, WC_Gateway_PPEC_Settings::markSizeLarge ); ?>><?php _e( 'Large', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
435
- </select>
436
- </fieldset>
437
- </tr>
438
- <tr>
439
- <th>
440
- <label for="woo_pp_logo_image_url"><?php _e( 'Logo image URL', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
441
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_logo_image_url_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
442
- </th>
443
- <td>
444
- <div id="woo_pp_logo_image_url_help" style="display: none;">
445
- <p>
446
- <h2><?php _e( 'Logo image URL', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
447
- <?php _e( '<p>If you want PayPal to co-brand the checkout page with your logo, enter the URL of your logo image here. The logo image must be no larger than 190x60, and should be in a format understood by most browsers (such as GIF, PNG, or JPG).</p><p><strong>Note:</strong> The URL you enter here should be on an HTTPS site (e.g., the URL should start with https://). If it is not, some browsers may display a warning or refuse to show the image.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
448
- </p>
449
- </div>
450
- <fieldset>
451
- <legend class="screen-reader-text"><span><?php _e( 'Logo image URL', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
452
- <input type="url" name="woo_pp_logo_image_url" id="woo_pp_logo_image_url" size="80" value="<?php echo esc_url( $logo_image_url ); ?>">
453
- </fieldset>
454
- </td>
455
- </tr>
456
- <tr>
457
- <th>
458
- <label for="woo_pp_payment_action"><?php _e( 'Payment type', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
459
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_payment_action_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
460
- </th>
461
- <td>
462
- <div id="woo_pp_payment_action_help" style="display: none;">
463
- <p>
464
- <h2><?php _e( 'Payment type', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
465
- <?php _e( '<p>PayPal supports three payment types: Sale, Authorization, and Order.</p><ul style="list-style: disc outside; margin-left: 20px;"><li><strong>Sale:</strong> Sale transactions complete as soon as the buyer finishes checking out on your site. The proceeds from the transaction are deposited into your PayPal account immediately (barring other factors, such as eChecks or other payment holds). Sale transactions cannot be captured or voided, but they can be refunded.</li><li><strong>Authorization:</strong> Authorization transactions place a hold on the buyer\'s funds at the time of checkout, but does not move the funds from the buyer\'s account. Funds are held for three days. To move money to your account, you must perform a capture within 29 days of the time the buyer checks out.</li><li><strong>Order:</strong> Orders represent an open-to-buy on the buyer\'s account &mdash; they do not move money or hold funds, but you can authorize and capture against them later. Orders are generally valid for 29 days from the time the buyer checks out. Orders are handy when a product is going to be backordered or you have to split an order into multiple shipments.</li></ul>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
466
- </p>
467
- </div>
468
- <fieldset>
469
- <legend class="screen-reader-text"><span><?php _e( 'Payment type', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
470
- <select name="woo_pp_payment_action" id="woo_pp_payment_action">
471
- <option value="<?php echo WC_Gateway_PPEC_Settings::PaymentActionSale; ?>"<?php selected( $payment_action, WC_Gateway_PPEC_Settings::PaymentActionSale ); ?>><?php _e( 'Sale', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
472
- <option value="<?php echo WC_Gateway_PPEC_Settings::PaymentActionAuthorization; ?>"<?php selected( $payment_action, WC_Gateway_PPEC_Settings::PaymentActionAuthorization ); ?>><?php _e( 'Authorization', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
473
- </select>
474
- </fieldset>
475
- </td>
476
- </tr>
477
- <tr>
478
- <th>
479
- <label for="woo_pp_allow_guest_checkout"><?php _e( 'Guest payments', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
480
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_allow_guest_checkout_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
481
- </th>
482
- <td>
483
- <div id="woo_pp_allow_guest_checkout_help" style="display: none;">
484
- <p>
485
- <h2><?php _e( 'Allow buyers to pay without a PayPal account', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
486
- <?php _e( '<p>This setting controls whether buyers can pay through PayPal without creating a PayPal account.</p><p>Unless you have a compelling reason to disable this setting, we recommend that you leave it enabled.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
487
- </p>
488
- </div>
489
- <fieldset>
490
- <legend class="screen-reader-text"><span><?php _e( 'Guest payments', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
491
- <input type="checkbox" name="woo_pp_allow_guest_checkout" id="woo_pp_allow_guest_checkout"<?php checked( $allow_guest_checkout ); ?> value="true">
492
- <label for="woo_pp_allow_guest_checkout"><?php _e( 'Allow buyers to pay without a PayPal account', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
493
- </fieldset>
494
- </td>
495
- </tr>
496
- <tr>
497
- <th>
498
- <label for="woo_pp_block_echecks"><?php _e( 'Instant payments', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
499
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_block_echecks_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
500
- </th>
501
- <td>
502
- <div id="woo_pp_block_echecks_help" style="display: none;">
503
- <p>
504
- <h2><?php _e( 'Require instant payments', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
505
- <?php _e( '<p>If you enable this setting, PayPal will be instructed not to allow the buyer to use funding sources that take additional time to complete (for example, eChecks). Instead, the buyer will be required to use an instant funding source, such as an instant transfer, a credit/debit card, or PayPal Credit.</p><p>If you sell virtual and/or downloadable goods, it may make more sense to enable this option (as buyers who are expecting instant fulfillment may be frustrated when they realize that they have to wait 3-5 days to receive their purchase). However, if you sell physical goods, we recommend that you leave this option disabled, as it will allow buyers to purchase from you who may have otherwise been unable to.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
506
- </p>
507
- </div>
508
- <fieldset>
509
- <legend class="screen-reader-text"><span><?php _e( 'Instant payments', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
510
- <input type="checkbox" name="woo_pp_block_echecks" id="woo_pp_block_echecks"<?php checked( $block_echecks ); ?> value="true">
511
- <label for="woo_pp_block_echecks"><?php _e( 'Require instant payments', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
512
- </fieldset>
513
- </td>
514
- </tr>
515
-
516
- <tr id="woo_pp_req_ba_row">
517
- <th>
518
- <label for="woo_pp_req_billing_address"><?php _e( 'Billing address', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
519
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_req_billing_address_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
520
- </th>
521
- <td>
522
- <div id="woo_pp_req_billing_address_help" style="display: none;">
523
- <p>
524
- <h2><?php _e( 'Require buyers to provide their billing address', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
525
- <?php _e( '<p>Normally, PayPal does not share the buyer\'s billing details with you. However, there are times when you must collect the buyer\'s billing address to fulfill an essential business function (such as determining whether you must charge the buyer tax).</p><p>If you need the buyer\'s billing address to fulfill an essential business function, enable this setting. Buyers will be notified during the PayPal checkout that you require their billing address to process the transaction.</p><p>Remember, PayPal will always provide you with the buyer\'s country, even if you do not enable this setting.</p><p><strong>Note: Do not enable this setting unless you have been approved by PayPal to do so.</strong></p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
526
- </p>
527
- </div>
528
- <fieldset>
529
- <legend class="screen-reader-text"><span><?php _e( 'Billing address', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
530
- <input type="checkbox" name="woo_pp_req_billing_address" id="woo_pp_req_billing_address"<?php checked( $require_billing_address ); ?> value="true">
531
- <label for="woo_pp_req_billing_address"><?php _e( 'Require buyers to provide their billing address', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
532
- </fieldset>
533
- </td>
534
- </tr>
535
-
536
- <tr>
537
- <th>
538
- <label for="woo_pp_zero_subtotal_behavior"><?php _e( 'Zero subtotal behavior', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
539
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_zero_subtotal_behavior_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
540
- </th>
541
- <td>
542
- <div id="woo_pp_zero_subtotal_behavior_help" style="display: none;">
543
- <p>
544
- <h2><?php _e( 'Zero subtotal behavior', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
545
- <?php _e( '<p>When a buyer opts to check out with PayPal, the contents of the buyer\'s shopping cart are sent over to PayPal and displayed on the PayPal checkout pages. PayPal does not have an intrinsic way of handling coupons, so any coupons that the buyer has redeemed are presented to PayPal as line items with a negative price.</p><p>PayPal requires that the subtotal of all line items be greater than zero. (The subtotal of all line items is the total of all the items in the cart, minus any coupons, but before shipping, handling, and tax are calculated.) However, in some situations, the subtotal is zero or less than zero &mdash; for example, if you offer an item that is free with shipping and handling, or when the buyer has redeemed enough coupons to bring their subtotal down to zero. PayPal cannot handle this situation, so this setting controls what will happen in this scenario:</p><ul style="list-style: disc outside; margin-left: 20px;"><li><strong>Modify line items prices and add a shipping discount:</strong> When this option is selected, an item called "Discount Offset" will be added to the list of line items sent to PayPal. This will raise the item subtotal above zero. A corresponding discount will be passed to PayPal to offset this amount; it will show up on PayPal as "Shipping Discount".</li><li><strong>Don\'t send line items to PayPal:</strong> When this option is selected, line items will not be passed to PayPal. The buyer will not see the amount they are paying or any of the items they are paying for in the PayPal checkout.</li><li><strong>Send the coupons to PayPal as a shipping discount:</strong> When this option is selected, any coupons in the buyer\'s cart will be aggregated together and passed as a single discount to PayPal. The discount will appear on the PayPal Checkout as "Shipping Discount".</li></ul><p><strong>Note:</strong> Regardless of which option you select, the total of the buyer\'s purchase will not change.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
546
- </p>
547
- </div>
548
- <fieldset>
549
- <legend class="screen-reader-text"><span><?php _e( 'Zero subtotal behavior', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
550
- <select name="woo_pp_zero_subtotal_behavior" id="woo_pp_zero_subtotal_behavior">
551
- <option value="<?php echo WC_Gateway_PPEC_Settings::zeroSubtotalBehaviorModifyItems; ?>"<?php selected( $zero_subtotal_behavior, WC_Gateway_PPEC_Settings::zeroSubtotalBehaviorModifyItems ); ?>><?php _e( 'Modify line item prices and add a shipping discount', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
552
- <option value="<?php echo WC_Gateway_PPEC_Settings::zeroSubtotalBehaviorOmitLineItems; ?>"<?php selected( $zero_subtotal_behavior, WC_Gateway_PPEC_Settings::zeroSubtotalBehaviorOmitLineItems ); ?>><?php _e( 'Don\'t send line items to PayPal', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
553
- <option value="<?php echo WC_Gateway_PPEC_Settings::zeroSubtotalBehaviorPassCouponsAsShippingDiscount; ?>"<?php selected( $zero_subtotal_behavior, WC_Gateway_PPEC_Settings::zeroSubtotalBehaviorPassCouponsAsShippingDiscount ); ?>><?php _e( 'Send the coupons to PayPal as a shipping discount', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
554
- </select>
555
- </fieldset>
556
- </td>
557
- </tr>
558
- <tr>
559
- <th>
560
- <label for="woo_pp_subtotal_mismatch_behavior"><?php _e( 'Subtotal mismatch behavior', 'woocommerce-gateway-paypal-express-checkout' ); ?></label>
561
- <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=woo_pp_subtotal_mismatch_behavior_help" class="thickbox"><img src="<?php echo esc_url( $help_image_url ); ?>" class="help_tip" style="cursor: pointer;" height="16" width="16" data-tip="<?php _e( 'Click here for help with this option.', 'woocommerce-gateway-paypal-express-checkout' ); ?>"></a>
562
- </th>
563
- <td>
564
- <div id="woo_pp_subtotal_mismatch_behavior_help" style="display: none;">
565
- <p>
566
- <h2><?php _e( 'Subtotal mismatch behavior', 'woocommerce-gateway-paypal-express-checkout' ); ?></h2>
567
- <?php _e( '<p>Internally, WooCommerce calculates line item prices and taxes out to four decimal places; however, PayPal can only handle amounts out to two decimal places (or, depending on the currency, no decimal places at all). Occasionally, this can cause discrepancies between the way WooCommerce calculates prices versus the way PayPal calculates them. Consider the following example:</p><p>You have an item that you sell for $0.0130 each, and a buyer buys 200 of them. When this line item is sent over to PayPal, it must be shortened to $0.01 each.</p><table style="border: 1px solid gray; border-collapse: collapse;"><tr><th style="border: 1px solid gray;">&nbsp;</th><th style="border: 1px solid gray; text-align: center; padding: 2px;">WooCommerce</th><th style="border: 1px solid gray; text-align: center; padding: 2px;">PayPal</th></tr><tr><th style="border: 1px solid gray; text-align: right; padding: 2px;">Unit Price</th><td style="border: 1px solid gray; text-align: center; padding: 2px;">$0.0130</td><td style="border: 1px solid gray; text-align: center; padding: 2px;">$0.01</td></tr><tr><th style="border: 1px solid gray; text-align: right; padding: 2px;">Quantity</th><td style="border: 1px solid gray; text-align: center; padding: 2px;">200</td><td style="border: 1px solid gray; text-align: center; padding: 2px;">200</td></tr><tr><th style="border: 1px solid gray; text-align: right; padding: 2px;">Total</th><th style="border: 1px solid gray; text-align: center; padding: 2px;">$2.60</th><th style="border: 1px solid gray; text-align: center; padding: 2px;">$2.00</th></tr></table><p>Discrepancies like this will cause PayPal to reject the transaction. This setting, therefore, controls what happens when a situation like this arises:</p><ul style="list-style: disc outside; margin-left: 20px;"><li><strong>Add another line item:</strong> When this option is selected, an extra line item will be sent to PayPal that will represent the difference between the way WooCommerce calculated the price versus the way PayPal would calculate it. This line item will appear on the PayPal checkout as "Line Item Amount Offset".</li><li><strong>Don\'t send line items to PayPal:</strong> When this option is selected, line items will not be passed to PayPal. The buyer will not see the amount they are paying or any of the items they are paying for in the PayPal checkout.</li></ul><p><strong>Note:</strong> Regardless of which option you select, the total of the buyer\'s purchase will not change.</p>', 'woocommerce-gateway-paypal-express-checkout' ); ?>
568
- </p>
569
- </div>
570
- <fieldset>
571
- <legend class="screen-reader-text"><span><?php _e( 'Subtotal mismatch behavior', 'woocommerce-gateway-paypal-express-checkout' ); ?></span></legend>
572
- <select name="woo_pp_subtotal_mismatch_behavior" id="woo_pp_subtotal_mismatch_behavior">
573
- <option value="<?php echo WC_Gateway_PPEC_Settings::subtotalMismatchBehaviorAddLineItem; ?>"<?php selected( $subtotal_mismatch_behavior, WC_Gateway_PPEC_Settings::subtotalMismatchBehaviorAddLineItem ); ?>><?php _e( 'Add another line item', 'woocommerce-gateway-paypal-express-checkout' ); ?></option>
574
- <option value="<?php echo WC_Gateway_PPEC_Settings::subtotalMismatchBehaviorDropLineItems; ?>"<?php selected( $subtotal_mismatch_behavior, WC_Gateway_PPEC_Settings::subtotalMismatchBehaviorDropLineItems ); ?>><?php _e( 'Don\'t send line items to PayPal', 'woocommerce-gateway-paypal-express-checkout' ); ?>'</option>
575
- </select>
576
- </fieldset>
577
- </td>
578
- </tr>
579
- </table>
580
-
581
- <script type="text/javascript" src="<?php echo esc_url( wc_gateway_ppec()->plugin_url . 'assets/js/wc-gateway-ppec-admin-settings.js' ); ?>"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
phpcs.xml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <ruleset name="WordPress Coding Standards for Plugins">
3
+ <description>Generally-applicable sniffs for WordPress plugins</description>
4
+
5
+ <rule ref="WordPress-Core" />
6
+ <rule ref="WordPress-Docs" />
7
+
8
+ <exclude-pattern>*/node_modules/*</exclude-pattern>
9
+ <exclude-pattern>*/vendor/*</exclude-pattern>
10
+ </ruleset>
phpunit.xml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <phpunit
3
+ bootstrap="tests/phpunit/bootstrap.php"
4
+ backupGlobals="false"
5
+ colors="true"
6
+ convertErrorsToExceptions="true"
7
+ convertNoticesToExceptions="true"
8
+ convertWarningsToExceptions="true"
9
+ verbose="true"
10
+ >
11
+ <testsuites>
12
+ <testsuite>
13
+ <directory prefix="test-" suffix=".php">./tests/phpunit</directory>
14
+ </testsuite>
15
+ </testsuites>
16
+ </phpunit>
readme.txt CHANGED
@@ -1,9 +1,9 @@
1
  === WooCommerce PayPal Express Checkout Payment Gateway ===
2
- Contributors: automattic, woothemes, akeda, dwainm, royho, allendav, slash1andy, woosteve, spraveenitpro, mikedmoore, fernashes, shellbeezy, danieldudzic, mikaey
3
  Tags: ecommerce, e-commerce, commerce, woothemes, wordpress ecommerce, store, sales, sell, shop, shopping, cart, checkout, configurable, paypal
4
  Requires at least: 4.4
5
  Tested up to: 4.4
6
- Stable tag: 1.1.2
7
  License: GPLv3
8
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
9
 
@@ -85,6 +85,15 @@ https://gist.github.com/mikejolley/ad2ecc286c9ad6cefbb7065ba6dfef48
85
 
86
  == Changelog ==
87
 
 
 
 
 
 
 
 
 
 
88
  = 1.1.2 =
89
  * Fix - Make sure translations are loaded properly.
90
  * Fix - Added IPN (Instant Payment Notification) handler.
1
  === WooCommerce PayPal Express Checkout Payment Gateway ===
2
+ Contributors: automattic, woothemes, akeda, dwainm, royho, allendav, slash1andy, woosteve, spraveenitpro, mikedmoore, fernashes, shellbeezy, danieldudzic, mikaey, fullysupportedphil, dsmithweb, corsonr, bor0
3
  Tags: ecommerce, e-commerce, commerce, woothemes, wordpress ecommerce, store, sales, sell, shop, shopping, cart, checkout, configurable, paypal
4
  Requires at least: 4.4
5
  Tested up to: 4.4
6
+ Stable tag: 1.2.0
7
  License: GPLv3
8
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
9
 
85
 
86
  == Changelog ==
87
 
88
+ = 1.2.0 =
89
+ * Fix - Prevent conflict with other gateways.
90
+ * Fix - Compatibility with WooCommerce 3.0, including ensuring the customer address is saved correctly.
91
+
92
+ = 1.1.3 =
93
+ * Fix - Guest users can checkout without giving shipping information when required.
94
+ * Fix - Modal popup not working properly. Changed to full page redirect with a hook to add back the modal/popup.
95
+ * Tweak - Guest checkout is on by default. Should be turned off by using this filter: woocommerce_paypal_express_checkout_allow_guests.
96
+
97
  = 1.1.2 =
98
  * Fix - Make sure translations are loaded properly.
99
  * Fix - Added IPN (Instant Payment Notification) handler.
woocommerce-gateway-paypal-express-checkout.php CHANGED
@@ -1,19 +1,19 @@
1
  <?php
2
  /**
3
  * Plugin Name: WooCommerce PayPal Express Checkout Gateway
4
- * Plugin URI: https://www.woothemes.com/products/woocommerce-gateway-paypal-express-checkout/
5
  * Description: A payment gateway for PayPal Express Checkout (https://www.paypal.com/us/webapps/mpp/express-checkout).
6
- * Version: 1.1.2
7
- * Author: Automattic
8
  * Author URI: https://woocommerce.com
9
- * Copyright: © 2016 WooCommerce / PayPal.
10
  * License: GNU General Public License v3.0
11
  * License URI: http://www.gnu.org/licenses/gpl-3.0.html
12
  * Text Domain: woocommerce-gateway-paypal-express-checkout
13
  * Domain Path: /languages
14
  */
15
  /**
16
- * Copyright (c) 2015 PayPal, Inc.
17
  *
18
  * The name of the PayPal may not be used to endorse or promote products derived from this
19
  * software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND
@@ -36,7 +36,7 @@ function wc_gateway_ppec() {
36
  if ( ! isset( $plugin ) ) {
37
  require_once( 'includes/class-wc-gateway-ppec-plugin.php' );
38
 
39
- $plugin = new WC_Gateway_PPEC_Plugin( __FILE__, '1.1.2' );
40
  }
41
 
42
  return $plugin;
1
  <?php
2
  /**
3
  * Plugin Name: WooCommerce PayPal Express Checkout Gateway
4
+ * Plugin URI: https://woocommerce.com/products/woocommerce-gateway-paypal-express-checkout/
5
  * Description: A payment gateway for PayPal Express Checkout (https://www.paypal.com/us/webapps/mpp/express-checkout).
6
+ * Version: 1.2.0
7
+ * Author: WooCommerce
8
  * Author URI: https://woocommerce.com
9
+ * Copyright: © 2017 WooCommerce / PayPal.
10
  * License: GNU General Public License v3.0
11
  * License URI: http://www.gnu.org/licenses/gpl-3.0.html
12
  * Text Domain: woocommerce-gateway-paypal-express-checkout
13
  * Domain Path: /languages
14
  */
15
  /**
16
+ * Copyright (c) 2017 PayPal, Inc.
17
  *
18
  * The name of the PayPal may not be used to endorse or promote products derived from this
19
  * software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND
36
  if ( ! isset( $plugin ) ) {
37
  require_once( 'includes/class-wc-gateway-ppec-plugin.php' );
38
 
39
+ $plugin = new WC_Gateway_PPEC_Plugin( __FILE__, '1.2.0' );
40
  }
41
 
42
  return $plugin;