WooCommerce PayPal Express Checkout Payment Gateway - Version 0.1.0

Version Description

  • Beta release
Download this release

Release Info

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

Version 0.1.0

Files changed (32) hide show
  1. assets/css/wc-gateway-ppec-frontend-cart.css +22 -0
  2. assets/css/wc-gateway-ppec-frontend-checkout.css +7 -0
  3. assets/img/in-context-composite.png +0 -0
  4. assets/js/wc-gateway-ppec-admin-settings.js +177 -0
  5. assets/js/wc-gateway-ppec-admin.js +49 -0
  6. assets/js/wc-gateway-ppec-frontend-checkout.js +47 -0
  7. includes/abstracts/abstract-wc-gateway-ppec-client-credential.php +110 -0
  8. includes/abstracts/abstract-wc-gateway-ppec.php +848 -0
  9. includes/class-wc-gateway-ppec-address.php +646 -0
  10. includes/class-wc-gateway-ppec-admin-handler.php +258 -0
  11. includes/class-wc-gateway-ppec-api-error.php +57 -0
  12. includes/class-wc-gateway-ppec-cart-handler.php +458 -0
  13. includes/class-wc-gateway-ppec-checkout-details.php +517 -0
  14. includes/class-wc-gateway-ppec-checkout-handler.php +673 -0
  15. includes/class-wc-gateway-ppec-client-credential-certificate.php +46 -0
  16. includes/class-wc-gateway-ppec-client-credential-signature.php +56 -0
  17. includes/class-wc-gateway-ppec-client.php +323 -0
  18. includes/class-wc-gateway-ppec-gateway-loader.php +45 -0
  19. includes/class-wc-gateway-ppec-ips-handler.php +218 -0
  20. includes/class-wc-gateway-ppec-payment-details.php +451 -0
  21. includes/class-wc-gateway-ppec-plugin.php +231 -0
  22. includes/class-wc-gateway-ppec-refund.php +32 -0
  23. includes/class-wc-gateway-ppec-session-data.php +36 -0
  24. includes/class-wc-gateway-ppec-settings.php +405 -0
  25. includes/class-wc-gateway-ppec-with-paypal-credit.php +22 -0
  26. includes/class-wc-gateway-ppec-with-paypal.php +22 -0
  27. includes/exceptions/class-wc-gateway-ppec-api-exception.php +39 -0
  28. includes/exceptions/class-wc-gateway-ppec-missing-session-exception.php +11 -0
  29. includes/functions.php +61 -0
  30. includes/views/admin-settings.php +581 -0
  31. readme.txt +84 -0
  32. woocommerce-gateway-paypal-express-checkout.php +45 -0
assets/css/wc-gateway-ppec-frontend-cart.css ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .woo_pp_cart_buttons_div {
2
+ margin-top: -20px;
3
+ }
4
+ .woo_pp_cart_buttons_div:after {
5
+ visibility: hidden;
6
+ display: block;
7
+ font-size: 0;
8
+ content: " ";
9
+ clear: both;
10
+ height: 0;
11
+ }
12
+
13
+ .paypal-button-hidden {
14
+ visibility: hidden;
15
+ }
16
+
17
+ .paypal-button-widget .paypal-button,
18
+ .paypal-button-widget .paypal-button:hover {
19
+ background: transparent;
20
+ box-shadow: none;
21
+ border: none;
22
+ }
assets/css/wc-gateway-ppec-frontend-checkout.css ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ .woocommerce-billing-fields .ppec-bypass.hidden {
2
+ display: none !important;
3
+ }
4
+
5
+ .payment_method_ppec_paypal img {
6
+ max-height: 68px !important;
7
+ }
assets/img/in-context-composite.png ADDED
Binary file
assets/js/wc-gateway-ppec-admin-settings.js ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var woo_pp_icc_started = false;
2
+ window.paypalCheckoutReady = function() {
3
+ paypal.checkout.setup( wc_ppec.payer_id, {
4
+ container: 'woo_pp_icc_container'
5
+ } );
6
+ };
7
+
8
+ jQuery( "form.checkout" ).submit(function() {
9
+ if ( jQuery( '#payment_method_ppec_paypal, #payment_method_ppec_paypal_credit' ).is( ':checked' ) ) {
10
+ woo_pp_icc_started = true;
11
+ paypal.checkout.initXO();
12
+ }
13
+ } );
14
+
15
+ jQuery( document ).ajaxComplete( function( event, xhr, settings ) {
16
+ if( ! woo_pp_icc_started ) {
17
+ return;
18
+ }
19
+
20
+ var c = xhr.responseText;
21
+ if ( c.indexOf( '<!--WC_START-->' ) < 0 ) {
22
+ return;
23
+ }
24
+ if( c.indexOf( '<!--WC_END-->' ) < 0 ) {
25
+ return;
26
+ }
27
+
28
+ var d = jQuery.parseJSON( c.split( '<!--WC_START-->' )[1].split( '<!--WC_END-->' )[0] );
29
+ if( !d ) {
30
+ return;
31
+ }
32
+ if( 'success' != d.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
+ }
includes/abstracts/abstract-wc-gateway-ppec-client-credential.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ abstract class WC_Gateway_PPEC_Client_Credential {
8
+
9
+ /**
10
+ * API username.
11
+ *
12
+ * @var string
13
+ */
14
+ protected $_username;
15
+
16
+ /**
17
+ * API password.
18
+ *
19
+ * @var string
20
+ */
21
+ protected $_password;
22
+
23
+ /**
24
+ * API subject.
25
+ *
26
+ * @var string
27
+ */
28
+ protected $_subject;
29
+
30
+ /**
31
+ * Payer ID.
32
+ *
33
+ * @var string
34
+ */
35
+ protected $_payer_id;
36
+
37
+ /**
38
+ * Get API username.
39
+ *
40
+ * @return string API username
41
+ */
42
+ public function get_username() {
43
+ return $this->_username;
44
+ }
45
+
46
+ /**
47
+ * Get API password.
48
+ *
49
+ * @return string API password
50
+ */
51
+ public function get_password() {
52
+ return $this->_password;
53
+ }
54
+
55
+ /**
56
+ * Get API subject.
57
+ *
58
+ * @return string API subject
59
+ */
60
+ public function get_subject() {
61
+ return $this->_subject;
62
+ }
63
+
64
+ /**
65
+ * Get payer ID.
66
+ *
67
+ * @return string Payer ID
68
+ */
69
+ public function get_payer_id() {
70
+ return $this->_payer_id;
71
+ }
72
+
73
+ /**
74
+ * Set payer ID.
75
+ *
76
+ * @param string $payer_id Payer ID
77
+ */
78
+ public function set_payer_id( $payer_id ) {
79
+ $this->_payer_id = $payer_id;
80
+ }
81
+
82
+ /**
83
+ * Retrieves the subdomain of the endpoint which should be used for this type
84
+ * of credentials.
85
+ *
86
+ * @return string The appropriate endpoint, e.g. https://api.paypal.com/nvp
87
+ * in this case the subdomain is 'api'
88
+ */
89
+ abstract public function get_endpoint_subdomain();
90
+
91
+ /**
92
+ * Retrieves a list of credentialing parameters that should be supplied to
93
+ * PayPal.
94
+ *
95
+ * @return array An array of name-value pairs containing the API credentials
96
+ * from this object.
97
+ */
98
+ protected function get_request_params() {
99
+ $params = array(
100
+ 'USER' => $this->_username,
101
+ 'PWD' => $this->_password,
102
+ );
103
+
104
+ if ( ! empty( $this->_subject ) ) {
105
+ $params['SUBJECT'] = $this->_subject;
106
+ }
107
+
108
+ return $params;
109
+ }
110
+ }
includes/abstracts/abstract-wc-gateway-ppec.php ADDED
@@ -0,0 +1,848 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
8
+
9
+ private static $process_admin_options_already_run = false;
10
+ private static $process_admin_options_validation_error = false;
11
+
12
+ protected $buyer_email = false;
13
+ public static $use_buyer_email = true;
14
+
15
+ public function __construct() {
16
+
17
+ $this->has_fields = false;
18
+ $this->icon = false;
19
+ $this->title = '';
20
+ $this->description = '';
21
+ $this->supports[] = 'refunds';
22
+
23
+ $this->method_title = __( 'PayPal Express Checkout', 'woocommerce-gateway-paypal-express-checkout' );
24
+ $this->method_description = __( 'Process payments quickly and securely with PayPal.', 'woocommerce-gateway-paypal-express-checkout' );
25
+
26
+ wc_gateway_ppec()->ips->maybe_received_credentials();
27
+
28
+ $this->init_form_fields();
29
+
30
+ $settings = wc_gateway_ppec()->settings->loadSettings();
31
+
32
+
33
+ add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
34
+
35
+ // Do we need to auto-select this payment method?
36
+ // TODO: Move this out to particular handler instead of gateway
37
+ if ( ! is_admin() ) {
38
+ $session = WC()->session->get( 'paypal' );
39
+ if ( null != $session && is_a( $session, 'WC_Gateway_PPEC_Session_Data' ) && $session->checkout_completed && $session->expiry_time >= time() && $session->payerID ) {
40
+ if ( $session->checkout_details && is_a( $session->checkout_details, 'PayPal_Checkout_Details' ) && ( is_checkout() || is_ajax() ) && self::$use_buyer_email ) {
41
+ $this->buyer_email = $session->checkout_details->payer_details->email;
42
+ $this->title .= ' - ' . esc_html( $this->buyer_email );
43
+ }
44
+
45
+ $posted = array(
46
+ 'billing_first_name' => $session->checkout_details->payer_details->first_name,
47
+ 'billing_last_name' => $session->checkout_details->payer_details->last_name,
48
+ 'billing_email' => $session->checkout_details->payer_details->email,
49
+ 'billing_phone' => $session->checkout_details->payer_details->phone_number,
50
+ 'billing_country' => $session->checkout_details->payer_details->country
51
+ );
52
+
53
+ if ( $session->shipping_required ) {
54
+ if ( false === strpos( $session->checkout_details->payments[0]->shipping_address->getName(), ' ' ) ) {
55
+ $posted['shipping_first_name'] = $session->checkout_details->payer_details->first_name;
56
+ $posted['shipping_last_name'] = $session->checkout_details->payer_details->last_name;
57
+ $posted['shipping_company'] = $session->checkout_details->payments[0]->shipping_address->getName();
58
+ } else {
59
+ $name = explode( ' ', $session->checkout_details->payments[0]->shipping_address->getName() );
60
+ $posted['shipping_first_name'] = $name[0];
61
+ array_shift( $name );
62
+ $posted['shipping_last_name'] = implode( ' ', $name );
63
+ }
64
+
65
+ $posted = array_merge( $posted, array(
66
+ 'shipping_company' => $session->checkout_details->payer_details->business_name,
67
+ 'shipping_address_1' => $session->checkout_details->payments[0]->shipping_address->getStreet1(),
68
+ 'shipping_address_2' => $session->checkout_details->payments[0]->shipping_address->getStreet2(),
69
+ 'shipping_city' => $session->checkout_details->payments[0]->shipping_address->getCity(),
70
+ 'shipping_state' => $session->checkout_details->payments[0]->shipping_address->getState(),
71
+ 'shipping_postcode' => $session->checkout_details->payments[0]->shipping_address->getZip(),
72
+ 'shipping_country' => $session->checkout_details->payments[0]->shipping_address->getCountry(),
73
+ 'ship_to_different_address' => true
74
+ ) );
75
+
76
+ } else {
77
+ $posted['ship_to_different_address'] = false;
78
+ }
79
+
80
+ $_POST = array_merge( $_POST, $posted );
81
+
82
+ // Make sure the proper option is selected based on what the buyer picked
83
+ if ( ! ( $session->using_ppc xor is_a( $this, 'WC_Gateway_PPEC_With_PayPal_Credit' ) ) ) {
84
+ $this->chosen = true;
85
+ } else {
86
+ $this->chosen = false;
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ public function before_checkout_billing_form( $checkout ) {
93
+ $checkout->checkout_fields['billing'] = array(
94
+ 'billing_first_name' => $checkout->checkout_fields['billing']['billing_first_name'],
95
+ 'billing_last_name' => $checkout->checkout_fields['billing']['billing_last_name'],
96
+ 'billing_country' => $checkout->checkout_fields['billing']['billing_country'],
97
+ 'billing_email' => $checkout->checkout_fields['billing']['billing_email'],
98
+ 'billing_phone' => $checkout->checkout_fields['billing']['billing_phone']
99
+ );
100
+ }
101
+
102
+ public function init_form_fields() {
103
+ $this->form_fields = array();
104
+ }
105
+
106
+ public function process_payment( $order_id ) {
107
+
108
+ $checkout = wc_gateway_ppec()->checkout;
109
+
110
+ // Check the session. Are we going to just complete an existing payment, or are we going to
111
+ // send the user over PayPal to pay?
112
+
113
+ $session = WC()->session->get( 'paypal' );
114
+ if ( ! $session || ! is_a( $session, 'WC_Gateway_PPEC_Session_Data' ) ||
115
+ ! $session->checkout_completed || $session->expiry_time < time() ||
116
+ ! $session->payerID ) {
117
+ // Redirect them over to PayPal.
118
+ try {
119
+ $redirect_url = $checkout->startCheckoutFromCheckout( $order_id, 'ppec_paypal_credit' === $this->id );
120
+ $settings = wc_gateway_ppec()->settings->loadSettings();
121
+ if ( $settings->enableInContextCheckout && $settings->getActiveApiCredentials()->get_payer_id() ) {
122
+ $redirect_url = 'javascript:woo_pp_checkout_callback("' . urlencode( $redirect_url ) . '");';
123
+ }
124
+ return array(
125
+ 'result' => 'success',
126
+ 'redirect' => $redirect_url
127
+ );
128
+ } catch( PayPal_API_Exception $e ) {
129
+ $final_output = '<ul>';
130
+ foreach ( $e->errors as $error ) {
131
+ $final_output .= '<li>' . $error->maptoBuyerFriendlyError() . '</li>';
132
+ }
133
+ $final_output .= '</ul>';
134
+ wc_add_notice( 'Payment error:' . $final_output, 'error' );
135
+ }
136
+ } else {
137
+ // We have a token we can work with. Just complete the payment now.
138
+ try {
139
+ $payment_details = $checkout->completePayment( $order_id, $session->token, $session->payerID );
140
+ $transaction_id = $payment_details->payments[0]->transaction_id;
141
+ $payment_status = $payment_details->payments[0]->payment_status;
142
+ $pending_reason = $payment_details->payments[0]->pending_reason;
143
+ $order = wc_get_order( $order_id );
144
+
145
+ if ( 'Pending' === $payment_status && 'authorization' === $pending_reason ) {
146
+ update_post_meta( $order->id, '_ppec_charge_captured', 'no' );
147
+ add_post_meta( $order->id, '_transaction_id', $transaction_id, true );
148
+
149
+ // Mark as on-hold
150
+ $order->update_status( 'on-hold', sprintf( __( 'PayPal Express Checkout charge authorized (Charge ID: %s). Process order to take payment, or cancel to remove the pre-authorization.', 'woocommerce-gateway-paypal-express-checkout' ), $transaction_id ) );
151
+
152
+ $order->reduce_order_stock();
153
+
154
+ } else {
155
+ // TODO: Handle things like eChecks, giropay, etc.
156
+ $order->payment_complete( $transaction_id );
157
+ $order->add_order_note( sprintf( __( 'PayPal Express Checkout transaction completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $transaction_id ) );
158
+
159
+ update_post_meta( $order->id, '_ppec_charge_captured', 'yes' );
160
+ }
161
+
162
+ unset( WC()->session->paypal );
163
+
164
+ return array(
165
+ 'result' => 'success',
166
+ 'redirect' => $this->get_return_url( $order )
167
+ );
168
+ } catch( PayPal_Missing_Session_Exception $e ) {
169
+ // For some reason, our session data is missing. Generally, if we've made it this far, this shouldn't happen.
170
+ wc_add_notice( __( 'Sorry, an error occurred while trying to process your payment. Please try again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
171
+ } catch( PayPal_API_Exception $e ) {
172
+ // Did we get a 10486 or 10422 back from PayPal? If so, this means we need to send the buyer back over to
173
+ // PayPal to have them pick out a new funding method.
174
+ $need_to_redirect_back = false;
175
+ foreach ( $e->errors as $error ) {
176
+ if ( '10486' == $error->error_code || '10422' == $error->error_code ) {
177
+ $need_to_redirect_back = true;
178
+ }
179
+ }
180
+
181
+ if ( $need_to_redirect_back ) {
182
+ // We're explicitly not loading settings here because we don't want in-context checkout
183
+ // shown when we're redirecting back to PP for a funding source error.
184
+ $settings = wc_gateway_ppec()->settings->loadSettings();
185
+
186
+ $session->checkout_completed = false;
187
+ $session->leftFrom = 'order';
188
+ $session->order_id = $order_id;
189
+ WC()->session->paypal = $session;
190
+ return array(
191
+ 'result' => 'success',
192
+ 'redirect' => $settings->getPayPalRedirectUrl( $session->token, true )
193
+ );
194
+ } else {
195
+ $final_output = '<ul>';
196
+ foreach ( $e->errors as $error ) {
197
+ $final_output .= '<li>' . $error->maptoBuyerFriendlyError() . '</li>';
198
+ }
199
+ $final_output .= '</ul>';
200
+ wc_add_notice( __( 'Payment error:', 'woocommerce-gateway-paypal-express-checkout' ) . $final_output, 'error' );
201
+ return;
202
+ }
203
+ }
204
+ }
205
+ }
206
+
207
+ private function get_certificate_info( $cert_string ) {
208
+ if ( ! strlen( $cert_string ) ) {
209
+ return __( 'No API certificate on file.', 'woocommerce-gateway-paypal-express-checkout' );
210
+ }
211
+
212
+ $cert = openssl_x509_read( $cert_string );
213
+ if ( false !== $cert ) {
214
+ $certinfo = openssl_x509_parse( $cert );
215
+ if ( false !== $certinfo ) {
216
+ $valid_until = $certinfo['validTo_time_t'];
217
+ if ( $valid_until < time() ) {
218
+ // Display in red if the cert is already expired
219
+ $expires = '<span style="color: red;">' . __( 'expired on %s', 'woocommerce-gateway-paypal-express-checkout' ) . '</span>';
220
+ } elseif ( $valid_until < ( time() - 2592000 ) ) {
221
+ // Also display in red if the cert is going to expire in the next 30 days
222
+ $expires = '<span style="color: red;">' . __( 'expires on %s', 'woocommerce-gateway-paypal-express-checkout' ) . '</span>';
223
+ } else {
224
+ // Otherwise just display a normal message
225
+ $expires = __( 'expires on %s', 'woocommerce-gateway-paypal-express-checkout' );
226
+ }
227
+
228
+ $expires = sprintf( $out, date_i18n( get_option( 'date_format' ), $valid_until ) );
229
+ $out = sprintf( __( 'Certificate belongs to API username %1$s; %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $certinfo['subject']['CN'], $expires );
230
+ } else {
231
+ $out = __( 'The certificate on file is not valid.', 'woocommerce-gateway-paypal-express-checkout' );
232
+ }
233
+ } else {
234
+ $out = __( 'The certificate on file is not valid.', 'woocommerce-gateway-paypal-express-checkout' );
235
+ }
236
+
237
+ return $out;
238
+ }
239
+
240
+ // We want to be able to do some magic JavaScript stuff that WooCommerce's settings API won't let us do, so we're just going
241
+ // to override how WooCommerce tells us it should be done.
242
+ public function admin_options() {
243
+ $enable_ips = wc_gateway_ppec()->ips->is_supported();
244
+
245
+ $error_msgs = get_option( 'woo_pp_admin_error' );
246
+ if ( $error_msgs ) {
247
+ foreach ( $error_msgs as $error_msg ) {
248
+ foreach ( $error_msg as $type => $message ) {
249
+ if ( 'error' == $type ) {
250
+ WC_Admin_Settings::add_error( 'Error: ' . $message );
251
+ } elseif ( 'warning' == $type ) {
252
+ $this->display_warning( $message );
253
+ } elseif ( 'success' == $type ) {
254
+ WC_Admin_Settings::add_message( $message );
255
+ }
256
+ }
257
+ }
258
+
259
+ WC_Admin_Settings::show_messages();
260
+ delete_option( 'woo_pp_admin_error' );
261
+ }
262
+
263
+ $settings = wc_gateway_ppec()->settings->loadSettings();
264
+
265
+ $enabled = false;
266
+ $logging_enabled = false;
267
+ $ppc_enabled = false;
268
+ $icc_enabled = false;
269
+
270
+ $live_api_username = '';
271
+ $sb_api_username = '';
272
+ $live_api_pass = '';
273
+ $live_api_sig = '';
274
+ $sb_api_pass = '';
275
+ $sb_api_sig = '';
276
+ $live_subject = '';
277
+ $sb_subject = '';
278
+
279
+ $live_style = 'signature';
280
+ $sb_style = 'signature';
281
+
282
+ $live_cert = false;
283
+ $sb_cert = false;
284
+
285
+ $live_cert_info = __( 'No API certificate on file', 'woocommerce-gateway-paypal-express-checkout' );
286
+ $sb_cert_info = __( 'No API certificate on file', 'woocommerce-gateway-paypal-express-checkout' );
287
+ $environment = 'sandbox';
288
+
289
+ // If we're re-rending the page after a validation error, make sure that we show the data the user entered instead of just reverting
290
+ // to what is stored in the database.
291
+ if ( self::$process_admin_options_validation_error ) {
292
+ // TODO: We should probably encrypt the cert in some manner instead of just Base64-encoding it
293
+ if ( ! empty( $_POST['woo_pp_enabled'] ) && 'true' == $_POST['woo_pp_enabled'] ) {
294
+ $enabled = true;
295
+ }
296
+
297
+ if ( ! empty( $_POST['woo_pp_ppc_enabled'] ) && 'true' == $_POST['woo_pp_ppc_enabled'] ) {
298
+ $ppc_enabled = true;
299
+ }
300
+
301
+ if ( ! empty( $_POST['woo_pp_icc_enabled'] ) && 'true' == $_POST['woo_pp_icc_enabled'] ) {
302
+ $icc_enabled = true;
303
+ }
304
+
305
+ if ( ! empty( $_POST['woo_pp_logging_enabled'] ) && 'true' == $_POST['woo_pp_logging_enabled'] ) {
306
+ $logging_enabled = true;
307
+ }
308
+
309
+ if ( array_key_exists( 'woo_pp_environment', $_POST ) ) {
310
+ if ( 'live' == $_POST['woo_pp_environment'] || 'sandbox' == $_POST['woo_pp_environment'] ) {
311
+ $environment = $_POST['woo_pp_environment'];
312
+ }
313
+ }
314
+
315
+ // Grab the live credentials.
316
+ $live_api_username = $_POST['woo_pp_live_api_username'];
317
+ $live_api_pass = $_POST['woo_pp_live_api_password'];
318
+ $live_subject = $_POST['woo_pp_live_subject' ];
319
+
320
+ if ( array_key_exists( 'woo_pp_live_api_style', $_POST ) ) {
321
+ if ( 'signature' == $_POST['woo_pp_live_api_style'] || 'certificate' == $_POST['woo_pp_live_api_style'] ) {
322
+ $live_style = $_POST['woo_pp_live_api_style'];
323
+ }
324
+ }
325
+
326
+ if ( 'signature' == $live_style ) {
327
+ $live_api_sig = $_POST['woo_pp_live_api_signature'];
328
+ } else {
329
+ if ( array_key_exists( 'woo_pp_live_api_certificate', $_FILES ) && array_key_exists( 'tmp_name', $_FILES['woo_pp_live_api_certificate'] )
330
+ && array_key_exists( 'size', $_FILES['woo_pp_live_api_certificate'] ) && $_FILES['woo_pp_live_api_certificate']['size'] ) {
331
+ $live_cert = file_get_contents( $_FILES['woo_pp_live_api_certificate']['tmp_name'] );
332
+ $live_cert_info = $this->get_certificate_info( $live_cert );
333
+ } elseif ( array_key_exists( 'woo_pp_live_api_cert_string', $_POST ) ) {
334
+ $live_cert = base64_decode( $_POST['woo_pp_live_api_cert_string'] );
335
+ $live_cert_info = $this->get_certificate_info( $live_cert );
336
+ }
337
+ }
338
+
339
+ // Grab the sandbox credentials.
340
+ $sb_api_username = $_POST['woo_pp_sandbox_api_username'];
341
+ $sb_api_pass = $_POST['woo_pp_sandbox_api_password'];
342
+ $sb_subject = $_POST['woo_pp_sandbox_subject' ];
343
+
344
+ if ( array_key_exists( 'woo_pp_sandbox_api_style', $_POST ) ) {
345
+ if ( 'signature' == $_POST['woo_pp_sandbox_api_style'] || 'certificate' == $_POST['woo_pp_sandbox_api_style'] ) {
346
+ $sb_style = $_POST['woo_pp_sandbox_api_style'];
347
+ }
348
+ }
349
+
350
+ if ( 'signature' == $sb_style ) {
351
+ $sb_api_sig = $_POST['woo_pp_sandbox_api_signature'];
352
+ } else {
353
+ if ( array_key_exists( 'woo_pp_sandbox_api_certificate', $_FILES ) && array_key_exists( 'tmp_name', $_FILES['woo_pp_sandbox_api_certificate'] )
354
+ && array_key_exists( 'size', $_FILES['woo_pp_sandbox_api_certificate'] ) && $_FILES['woo_pp_sandbox_api_certificate']['size'] ) {
355
+ $sb_cert = file_get_contents( $_FILES['woo_pp_sandbox_api_certificate']['tmp_name'] );
356
+ $sb_cert_info = $this->get_certificate_info( $sb_cert );
357
+ } elseif ( array_key_exists( 'woo_pp_sandbox_api_cert_string', $_POST ) ) {
358
+ $sb_cert = base64_decode( $_POST['woo_pp_sandbox_api_cert_string'] );
359
+ $sb_cert_info = $this->get_certificate_info( $sb_cert );
360
+ }
361
+ }
362
+
363
+ if ( ! empty( $_POST['woo_pp_allow_guest_checkout'] ) && 'true' == $_POST['woo_pp_allow_guest_checkout'] ) {
364
+ $allow_guest_checkout = true;
365
+ } else {
366
+ $allow_guest_checkout = false;
367
+ }
368
+
369
+ if ( ! empty( $_POST['woo_pp_block_echecks'] ) && 'true' == $_POST['woo_pp_block_echecks'] ) {
370
+ $block_echecks = true;
371
+ } else {
372
+ $block_echecks = false;
373
+ }
374
+
375
+ if ( ! empty( $_POST['woo_pp_req_billing_address'] ) && 'true' == $_POST['woo_pp_req_billing_address'] ) {
376
+ $require_billing_address = true;
377
+ } else {
378
+ $require_billing_address = false;
379
+ }
380
+
381
+ $button_size = $_POST['woo_pp_button_size' ];
382
+ $mark_size = $_POST['woo_pp_mark_size' ];
383
+ $logo_image_url = $_POST['woo_pp_logo_image_url' ];
384
+ $payment_action = $_POST['woo_pp_payment_action' ];
385
+ $zero_subtotal_behavior = $_POST['woo_pp_zero_subtotal_behavior' ];
386
+ $subtotal_mismatch_behavior = $_POST['woo_pp_subtotal_mismatch_behavior'];
387
+ } else {
388
+
389
+ if ( is_object( $settings->liveApiCredentials ) && is_a( $settings->liveApiCredentials, 'WC_Gateway_PPEC_Client_Credential' ) ) {
390
+ $live_api_username = $settings->liveApiCredentials->get_username();
391
+ $live_subject = $settings->liveApiCredentials->get_subject();
392
+ $live_api_pass = $settings->liveApiCredentials->get_password();
393
+
394
+ if ( is_a( $settings->liveApiCredentials, 'WC_Gateway_PPEC_Client_Credential_Signature' ) && $settings->liveApiCredentials->get_signature() ) {
395
+ $live_api_sig = $settings->liveApiCredentials->get_signature();
396
+ }
397
+ if ( is_a( $settings->liveApiCredentials, 'WC_Gateway_PPEC_Client_Credential_Certificate' ) && $settings->liveApiCredentials->get_certificate() ) {
398
+ $live_cert_info = $this->get_certificate_info( $settings->liveApiCredentials->get_certificate() );
399
+ $live_style = 'certificate';
400
+ }
401
+ }
402
+
403
+ if ( is_object( $settings->sandboxApiCredentials ) && is_a( $settings->sandboxApiCredentials, 'WC_Gateway_PPEC_Client_Credential' ) ) {
404
+ $sb_api_username = $settings->sandboxApiCredentials->get_username();
405
+ $sb_subject = $settings->sandboxApiCredentials->get_subject();
406
+ $sb_api_pass = $settings->sandboxApiCredentials->get_password();
407
+
408
+ if ( is_a( $settings->sandboxApiCredentials, 'WC_Gateway_PPEC_Client_Credential_Signature' ) && $settings->sandboxApiCredentials->get_signature() ) {
409
+ $sb_api_sig = $settings->sandboxApiCredentials->get_signature();
410
+ }
411
+ if ( is_a ( $settings->sandboxApiCredentials, 'WC_Gateway_PPEC_Client_Credential_Certificate' ) && $settings->sandboxApiCredentials->get_certificate() ) {
412
+ $sb_style = 'certificate';
413
+ $sb_cert_info = $this->get_certificate_info( $settings->sandboxApiCredentials->get_certificate() );
414
+ }
415
+ }
416
+
417
+ $enabled = $settings->enabled;
418
+ $logging_enabled = $settings->logging_enabled;
419
+ $ppc_enabled = $settings->ppcEnabled;
420
+ $icc_enabled = $settings->enableInContextCheckout;
421
+ $environment = $settings->environment;
422
+ $button_size = $settings->buttonSize;
423
+ $mark_size = $settings->markSize;
424
+ $logo_image_url = $settings->logoImageUrl;
425
+ $payment_action = $settings->paymentAction;
426
+ $allow_guest_checkout = $settings->allowGuestCheckout;
427
+ $block_echecks = $settings->blockEChecks;
428
+ $require_billing_address = $settings->requireBillingAddress;
429
+ $zero_subtotal_behavior = $settings->zeroSubtotalBehavior;
430
+ $subtotal_mismatch_behavior = $settings->subtotalMismatchBehavior;
431
+ }
432
+
433
+ $help_image_url = plugins_url( 'assets/images/help.png', 'woocommerce/.' );
434
+ $ips_url = admin_url( 'admin.php?page=wc-settings&tab=checkout&section=wc_gateway_ppec_with_paypal&ips-signup=true' );
435
+ add_thickbox();
436
+
437
+ require_once( wc_gateway_ppec()->includes_path . 'views/admin-settings.php' );
438
+ }
439
+
440
+ /**
441
+ * This function fills in the $credentials variable with the credentials
442
+ * the user filled in on the page, and returns true or false to indicate
443
+ * a success or error, respectively.
444
+ *
445
+ * Why not just return the credentials or false on failure? Because the user
446
+ * might not fill in the credentials at all, which isn't an error. This way
447
+ * allows us to do it without returning an error because the user didn't fill
448
+ * in the credentials.
449
+ *
450
+ * @param string $environment Environment. Either 'live' or 'sandbox'
451
+ *
452
+ * @return WC_Gateway_PPEC_Client_Credential Credential object
453
+ */
454
+ private function validate_credentials( $environment ) {
455
+ $settings = wc_gateway_ppec()->settings->loadSettings();
456
+ if ( 'sandbox' == $environment ) {
457
+ $creds = $settings->sandboxApiCredentials;
458
+ } else {
459
+ $creds = $settings->liveApiCredentials;
460
+ }
461
+
462
+ $api_user = trim( $_POST[ 'woo_pp_' . $environment . '_api_username' ] );
463
+ $api_pass = trim( $_POST[ 'woo_pp_' . $environment . '_api_password' ] );
464
+ $api_style = trim( $_POST[ 'woo_pp_' . $environment . '_api_style' ] );
465
+
466
+ $subject = trim( $_POST[ 'woo_pp_' . $environment . '_subject' ] );
467
+ if ( empty( $subject ) ) {
468
+ $subject = false;
469
+ }
470
+
471
+ $credential = false;
472
+ if ( 'signature' === $api_style ) {
473
+ $api_sig = trim( $_POST[ 'woo_pp_' . $environment . '_api_signature' ] );
474
+ } elseif ( 'certificate' === $api_style ) {
475
+ if ( array_key_exists( 'woo_pp_' . $environment . '_api_certificate', $_FILES )
476
+ && array_key_exists( 'tmp_name', $_FILES[ 'woo_pp_' . $environment . '_api_certificate' ] )
477
+ && array_key_exists( 'size', $_FILES[ 'woo_pp_' . $environment . '_api_certificate' ] )
478
+ && $_FILES[ 'woo_pp_' . $environment . '_api_certificate' ]['size'] ) {
479
+ $api_cert = file_get_contents( $_FILES[ 'woo_pp_' . $environment . '_api_certificate' ]['tmp_name'] );
480
+ $_POST[ 'woo_pp_' . $environment . '_api_cert_string' ] = base64_encode( $api_cert );
481
+ unlink( $_FILES[ 'woo_pp_' . $environment . '_api_certificate' ]['tmp_name'] );
482
+ unset( $_FILES[ 'woo_pp_' . $environment . '_api_certificate' ] );
483
+ } elseif ( array_key_exists( 'woo_pp_' . $environment . '_api_cert_string', $_POST ) && ! empty( $_POST[ 'woo_pp_' . $environment . '_api_cert_string' ] ) ) {
484
+ $api_cert = base64_decode( $_POST[ 'woo_pp_' . $environment . '_api_cert_string' ] );
485
+ }
486
+ } else {
487
+ WC_Admin_Settings::add_error( sprintf( __( 'Error: You selected an invalid credential type for your %s API credentials.', 'woocommerce-gateway-paypal-express-checkout' ), __( $environment, 'woocommerce-gateway-paypal-express-checkout' ) ) );
488
+ return false;
489
+ }
490
+
491
+ if ( ! empty( $api_user ) ) {
492
+ if ( empty( $api_pass ) ) {
493
+ WC_Admin_Settings::add_error( sprintf( __( 'Error: You must enter a %s API password.' ), __( $environment, 'woocommerce-gateway-paypal-express-checkout' ) ) );
494
+ return false;
495
+ }
496
+
497
+ if ( 'signature' === $api_style ) {
498
+ if ( ! empty( $api_sig ) ) {
499
+
500
+ // Ok, test them out.
501
+ $api_credentials = new WC_Gateway_PPEC_Client_Credential_Signature( $api_user, $api_pass, $api_sig, $subject );
502
+ try {
503
+ $payer_id = wc_gateway_ppec()->client->test_api_credentials( $api_credentials, $environment );
504
+ if ( ! $payer_id ) {
505
+ 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' ), __( $environment, 'woocommerce-gateway-paypal-express-checkout' ) ) );
506
+ return false;
507
+ }
508
+ $api_credentials->set_payer_id( $payer_id );
509
+ } catch( PayPal_API_Exception $ex ) {
510
+ $this->display_warning( 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' ), __( $environment, 'woocommerce-gateway-paypal-express-checkout' ) ) );
511
+ }
512
+
513
+ $credential = $api_credentials;
514
+
515
+ } else {
516
+ WC_Admin_Settings::add_error( sprintf( __( 'Error: You must provide a %s API signature.', 'woocommerce-gateway-paypal-express-checkout' ), __( $environment, 'woocommerce-gateway-paypal-express-checkout' ) ) );
517
+ return false;
518
+ }
519
+
520
+ } else {
521
+ if ( ! empty( $api_cert ) ) {
522
+ $cert = openssl_x509_read( $api_cert );
523
+ if ( false === $cert ) {
524
+ WC_Admin_Settings::add_error( sprintf( __( 'Error: The %s API certificate is not valid.', 'woocommerce-gateway-paypal-express-checkout' ), __( $environment, 'woocommerce-gateway-paypal-express-checkout' ) ) );
525
+ self::$process_admin_options_validation_error = true;
526
+ return false;
527
+ }
528
+
529
+ $cert_info = openssl_x509_parse( $cert );
530
+ $valid_until = $cert_info['validTo_time_t'];
531
+ if ( $valid_until < time() ) {
532
+ WC_Admin_Settings::add_error( sprintf( __( 'Error: The %s API certificate has expired.', 'woocommerce-gateway-paypal-express-checkout' ), __( $environment, 'woocommerce-gateway-paypal-express-checkout' ) ) );
533
+ return false;
534
+ }
535
+
536
+ if ( $cert_info['subject']['CN'] != $api_user ) {
537
+ WC_Admin_Settings::add_error( __( 'Error: The API username does not match the name in the API certificate. Make sure that you have the correct API certificate.', 'woocommerce-gateway-paypal-express-checkout' ) );
538
+ return false;
539
+ }
540
+ } else {
541
+ // If we already have a cert on file, don't require one.
542
+ if ( $creds && is_a( $creds, 'WC_Gateway_PPEC_Client_Credential_Certificate' ) ) {
543
+ if ( ! $creds->get_certificate() ) {
544
+ WC_Admin_Settings::add_error( sprintf( __( 'Error: You must provide a %s API certificate.', 'woocommerce-gateway-paypal-express-checkout' ), __( $environment, 'woocommerce-gateway-paypal-express-checkout' ) ) );
545
+ return false;
546
+ }
547
+ $api_cert = $creds->get_certificate();
548
+ } else {
549
+ WC_Admin_Settings::add_error( sprintf( __( 'Error: You must provide a %s API certificate.', 'woocommerce-gateway-paypal-express-checkout' ), __( $environment, 'woocommerce-gateway-paypal-express-checkout' ) ) );
550
+ return false;
551
+ }
552
+ }
553
+
554
+ $api_credentials = new WC_Gateway_PPEC_Client_Credential_Certificate( $api_user, $api_pass, $api_cert, $subject );
555
+ try {
556
+ $payer_id = wc_gateway_ppec()->client->test_api_credentials( $api_credentials, $environment );
557
+ if ( ! $payer_id ) {
558
+ 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' ), __( $environment, 'woocommerce-gateway-paypal-express-checkout' ) ) );
559
+ return false;
560
+ }
561
+ $api_credentials->set_payer_id( $payer_id );
562
+ } catch( PayPal_API_Exception $ex ) {
563
+ $this->display_warning( 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' ), __( $environment, 'woocommerce-gateway-paypal-express-checkout' ) ) );
564
+ }
565
+
566
+ $credential = $api_credentials;
567
+ }
568
+ }
569
+
570
+ return $credential;
571
+ }
572
+
573
+ public function process_admin_options() {
574
+ // For some reason, this function is being fired twice, so this bit of code is here to prevent that from happening.
575
+ if ( self::$process_admin_options_already_run ) {
576
+ return false;
577
+ }
578
+
579
+ self::$process_admin_options_already_run = true;
580
+
581
+ $settings = wc_gateway_ppec()->settings->loadSettings();
582
+
583
+ $environment = $_POST['woo_pp_environment'];
584
+
585
+ if ( ! in_array( $environment, array( 'live', 'sandbox' ) ) ) {
586
+ WC_Admin_Settings::add_error( __( 'Error: The environment you selected is not valid.', 'woocommerce-gateway-paypal-express-checkout' ) );
587
+ return false;
588
+ }
589
+
590
+ $credential = $this->validate_credentials( $environment );
591
+ if ( ! is_a( $credential, 'WC_Gateway_PPEC_Client_Credential' ) ) {
592
+ if ( array_key_exists( 'woo_pp_sandbox_api_certificate', $_FILES )
593
+ && array_key_exists( 'tmp_name', $_FILES['woo_pp_sandbox_api_certificate'] )
594
+ && array_key_exists( 'size', $_FILES['woo_pp_sandbox_api_certificate'] )
595
+ && $_FILES['woo_pp_sandbox_api_certificate']['size'] ) {
596
+
597
+ $_POST['woo_pp_sandbox_api_cert_string'] = base64_encode( file_get_contents( $_FILES['woo_pp_sandbox_api_certificate']['tmp_name'] ) );
598
+ unlink( $_FILES['woo_pp_sandbox_api_certificate']['tmp_name'] );
599
+ unset( $_FILES['woo_pp_sandbox_api_certificate'] );
600
+
601
+ }
602
+
603
+ WC_Admin_Settings::add_error( __( 'Error: You must supply a valid set of credentials before enabling the plugin.', 'woocommerce-gateway-paypal-express-checkout' ) );
604
+ self::$process_admin_options_validation_error = true;
605
+ return false;
606
+ }
607
+
608
+ // Validate the URL.
609
+ $logo_image_url = trim( $_POST['woo_pp_logo_image_url'] );
610
+ 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 ) ) {
611
+ WC_Admin_Settings::add_error( __( 'Error: The logo image URL you provided is not valid.', 'woocommerce-gateway-paypal-express-checkout' ) );
612
+ self::$process_admin_options_validation_error = true;
613
+ return false;
614
+ }
615
+
616
+ if ( empty( $logo_image_url ) ) {
617
+ $logo_image_url = false;
618
+ }
619
+
620
+ $enabled = false;
621
+ $logging_enabled = false;
622
+ $ppc_enabled = false;
623
+ $icc_enabled = false;
624
+ $allow_guest_checkout = false;
625
+ $block_echecks = false;
626
+ $require_billing_address = false;
627
+ $live_account_enabled_for_billing_address = false;
628
+ $sb_account_enabled_for_billing_address = false;
629
+
630
+ if ( isset( $_POST['woo_pp_enabled'] ) && 'true' == $_POST['woo_pp_enabled'] ) {
631
+ $enabled = true;
632
+ }
633
+
634
+ if ( isset( $_POST['woo_pp_ppc_enabled'] ) && 'true' == $_POST['woo_pp_ppc_enabled'] ) {
635
+ $ppc_enabled = true;
636
+ }
637
+
638
+ if ( isset( $_POST['woo_pp_allow_guest_checkout'] ) && 'true' == $_POST['woo_pp_allow_guest_checkout'] ) {
639
+ $allow_guest_checkout = true;
640
+ }
641
+
642
+ if ( isset( $_POST['woo_pp_block_echecks'] ) && 'true' == $_POST['woo_pp_block_echecks'] ) {
643
+ $block_echecks = true;
644
+ }
645
+
646
+ if ( isset( $_POST['woo_pp_req_billing_address'] ) && 'true' == $_POST['woo_pp_req_billing_address'] ) {
647
+ $require_billing_address = true;
648
+ }
649
+
650
+ if ( isset( $_POST['woo_pp_icc_enabled'] ) && 'true' == $_POST['woo_pp_icc_enabled'] ) {
651
+ $icc_enabled = true;
652
+ }
653
+
654
+ if ( isset( $_POST['woo_pp_logging_enabled'] ) && 'true' == $_POST['woo_pp_logging_enabled'] ) {
655
+ $logging_enabled = true;
656
+ }
657
+
658
+ $is_account_enabled_for_billing_address = false;
659
+ try {
660
+ $is_account_enabled_for_billing_address = wc_gateway_ppec()->client->test_for_billing_address_enabled( $credential, $environment );
661
+ } catch( PayPal_API_Exception $ex ) {
662
+ $this->display_warning( __( 'An error occurred while trying to determine which features are enabled on your live account. You may not have access to all of the settings allowed by your PayPal account. Please click "Save Changes" to try again.', 'woocommerce-gateway-paypal-express-checkout' ) );
663
+ }
664
+
665
+ switch ( $environment ) {
666
+ case 'live':
667
+ $live_account_enabled_for_billing_address = $is_account_enabled_for_billing_address;
668
+ break;
669
+ case 'sandbox':
670
+ $sb_account_enabled_for_billing_address = $is_account_enabled_for_billing_address;
671
+ break;
672
+ }
673
+
674
+ // WC_Gateway_PPEC_Settings already has sanitizers for these values, so we don't need to check them.
675
+ $button_size = $_POST['woo_pp_button_size'];
676
+ $mark_size = $_POST['woo_pp_mark_size'];
677
+ $payment_action = $_POST['woo_pp_payment_action'];
678
+ $zero_subtotal_behavior = $_POST['woo_pp_zero_subtotal_behavior'];
679
+ $subtotal_mismatch_behavior = $_POST['woo_pp_subtotal_mismatch_behavior'];
680
+
681
+ // Go ahead and save everything.
682
+ $settings->enabled = $enabled;
683
+ $settings->logging_enabled = $logging_enabled;
684
+ $settings->ppcEnabled = $ppc_enabled;
685
+ $settings->enableInContextCheckout = $icc_enabled;
686
+ $settings->buttonSize = $button_size;
687
+ $settings->markSize = $mark_size;
688
+ $settings->environment = $environment;
689
+ $settings->liveApiCredentials = 'live' === $environment ? $credential : false;
690
+ $settings->sandboxApiCredentials = 'sandbox' === $environment ? $credential : false;
691
+ $settings->allowGuestCheckout = $allow_guest_checkout;
692
+ $settings->blockEChecks = $block_echecks;
693
+ $settings->requireBillingAddress = $require_billing_address;
694
+ $settings->paymentAction = $payment_action;
695
+ $settings->zeroSubtotalBehavior = $zero_subtotal_behavior;
696
+ $settings->subtotalMismatchBehavior = $subtotal_mismatch_behavior;
697
+ $settings->liveAccountIsEnabledForBillingAddress = $live_account_enabled_for_billing_address;
698
+ $settings->sbAccountIsEnabledForBillingAddress = $sb_account_enabled_for_billing_address;
699
+
700
+ $settings->saveSettings();
701
+ }
702
+
703
+ public function display_warning( $message ) {
704
+ echo '<div class="error"><p>Warning: ' . $message . '</p></div>';
705
+ }
706
+
707
+ public function process_refund( $order_id, $amount = null, $reason = '' ) {
708
+
709
+ $settings = wc_gateway_ppec()->settings->loadSettings();
710
+
711
+ $order = wc_get_order( $order_id );
712
+
713
+ if ( 0 == $amount || null == $amount ) {
714
+ return new WP_Error( 'paypal_refund_error', __( 'Refund Error: You need to specify a refund amount.', 'woocommerce-gateway-paypal-express-checkout' ) );
715
+ }
716
+
717
+ // load up refundable_txns from Post Meta
718
+ // loop through each transaction to compile list of txns that are able to be refunded
719
+ // process refunds against each txn in the list until full amount of refund is reached
720
+ // first loop through, try to find a transaction that equals the refund amount being requested
721
+ $txnData = get_post_meta( $order_id, '_woo_pp_txnData', true );
722
+ $didRefund = false;
723
+
724
+ foreach ( $txnData['refundable_txns'] as $key => $value ) {
725
+ $refundableAmount = $value['amount'] - $value['refunded_amount'];
726
+
727
+
728
+ if ( $amount == $refundableAmount ) {
729
+ if ( 0 == $value['refunded_amount'] ) {
730
+ $refundType = 'Full';
731
+ } else {
732
+ $refundType = 'Partial';
733
+ }
734
+
735
+ try {
736
+ $refundTxnID = WC_Gateway_PPEC_Refund::refund_order( $order, $amount, $refundType, $reason, $order->get_order_currency() );
737
+ $txnData['refundable_txns'][ $key ]['refunded_amount'] += $amount;
738
+ $order->add_order_note( sprintf( $refundTxnID, __( 'PayPal refund completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $refundTxnID ) );
739
+ update_post_meta( $order_id, '_woo_pp_txnData', $txnData );
740
+
741
+ return true;
742
+
743
+ } catch( PayPal_API_Exception $e ) {
744
+ foreach ( $e->errors as $error ) {
745
+ $final_output .= sprintf( __( 'Error: %1$s - %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $error->error_code, $error->long_message );
746
+ }
747
+
748
+ return new WP_Error( 'paypal_refund_error', $final_output );
749
+ }
750
+
751
+ }
752
+
753
+ }
754
+
755
+
756
+ foreach ( $txnData['refundable_txns'] as $key => $value ) {
757
+ $refundableAmount = $value['amount'] - $value['refunded_amount'];
758
+
759
+ if ( $amount < $refundableAmount ) {
760
+
761
+ try {
762
+ $refundTxnID = WC_Gateway_PPEC_Refund::refund_order( $order, $amount, 'Partial', $reason, $order->get_order_currency() );
763
+ $txnData['refundable_txns'][ $key ]['refunded_amount'] += $amount;
764
+ $order->add_order_note( sprintf( __( 'PayPal refund completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $refundTxnID ) );
765
+ update_post_meta( $order_id, '_woo_pp_txnData', $txnData );
766
+
767
+ return true;
768
+
769
+ } catch( PayPal_API_Exception $e ) {
770
+ foreach ( $e->errors as $error ) {
771
+ $final_output .= sprintf( __( 'Error: %1$s - %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $error->error_code, $error->long_message );
772
+ }
773
+
774
+ return new WP_Error( 'paypal_refund_error', $final_output );
775
+ }
776
+
777
+ }
778
+ }
779
+
780
+ $totalRefundableAmount = 0;
781
+ foreach ( $txnData['refundable_txns'] as $key => $value ) {
782
+ $refundableAmount = $value['amount'] - $value['refunded_amount'];
783
+ $totalRefundableAmount += $refundableAmount;
784
+ }
785
+
786
+ if ( $totalRefundableAmount < $amount ) {
787
+ if ( 0 == $totalRefundableAmount ) {
788
+ 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' ) );
789
+ } else {
790
+ 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 ) );
791
+ }
792
+ } else {
793
+ $total_to_refund = $amount;
794
+
795
+ foreach ( $txnData['refundable_txns'] as $key => $value ) {
796
+ $refundableAmount = $value['amount'] - $value['refunded_amount'];
797
+
798
+ if ( $refundableAmount > $total_to_refund ) {
799
+ $amount_to_refund = $total_to_refund;
800
+ } else {
801
+ $amount_to_refund = $refundableAmount;
802
+ }
803
+
804
+ if ( 0 < $amount_to_refund ) {
805
+ if ( 0 == $value['refunded_amount'] && $amount_to_refund == $value['amount'] ) {
806
+ $refundType = 'Full';
807
+ } else {
808
+ $refundType = 'Partial';
809
+ }
810
+
811
+ try {
812
+ $refundTxnID = WC_Gateway_PPEC_Refund::refund_order( $order, $amount_to_refund, $refundType, $reason, $order->get_order_currency() );
813
+ $total_to_refund -= $amount_to_refund;
814
+ $txnData['refundable_txns'][ $key ]['refunded_amount'] += $amount_to_refund;
815
+ $order->add_order_note( sprintf( __( 'PayPal refund completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $refundTxnID ) );
816
+ update_post_meta( $order_id, '_woo_pp_txnData', $txnData );
817
+
818
+ return true;
819
+ } catch( PayPal_API_Exception $e ) {
820
+ foreach ( $e->errors as $error ) {
821
+ $final_output .= sprintf( __( 'Error: %1$s - %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $error->error_code, $error->long_message );
822
+ }
823
+
824
+ return new WP_Error( 'paypal_refund_error', $final_output );
825
+ }
826
+ }
827
+ }
828
+ }
829
+ }
830
+
831
+ /**
832
+ * Get the transaction URL.
833
+ *
834
+ * @param WC_Order $order
835
+ * @return string
836
+ */
837
+ public function get_transaction_url( $order ) {
838
+ $settings = wc_gateway_ppec()->settings->loadSettings();
839
+ if ( 'sandbox' === $settings->environment ) {
840
+ $this->view_transaction_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_view-a-trans&id=%s';
841
+ } else {
842
+ $this->view_transaction_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=_view-a-trans&id=%s';
843
+ }
844
+
845
+ return parent::get_transaction_url( $order );
846
+ }
847
+
848
+ }
includes/class-wc-gateway-ppec-address.php ADDED
@@ -0,0 +1,646 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ class PayPal_Address {
8
+ protected $_name;
9
+ protected $_street1;
10
+ protected $_street2;
11
+ protected $_city;
12
+ protected $_state;
13
+ protected $_zip;
14
+ protected $_country;
15
+ protected $_phoneNumber;
16
+ protected $_addressOwner;
17
+ protected $_addressStatus;
18
+
19
+ const AddressStatusNone = 'none';
20
+ const AddressStatusConfirmed = 'Confirmed';
21
+ const AddressStatusUnconfirmed = 'Unconfirmed';
22
+
23
+ public function setName( $name ) {
24
+ $this->_name = $name;
25
+ }
26
+
27
+ public function getName() {
28
+ return $this->_name;
29
+ }
30
+
31
+ public function setStreet1( $street1 ) {
32
+ $this->_street1 = $street1;
33
+ }
34
+
35
+ public function getStreet1() {
36
+ return $this->_street1;
37
+ }
38
+
39
+ public function setStreet2( $street2 ) {
40
+ $this->_street2 = $street2;
41
+ }
42
+
43
+ public function getStreet2() {
44
+ return $this->_street2;
45
+ }
46
+
47
+ public function setCity( $city ) {
48
+ $this->_city = $city;
49
+ }
50
+
51
+ public function getCity() {
52
+ return $this->_city;
53
+ }
54
+
55
+ public function setState( $state ) {
56
+ $this->_state = $state;
57
+ }
58
+
59
+ public function getState() {
60
+ return $this->_state;
61
+ }
62
+
63
+ public function setZip( $zip ) {
64
+ $this->_zip = $zip;
65
+ }
66
+
67
+ public function getZip() {
68
+ return $this->_zip;
69
+ }
70
+
71
+ public function setCountry( $country ) {
72
+ $this->_country = $country;
73
+ }
74
+
75
+ public function getCountry() {
76
+ return $this->_country;
77
+ }
78
+
79
+ public function setPhoneNumber( $phoneNumber ) {
80
+ $this->_phoneNumber = $phoneNumber;
81
+ }
82
+
83
+ public function getPhoneNumber() {
84
+ return $this->_phoneNumber;
85
+ }
86
+
87
+ public function setAddressOwner( $addressOwner ) {
88
+ $this->_addressOwner = $addressOwner;
89
+ }
90
+
91
+ public function getAddressOwner() {
92
+ return $this->_addressOwner;
93
+ }
94
+
95
+ public function setAddressStatus( $addressStatus ) {
96
+ $this->_addressStatus = $addressStatus;
97
+ }
98
+
99
+ public function getAddressStatus() {
100
+ return $this->_addressStatus;
101
+ }
102
+
103
+ public function normalizeAddress() {
104
+ $this->normalizeCountry();
105
+ $this->normalizeState();
106
+ $this->normalizeZip();
107
+ }
108
+
109
+ public function normalizeCountry() {
110
+ // Since many shopping carts might use the full country name for their internal representation of
111
+ // the country, and since PayPal expects the ISO 3166-1 alpha-2 identifier, we'll attempt to
112
+ // translate from the various internal representations that might be used. Child classes can
113
+ // override this method to provide language-specific or cart-specific translations. Many Bothans
114
+ // died to bring us this information...
115
+
116
+ // This list was taken from https://developer.paypal.com/docs/classic/api/country_codes/.
117
+
118
+ $translation_table = array(
119
+ 'albania' => 'AL',
120
+ 'algeria' => 'DZ',
121
+ 'andorra' => 'AD',
122
+ 'angola' => 'AO',
123
+ 'anguilla' => 'AI',
124
+ 'antigua and barbuda' => 'AG',
125
+ 'argentina' => 'AR',
126
+ 'armenia' => 'AM',
127
+ 'aruba' => 'AW',
128
+ 'australia' => 'AU',
129
+ 'austria' => 'AT',
130
+ 'azerbaijan' => 'AZ',
131
+ 'bahamas' => 'BS',
132
+ 'bahrain' => 'BH',
133
+ 'barbados' => 'BB',
134
+ 'belgium' => 'BE',
135
+ 'belize' => 'BZ',
136
+ 'benin' => 'BJ',
137
+ 'bermuda' => 'BM',
138
+ 'bhutan' => 'BT',
139
+ 'bolivia' => 'BO',
140
+ 'bosnia-herzegovina' => 'BA',
141
+ 'botswana' => 'BW',
142
+ 'brazil' => 'BR',
143
+ 'brunei darussalam' => 'BN',
144
+ 'bulgaria' => 'BG',
145
+ 'burkina faso' => 'BF',
146
+ 'burundi' => 'BI',
147
+ 'cambodia' => 'KH',
148
+ 'canada' => 'CA',
149
+ 'cape verde' => 'CV',
150
+ 'cayman islands' => 'KY',
151
+ 'chad' => 'TD',
152
+ 'chile' => 'CL',
153
+ 'china' => 'CN',
154
+ 'colombia' => 'CO',
155
+ 'comoros' => 'KM',
156
+ 'democratic republic of congo' => 'CD',
157
+ 'congo' => 'CG',
158
+ 'cook islands' => 'CK',
159
+ 'costa rica' => 'CR',
160
+ 'croatia' => 'HR',
161
+ 'cyprus' => 'CY',
162
+ 'czech republic' => 'CZ',
163
+ 'denmark' => 'DK',
164
+ 'djibouti' => 'DJ',
165
+ 'dominica' => 'DM',
166
+ 'dominican republic' => 'DO',
167
+ 'ecuador' => 'EC',
168
+ 'egypt' => 'EG',
169
+ 'el salvador' => 'SV',
170
+ 'eriteria' => 'ER',
171
+ 'estonia' => 'EE',
172
+ 'ethiopia' => 'ET',
173
+ 'falkland islands (malvinas)' => 'FK',
174
+ // This derivation doesn't show up in the list, but seems obvious
175
+ 'falkland islands' => 'FK',
176
+ 'fiji' => 'FJ',
177
+ 'finland' => 'FI',
178
+ 'france' => 'FR',
179
+ 'french guiana' => 'GF',
180
+ 'french polynesia' => 'PF',
181
+ 'gabon' => 'GA',
182
+ 'gambia' => 'GM',
183
+ 'georgia' => 'GE',
184
+ 'germany' => 'DE',
185
+ 'gibraltar' => 'GI',
186
+ 'greece' => 'GR',
187
+ 'greenland' => 'GL',
188
+ 'grenada' => 'GD',
189
+ 'guadeloupe' => 'GP',
190
+ 'guam' => 'GU',
191
+ 'guatemala' => 'GT',
192
+ 'guinea' => 'GN',
193
+ 'guinea bissau' => 'GW',
194
+ 'guyana' => 'GY',
195
+ 'holy see (vatican city state)' => 'VA',
196
+ // This derivation doesn't show up in the list, but seems obvious
197
+ 'holy see' => 'VA',
198
+ 'honduras' => 'HN',
199
+ 'hong kong' => 'HK',
200
+ 'hungary' => 'HU',
201
+ 'iceland' => 'IS',
202
+ 'india' => 'IN',
203
+ 'indonesia' => 'ID',
204
+ 'ireland' => 'IE',
205
+ 'israel' => 'IL',
206
+ 'italy' => 'IT',
207
+ 'jamaica' => 'JM',
208
+ 'japan' => 'JP',
209
+ 'jordan' => 'JO',
210
+ 'kazakhstan' => 'KZ',
211
+ 'kenya' => 'KE',
212
+ 'kiribati' => 'KI',
213
+ 'korea, republic of' => 'KR',
214
+ // This derivation doesn't show up in the list, but seems obvious
215
+ 'republic of korea' => 'KR',
216
+ 'kuwait' => 'KW',
217
+ 'kyrgyzstan' => 'KG',
218
+ 'laos' => 'LA',
219
+ 'latvia' => 'LV',
220
+ 'lesotho' => 'LS',
221
+ 'liechtenstein' => 'LI',
222
+ 'lithuania' => 'LT',
223
+ 'luxembourg' => 'LU',
224
+ 'madagascar' => 'MG',
225
+ 'malawi' => 'MW',
226
+ 'malaysia' => 'MY',
227
+ 'maldives' => 'MV',
228
+ 'mali' => 'ML',
229
+ 'malta' => 'MT',
230
+ 'marshall islands' => 'MH',
231
+ 'martinique' => 'MQ',
232
+ 'mauritania' => 'MR',
233
+ 'mauritius' => 'MU',
234
+ 'mayotte' => 'YT',
235
+ 'mexico' => 'MX',
236
+ 'micronesia, federated states of' => 'FM',
237
+ // The next two derivations don't show up in the list, but seem obvious
238
+ 'federated states of micronesia' => 'FM',
239
+ 'micronesia' => 'FM',
240
+ 'mongolia' => 'MN',
241
+ 'montserrat' => 'MS',
242
+ 'morocco' => 'MA',
243
+ 'mozambique' => 'MZ',
244
+ 'namibia' => 'NA',
245
+ 'nauru' => 'NR',
246
+ 'nepal' => 'NP',
247
+ 'netherlands' => 'NL',
248
+ 'netherlands antilles' => 'AN',
249
+ 'new caledonia' => 'NC',
250
+ 'new zealand' => 'NZ',
251
+ 'nicaragua' => 'NI',
252
+ 'niger' => 'NE',
253
+ 'niue' => 'NU',
254
+ 'norfolk island' => 'NF',
255
+ 'norway' => 'NO',
256
+ 'oman' => 'OM',
257
+ 'palau' => 'PW',
258
+ 'panama' => 'PA',
259
+ 'papau new guinea' => 'PG',
260
+ 'peru' => 'PE',
261
+ 'philippines' => 'PH',
262
+ 'pitcairn' => 'PN',
263
+ 'poland' => 'PL',
264
+ 'portugal' => 'PT',
265
+ 'qatar' => 'QA',
266
+ 'reunion' => 'RE',
267
+ 'romania' => 'RO',
268
+ 'russian federation' => 'RU',
269
+ // This derivation doesn't show up in the list, but seems obvious
270
+ 'russia' => 'RU',
271
+ 'rwanda' => 'RW',
272
+ 'saint helena' => 'SH',
273
+ 'saint kitts and nevis' => 'KN',
274
+ 'saint lucia' => 'LC',
275
+ 'saint pierre and miquelon' => 'PM',
276
+ 'saint vincent and the grenadines' => 'VC',
277
+ 'samoa' => 'WS',
278
+ 'san marino' => 'SM',
279
+ 'sao tome and principe' => 'ST',
280
+ 'saudi arabia' => 'SA',
281
+ 'senegal' => 'SN',
282
+ 'serbia' => 'RS',
283
+ 'seychelles' => 'SC',
284
+ 'sierra leone' => 'SL',
285
+ 'singapore' => 'SG',
286
+ 'slovakia' => 'SK',
287
+ 'slovenia' => 'SI',
288
+ 'solomon islands' => 'SB',
289
+ 'somalia' => 'SO',
290
+ 'south africa' => 'ZA',
291
+ 'south korea' => 'KR',
292
+ 'spain' => 'ES',
293
+ 'sri lanka' => 'LK',
294
+ 'suriname' => 'SR',
295
+ 'svalbard and jan mayen' => 'SJ',
296
+ 'swaziland' => 'SZ',
297
+ 'sweden' => 'SE',
298
+ 'switzerland' => 'CH',
299
+ 'taiwan, province of china' => 'TW',
300
+ // This derivation doesn't show up in the list, but seems obvious
301
+ 'taiwan' => 'TW',
302
+ 'tajikistan' => 'TJ',
303
+ 'tanzania, united republic of' => 'TZ',
304
+ // The next two derivations don't show up in the list, but seem obvious
305
+ 'united republic of tanzania' => 'TZ',
306
+ 'tanzania' => 'TZ',
307
+ 'thailand' => 'TH',
308
+ 'togo' => 'TG',
309
+ 'tonga' => 'TO',
310
+ 'trinidad and tobago' => 'TT',
311
+ 'tunisia' => 'TN',
312
+ 'turkey' => 'TR',
313
+ 'turkmenistan' => 'TM',
314
+ 'turks and caicos islands' => 'TC',
315
+ // This derivation doesn't show up in the list, but seems obvious
316
+ 'turks and caicos' => 'TC',
317
+ 'tuvalu' => 'TV',
318
+ 'uganda' => 'UG',
319
+ 'ukraine' => 'UA',
320
+ 'united arab emirates' => 'AE',
321
+ 'united kingdom' => 'GB',
322
+ 'united states' => 'US',
323
+ // This derivation doesn't show up in the list, but seems obvious
324
+ 'united states of america' => 'US',
325
+ 'uruguay' => 'UY',
326
+ 'vanuatu' => 'VU',
327
+ 'venezuela' => 'VE',
328
+ 'vietnam' => 'VN',
329
+ 'virgin islands, british' => 'VG',
330
+ // This derivation doesn't show up in the list, but seems obvious
331
+ 'british virgin islands' => 'VG',
332
+ 'wallis and futana' => 'WF',
333
+ 'yemen' => 'YE',
334
+ 'zambia' => 'ZM',
335
+ // This one is here because some carts will make the mistake of using 'uk' instead of 'gb'.
336
+ 'uk' => 'GB'
337
+ );
338
+
339
+ // And now, the actual translation is as simple as...
340
+ if ( array_key_exists( strtolower( trim( $this->_country ) ), $translation_table ) ) {
341
+ $this->_country = $translation_table[ strtolower( trim( $this->_country ) ) ];
342
+ }
343
+ }
344
+
345
+ public function normalizeState() {
346
+ // Since some shopping carts might use the full state name for their internal representation of the
347
+ // state, and since PayPal expects the 2-character state/province abbreviation (for US/Canada
348
+ // addresses, at least), we'll attempt to translate from the various internal representations
349
+ // that might be used. Child classes can override this method to provide additional
350
+ // language-specific or cart-specific translations.
351
+
352
+ // This call should be made AFTER normalizeCountry() has been called, so that the country can be
353
+ // properly detected.
354
+
355
+ // PayPal's documentation also defines state codes for Italy and the Netherlands, so we'll provide
356
+ // translations for those as well.
357
+
358
+ $translation_table = array();
359
+
360
+ if ( 'US' == $this->_countryCode ) {
361
+ $translation_table = array(
362
+ 'alabama' => 'AL',
363
+ 'alaska' => 'AK',
364
+ 'arizona' => 'AZ',
365
+ 'arkansas' => 'AR',
366
+ 'california' => 'CA',
367
+ 'colorado' => 'CO',
368
+ 'connecticut' => 'CT',
369
+ 'deleware' => 'DE',
370
+ 'district of columbia (washington, d.c.)' => 'DC',
371
+ // The next several derivations don't show up in the list, but seem obvious
372
+ 'district of columbia' => 'DC',
373
+ 'washington, d.c.' => 'DC',
374
+ 'washington d.c.' => 'DC',
375
+ 'washington, dc' => 'DC',
376
+ 'washington dc' => 'DC',
377
+ 'washington, d. c.' => 'DC',
378
+ 'washington d. c.' => 'DC',
379
+ 'washington, d c' => 'DC',
380
+ 'washington d c' => 'DC',
381
+ 'florida' => 'FL',
382
+ 'georgia' => 'GA',
383
+ 'hawaii' => 'HI',
384
+ 'idaho' => 'ID',
385
+ 'illinois' => 'IL',
386
+ 'indiana' => 'IN',
387
+ 'iowa' => 'IA',
388
+ 'kansas' => 'KS',
389
+ 'kentucky' => 'KY',
390
+ 'louisiana' => 'LA',
391
+ 'maine' => 'ME',
392
+ 'maryland' => 'MD',
393
+ 'massachusetts' => 'MA',
394
+ 'michigan' => 'MI',
395
+ 'minnesota' => 'MN',
396
+ 'mississippi' => 'MS',
397
+ 'missouri' => 'MO',
398
+ 'montana' => 'MT',
399
+ 'nebraska' => 'NE',
400
+ 'nevada' => 'NV',
401
+ 'new hampshire' => 'NH',
402
+ 'new jersey' => 'NJ',
403
+ 'new mexico' => 'NM',
404
+ 'new jersey' => 'NJ',
405
+ 'new mexico' => 'NM',
406
+ 'new york' => 'NY',
407
+ 'north carolina' => 'NC',
408
+ 'north dakota' => 'ND',
409
+ 'ohio' => 'OH',
410
+ 'oklahoma' => 'OK',
411
+ 'oregon' => 'OR',
412
+ 'pennsylvania' => 'PA',
413
+ 'puerto rico' => 'PR',
414
+ 'rhode island' => 'RI',
415
+ 'south carolina' => 'SC',
416
+ 'south dakota' => 'SD',
417
+ 'tennessee' => 'TN',
418
+ 'texas' => 'TX',
419
+ 'utah' => 'UT',
420
+ 'vermont' => 'VT',
421
+ 'virginia' => 'VA',
422
+ 'washington' => 'WA',
423
+ 'west virginia' => 'WV',
424
+ 'wisconsin' => 'WI',
425
+ 'wyoming' => 'WY',
426
+ 'armed forces americas' => 'AA',
427
+ 'armed forces' => 'AE',
428
+ 'armed forces pacific' => 'AP',
429
+ 'american samoa' => 'AS',
430
+ 'guam' => 'GU',
431
+ 'northern mariana islands' => 'MP',
432
+ 'virgin islands' => 'VI',
433
+ // The next few derivations don't show up on the list, but seem obvious
434
+ 'us virgin islands' => 'VI',
435
+ 'u.s. virgin islands' => 'VI',
436
+ 'u s virgin islands' => 'VI',
437
+ 'u. s. virgin islands' => 'VI'
438
+ );
439
+ } elseif ( 'CA' == $this->_country ) {
440
+ $translation_table = array(
441
+ 'alberta' => 'AB',
442
+ 'british columbia' => 'BC',
443
+ 'manitoba' => 'MB',
444
+ 'new brunswick' => 'NB',
445
+ 'newfoundland' => 'NL',
446
+ 'northwest territories' => 'NT',
447
+ 'nova scotia' => 'NS',
448
+ 'nunavut' => 'NU',
449
+ 'ontario' => 'ON',
450
+ 'prince edward island' => 'PE',
451
+ 'quebec' => 'QC',
452
+ 'saskatchewan' => 'SK',
453
+ 'yukon' => 'YT',
454
+ // This derivation doesn't show up on the list, but seems obvious
455
+ 'yukon territory' => 'YT'
456
+ );
457
+ } elseif ( 'IT' == $this->_country ) {
458
+ $translation_table = array(
459
+ 'agrigento' => 'AG',
460
+ 'alessandria' => 'AL',
461
+ 'ancona' => 'AN',
462
+ 'aosta' => 'AO',
463
+ 'arezzo' => 'AR',
464
+ 'ascoli piceno' => 'AP',
465
+ 'asti' => 'AT',
466
+ 'avellino' => 'AV',
467
+ 'bari' => 'BA',
468
+ 'belluno' => 'BL',
469
+ 'benevento' => 'BN',
470
+ 'bergamo' => 'BG',
471
+ 'biella' => 'BI',
472
+ 'bologna' => 'BO',
473
+ 'bolzano' => 'BZ',
474
+ 'brescia' => 'BS',
475
+ 'brindisi' => 'BR',
476
+ 'cagliari' => 'CA',
477
+ 'caltanissetta' => 'CL',
478
+ 'campobasso' => 'CB',
479
+ 'caserta' => 'CE',
480
+ 'catania' => 'CT',
481
+ 'catanzaro' => 'CZ',
482
+ 'chieti' => 'CH',
483
+ 'como' => 'CO',
484
+ 'cosenza' => 'CS',
485
+ 'cremona' => 'CR',
486
+ 'crotone' => 'KR',
487
+ 'cuneo' => 'CN',
488
+ 'enna' => 'EN',
489
+ 'ferrara' => 'FE',
490
+ 'firenze' => 'FI',
491
+ 'foggia' => 'FG',
492
+ 'forli-cesena' => 'FO',
493
+ 'frosinone' => 'FR',
494
+ 'genova' => 'GE',
495
+ 'gorizia' => 'GO',
496
+ 'grosseto' => 'GR',
497
+ 'imperia' => 'IM',
498
+ 'isernia' => 'IS',
499
+ 'la spezia' => 'SP',
500
+ 'l\'aquila' => 'AQ',
501
+ 'latina' => 'LT',
502
+ 'lecce' => 'LE',
503
+ 'lecco' => 'LC',
504
+ 'livorno' => 'LI',
505
+ 'lodi' => 'LO',
506
+ 'lucca' => 'LU',
507
+ 'macerata' => 'MC',
508
+ 'mantova' => 'MN',
509
+ 'massa-carrara' => 'MS',
510
+ 'matera' => 'MT',
511
+ 'messina' => 'ME',
512
+ 'milano' => 'MI',
513
+ 'modena' => 'MO',
514
+ 'monza e brianza' => 'MB',
515
+ // The next couple of derivations are based off information from Wikipedia
516
+ 'monza and brianza' => 'MB',
517
+ 'monza e della brianza' => 'MB',
518
+ 'napoli' => 'NA',
519
+ 'novara' => 'NO',
520
+ 'nuoro' => 'NU',
521
+ 'oristano' => 'OR',
522
+ 'padova' => 'PD',
523
+ 'palermo' => 'PA',
524
+ 'parma' => 'PR',
525
+ 'pavia' => 'PV',
526
+ 'perugia' => 'PG',
527
+ 'pesaro' => 'PS',
528
+ 'pescara' => 'PE',
529
+ 'piacenza' => 'PC',
530
+ 'pisa' => 'PI',
531
+ 'pistoia' => 'PT',
532
+ 'pordenone' => 'PN',
533
+ 'potenza' => 'PZ',
534
+ 'prato' => 'PO',
535
+ 'ragusa' => 'RG',
536
+ 'ravenna' => 'RA',
537
+ 'reggio calabria' => 'RC',
538
+ 'reggio emilia' => 'RE',
539
+ 'rieti' => 'RI',
540
+ 'rimini' => 'RN',
541
+ 'roma' => 'RM',
542
+ 'rovigo' => 'RO',
543
+ 'salerno' => 'SA',
544
+ 'sassari' => 'SS',
545
+ 'savona' => 'SV',
546
+ 'siena' => 'SI',
547
+ 'siracusa' => 'SR',
548
+ 'sondrio' => 'SO',
549
+ 'taranto' => 'TA',
550
+ 'teramo' => 'TE',
551
+ 'terni' => 'TR',
552
+ 'torino' => 'TO',
553
+ 'trapani' => 'TP',
554
+ 'trento' => 'TN',
555
+ 'treviso' => 'TV',
556
+ 'trieste' => 'TS',
557
+ 'udine' => 'UD',
558
+ 'varese' => 'VA',
559
+ 'venezia' => 'VE',
560
+ 'verbania-cusio-ossola' => 'VB',
561
+ // This derivation doesn't appear in the list, but seems obvious
562
+ 'verbania cusio ossola' => 'VB',
563
+ 'vercelli' => 'VC',
564
+ 'verona' => 'VR',
565
+ 'vibo valentia' => 'VV',
566
+ 'vicenza' => 'VI',
567
+ 'viterbo' => 'VT'
568
+ );
569
+ } elseif ( 'NL' == $this->_country ) {
570
+ $translation_table = array(
571
+ 'drenthe' => 'DR',
572
+ 'flevoland' => 'FL',
573
+ 'friesland' => 'FR',
574
+ 'gelderland' => 'GE',
575
+ 'groningen' => 'GR',
576
+ 'limburg' => 'LI',
577
+ 'noord-brabant' => 'NB',
578
+ 'noord-holland' => 'NH',
579
+ 'overijssel' => 'OV',
580
+ 'utrecht' => 'UT',
581
+ 'zeeland' => 'ZE',
582
+ 'zuid-holland' => 'ZH'
583
+ );
584
+ }
585
+
586
+ if ( array_key_exists( strtolower( trim( $this->_state ) ), $translation_table ) ) {
587
+ $this->_state = $translation_table[ strtolower( trim( $this->_state ) ) ];
588
+ }
589
+
590
+ }
591
+
592
+ public function normalizeZip() {
593
+ // TODO: Try to do some ZIP code normalization
594
+ }
595
+
596
+ public function getAddressParams( $prefix = '' ) {
597
+ $params = array(
598
+ $prefix . 'NAME' => $this->_name,
599
+ $prefix . 'STREET' => $this->_street1,
600
+ $prefix . 'STREET2' => $this->_street2,
601
+ $prefix . 'CITY' => $this->_city,
602
+ $prefix . 'STATE' => $this->_state,
603
+ $prefix . 'ZIP' => $this->_zip,
604
+ $prefix . 'COUNTRYCODE' => $this->_country
605
+ );
606
+
607
+ return $params;
608
+ }
609
+
610
+ public function loadFromGetECResponse( $getECResponse, $prefix, $isBillingAddress = false ) {
611
+ $map = array(
612
+ 'NAME' => '_name',
613
+ 'STREET' => '_street1',
614
+ 'STREET2' => '_street2',
615
+ 'CITY' => '_city',
616
+ 'STATE' => '_state',
617
+ 'ZIP' => '_zip',
618
+ 'PHONENUM' => '_phoneNumber',
619
+ 'ADDRESSSTATUS' => '_addressStatus',
620
+ 'ADDRESSOWNER' => '_addressOwner'
621
+ );
622
+
623
+ if ( $isBillingAddress ) {
624
+ $map['COUNTRY'] = '_country';
625
+ } else {
626
+ $map['COUNTRYCODE'] = '_country';
627
+ }
628
+
629
+ $found_any = false;
630
+
631
+ foreach ( $map as $index => $value ) {
632
+ $var_name = $prefix . $index;
633
+ if ( array_key_exists( $var_name, $getECResponse ) ) {
634
+ $this->$value = $getECResponse[ $var_name ];
635
+ // ADDRESSSTATUS is returned whether or not a billing address is requested, so we don't want
636
+ // the presence of this variable alone be enough to trigger recognition of a complete
637
+ // billing address.
638
+ if ( 'ADDRESSSTATUS' != $index || ! $isBillingAddress ) {
639
+ $found_any = true;
640
+ }
641
+ }
642
+ }
643
+
644
+ return $found_any;
645
+ }
646
+ }
includes/class-wc-gateway-ppec-admin-handler.php ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin bootstrapper.
4
+ */
5
+
6
+ if ( ! defined( 'ABSPATH' ) ) {
7
+ exit; // Exit if accessed directly
8
+ }
9
+
10
+ class WC_Gateway_PPEC_Admin_Handler {
11
+
12
+ /**
13
+ * Constructor.
14
+ */
15
+ public function __construct() {
16
+ add_action( 'woocommerce_update_options_general', array( $this, 'force_zero_decimal' ) );
17
+ add_action( 'admin_notices', array( $this, 'show_decimal_warning' ) );
18
+
19
+ // defer this until for next release.
20
+ // add_filter( 'woocommerce_get_sections_checkout', array( $this, 'filter_checkout_sections' ) );
21
+
22
+ add_action( 'woocommerce_order_status_on-hold_to_processing', array( $this, 'capture_payment' ) );
23
+ add_action( 'woocommerce_order_status_on-hold_to_completed', array( $this, 'capture_payment' ) );
24
+ add_action( 'woocommerce_order_status_on-hold_to_cancelled', array( $this, 'cancel_payment' ) );
25
+ add_action( 'woocommerce_order_status_on-hold_to_refunded', array( $this, 'cancel_payment' ) );
26
+
27
+ add_filter( 'woocommerce_order_actions', array( $this, 'add_capture_charge_order_action' ) );
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
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
33
+ }
34
+
35
+ public function add_capture_charge_order_action() {
36
+ if ( ! isset( $_REQUEST['post'] ) ) {
37
+ return;
38
+ }
39
+
40
+ $order = wc_get_order( $_REQUEST['post'] );
41
+
42
+ // bail if the order wasn't paid for with this gateway
43
+ if ( 'ppec_paypal' !== $order->payment_method ) {
44
+ return;
45
+ }
46
+
47
+ if ( 'yes' === get_post_meta( $order->id, '_ppec_charge_captured', true ) ) {
48
+ return;
49
+ }
50
+
51
+ return array( 'ppec_capture_charge' => esc_html__( 'Capture Charge', 'woocommerce-gateway-paypal-express-checkout' ) );
52
+ }
53
+
54
+ /**
55
+ * Force zero decimal on specific currencies.
56
+ */
57
+ public function force_zero_decimal() {
58
+ $settings = wc_gateway_ppec()->settings;
59
+ if ( $settings->currency_has_decimal_restriction() ) {
60
+ update_option( 'woocommerce_price_num_decimals', 0 );
61
+ update_option( 'wc_gateway_ppce_display_decimal_msg', true );
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Show decimal warning.
67
+ */
68
+ public function show_decimal_warning() {
69
+ if ( get_option( 'wc_gateway_ppce_display_decimal_msg', false ) ) {
70
+ ?>
71
+ <div class="updated fade">
72
+ <p>
73
+ <strong><?php _e( 'NOTE: PayPal does not accept decimal places for the currency in which you are transacting. The "Number of Decimals" option in WooCommerce has automatically been set to 0 for you.', 'woocommerce-gateway-paypal-express-checkout' ); ?></strong>
74
+ </p>
75
+ </div>
76
+ <?php
77
+ delete_option( 'wc_gateway_ppce_display_decimal_msg' );
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Prevent PPEC Credit showing up in the admin, because it shares its settings
83
+ * with the PayPal Express Checkout class.
84
+ *
85
+ * @param array $sections List of sections in checkout
86
+ *
87
+ * @return array Sections in checkout
88
+ */
89
+ public function filter_checkout_sections( $sections ) {
90
+
91
+ $paypal_sections = array(
92
+ 'wc_gateway_ppec_with_paypal',
93
+ );
94
+
95
+ $card_sections = array(
96
+ 'wc_gateway_ppec_with_paypal_credit',
97
+ );
98
+
99
+ $current_section = isset( $_GET['section'] ) ? $_GET['section'] : '';
100
+
101
+ // If the current section is a paypal section, remove the card section,
102
+ // otherwise, remove the paypal section
103
+ $sections_to_remove = in_array( $current_section, $paypal_sections ) ? $card_sections : $paypal_sections;
104
+
105
+ // And, let's also remove simplify commerce from the sections if it is not enabled and it is not the
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
+ }
113
+ if ( 'wc_addons_gateway_simplify_commerce' !== $current_section ) {
114
+ $sections_to_remove[] = 'wc_addons_gateway_simplify_commerce';
115
+ }
116
+ }
117
+
118
+ foreach( $sections_to_remove as $section_to_remove ) {
119
+ unset( $sections[$section_to_remove] );
120
+ }
121
+
122
+ return $sections;
123
+
124
+ }
125
+
126
+ public function maybe_capture_charge( $order ) {
127
+ if ( ! is_object( $order ) ) {
128
+ $order = wc_get_order( $order );
129
+ }
130
+
131
+ $this->capture_payment( $order->id );
132
+
133
+ return true;
134
+ }
135
+
136
+ /**
137
+ * Capture payment when the order is changed from on-hold to complete or processing
138
+ *
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 );
154
+
155
+ if ( is_wp_error( $result ) ) {
156
+ $order->add_order_note( __( 'Unable to capture charge!', 'woocommerce-gateway-paypal-express-checkout' ) . ' ' . $result->get_error_message() );
157
+ } else {
158
+ $order->add_order_note( sprintf( __( 'PayPal Express Checkout charge complete (Charge ID: %s)', 'woocommerce-gateway-paypal-express-checkout' ), $trans_id ) );
159
+
160
+ update_post_meta( $order->id, '_ppec_charge_captured', 'yes' );
161
+ }
162
+ }
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Checks to see if the transaction can be captured
168
+ *
169
+ * @param array $trans_details
170
+ */
171
+ public function is_authorized_only( $trans_details = array() ) {
172
+ if ( ! is_wp_error( $trans_details ) && ! empty( $trans_details ) ) {
173
+ if ( 'Pending' === $trans_details['PAYMENTSTATUS'] && 'authorization' === $trans_details['PENDINGREASON'] ) {
174
+ return true;
175
+ }
176
+ }
177
+
178
+ return false;
179
+ }
180
+
181
+ /**
182
+ * Cancel authorization
183
+ *
184
+ * @param int $order_id
185
+ */
186
+ public function cancel_payment( $order_id ) {
187
+ $order = wc_get_order( $order_id );
188
+
189
+ if ( 'ppec_paypal' === $order->payment_method ) {
190
+ $trans_id = get_post_meta( $order_id, '_transaction_id', true );
191
+ $trans_details = wc_gateway_ppec()->client->get_transaction_details( array( 'TRANSACTIONID' => $trans_id ) );
192
+
193
+ if ( $trans_id && $this->is_authorized_only( $trans_details ) ) {
194
+ $params['AUTHORIZATIONID'] = $trans_id;
195
+
196
+ $result = wc_gateway_ppec()->client->do_express_checkout_void( $params );
197
+
198
+ if ( is_wp_error( $result ) ) {
199
+ $order->add_order_note( __( 'Unable to void charge!', 'woocommerce-gateway-paypal-express-checkout' ) . ' ' . $result->get_error_message() );
200
+ } else {
201
+ $order->add_order_note( sprintf( __( 'PayPal Express Checkout charge voided (Charge ID: %s)', 'woocommerce-gateway-paypal-express-checkout' ), $trans_id) );
202
+ delete_post_meta( $order->id, '_ppec_charge_captured' );
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Get admin URL for this gateway setting.
210
+ *
211
+ * @return string URL
212
+ */
213
+ public function gateway_admin_url( $gateway_class ) {
214
+ return add_query_arg(
215
+ array(
216
+ 'page' => 'wc-settings',
217
+ 'tab' => 'checkout',
218
+ 'section' => strtolower( $gateway_class ),
219
+ ),
220
+ admin_url( 'admin.php' )
221
+ );
222
+ }
223
+
224
+ /**
225
+ * Maybe redirect to wc_gateway_ppec_with_paypal from PayPal standard
226
+ * checkout settings.
227
+ *
228
+ * @return void
229
+ */
230
+ public function maybe_redirect_to_ppec_settings() {
231
+ if ( ! wc_gateway_ppec()->settings->loadSettings()->enabled ) {
232
+ return;
233
+ }
234
+
235
+ if ( empty( $_GET['tab'] ) || empty( $_GET['section'] ) ) {
236
+ return;
237
+ }
238
+
239
+ if ( 'checkout' === $_GET['tab'] && 'wc_gateway_paypal' === $_GET['section'] ) {
240
+ $redirect = add_query_arg( array( 'section' => 'wc_gateway_ppec_with_paypal' ) );
241
+ wp_safe_redirect( $redirect );
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Enqueue script related to admin.
247
+ *
248
+ * @return void
249
+ */
250
+ public function enqueue_scripts() {
251
+ $settings = wc_gateway_ppec()->settings->loadSettings();
252
+
253
+ wp_enqueue_script( 'wc-gateway-ppec-admin', wc_gateway_ppec()->plugin_url . 'assets/js/wc-gateway-ppec-admin.js', array( 'jquery' ), wc_gateway_ppec()->version, true );
254
+ wp_localize_script( 'wc-gateway-ppec-admin', 'wc_ppec_settings', array(
255
+ 'enabled' => $settings->enabled,
256
+ ) );
257
+ }
258
+ }
includes/class-wc-gateway-ppec-api-error.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ class PayPal_API_Error {
8
+ public $error_code;
9
+ public $short_message;
10
+ public $long_message;
11
+ public $severity_code;
12
+
13
+ public function __construct( $error_code, $short_message, $long_message, $severity_code ) {
14
+ $this->error_code = $error_code;
15
+ $this->short_message = $short_message;
16
+ $this->long_message = $long_message;
17
+ $this->severity_code = $severity_code;
18
+ }
19
+
20
+ public function mapToBuyerFriendlyError() {
21
+ switch ( $this->error_code ) {
22
+ case '-1': return 'Unable to communicate with PayPal. Please try your payment again.';
23
+ case '10407': return 'PayPal rejected your email address because it is not valid. Please double-check your email address and try again.';
24
+ case '10409':
25
+ case '10421':
26
+ case '10410': return 'Your PayPal checkout session is invalid. Please check out again.';
27
+ case '10411': return 'Your PayPal checkout session has expired. Please check out again.';
28
+ case '11607':
29
+ case '10415': return 'Your PayPal payment has already been completed. Please contact the store owner for more information.';
30
+ case '10416': return 'Your PayPal payment could not be processed. Please check out again or contact PayPal for assistance.';
31
+ case '10417': return 'Your PayPal payment could not be processed. Please select an alternative method of payment or contact PayPal for assistance.';
32
+ case '10486':
33
+ case '10422': return 'Your PayPal payment could not be processed. Please return to PayPal and select a new method of payment.';
34
+ case '10485':
35
+ case '10435': return 'You have not approved this transaction on the PayPal website. Please check out again and be sure to complete all steps of the PayPal checkout process.';
36
+ case '10474': return 'Your shipping address may not be in a different country than your country of residence. Please double-check your shipping address and try again.';
37
+ case '10537': return 'This store does not accept transactions from buyers in your country. Please contact the store owner for assistance.';
38
+ case '10538': return 'The transaction is over the threshold allowed by this store. Please contact the store owner for assistance.';
39
+ case '11611':
40
+ case '10539': return 'Your transaction was declined. Please contact the store owner for assistance.';
41
+ case '10725': return 'The country in your shipping address is not valid. Please double-check your shipping address and try again.';
42
+ case '10727': return 'The street address in your shipping address is not valid. Please double-check your shipping address and try again.';
43
+ case '10728': return 'The city in your shipping address is not valid. Please double-check your shipping address and try again.';
44
+ case '10729': return 'The state in your shipping address is not valid. Please double-check your shipping address and try again.';
45
+ case '10730': return 'The ZIP code or postal code in your shipping address is not valid. Please double-check your shipping address and try again.';
46
+ case '10736': return 'PayPal rejected your shipping address because the city, state, and/or ZIP code are incorrect. Please double-check that they are all spelled correctly and try again.';
47
+ case '13113':
48
+ case '11084': return 'Your PayPal payment could not be processed. Please contact PayPal for assistance.';
49
+ case '12126':
50
+ case '12125': return 'The redemption code(s) you entered on PayPal cannot be used at this time. Please return to PayPal and remove them.';
51
+ case '17203':
52
+ case '17204':
53
+ case '17200': return 'Your funding instrument is invalid. Please check out again and select a new funding source.';
54
+ default: return 'An error occurred while processing your PayPal payment. Please contact the store owner for assistance.';
55
+ }
56
+ }
57
+ }
includes/class-wc-gateway-ppec-cart-handler.php ADDED
@@ -0,0 +1,458 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Cart handler.
4
+ */
5
+
6
+ if ( ! defined( 'ABSPATH' ) ) {
7
+ exit; // Exit if accessed directly
8
+ }
9
+
10
+ class WC_Gateway_PPEC_Cart_Handler {
11
+
12
+ /**
13
+ * TODO rename this to underscore var names
14
+ */
15
+ protected $orderTotal;
16
+ protected $orderTax;
17
+ protected $shipping;
18
+ protected $insurance;
19
+ protected $handling;
20
+ protected $items;
21
+ protected $totalItemAmount;
22
+ protected $currency;
23
+ protected $custom;
24
+ protected $invoiceNumber;
25
+ protected $shipDiscountAmount;
26
+
27
+ /**
28
+ * Currencies that support 0 decimal places -- "zero decimal place" currencies
29
+ *
30
+ * @var array
31
+ */
32
+ protected $zdp_currencies = array( 'HUF', 'JPY', 'TWD' );
33
+
34
+ /**
35
+ * Constructor.
36
+ */
37
+ public function __construct() {
38
+ add_action( 'woocommerce_before_cart_totals', array( $this, 'before_cart_totals' ) );
39
+
40
+ if ( version_compare( WC()->version, '2.3', '>=' ) ) {
41
+ add_action( 'woocommerce_after_cart_totals', array( $this, 'display_paypal_button' ) );
42
+ } else {
43
+ add_action( 'woocommerce_proceed_to_checkout', array( $this, 'display_paypal_button' ) );
44
+ }
45
+
46
+ add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
47
+ }
48
+
49
+ public function before_cart_totals() {
50
+ // If there then call start_checkout() else do nothing so page loads as normal.
51
+ if ( ! empty( $_GET['startcheckout'] ) && 'true' === $_GET['startcheckout'] ) {
52
+ // Trying to prevent auto running checkout when back button is pressed from PayPal page.
53
+ $_GET['startcheckout'] = 'false';
54
+ woo_pp_start_checkout();
55
+ }
56
+ }
57
+
58
+ public function display_paypal_button() {
59
+ $settings = wc_gateway_ppec()->settings->loadSettings();
60
+ if( ! $settings->enabled ) {
61
+ return;
62
+ }
63
+
64
+ if ( version_compare( WC()->version, '2.3', '>' ) ) {
65
+ $class = 'woo_pp_cart_buttons_div';
66
+ } else {
67
+ $class = 'woo_pp_checkout_buttons_div';
68
+ }
69
+
70
+ if ( $settings->enableInContextCheckout && $settings->getActiveApiCredentials()->get_payer_id() ) {
71
+ $class .= ' paypal-button-hidden';
72
+ }
73
+
74
+ $redirect_arg = array( 'startcheckout' => 'true' );
75
+ $redirect = add_query_arg( $redirect_arg );
76
+
77
+ $checkout_logo = 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-' . $settings->buttonSize . '.png';
78
+ $credit_logo = 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/ppcredit-logo-' . $settings->buttonSize . '.png';
79
+ ?>
80
+ <div class="<?php echo esc_attr( $class ); ?>">
81
+ <span style="float: right;">
82
+ <a href="<?php echo esc_url( $redirect ); ?>" id="woo_pp_ec_button">
83
+ <?php if ( ! $settings->enableInContextCheckout ) : ?>
84
+ <img src="<?php echo esc_url( $checkout_logo ); ?>" alt="<?php _e( 'Check out with PayPal', 'woocommerce-gateway-paypal-express-checkout' ); ?>" style="width: auto; height: auto;">
85
+ <?php endif; ?>
86
+ </a>
87
+ </span>
88
+
89
+ <? /* defer ppc for next release.
90
+ <?php if ( $settings->ppcEnabled && 'US' === WC()->countries->get_base_country() ) : ?>
91
+ <?php
92
+ $redirect = add_query_arg( array( 'use-ppc' => 'true' ), $redirect );
93
+ ?>
94
+ <span style="float: right; padding-right: 5px;">
95
+
96
+ <a href="<?php echo esc_url( $redirect ); ?>" id="woo_pp_ppc_button">
97
+ <img src="<?php echo esc_url( $credit_logo ); ?>" alt="<?php _e( 'Pay with PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' ); ?>" style="width: auto; height: auto;">
98
+ </a>
99
+ </span>
100
+ <?php endif; ?>
101
+ */ ?>
102
+ </div>
103
+
104
+ <?php
105
+ if ( $settings->enableInContextCheckout && $settings->getActiveApiCredentials()->get_payer_id() ) {
106
+ $payer_id = $settings->getActiveApiCredentials()->get_payer_id();
107
+ $setup_args = array(
108
+ // 'button' => array( 'woo_pp_ec_button', 'woo_pp_ppc_button' ),
109
+ 'buttons' => array(
110
+ array(
111
+ 'container' => 'woo_pp_ec_button',
112
+ 'size' => $settings->buttonSize,
113
+ 'shape' => 'rect',
114
+ )
115
+ ),
116
+ 'locale' => $settings->get_paypal_locale(),
117
+ );
118
+ ?>
119
+ <script type="text/javascript">
120
+ window.paypalCheckoutReady = function() {
121
+ paypal.checkout.setup( <?php echo json_encode( $payer_id ); ?>, <?php echo json_encode( $setup_args ); ?> );
122
+ }
123
+ </script>
124
+ <?php
125
+ }
126
+ }
127
+
128
+ public function enqueue_scripts() {
129
+ if ( ! is_cart() ) {
130
+ return;
131
+ }
132
+
133
+ wp_enqueue_style( 'wc-gateway-ppec-frontend-cart', wc_gateway_ppec()->plugin_url . 'assets/css/wc-gateway-ppec-frontend-cart.css' );
134
+
135
+ $settings = wc_gateway_ppec()->settings->loadSettings();
136
+ if ( $settings->enabled && $settings->enableInContextCheckout && $settings->getActiveApiCredentials()->get_payer_id() ) {
137
+ wp_enqueue_script( 'paypal-checkout-js', 'https://www.paypalobjects.com/api/checkout.js', array(), null, true );
138
+ }
139
+
140
+ }
141
+
142
+ /**
143
+ * Load cart details.
144
+ */
145
+ public function loadCartDetails() {
146
+
147
+ $this->totalItemAmount = 0;
148
+ $this->items = array();
149
+
150
+ // load all cart items into an array
151
+ $roundedPayPalTotal = 0;
152
+
153
+ $is_zdp_currency = in_array( get_woocommerce_currency(), $this->zdp_currencies );
154
+ if ( $is_zdp_currency ) {
155
+ $decimals = 0;
156
+ } else {
157
+ $decimals = 2;
158
+ }
159
+
160
+ $discounts = round( WC()->cart->get_cart_discount_total(), $decimals );
161
+ foreach ( WC()->cart->cart_contents as $cart_item_key => $values ) {
162
+ $amount = round( $values['line_total'] / $values['quantity'] , $decimals );
163
+ $item = array(
164
+ 'name' => $values['data']->post->post_title,
165
+ 'description' => $values['data']->post->post_content,
166
+ 'quantity' => $values['quantity'],
167
+ 'amount' => $amount,
168
+ );
169
+
170
+ $this->items[] = $item;
171
+
172
+ $roundedPayPalTotal += round( $amount * $values['quantity'], $decimals );
173
+ }
174
+
175
+ $this->orderTax = round( WC()->cart->tax_total, $decimals );
176
+ $this->shipping = round( WC()->cart->shipping_total, $decimals );
177
+ if ( WC()->cart->shipping_tax_total != 0 ) {
178
+ $this->orderTax += round( WC()->cart->shipping_tax_total, $decimals );
179
+ }
180
+ $this->totalItemAmount = round( WC()->cart->cart_contents_total, $decimals );
181
+ $this->orderTotal = $this->totalItemAmount + $this->orderTax + $this->shipping;
182
+
183
+ // need to compare WC totals with what PayPal will calculate to see if they match
184
+ // if they do not match, check to see what the merchant would like to do
185
+ // options are to remove line items or add a line item to adjust for the difference
186
+ if ( $this->totalItemAmount != $roundedPayPalTotal ) {
187
+ $settings = wc_gateway_ppec()->settings->loadSettings();
188
+ $subtotalBehavior = $settings->subtotalMismatchBehavior;
189
+
190
+ if ( WC_Gateway_PPEC_Settings::subtotalMismatchBehaviorAddLineItem == $subtotalBehavior ) {
191
+ // ...
192
+ // Add line item to make up different between WooCommerce calculations and PayPal calculations
193
+ $cartItemAmountDifference = $this->totalItemAmount - $roundedPayPalTotal;
194
+
195
+ $modifyLineItem = array(
196
+ 'name' => 'Line Item Amount Offset',
197
+ 'description' => 'Adjust cart calculation discrepancy',
198
+ 'quantity' => 1,
199
+ 'amount' => round( $cartItemAmountDifference, $decimals )
200
+ );
201
+
202
+ $this->items[] = $modifyLineItem;
203
+
204
+ } elseif ( WC_Gateway_PPEC_Settings::subtotalMismatchBehaviorDropLineItems == $subtotalBehavior ) {
205
+ // ...
206
+ // Omit line items altogether
207
+ unset($this->items);
208
+ }
209
+
210
+ }
211
+
212
+ // enter discount shenanigans. item total cannot be 0 so make modifications accordingly
213
+ if ( $this->totalItemAmount == $discounts ) {
214
+ $settings = wc_gateway_ppec()->loadSettings();
215
+ $behavior = $settings->zeroSubtotalBehavior;
216
+
217
+ if ( WC_Gateway_PPEC_Settings::zeroSubtotalBehaviorModifyItems == $behavior ) {
218
+ // ...
219
+ // Go ahead and pass the discounts with the cart, but then add in a 0.01 line
220
+ // item and add a 0.01 shipping discount.
221
+ $discountLineItem = array(
222
+ 'name' => 'Discount',
223
+ 'description' => 'Discount Amount',
224
+ 'quantity' => 1,
225
+ 'amount' => $discounts
226
+ );
227
+
228
+ $this->items[] = $discountLineItme;
229
+
230
+ if ( $is_zdp_currency ) {
231
+ $discount = 1;
232
+ } else {
233
+ $discount = 0.01;
234
+ }
235
+
236
+ $modifyLineItem = array(
237
+ 'name' => 'Discount Offset',
238
+ 'description' => 'Amount Discounted in Shipping',
239
+ 'quantity' => 1,
240
+ 'amount' => $discount
241
+ );
242
+
243
+ $this->items[] = $modifyLineItem;
244
+ $this->shipDiscountAmount = $discount;
245
+
246
+ } elseif ( WC_Gateway_PPEC_Settings::zeroSubtotalBehaviorOmitLineItems == $behavior ) {
247
+ // ...
248
+ // Omit line items altogether
249
+ unset($this->items);
250
+ $this->shipDiscountAmount = 0;
251
+
252
+ } else {
253
+ // ...
254
+ // Increase SHIPDISCAMT by the amount of all the coupons in the cart
255
+ $this->shipDiscountAmount = round( WC()->cart->get_order_discount_total(), $decimals );
256
+
257
+ }
258
+ } else {
259
+ // Build PayPal_Cart object as normal
260
+ if ( $discounts > 0 ) {
261
+ $discLineItem = array(
262
+ 'name' => 'Discount',
263
+ 'description' => 'Discount Amount',
264
+ 'quantity' => 1,
265
+ 'amount' => '-' . $discounts
266
+ );
267
+
268
+ $this->items[] = $discLineItem;
269
+ }
270
+
271
+ $this->shipDiscountAmount = 0;
272
+ }
273
+
274
+ // after all of the discount shenanigans, load up the other standard variables
275
+ $this->insurance = 0;
276
+ $this->handling = 0;
277
+ $this->currency = get_woocommerce_currency();
278
+ $this->custom = '';
279
+ $this->invoiceNumber = '';
280
+
281
+ if ( ! is_numeric( $this->shipping ) )
282
+ $this->shipping = 0;
283
+
284
+ }
285
+
286
+ public function loadOrderDetails( $order_id ) {
287
+
288
+ $order = wc_get_order( $order_id );
289
+ $this->totalItemAmount = 0;
290
+ $this->items = array();
291
+
292
+ // load all cart items into an array
293
+ $roundedPayPalTotal = 0;
294
+
295
+ $is_zdp_currency = in_array( get_woocommerce_currency(), $this->zdp_currencies );
296
+ if ( $is_zdp_currency ) {
297
+ $decimals = 0;
298
+ } else {
299
+ $decimals = 2;
300
+ }
301
+
302
+ $discounts = round( $order->get_total_discount(), $decimals );
303
+ foreach ( $order->get_items() as $cart_item_key => $values ) {
304
+ $amount = round( $values['line_total'] / $values['qty'] , $decimals );
305
+ $item = array(
306
+ 'name' => $values['name'],
307
+ 'quantity' => $values['qty'],
308
+ 'amount' => $amount,
309
+ );
310
+
311
+ $this->items[] = $item;
312
+
313
+ $roundedPayPalTotal += round( $amount * $values['qty'], $decimals );
314
+ }
315
+
316
+ $this->orderTax = round( $order->get_total_tax(), $decimals );
317
+ $this->shipping = round( $order->get_total_shipping(), $decimals );
318
+ // if ( $order->get_shipping_tax() != 0 ) {
319
+ // $this->shipping += round( $order->get_shipping_tax(), $decimals );
320
+ // }
321
+ $this->totalItemAmount = round( $order->get_subtotal(), $decimals );
322
+ $this->orderTotal = $this->totalItemAmount + $this->orderTax + $this->shipping;
323
+
324
+ // need to compare WC totals with what PayPal will calculate to see if they match
325
+ // if they do not match, check to see what the merchant would like to do
326
+ // options are to remove line items or add a line item to adjust for the difference
327
+ if ( $this->totalItemAmount != $roundedPayPalTotal ) {
328
+ $settings = wc_gateway_ppec()->settings->loadSettings();
329
+ $subtotalBehavior = $settings->subtotalMismatchBehavior;
330
+
331
+ if ( WC_Gateway_PPEC_Settings::subtotalMismatchBehaviorAddLineItem == $subtotalBehavior ) {
332
+ // ...
333
+ // Add line item to make up different between WooCommerce calculations and PayPal calculations
334
+ $cartItemAmountDifference = $this->totalItemAmount - $roundedPayPalTotal;
335
+
336
+ $modifyLineItem = array(
337
+ 'name' => 'Line Item Amount Offset',
338
+ 'description' => 'Adjust cart calculation discrepancy',
339
+ 'quantity' => 1,
340
+ 'amount' => round( $cartItemAmountDifference, $decimals )
341
+ );
342
+
343
+ $this->items[] = $modifyLineItem;
344
+
345
+ } elseif ( WC_Gateway_PPEC_Settings::subtotalMismatchBehaviorDropLineItems == $subtotalBehavior ) {
346
+ // ...
347
+ // Omit line items altogether
348
+ unset($this->items);
349
+ }
350
+
351
+ }
352
+
353
+ // enter discount shenanigans. item total cannot be 0 so make modifications accordingly
354
+ if ( $this->totalItemAmount == $discounts ) {
355
+ $settings = wc_gateway_ppec()->settings->loadSettings();
356
+ $behavior = $settings->zeroSubtotalBehavior;
357
+
358
+ if ( WC_Gateway_PPEC_Settings::zeroSubtotalBehaviorModifyItems == $behavior ) {
359
+ // ...
360
+ // Go ahead and pass the discounts with the cart, but then add in a 0.01 line
361
+ // item and add a 0.01 shipping discount.
362
+ $discountLineItem = array(
363
+ 'name' => 'Discount',
364
+ 'description' => 'Discount Amount',
365
+ 'quantity' => 1,
366
+ 'amount' => $discounts
367
+ );
368
+
369
+ $this->items[] = $discountLineItme;
370
+
371
+ if ( $is_zdp_currency ) {
372
+ $discount = 1;
373
+ } else {
374
+ $discount = 0.01;
375
+ }
376
+
377
+ $modifyLineItem = array(
378
+ 'name' => 'Discount Offset',
379
+ 'description' => 'Amount Discounted in Shipping',
380
+ 'quantity' => 1,
381
+ 'amount' => $discount
382
+ );
383
+
384
+ $this->items[] = $modifyLineItem;
385
+ $this->shipDiscountAmount = $discount;
386
+
387
+ } elseif ( WC_Gateway_PPEC_Settings::zeroSubtotalBehaviorOmitLineItems == $behavior ) {
388
+ // ...
389
+ // Omit line items altogether
390
+ unset($this->items);
391
+ $this->shipDiscountAmount = 0;
392
+
393
+ } else {
394
+ // ...
395
+ // Increase SHIPDISCAMT by the amount of all the coupons in the cart
396
+ $this->shipDiscountAmount = round( WC()->cart->get_order_discount_total(), $decimals );
397
+
398
+ }
399
+ } else {
400
+ // Build PayPal_Cart object as normal
401
+ if ( $discounts > 0 ) {
402
+ $discLineItem = array(
403
+ 'name' => 'Discount',
404
+ 'description' => 'Discount Amount',
405
+ 'quantity' => 1,
406
+ 'amount' => '-' . $discounts
407
+ );
408
+
409
+ $this->items[] = $discLineItem;
410
+ }
411
+
412
+ $this->shipDiscountAmount = 0;
413
+ }
414
+
415
+ // after all of the discount shenanigans, load up the other standard variables
416
+ $this->insurance = 0;
417
+ $this->handling = 0;
418
+ $this->currency = get_woocommerce_currency();
419
+ $this->custom = '';
420
+ $this->invoiceNumber = '';
421
+
422
+ if ( ! is_numeric( $this->shipping ) )
423
+ $this->shipping = 0;
424
+
425
+ }
426
+
427
+ public function setECParams() {
428
+
429
+ $stdParams = array (
430
+ 'PAYMENTREQUEST_0_AMT' => $this->orderTotal,
431
+ 'PAYMENTREQUEST_0_CURRENCYCODE' => $this->currency,
432
+ 'PAYMENTREQUEST_0_ITEMAMT' => $this->totalItemAmount,
433
+ 'PAYMENTREQUEST_0_SHIPPINGAMT' => $this->shipping,
434
+ 'PAYMENTREQUEST_0_INSURANCEAMT' => $this->insurance,
435
+ 'PAYMENTREQUEST_0_HANDLINGAMT' => $this->handling,
436
+ 'PAYMENTREQUEST_0_TAXAMT' => $this->orderTax,
437
+ 'PAYMENTREQUEST_0_CUSTOM' => $this->custom,
438
+ 'PAYMENTREQUEST_0_INVNUM' => $this->invoiceNumber,
439
+ 'PAYMENTREQUEST_0_SHIPDISCAMT' => $this->shipDiscountAmount
440
+ );
441
+
442
+ if ( ! empty( $this->items ) ) {
443
+ $count = 0;
444
+ foreach ( $this->items as $line_item_key => $values ) {
445
+ $lineItemParams = array(
446
+ 'L_PAYMENTREQUEST_0_NAME' . $count => $values['name'],
447
+ 'L_PAYMENTREQUEST_0_DESC' . $count => ! empty( $values['description'] ) ? $values['description'] : '',
448
+ 'L_PAYMENTREQUEST_0_QTY' . $count => $values['quantity'],
449
+ 'L_PAYMENTREQUEST_0_AMT' . $count => $values['amount']
450
+ );
451
+
452
+ $stdParams = array_merge( $stdParams, $lineItemParams );
453
+ $count++;
454
+ }
455
+ }
456
+ return $stdParams;
457
+ }
458
+ }
includes/class-wc-gateway-ppec-checkout-details.php ADDED
@@ -0,0 +1,517 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * TODO: Move each class into its own file and group them under one dir, checkout-details.
5
+ */
6
+
7
+ if ( ! defined( 'ABSPATH' ) ) {
8
+ exit; // Exit if accessed directly
9
+ }
10
+
11
+ $includes_path = wc_gateway_ppec()->includes_path;
12
+
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;
19
+ public $phone_number = false;
20
+ public $billing_agreement_accepted = false;
21
+
22
+ const BillingAgreementNotAccepted = '0';
23
+ const BillingAgreementAccepted = '1';
24
+
25
+ public $paypal_adjustment = false;
26
+ public $redirect_required_after_payment = false;
27
+ public $checkout_status = false;
28
+
29
+ const PaymentNotAttempted = 'PaymentActionNotInitiated';
30
+ const PaymentFailed = 'PaymentActionFailed';
31
+ const PaymentInProgress = 'PaymentActionInProgress';
32
+ const PaymentCompleted = 'PaymentActionCompleted';
33
+
34
+ public $gift_details = false;
35
+
36
+ public $buyer_marketing_email = false;
37
+ public $survey_question = false;
38
+ public $survey_choice_selected = false;
39
+
40
+ public $payer_details = false;
41
+ public $wallets = false;
42
+
43
+ public $instrument_details = false;
44
+
45
+ public $shipping_option_details = false;
46
+
47
+ public $payments = false;
48
+
49
+ public function loadFromGetECResponse( $getECResponse ) {
50
+ $map = array(
51
+ 'TOKEN' => 'token',
52
+ 'CUSTOM' => 'custom',
53
+ 'INVNUM' => 'invnum',
54
+ 'PHONENUM' => 'phone_number',
55
+ 'BILLINGAGREEMENTACCEPTEDSTATUS' => 'billing_agreement_accepted',
56
+ 'PAYPALADJUSTMENT' => 'paypal_adjustment',
57
+ 'REDIRECTREQUIRED' => 'redirect_required_after_payment',
58
+ 'CHECKOUTSTATUS' => 'checkout_status',
59
+ 'BUYERMARKETINGEMAIL' => 'buyer_marketing_email',
60
+ 'SURVEYQUESTION' => 'survey_question',
61
+ 'SURVEYCHOICESELECTED' => 'survey_choice_selected'
62
+ );
63
+
64
+ foreach ( $getECResponse as $index => $value ) {
65
+ if ( array_key_exists( $index, $map ) ) {
66
+ $this->$map[ $index ] = $value;
67
+ }
68
+ }
69
+
70
+ $this->gift_details = new PayPal_Checkout_Gift_Details();
71
+ if ( ! $this->gift_details->loadFromGetECResponse( $getECResponse ) ) {
72
+ $this->gift_details = false;
73
+ }
74
+
75
+ $this->payer_details = new PayPal_Checkout_Payer_Details();
76
+ if ( ! $this->payer_details->loadFromGetECResponse( $getECResponse ) ) {
77
+ $this->payer_details = false;
78
+ }
79
+
80
+ $this->instrument_details = new PayPal_Checkout_Instrument_Details();
81
+ if ( ! $this->instrument_details->loadFromGetECResponse( $getECResponse ) ) {
82
+ $this->instrument_details = false;
83
+ }
84
+
85
+ $this->shipping_option_details = new PayPal_Checkout_Shipping_Option_Details();
86
+ if ( ! $this->shipping_option_details->loadFromGetECResponse( $getECResponse ) ) {
87
+ $this->shipping_option_details = false;
88
+ }
89
+
90
+ $max_wallet_num = -1;
91
+ $max_payment_num = -1;
92
+ foreach ( $getECResponse as $index => $value ) {
93
+ if ( preg_match( '/^(WALLETTYPE|WALLETID|WALLETDESCRIPTION)(\d+)$/', $index, $matches ) ) {
94
+ if ( $matches[2] > $max_wallet_num ) {
95
+ $max_wallet_num = $matches[2];
96
+ }
97
+ } elseif ( preg_match( '/^PAYMENTREQUEST_(\d)_(AMT|CURRENCYCODE|ITEMAMT|SHIPPINGAMT|INSURANCEAMT|SHIPDISCAMT|INSURANCEOPTIONOFFERED|HANDLINGAMT|TAXAMT|DESC|CUSTOM|INVNUM|NOTIFYURL|NOTETEXT|TRANSACTIONID|ALLOWEDPAYMENTMETHOD|PAYMENTREQUESTID|BUCKETCATEGORYTYPE)$/', $index, $matches )
98
+ || preg_match( '/^L_PAYMENTREQUEST_(\d)_(NAME|DESC|AMT|NUMBER|QTY|TAXAMT|ITEMWEIGHTVALUE|ITEMWEIGHTUNIT|ITEMLENGTHVALUE|ITEMLENGTHUNIT|ITEMWIDTHVALUE|ITEMWIDTHUNIT|ITEMHEIGHTVALUE|ITEMHEIGHTUNIT)\d+$/', $index, $matches ) ) {
99
+ if ( $matches[1] > $max_payment_num ) {
100
+ $max_payment_num = $matches[1];
101
+ }
102
+ }
103
+ }
104
+
105
+ if ( $max_wallet_num > -1 ) {
106
+ $this->wallets = array();
107
+ for ( $i = 0; $i <= $max_wallet_num; $i++ ) {
108
+ $this->wallets[ $i ] = new PayPal_Checkout_Wallet_Details();
109
+ $this->wallets[ $i ]->loadFromGetECResponse( $getECResponse, $i );
110
+ }
111
+ }
112
+
113
+ if ( $max_payment_num > -1 ) {
114
+ $this->payments = array();
115
+ for ( $i = 0; $i <= $max_payment_num; $i++ ) {
116
+ $this->payments[ $i ] = new PayPal_Checkout_Payment_Details();
117
+ $this->payments[ $i ]->loadFromGetECResponse( $getECResponse, $i );
118
+ }
119
+ }
120
+
121
+ }
122
+
123
+ }
124
+
125
+ class PayPal_Checkout_Payment_Details {
126
+ public $shipping_address = false;
127
+ public $shipping_address_confirmed = false;
128
+
129
+ public $shipping_address_normalization_status = false;
130
+
131
+ const AddressNormalizationNone = 'None';
132
+ const AddressNormalizationNormalized = 'Normalized';
133
+ const AddressNormalizationUnnormalized = 'Unnormalized';
134
+ const AddressNormalizationUserPreferred = 'UserPreferred';
135
+
136
+ public $amount = false;
137
+ public $currency_code = false;
138
+
139
+ public $item_amount = false;
140
+ public $shipping_amount = false;
141
+ public $insurance_amount = false;
142
+ public $shipping_discount_amount = false;
143
+ public $insurance_option_offered = false;
144
+ public $handling_amount = false;
145
+ public $tax_amount = false;
146
+ public $description = false;
147
+ public $custom = false;
148
+ public $invoice_number = false;
149
+ public $notify_url = false;
150
+ public $note_text = false;
151
+ public $transaction_id = false;
152
+ public $allowed_payment_method = false;
153
+
154
+ const AllowedPaymentMethodInstantPaymentOnly = 'InstantPaymentOnly';
155
+
156
+ public $payment_request_id = false;
157
+ public $bucket_category_type = false;
158
+
159
+ const BucketCategoryInternationalShipping = '1';
160
+ const BucketCategoryLocalDelivery = '2';
161
+
162
+ public $items = false;
163
+
164
+ public function loadFromGetECResponse( $getECResponse, $bucketNum ) {
165
+ $map = array(
166
+ 'AMT' => 'amount',
167
+ 'CURRENCYCODE' => 'currency_code',
168
+ 'ITEMAMT' => 'item_subtotal',
169
+ 'SHIPPINGAMT' => 'shipping_amount',
170
+ 'INSURANCEAMT' => 'insurance_amount',
171
+ 'SHIPDISCAMT' => 'shipping_discount_amount',
172
+ 'INSURANCEOPTIONOFFERED' => 'insurance_option_offered',
173
+ 'HANDLINGAMT' => 'handling_amount',
174
+ 'TAXAMT' => 'tax_amount',
175
+ 'DESC' => 'description',
176
+ 'CUSTOM' => 'custom',
177
+ 'INVNUM' => 'invoice_number',
178
+ 'NOTIFYURL' => 'notify_url',
179
+ 'NOTETEXT' => 'note_text',
180
+ 'TRANSACTIONID' => 'transaction_id',
181
+ 'ALLOWEDPAYMENTMETHOD' => 'allowed_payment_method',
182
+ 'PAYMENTREQUESTID' => 'payment_request_id',
183
+ 'BUCKETCATEGORYTYPE' => 'bucket_category_type',
184
+ 'ADDRESSNORMALIZATIONSTATUS' => 'shipping_address_normalization_status'
185
+ );
186
+
187
+ $found_any = false;
188
+ foreach ( $map as $index => $value ) {
189
+ $var_name = 'PAYMENTREQUEST_' . $bucketNum . '_' . $index;
190
+ if ( array_key_exists( $var_name, $getECResponse ) ) {
191
+ $this->$value = $getECResponse[ $var_name ];
192
+ $found_any = true;
193
+ }
194
+ }
195
+
196
+ // See if we have any line items that need to be parsed
197
+ $max_line_item_num = -1;
198
+ foreach ( $getECResponse as $index => $value ) {
199
+ if ( preg_match( '/^L_PAYMENTREQUEST_' . $bucketNum . '_(NAME|DESC|AMT|NUMBER|QTY|TAXAMT|ITEMWEIGHTVALUE|ITEMWEIGHTUNIT|ITEMLENGTHVALUE|ITEMLENGTHUNIT|ITEMWIDTHVALUE|ITEMWIDTHUNIT|ITEMHEIGHTVALUE|ITEMHEIGHTUNIT|ITEMCATEGORY|EBAYITEMNUMBER|EBAYITEMAUCTIONTXNID|EBAYITEMORDERID|EBAYITEMCARTID)(\d+)$/', $index, $matches ) ) {
200
+ if ( isset( $matches[2] ) && $matches[2] > $max_line_item_num ) {
201
+ $max_line_item_num = $matches[2];
202
+ }
203
+ }
204
+ }
205
+
206
+ if ( $max_line_item_num > -1 ) {
207
+ $found_any = true;
208
+ $this->items = array();
209
+ for ( $i = 0; $i <= $max_line_item_num; $i++ ) {
210
+ $items[ $i ] = new PayPal_Checkout_Payment_Item_Details();
211
+ $items[ $i ]->loadFromGetECResponse( $getECResponse, $bucketNum, $i );
212
+ }
213
+ }
214
+
215
+ $this->shipping_address = new PayPal_Address();
216
+ if ( ! $this->shipping_address->loadFromGetECResponse( $getECResponse, 'PAYMENTREQUEST_' . $bucketNum . '_SHIPTO' ) ) {
217
+ $this->shipping_address = false;
218
+ } else {
219
+ $found_any = true;
220
+ }
221
+
222
+ return $found_any;
223
+ }
224
+ }
225
+
226
+ class PayPal_Checkout_Payment_Item_Details {
227
+ public $name = false;
228
+ public $description = false;
229
+ public $amount = false;
230
+ public $item_number = false;
231
+ public $quantity = false;
232
+ public $tax_amount = false;
233
+
234
+ public $physical_details = false;
235
+ public $ebay_item_details = false;
236
+
237
+ public function loadFromGetECResponse( $getECResponse, $bucketNum, $itemNum ) {
238
+ $map = array(
239
+ 'NAME' => 'name',
240
+ 'DESC' => 'description',
241
+ 'AMT' => 'amount',
242
+ 'NUMBER' => 'item_number',
243
+ 'QTY' => 'quantity',
244
+ 'TAXAMT' => 'tax_amount'
245
+ );
246
+
247
+ foreach ( $map as $index => $value ) {
248
+ $var_name = 'L_PAYMENTREQUEST_' . $bucketNum . '_' . $index . $itemNum;
249
+ if ( array_key_exists( $var_name, $getECResponse ) ) {
250
+ $this->$value = $getECResponse[ $var_name ];
251
+ }
252
+ }
253
+
254
+ $this->physical_details = new PayPal_Checkout_Payment_Item_Physical_Details();
255
+ if ( ! $this->physical_details->loadFromGetECResponse( $getECResponse, $bucketNum, $itemNum ) ) {
256
+ $this->physical_details = false;
257
+ }
258
+
259
+ $this->ebay_item_details = new PayPal_Checkout_Payment_Item_Ebay_Item_Details();
260
+ if ( ! $this->ebay_item_details->loadFromGetECResponse( $getECResponse, $bucketNum, $itemNum ) ) {
261
+ $this->ebay_item_details = false;
262
+ }
263
+ }
264
+ }
265
+
266
+ class PayPal_Checkout_Payment_Item_Physical_Details {
267
+ public $weight;
268
+ public $weight_units;
269
+
270
+ public $length;
271
+ public $length_units;
272
+
273
+ public $width;
274
+ public $width_units;
275
+
276
+ public $height;
277
+ public $height_units;
278
+
279
+ public function loadFromGetECResponse( $getECResponse, $bucketNum, $itemNum ) {
280
+ $map = array(
281
+ 'WEIGHTVALUE' => 'weight',
282
+ 'WEIGHTUNIT' => 'weight_units',
283
+ 'LENGTHVALUE' => 'length',
284
+ 'LENGTHUNIT' => 'length_units',
285
+ 'WIDTHVALUE' => 'width',
286
+ 'WIDTHUNIT' => 'width_units',
287
+ 'HEIGHTVALUE' => 'height',
288
+ 'HEIGHTUNIT' => 'height_units'
289
+ );
290
+ $found_any = false;
291
+
292
+ foreach ( $map as $index => $value ) {
293
+ $var_name = 'L_PAYMENTREQUEST_' . $bucketNum . '_ITEM' . $index . $itemNum;
294
+ if ( array_key_exists( $var_name, $getECResponse ) ) {
295
+ $this->$value = $getECResponse[ $var_name ];
296
+ $found_any = true;
297
+ }
298
+ }
299
+
300
+ return $found_any;
301
+ }
302
+ }
303
+
304
+ class PayPal_Checkout_Payment_Item_Ebay_Item_Details {
305
+ public $item_number = false;
306
+ public $auction_transaction_id = false;
307
+ public $order_id = false;
308
+ public $cart_id = false;
309
+
310
+ public function loadFromGetECResponse( $getECResponse, $bucketNum, $itemNum ) {
311
+ $map = array(
312
+ 'ITEMNUMBER' => 'item_number',
313
+ 'AUCTIONTXNID' => 'auction_transaction_id',
314
+ 'ORDERID' => 'order_id',
315
+ 'CARTID' => 'cart_id'
316
+ );
317
+
318
+ $found_any = false;
319
+ foreach ( $map as $index => $value ) {
320
+ $var_name = 'L_PAYMENTREQUEST_' . $bucketNum . '_' . $index . $itemNum;
321
+ if ( array_key_exists( $var_name, $getECResponse ) ) {
322
+ $this->$value = $getECResponse[ $var_name ];
323
+ $found_any = true;
324
+ }
325
+ }
326
+
327
+ return $found_any;
328
+ }
329
+ }
330
+
331
+ class PayPal_Checkout_Shipping_Option_Details {
332
+ public $calculation_mode = false;
333
+
334
+ const CalculationModeCallback = 'Callback';
335
+ const CalculationModeFlatrate = 'FlatRate';
336
+
337
+ public $insurance_option_selected = false;
338
+ public $shipping_option_is_default = false;
339
+ public $shipping_option_amount = false;
340
+ public $shipping_option_name = false;
341
+
342
+ // Returns true to indicate that the getECResponse array contained variables that were pertinent to this object.
343
+ // If not, it returns false to indicate that the caller can destroy this object.
344
+ public function loadFromGetECResponse( $getECResponse ) {
345
+ $map = array(
346
+ 'SHIPPINGCALCULATIONMODE' => 'calculation_mode',
347
+ 'INSURANCEOPTIONSELECTED' => 'insurance_option_selected',
348
+ 'SHIPPINGOPTIONISDEFAULT' => 'shipping_option_is_default',
349
+ 'SHIPPINGOPTIONAMOUNT' => 'shipping_option_amount',
350
+ 'SHIPPINGOPTIONNAME' => 'shipping_option_name'
351
+ );
352
+ $found_any = false;
353
+ foreach ( $getECResponse as $index => $value ) {
354
+ if ( array_key_exists( $index, $map ) ) {
355
+ $this->$map[ $index ] = $value;
356
+ $found_any = true;
357
+ }
358
+ }
359
+
360
+ return $found_any;
361
+ }
362
+ }
363
+
364
+ class PayPal_Checkout_Instrument_Details {
365
+ public $instrument_category = false;
366
+
367
+ const InstrumentCategoryPayPalCredit = '1';
368
+ const InstrumentCategoryPrivateCard = '2';
369
+
370
+ public $instrument_id = false;
371
+
372
+ // Returns true to indicate that the getECResponse array contained variables that were pertinent to this object.
373
+ // If not, it returns false to indicate that the caller can destroy this object.
374
+ public function loadFromGetECResponse( $getECResponse ) {
375
+ $map = array(
376
+ 'INSTRUMENTCATEGORY' => 'instrument_category',
377
+ 'INSTRUMENTID' => 'instrument_id'
378
+ );
379
+ $found_any = false;
380
+
381
+ foreach ( $getECResponse as $index => $value ) {
382
+ if ( array_key_exists( $index, $map ) ) {
383
+ $this->$map[ $index ] = $value;
384
+ $found_any = true;
385
+ }
386
+ }
387
+
388
+ return $found_any;
389
+ }
390
+ }
391
+
392
+ class PayPal_Checkout_Wallet_Details {
393
+ public $wallet_type = false;
394
+
395
+ const WalletTypeLoyaltyCard = 'LOYALTY_CARD';
396
+ const WalletTypeMerchantCoupon = 'MERCHANT_COUPON';
397
+ const WalletTypeMerchantClosedLoopOffer = 'MERCHANT_CLOSED_LOOP_OFFER';
398
+
399
+ public $wallet_id = false;
400
+ public $wallet_description = false;
401
+
402
+ public function __construct( $wallet_type = false, $wallet_id = false, $wallet_description = false ) {
403
+ $this->wallet_type = $wallet_type;
404
+ $this->wallet_id = $wallet_id;
405
+ $this->wallet_description = $wallet_description;
406
+ }
407
+
408
+ // Returns true to indicate that the getECResponse array contained variables that were pertinent to this object.
409
+ // If not, it returns false to indicate that the caller can destroy this object.
410
+ public function loadFromGetECResponse( $getECResponse, $wallet_num ) {
411
+ $found_any = false;
412
+ foreach ( $getECResponse as $index => $value ) {
413
+ if ( ( 'WALLETTYPE' . $wallet_num ) == $index ) {
414
+ $this->wallet_type = $value;
415
+ $found_any = true;
416
+ } elseif ( ( 'WALLETID' . $wallet_num ) == $index ) {
417
+ $this->wallet_id = $value;
418
+ $found_any = true;
419
+ } elseif ( ( 'WALLETDESCRIPTION' . $wallet_num ) == $index ) {
420
+ $this->wallet_description = $value;
421
+ $found_any = true;
422
+ }
423
+ }
424
+
425
+ return $found_any;
426
+ }
427
+ }
428
+
429
+ class PayPal_Checkout_Payer_Details {
430
+ public $phone_number = false;
431
+ public $email = false;
432
+ public $payer_id = false;
433
+ public $payer_status = false;
434
+
435
+ const PayerStatusVerified = 'verified';
436
+ const PayerStatusUnverified = 'unverified';
437
+
438
+ public $country = false;
439
+ public $business_name = false;
440
+ public $first_name = false;
441
+ public $last_name = false;
442
+ public $middle_name = false;
443
+ public $suffix = false;
444
+
445
+ public $billing_address = false;
446
+
447
+ // Returns true to indicate that the getECResponse array contained variables that were pertinent to this object.
448
+ // If not, it returns false to indicate that the caller can destroy this object.
449
+ public function loadFromGetECResponse( $getECResponse ) {
450
+ $map = array(
451
+ 'PHONENUM' => 'phone_number',
452
+ 'EMAIL' => 'email',
453
+ 'PAYERID' => 'payer_id',
454
+ 'PAYERSTATUS' => 'payer_status',
455
+ 'COUNTRYCODE' => 'country',
456
+ 'BUSINESS' => 'business_name',
457
+ 'FIRSTNAME' => 'first_name',
458
+ 'MIDDLENAME' => 'middle_name',
459
+ 'LASTNAME' => 'last_name',
460
+ 'SUFFIX' => 'suffix'
461
+ );
462
+ $found_any = false;
463
+
464
+ // At the same time, see if we have a billing address that needs to be parsed out.
465
+ $billing_address_present = false;
466
+
467
+ foreach ( $getECResponse as $index => $value ) {
468
+ if ( array_key_exists( $index, $map ) ) {
469
+ $this->$map[ $index ] = $value;
470
+ $found_any = true;
471
+ }
472
+ if ( preg_match( '/^BILLTONAME|STREET|STREET2|CITY|STATE|ZIP|COUNTRY|COUNTRYNAME|ADDRESSOWNER|ADDRESSSTATUS$/', $index ) ) {
473
+ $billing_address_present = true;
474
+ }
475
+ }
476
+
477
+ if ( $billing_address_present ) {
478
+ $this->billing_address = new PayPal_Address();
479
+ if ( $this->billing_address->loadFromGetECResponse( $getECResponse, '', true ) ) {
480
+ $found_any = true;
481
+ } else {
482
+ $this->billing_address = false;
483
+ }
484
+ }
485
+
486
+ return $found_any;
487
+ }
488
+
489
+ }
490
+
491
+ class PayPal_Checkout_Gift_Details {
492
+ public $gift_message = false;
493
+ public $gift_receipt_enabled = false;
494
+ public $gift_wrap_name = false;
495
+ public $gift_wrap_amount = false;
496
+
497
+ // Returns true to indicate that the getECResponse array contained variables that were pertinent to this object.
498
+ // If not, it returns false to indicate that the caller can destroy this object.
499
+ public function loadFromGetECResponse( $getECResponse ) {
500
+ $map = array(
501
+ 'GIFTMESSAGE' => 'gift_message',
502
+ 'GIFTWRAPNAME' => 'gift_wrap_name',
503
+ 'GIFTRECEIPTENABLE' => 'gift_receipt_enabled',
504
+ 'GIFTWRAPAMOUNT' => 'gift_wrap_amount'
505
+ );
506
+ $found_any = false;
507
+
508
+ foreach ( $getECResponse as $index => $value ) {
509
+ if ( array_key_exists( $index, $map ) ) {
510
+ $this->$map[ $index ] = $value;
511
+ $found_any = true;
512
+ }
513
+ }
514
+
515
+ return $found_any;
516
+ }
517
+ }
includes/class-wc-gateway-ppec-checkout-handler.php ADDED
@@ -0,0 +1,673 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Cart handler.
4
+ */
5
+
6
+ if ( ! defined( 'ABSPATH' ) ) {
7
+ exit; // Exit if accessed directly
8
+ }
9
+
10
+ $includes_path = wc_gateway_ppec()->includes_path;
11
+
12
+ // TODO: Use spl autoload to require on-demand maybe?
13
+
14
+ require_once( $includes_path . 'class-wc-gateway-ppec-settings.php' );
15
+ require_once( $includes_path . 'class-wc-gateway-ppec-session-data.php' );
16
+ require_once( $includes_path . 'class-wc-gateway-ppec-checkout-details.php' );
17
+
18
+ require_once( $includes_path . 'class-wc-gateway-ppec-api-error.php' );
19
+ require_once( $includes_path . 'exceptions/class-wc-gateway-ppec-api-exception.php' );
20
+ require_once( $includes_path . 'exceptions/class-wc-gateway-ppec-missing-session-exception.php' );
21
+
22
+ require_once( $includes_path . 'class-wc-gateway-ppec-payment-details.php' );
23
+ require_once( $includes_path . 'class-wc-gateway-ppec-address.php' );
24
+
25
+ class WC_Gateway_PPEC_Checkout_Handler {
26
+
27
+ protected $_suppressShippingAddress;
28
+
29
+ // $_shippingAddress can be a single PayPal_Address object, or an array of PayPal_Address objects
30
+ // (for the purposes of doing parallel payments).
31
+ protected $_shippingAddress;
32
+ protected $_requestBillingAgreement;
33
+ protected $_enablePayPalCredit;
34
+
35
+ public function __construct() {
36
+ $this->_suppressShippingAddress = false;
37
+ $this->_shippingAddress = false;
38
+ $this->_requestBillingAgreement = false;
39
+
40
+ add_action( 'woocommerce_init', array( $this, 'init' ) );
41
+
42
+ add_action( 'wp', array( $this, 'maybe_return_from_paypal' ) );
43
+
44
+ add_action( 'woocommerce_before_checkout_process', array( $this, 'before_checkout_process' ) );
45
+ add_filter( 'woocommerce_checkout_fields', array( $this, 'make_billing_address_optional' ) );
46
+ add_action( 'woocommerce_after_checkout_form', array( $this, 'after_checkout_form' ) );
47
+ add_action( 'woocommerce_available_payment_gateways', array( $this, 'maybe_disable_other_gateways' ) );
48
+ add_action( 'woocommerce_available_payment_gateways', array( $this, 'maybe_disable_paypal_credit' ) );
49
+
50
+ add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
51
+ }
52
+
53
+ public function init() {
54
+ // If the buyer clicked on the "Check Out with PayPal" button, we need to wait for the cart
55
+ // totals to be available. Unfortunately that doesn't happen until
56
+ // woocommerce_before_cart_totals executes, and there is already output sent to the browser by
57
+ // this point. So, to get around this issue, we'll enable output buffering to prevent WP from
58
+ // sending anything back to the browser.
59
+ if ( isset( $_GET['startcheckout'] ) && 'true' === $_GET['startcheckout'] ) {
60
+ ob_start();
61
+ }
62
+ }
63
+
64
+ public function maybe_return_from_paypal() {
65
+ if ( isset( $_GET['woo-paypal-return'] ) && 'true' === $_GET['woo-paypal-return'] ) {
66
+
67
+ // call get ec and do ec
68
+ // Make sure we have our token and payer ID
69
+ if ( array_key_exists( 'token', $_GET )
70
+ && array_key_exists( 'PayerID', $_GET )
71
+ && ! empty( $_GET['token'] )
72
+ && ! empty( $_GET['PayerID'] ) ) {
73
+
74
+ $token = $_GET['token'];
75
+ $payer_id = $_GET['PayerID'];
76
+
77
+ } else {
78
+
79
+ // If the token and payer ID aren't there, just ignore this request
80
+ return;
81
+
82
+ }
83
+
84
+ try {
85
+ $checkout_details = $this->getCheckoutDetails( $token );
86
+ } catch( PayPal_API_Exception $e ) {
87
+ wc_add_notice( __( 'Sorry, an error occurred while trying to retrieve your information from PayPal. Please try again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
88
+ return;
89
+ } catch( PayPal_Missing_Session_Exception $e ) {
90
+ wc_add_notice( __( 'Your PayPal checkout session has expired. Please check out again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
91
+ return;
92
+ }
93
+
94
+ $session = WC()->session->paypal;
95
+ if ( ! $session || ! is_a( $session, 'WC_Gateway_PPEC_Session_Data' ) ||
96
+ $session->expiry_time < time() || $token != $session->token ) {
97
+ wc_add_notice( __( 'Your PayPal checkout session has expired. Please check out again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
98
+ return;
99
+ }
100
+
101
+ $session->checkout_completed = true;
102
+ $session->payerID = $payer_id;
103
+
104
+ WC()->session->paypal = $session;
105
+
106
+ if ( $session->using_ppc ) {
107
+ WC()->session->chosen_payment_method = 'ppec_paypal_credit';
108
+ } else {
109
+ WC()->session->chosen_payment_method = 'ppec_paypal';
110
+ }
111
+
112
+ if ( 'order' == $session->leftFrom && $session->order_id ) {
113
+ // Try to complete the payment now.
114
+ try {
115
+ $order_id = $session->order_id;
116
+ $payment_details = $this->completePayment( $order_id, $session->token, $session->payerID );
117
+ $transaction_id = $payment_details->payments[0]->transaction_id;
118
+
119
+ // TODO: Handle things like eChecks, giropay, etc.
120
+ $order = wc_get_order( $order_id );
121
+ $order->payment_complete( $transaction_id );
122
+ $order->add_order_note( sprintf( __( 'PayPal transaction completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $transaction_id ) );
123
+ $order->reduce_order_stock();
124
+ WC()->cart->empty_cart();
125
+ unset( WC()->session->paypal );
126
+
127
+ wp_safe_redirect( $order->get_checkout_order_received_url() );
128
+ exit;
129
+ } catch( PayPal_Missing_Session_Exception $e ) {
130
+ // For some reason, our session data is missing. Generally, if we've made it this far,
131
+ // this shouldn't happen.
132
+ wc_add_notice( __( 'Sorry, an error occurred while trying to process your payment. Please try again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
133
+ } catch( PayPal_API_Exception $e ) {
134
+ // Did we get a 10486 or 10422 back from PayPal? If so, this means we need to send the buyer back over to
135
+ // PayPal to have them pick out a new funding method.
136
+ $need_to_redirect_back = false;
137
+ foreach ( $e->errors as $error ) {
138
+ if ( '10486' == $error->error_code || '10422' == $error->error_code ) {
139
+ $need_to_redirect_back = true;
140
+ }
141
+ }
142
+
143
+ if ( $need_to_redirect_back ) {
144
+ $settings = new WC_Gateway_PPEC_Settings();
145
+ $session->checkout_completed = false;
146
+ $session->leftFrom = 'order';
147
+ $session->order_id = $order_id;
148
+ WC()->session->paypal = $session;
149
+ wp_safe_redirect( $settings->getPayPalRedirectUrl( $session->token, true ) );
150
+ exit;
151
+ } else {
152
+ $final_output = '<ul>';
153
+ foreach ( $e->errors as $error ) {
154
+ $final_output .= '<li>' . __( $error->maptoBuyerFriendlyError(), 'woocommerce-gateway-paypal-express-checkout' ) . '</li>';
155
+ }
156
+ $final_output .= '</ul>';
157
+ wc_add_notice( __( 'Payment error:', 'woocommerce-gateway-paypal-express-checkout' ) . $final_output, 'error' );
158
+ return;
159
+ }
160
+ }
161
+ }
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Before checkout process.
167
+ *
168
+ * Turn off use of the buyer email in the payment method title so that it
169
+ * doesn't appear in emails.
170
+ *
171
+ * @return void
172
+ */
173
+ public function before_checkout_process() {
174
+ WC_Gateway_PPEC::$use_buyer_email = false;
175
+ }
176
+
177
+ public function make_billing_address_optional( $checkout_fields ) {
178
+ $session = WC()->session->paypal;
179
+ if ( is_a( $session, 'WC_Gateway_PPEC_Session_Data' ) && $session->checkout_completed && $session->expiry_time >= time() && $session->payerID ) {
180
+ $checkout_fields['billing']['billing_address_1']['required'] = false;
181
+ $checkout_fields['billing']['billing_address_1']['class'][] = 'ppec-bypass';
182
+ $checkout_fields['billing']['billing_address_1']['class'][] = 'hidden';
183
+
184
+ $checkout_fields['billing']['billing_address_2']['required'] = false;
185
+ $checkout_fields['billing']['billing_address_2']['class'][] = 'ppec-bypass';
186
+ $checkout_fields['billing']['billing_address_2']['class'][] = 'hidden';
187
+
188
+ $checkout_fields['billing']['billing_city']['required'] = false;
189
+ $checkout_fields['billing']['billing_city']['class'][] = 'ppec-bypass';
190
+ $checkout_fields['billing']['billing_city']['class'][] = 'hidden';
191
+
192
+ $checkout_fields['billing']['billing_state']['required'] = false;
193
+ $checkout_fields['billing']['billing_state']['class'][] = 'ppec-bypass';
194
+ $checkout_fields['billing']['billing_state']['class'][] = 'hidden';
195
+
196
+ $checkout_fields['billing']['billing_postcode' ]['required'] = false;
197
+ $checkout_fields['billing']['billing_postcode']['class'][] = 'ppec-bypass';
198
+ $checkout_fields['billing']['billing_postcode']['class'][] = 'hidden';
199
+ }
200
+
201
+ return $checkout_fields;
202
+ }
203
+
204
+ /**
205
+ * After checkout form.
206
+ */
207
+ public function after_checkout_form() {
208
+ $settings = wc_gateway_ppec()->settings->loadSettings();
209
+
210
+ if ( $settings->enabled && $settings->enableInContextCheckout && $settings->getActiveApiCredentials()->get_payer_id() ) {
211
+ $session = WC()->session->paypal;
212
+
213
+ // Make sure no session being set from cart.
214
+ if ( $session && is_a( $session, 'WC_Gateway_PPEC_Session_Data' ) ) {
215
+ if ( $session->checkout_completed ) {
216
+ return;
217
+ }
218
+
219
+ if ( $session->expiry_time > time() ) {
220
+ return;
221
+ }
222
+
223
+ if ( ! empty( $session->payerID ) ) {
224
+ return;
225
+ }
226
+ }
227
+
228
+ // This div is necessary for PayPal to properly display its lightbox.
229
+ ?>
230
+ <div id="woo_pp_icc_container" style="display: none;"></div>
231
+ <?php
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Enqueue front-end scripts on checkout page.
237
+ */
238
+ public function enqueue_scripts() {
239
+ if ( ! is_checkout() ) {
240
+ return;
241
+ }
242
+
243
+ $settings = wc_gateway_ppec()->settings->loadSettings();
244
+ if ( ! $settings->enabled ) {
245
+ return;
246
+ }
247
+
248
+ wp_enqueue_style( 'wc-gateway-ppec-frontend-checkout', wc_gateway_ppec()->plugin_url . 'assets/css/wc-gateway-ppec-frontend-checkout.css', array(), wc_gateway_ppec()->version );
249
+
250
+ // On the checkout page, only load the JS if we plan on sending them over to PayPal.
251
+ $payer_id = $settings->getActiveApiCredentials()->get_payer_id();
252
+ if ( $settings->enableInContextCheckout && ! empty( $payer_id ) ) {
253
+ $session = WC()->session->paypal;
254
+ if ( ! $session
255
+ || ! is_a( $session, 'WC_Gateway_PPEC_Session_Data' )
256
+ || ! $session->checkout_completed || $session->expiry_time < time()
257
+ || ! $session->payerID ) {
258
+
259
+ wp_enqueue_script( 'wc-gateway-ppec-frontend-checkout', wc_gateway_ppec()->plugin_url . 'assets/js/wc-gateway-ppec-frontend-checkout.js', array( 'jquery' ), wc_gateway_ppec()->version, true );
260
+ wp_localize_script( 'wc-gateway-ppec-frontend-checkout', 'wc_ppec', array( 'payer_id' => $payer_id ) );
261
+
262
+ wp_enqueue_script( 'paypal-checkout-js', 'https://www.paypalobjects.com/api/checkout.js', array(), null, true );
263
+ }
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Maybe disable other gateways.
269
+ *
270
+ * @since 1.0.0
271
+ * @param array $gateways Available gateways
272
+ *
273
+ * @return array Available gateways
274
+ */
275
+ public function maybe_disable_other_gateways( $gateways ) {
276
+ $session = WC()->session->paypal;
277
+
278
+ // Unset all other gateways after checking out from cart.
279
+ if ( is_a( $session, 'WC_Gateway_PPEC_Session_Data' ) && $session->payerID && $session->expiry_time > time() ) {
280
+ foreach ( $gateways as $id => $gateway ) {
281
+ if ( 'ppec_paypal' !== $id ) {
282
+ unset( $gateways[ $id ] );
283
+ }
284
+ }
285
+ }
286
+
287
+ // If PPEC is enabled, removed PayPal standard.
288
+ if ( wc_gateway_ppec()->settings->loadSettings()->enabled ) {
289
+ unset( $gateways['paypal'] );
290
+ }
291
+
292
+ return $gateways;
293
+ }
294
+
295
+ /**
296
+ * If base location is not US, disable PayPal Credit.
297
+ *
298
+ * @since 1.0.0
299
+ * @param array $gateways Available gateways
300
+ *
301
+ * @return array Available gateways
302
+ */
303
+ public function maybe_disable_paypal_credit( $gateways ) {
304
+ if ( isset( $gateways['ppec_paypal_credit'] ) && 'US' !== WC()->countries->get_base_country() ) {
305
+ unset( $gateways['ppec_paypal_credit'] );
306
+ }
307
+
308
+ return $gateways;
309
+ }
310
+
311
+ public function enablePayPalCredit( $enable = true ) {
312
+ $this->_enablePayPalCredit = $enable;
313
+ }
314
+
315
+ public function suppressShippingAddress( $suppress = true ) {
316
+ if ( $suppress ) {
317
+ $this->_suppressShippingAddress = true;
318
+ } else {
319
+ $this->_suppressShippingAddress = false;
320
+ }
321
+ }
322
+
323
+ public function setShippingAddress( $address ) {
324
+ if ( is_a( $address, 'PayPal_Address' ) ) {
325
+ $this->_shippingAddress = $address;
326
+ }
327
+ if ( is_array( $address ) ) {
328
+ // Check each of the elements to make sure they're all PayPal_Address objects as well
329
+ foreach ( $address as $index => $value ) {
330
+ if ( ! is_a( $value, 'PayPal_Address' ) ) {
331
+ return;
332
+ }
333
+ // And also check to make sure we're not exceeding the maximum number of parallel
334
+ // payments PayPal will allow
335
+ if ( ! is_int( $index ) || $value > 9 ) {
336
+ return;
337
+ }
338
+ }
339
+
340
+ $this->_shippingAddress = $address;
341
+ }
342
+ }
343
+
344
+ public function requestBillingAgreement( $request = true ) {
345
+ if ( $request ) {
346
+ $this->_requestBillingAgreement = true;
347
+ } else {
348
+ $this->_requestBillingAgreement = false;
349
+ }
350
+ }
351
+
352
+ public function getSetExpressCheckoutParameters() {
353
+ // First off, get the cart parameters
354
+ $params = wc_gateway_ppec()->cart->setECParams();
355
+
356
+ // Now work through the checkout-level variables.
357
+ if ( $this->_suppressShippingAddress ) {
358
+ $params['NOSHIPPING'] = 1;
359
+ }
360
+
361
+ if ( $this->_requestBillingAgreement ) {
362
+ $params['BILLINGTYPE'] = 'MerchantInitiatedBilling';
363
+ }
364
+
365
+ if ( $this->_enablePayPalCredit ) {
366
+ $params['USERSELECTEDFUNDINGSOURCE'] = 'Finance';
367
+ }
368
+
369
+ if ( false !== $this->_shippingAddress ) {
370
+ if ( is_array( $this->_shippingAddress ) ) {
371
+ foreach ( $this->_shippingAddress as $index => $value ) {
372
+ $params = array_merge( $params, $value->getAddressParams( 'PAYMENTREQUEST_' . $index . '_SHIPTO' ) );
373
+ }
374
+ } else {
375
+ $params = array_merge( $params, $this->_shippingAddress->getAddressParams( 'PAYMENTREQUEST_0_SHIPTO' ) );
376
+ }
377
+ }
378
+
379
+ return $params;
380
+ }
381
+
382
+ public function getDoExpressCheckoutParameters( $token, $payer_id ) {
383
+ $params = wc_gateway_ppec()->cart->setECParams();
384
+
385
+ if ( false !== $this->_shippingAddress ) {
386
+ if ( is_array( $this->_shippingAddress ) ) {
387
+ foreach ( $this->_shippingAddress as $index => $value ) {
388
+ $params = array_merge( $params, $value->getAddressParams( 'PAYMENTREQUEST_' . $index . '_SHIPTO' ) );
389
+ }
390
+ } else {
391
+ $params = array_merge( $params, $this->_shippingAddress->getAddressParams( 'PAYMENTREQUEST_0_SHIPTO' ) );
392
+ }
393
+ }
394
+
395
+ $params['TOKEN'] = $token;
396
+ $params['PAYERID'] = $payer_id;
397
+
398
+ return $params;
399
+ }
400
+
401
+ protected function isSuccess( $response ) {
402
+ if ( 'Success' == $response['ACK'] || 'SuccessWithWarning' == $response['ACK'] ) {
403
+ return true;
404
+ } else {
405
+ return false;
406
+ }
407
+ }
408
+
409
+ protected function getReturnUrl() {
410
+
411
+ $url = WC()->cart->get_checkout_url();
412
+ if ( strpos( $url, '?' ) ) {
413
+ $url .= '&';
414
+ } else {
415
+ $url .= '?';
416
+ }
417
+
418
+ $url .= 'woo-paypal-return=true';
419
+
420
+ return $url;
421
+ }
422
+
423
+ protected function getCancelUrl() {
424
+
425
+ $url = WC()->cart->get_cart_url();
426
+ if ( strpos( $url, '?' ) ) {
427
+ $url .= '&';
428
+ } else {
429
+ $url .= '?';
430
+ }
431
+
432
+ $url .= 'woo-paypal-cancel=true';
433
+
434
+ return $url;
435
+ }
436
+
437
+ public function startCheckoutFromCart() {
438
+
439
+ wc_gateway_ppec()->cart->loadCartDetails();
440
+
441
+ $settings = wc_gateway_ppec()->settings->loadSettings();
442
+
443
+ $needs_shipping = WC()->cart->needs_shipping();
444
+ $this->suppressShippingAddress( ! $needs_shipping );
445
+
446
+ $using_ppc = false;
447
+
448
+ if ( array_key_exists( 'use-ppc', $_GET ) && 'true' == $_GET['use-ppc'] ) {
449
+ $this->enablePayPalCredit();
450
+ $using_ppc = true;
451
+ }
452
+
453
+ $params = array_merge(
454
+ $settings->getSetECShortcutParameters(),
455
+ $this->getSetExpressCheckoutParameters()
456
+ );
457
+
458
+ $brand_name = get_bloginfo( 'name', 'display' );
459
+ if ( ! empty( $brand_name ) ) {
460
+ $brand_name = substr( $brand_name, 0, 127 );
461
+ $params['BRANDNAME'] = $brand_name;
462
+ }
463
+
464
+ $params['RETURNURL'] = $this->getReturnUrl();
465
+ $params['CANCELURL'] = $this->getCancelUrl();
466
+
467
+ if ( $this->_requestBillingAgreement ) {
468
+ $params['BILLINGTYPE'] = 'MerchantInitiatedBilling';
469
+ }
470
+
471
+ $response = wc_gateway_ppec()->client->set_express_checkout( $params );
472
+ if ( $this->isSuccess( $response ) ) {
473
+ // Save some data to the session.
474
+ WC()->session->paypal = new WC_Gateway_PPEC_Session_Data(
475
+ $response['TOKEN'],
476
+ 'cart',
477
+ false,
478
+ $needs_shipping,
479
+ $this->_requestBillingAgreement,
480
+ $settings->getECTokenSessionLength(),
481
+ $using_ppc
482
+ );
483
+
484
+ return $settings->getPayPalRedirectUrl( $response['TOKEN'], false );
485
+ } else {
486
+ throw new PayPal_API_Exception( $response );
487
+ }
488
+ }
489
+
490
+ public function startCheckoutFromCheckout( $order_id, $use_ppc = false ) {
491
+
492
+ wc_gateway_ppec()->cart->loadOrderDetails( $order_id );
493
+
494
+ $settings = wc_gateway_ppec()->settings->loadSettings();
495
+
496
+ //new wc order > get address from that order > new pp address > assign address from order to new pp address > $this->setShippingAddress(pp address object)
497
+ $getAddress = wc_get_order( $order_id );
498
+ $shipAddressName = $getAddress->shipping_first_name . ' ' . $getAddress->shipping_last_name;
499
+
500
+ $shipAddress = new PayPal_Address;
501
+ $shipAddress->setName($shipAddressName);
502
+ $shipAddress->setStreet1($getAddress->shipping_address_1);
503
+ $shipAddress->setStreet2($getAddress->shipping_address_2);
504
+ $shipAddress->setCity($getAddress->shipping_city);
505
+ $shipAddress->setState($getAddress->shipping_state);
506
+ $shipAddress->setZip($getAddress->shipping_postcode);
507
+ $shipAddress->setCountry($getAddress->shipping_country);
508
+
509
+ $this->setShippingAddress( $shipAddress );
510
+ $this->enablePayPalCredit( $use_ppc );
511
+
512
+ // Do we also need to grab the phone number and pass it through?
513
+
514
+ $params = array_merge(
515
+ $settings->getSetECMarkParameters(),
516
+ $this->getSetExpressCheckoutParameters()
517
+ );
518
+
519
+ $brand_name = get_bloginfo( 'name', 'display' );
520
+ if ( ! empty( $brand_name ) ) {
521
+ $brand_name = substr( $brand_name, 0, 127 );
522
+ $params['BRANDNAME'] = $brand_name;
523
+ }
524
+
525
+ $params['RETURNURL'] = $this->getReturnUrl();
526
+ $params['CANCELURL'] = $this->getCancelUrl();
527
+
528
+ if ( $this->_requestBillingAgreement ) {
529
+ $params['BILLINGTYPE'] = 'MerchantInitiatedBilling';
530
+ }
531
+
532
+ $params['ADDROVERRIDE'] = '1';
533
+
534
+ $needs_shipping = WC()->cart->needs_shipping();
535
+ $this->suppressShippingAddress( $needs_shipping );
536
+
537
+ $response = wc_gateway_ppec()->client->set_express_checkout( $params );
538
+
539
+ if ( $this->isSuccess( $response ) ) {
540
+ // Save some data to the session.
541
+ WC()->session->paypal = new WC_Gateway_PPEC_Session_Data(
542
+ $response['TOKEN'],
543
+ 'order',
544
+ $order_id,
545
+ $needs_shipping,
546
+ $this->_requestBillingAgreement,
547
+ $settings->getECTokenSessionLength(),
548
+ $use_ppc
549
+ );
550
+
551
+ return $settings->getPayPalRedirectUrl( $response['TOKEN'], true );
552
+ } else {
553
+ throw new PayPal_API_Exception( $response );
554
+ }
555
+
556
+ }
557
+
558
+ public function getCheckoutDetails( $token = false ) {
559
+
560
+ if ( false === $token ) {
561
+ $token = $_GET['token'];
562
+ }
563
+
564
+ $response = wc_gateway_ppec()->client->get_express_checkout_details( $token );
565
+
566
+ if ( 'Success' == $response['ACK'] || 'SuccessWithWarning' == $response['ACK'] ) {
567
+ $checkout_details = new PayPal_Checkout_Details();
568
+ $checkout_details->loadFromGetECResponse( $response );
569
+
570
+ $session_data = WC()->session->paypal;
571
+ if ( null === $session_data ) {
572
+ throw new PayPal_Missing_Session_Exception();
573
+ }
574
+
575
+ if ( is_a( $session_data, 'WC_Gateway_PPEC_Session_Data' ) && $token === $session_data->token ) {
576
+ $session_data->checkout_details = $checkout_details;
577
+ WC()->session->paypal = $session_data;
578
+ } else {
579
+ throw new PayPal_Missing_Session_Exception();
580
+ }
581
+
582
+ return $checkout_details;
583
+ } else {
584
+ throw new PayPal_API_Exception( $response );
585
+ }
586
+ }
587
+
588
+ public function completePayment( $order_id, $token, $payerID ) {
589
+
590
+ // Make sure our session data is there before we do something we might regret later
591
+ $session_data = WC()->session->paypal;
592
+ if ( null === $session_data ) {
593
+ throw new PayPal_Missing_Session_Exception();
594
+ }
595
+
596
+ if ( is_a( $session_data, 'WC_Gateway_PPEC_Session_Data' ) && $token == $session_data->token ) {
597
+ WC()->session->paypal = $session_data;
598
+ } else {
599
+ throw new PayPal_Missing_Session_Exception();
600
+ }
601
+
602
+ // Now make sure we have the GetEC data. If not, well then we'll just fetch it now, pardner.
603
+ if ( ! $session_data->checkout_details || ! is_a( $session_data->checkout_details, 'PayPal_Checkout_Details' ) ) {
604
+ $this->getCheckoutDetails( $token );
605
+ }
606
+
607
+ wc_gateway_ppec()->cart->loadOrderDetails( $order_id );
608
+
609
+ $settings = wc_gateway_ppec()->settings->loadSettings();
610
+
611
+ $order = wc_get_order( $order_id );
612
+
613
+ if ( $session_data->shipping_required ) {
614
+ $shipAddressName = $order->shipping_first_name . ' ' . $order->shipping_last_name;
615
+ $shipAddress = new PayPal_Address;
616
+ $shipAddress->setName($shipAddressName);
617
+ $shipAddress->setStreet1($order->shipping_address_1);
618
+ $shipAddress->setStreet2($order->shipping_address_2);
619
+ $shipAddress->setCity($order->shipping_city);
620
+ $shipAddress->setState($order->shipping_state);
621
+ $shipAddress->setZip($order->shipping_postcode);
622
+ $shipAddress->setCountry($order->shipping_country);
623
+ $this->setShippingAddress( $shipAddress );
624
+ }
625
+
626
+ $params = array_merge(
627
+ $settings->getDoECParameters(),
628
+ $this->getDoExpressCheckoutParameters( $token, $payerID )
629
+ );
630
+
631
+ $params['PAYMENTREQUEST_0_INVNUM'] = $order->get_order_number();
632
+
633
+ $response = wc_gateway_ppec()->client->do_express_checkout_payment( $params );
634
+
635
+ if ( $this->isSuccess( $response ) ) {
636
+ $payment_details = new PayPal_Payment_Details();
637
+ $payment_details->loadFromDoECResponse( $response );
638
+
639
+ $meta = get_post_meta( $order_id, '_woo_pp_txnData', true );
640
+ if ( ! empty($meta) ) {
641
+ $txnData = $meta;
642
+ } else {
643
+ $txnData = array( 'refundable_txns' => array() );
644
+ }
645
+
646
+ $paymentAction = $settings->paymentAction;
647
+ if ( 'Sale' == $paymentAction ) {
648
+ $txn = array(
649
+ 'txnID' => $payment_details->payments[0]->transaction_id,
650
+ 'amount' => $order->get_total(),
651
+ 'refunded_amount' => 0
652
+ );
653
+ if ( 'Completed' == $payment_details->payments[0]->payment_status ) {
654
+ $txn['status'] = 'Completed';
655
+ } else {
656
+ $txn['status'] = $payment_details->payments[0]->payment_status . '_' . $payment_details->payments[0]->pending_reason;
657
+ }
658
+ $txnData['refundable_txns'][] = $txn;
659
+
660
+ } elseif ( 'Authorization' == $paymentAction ) {
661
+ $txnData['auth_status'] = 'NotCompleted';
662
+ }
663
+
664
+ $txnData['txn_type'] = $paymentAction;
665
+
666
+ update_post_meta( $order_id, '_woo_pp_txnData', $txnData );
667
+
668
+ return $payment_details;
669
+ } else {
670
+ throw new PayPal_API_Exception( $response );
671
+ }
672
+ }
673
+ }
includes/class-wc-gateway-ppec-client-credential-certificate.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ class WC_Gateway_PPEC_Client_Credential_Certificate extends WC_Gateway_PPEC_Client_Credential {
8
+
9
+ /**
10
+ * Certificate string.
11
+ *
12
+ * @var string
13
+ */
14
+ protected $_certificate;
15
+
16
+ /**
17
+ * Creates a new instance of certificate-based credential.
18
+ *
19
+ * @param string $username The API username that will be set on this object.
20
+ * @param string $password The API password that will be set on this object.
21
+ * @param string $certificate The API certificate that will be set on this object.
22
+ * @param string $subject The API subject that will be set on this object, or false if there is no subject.
23
+ */
24
+ public function __construct( $username, $password, $certificate, $subject = '' ) {
25
+ $this->_username = $username;
26
+ $this->_password = $password;
27
+ $this->_certificate = $certificate;
28
+ $this->_subject = $subject;
29
+ }
30
+
31
+ /**
32
+ * {@inheritdoc}
33
+ */
34
+ public function get_endpoint_subdomain() {
35
+ return 'api';
36
+ }
37
+
38
+ /**
39
+ * Get certificate.
40
+ *
41
+ * @return string
42
+ */
43
+ public function get_certificate() {
44
+ return $this->_certificate;
45
+ }
46
+ }
includes/class-wc-gateway-ppec-client-credential-signature.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ class WC_Gateway_PPEC_Client_Credential_Signature extends WC_Gateway_PPEC_Client_Credential {
8
+
9
+ /**
10
+ * Signature string.
11
+ *
12
+ * @var string
13
+ */
14
+ protected $_signature;
15
+
16
+ /**
17
+ * Creates a new instance of signature-based credential.
18
+ *
19
+ * @param string $username The API username that will be set on this object.
20
+ * @param string $password The API password that will be set on this object.
21
+ * @param string $signature The API signature that will be set on this object.
22
+ * @param string $subject The API subject that will be set on this object, or false if there is no subject.
23
+ */
24
+ public function __construct( $username, $password, $signature, $subject = '' ) {
25
+ $this->_username = $username;
26
+ $this->_password = $password;
27
+ $this->_signature = $signature;
28
+ $this->_subject = $subject;
29
+ }
30
+
31
+ /**
32
+ * {@inheritdoc}
33
+ */
34
+ public function get_request_params() {
35
+ $params = parent::get_request_params();
36
+ $params['SIGNATURE'] = $this->_signature;
37
+
38
+ return $params;
39
+ }
40
+
41
+ /**
42
+ * {@inheritdoc}
43
+ */
44
+ public function get_endpoint_subdomain() {
45
+ return 'api-3t';
46
+ }
47
+
48
+ /**
49
+ * Get signature.
50
+ *
51
+ * @return string
52
+ */
53
+ public function get_signature() {
54
+ return $this->_signature;
55
+ }
56
+ }
includes/class-wc-gateway-ppec-client.php ADDED
@@ -0,0 +1,323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ /**
8
+ * PayPal NVP (Name-Value Pair) API client. This client supports both certificate
9
+ * and signature for authentication.
10
+ *
11
+ * @see https://developer.paypal.com/docs/classic/api/#ec
12
+ */
13
+ class WC_Gateway_PPEC_Client {
14
+
15
+ /**
16
+ * Client credential.
17
+ *
18
+ * @var WC_Gateway_PPEC_Client_Credential
19
+ */
20
+ protected $_credential;
21
+
22
+ /**
23
+ * PayPal environment. Either 'sandbox' or 'live'.
24
+ *
25
+ * @var string
26
+ */
27
+ protected $_environment;
28
+
29
+ const INVALID_CREDENTIAL_ERROR = 1;
30
+ const INVALID_ENVIRONMENT_ERROR = 2;
31
+ const REQUEST_ERROR = 3;
32
+ const API_VERSION = '120.0';
33
+
34
+ /**
35
+ * Constructor.
36
+ *
37
+ * @param mixed $credential Client's credential
38
+ * @param string $environment Client's environment
39
+ *
40
+ */
41
+ public function __construct( $credential, $environment = 'live' ) {
42
+ $this->_credential = $credential;
43
+ $this->_environment = $environment;
44
+ }
45
+
46
+ /**
47
+ * Set credential for the client.
48
+ *
49
+ * @param WC_Gateway_PPEC_Client_Credential $credential Client's credential
50
+ */
51
+ public function set_credential( WC_Gateway_PPEC_Client_Credential $credential ) {
52
+ $this->_credential = $credential;
53
+ }
54
+
55
+ /**
56
+ * Set environment for the client.
57
+ *
58
+ * @param string $environment Environment. Either 'live' or 'sandbox'
59
+ */
60
+ public function set_environment( $environment ) {
61
+ if ( ! in_array( $environment, array( 'live', 'sandbox' ) ) ) {
62
+ $environment = 'live';
63
+ }
64
+
65
+ $this->_environment = $environment;
66
+ }
67
+
68
+ /**
69
+ * Get PayPal endpoint.
70
+ *
71
+ * @see https://developer.paypal.com/docs/classic/api/#ec
72
+ *
73
+ * @return string
74
+ */
75
+ public function get_endpoint() {
76
+ return sprintf(
77
+ 'https://%s%s.paypal.com/nvp',
78
+
79
+ $this->_credential->get_endpoint_subdomain(),
80
+ 'sandbox' === $this->_environment ? '.sandbox' : ''
81
+ );
82
+ }
83
+
84
+ /**
85
+ * Make a remote request to PayPal API.
86
+ *
87
+ * @see https://developer.paypal.com/docs/classic/api/NVPAPIOverview/#creating-an-nvp-request
88
+ *
89
+ * @param array $params NVP request parameters
90
+ * @return array NVP response
91
+ */
92
+ protected function _request( array $params ) {
93
+ try {
94
+ wc_gateway_ppec_log( sprintf( '%s: trying to make a request to PayPal with params: %s', __METHOD__, print_r( $params, true ) ) );
95
+
96
+ // Make sure $_credential and $_environment have been configured.
97
+ if ( ! $this->_credential ) {
98
+ throw new Exception( __( 'Missing credential', 'woocommerce-gateway-ppec' ), self::INVALID_CREDENTIAL_ERROR );
99
+ }
100
+
101
+ if ( ! is_a( $this->_credential, 'WC_Gateway_PPEC_Client_Credential' ) ) {
102
+ throw new Exception( __( 'Invalid credential object', 'woocommerce-gateway-ppec' ), self::INVALID_CREDENTIAL_ERROR );
103
+ }
104
+
105
+ if ( ! in_array( $this->_environment, array( 'live', 'sandbox' ) ) ) {
106
+ throw new Exception( __( 'Invalid environment', 'woocommerce-gateway-ppec' ), self::INVALID_ENVIRONMENT_ERROR );
107
+ }
108
+
109
+ // First, add in the necessary credential parameters.
110
+ $body = array_merge( $params, $this->_credential->get_request_params() );
111
+ $args = array(
112
+ 'method' => 'POST',
113
+ 'body' => $body,
114
+ 'user-agent' => __CLASS__,
115
+ 'httpversion' => '1.1',
116
+ );
117
+
118
+ wc_gateway_ppec_log( sprintf( '%s: remote request to %s with args: %s', __METHOD__, $this->get_endpoint(), print_r( $args, true ) ) );
119
+
120
+ $resp = wp_safe_remote_post( $this->get_endpoint(), $args );
121
+
122
+ wc_gateway_ppec_log( sprintf( '%s: response from remote request to %s: %s', __METHOD__, $this->get_endpoint(), print_r( $resp, true ) ) );
123
+
124
+ if ( is_wp_error( $resp ) ) {
125
+ throw new Exception( sprintf( __( 'An error occurred while trying to connect to PayPal: %s', 'woocommerce-gateway-ppec' ), $resp->get_error_message() ), self::REQUEST_ERROR );
126
+ }
127
+
128
+ parse_str( wp_remote_retrieve_body( $resp ), $result );
129
+
130
+ if ( ! array_key_exists( 'ACK', $result ) ) {
131
+ throw new Exception( __( 'Malformed response received from PayPal', 'woocommerce-gateway-ppec' ), self::REQUEST_ERROR );
132
+ }
133
+
134
+ wc_gateway_ppec_log( sprintf( '%s: acknowleged response body: %s', __METHOD__, print_r( $result, true ) ) );
135
+
136
+ // Let the caller deals with the response.
137
+ return $result;
138
+
139
+ } catch ( Exception $e ) {
140
+
141
+ // TODO: Maybe returns WP_Error ?
142
+ $error = array(
143
+ 'ACK' => 'Failure',
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: exception is thrown while trying to make a request to PayPal: %s', __METHOD__, $e->getMessage() ) );
151
+ wc_gateway_ppec_log( sprintf( '%s: returns error: %s', __METHOD__, print_r( $error, true ) ) );
152
+
153
+ return $error;
154
+
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Initiates an Express Checkout transaction.
160
+ *
161
+ * @see https://developer.paypal.com/docs/classic/api/merchant/SetExpressCheckout_API_Operation_NVP/
162
+ *
163
+ * @param array $params NVP params
164
+ * @return array NVP response
165
+ */
166
+ public function set_express_checkout( array $params ) {
167
+ $params['METHOD'] = 'SetExpressCheckout';
168
+ $params['VERSION'] = self::API_VERSION;
169
+
170
+ return $this->_request( $params );
171
+ }
172
+
173
+ /**
174
+ * Get details from a given token.
175
+ *
176
+ * @see https://developer.paypal.com/docs/classic/api/merchant/GetExpressCheckoutDetails_API_Operation_NVP/
177
+ *
178
+ * @param array $params NVP params
179
+ * @return array NVP response
180
+ */
181
+ public function get_express_checkout_details( $token ) {
182
+ $params = array(
183
+ 'METHOD' => 'GetExpressCheckoutDetails',
184
+ 'VERSION' => self::API_VERSION,
185
+ 'TOKEN' => $token,
186
+ );
187
+
188
+ return $this->_request( $params );
189
+ }
190
+
191
+ /**
192
+ * Completes an Express Checkout transaction. If you set up a billing agreement
193
+ * in your 'SetExpressCheckout' API call, the billing agreement is created
194
+ * when you call the DoExpressCheckoutPayment API operation.
195
+ *
196
+ * @see https://developer.paypal.com/docs/classic/api/merchant/DoExpressCheckoutPayment_API_Operation_NVP/
197
+ *
198
+ * @param array $params NVP params
199
+ * @return array NVP response
200
+ */
201
+ public function do_express_checkout_payment( $params ) {
202
+ $params['METHOD'] = 'DoExpressCheckoutPayment';
203
+ $params['VERSION'] = self::API_VERSION;
204
+ $params['BUTTONSOURCE'] = 'WooThemes_EC';
205
+
206
+ return $this->_request( $params );
207
+ }
208
+
209
+ public function do_express_checkout_capture( $params ) {
210
+ $params['METHOD'] = 'DoCapture';
211
+ $params['VERSION'] = self::API_VERSION;
212
+
213
+ return $this->_request( $params );
214
+ }
215
+
216
+ public function do_express_checkout_void( $params ) {
217
+ $params['METHOD'] = 'DoVoid';
218
+ $params['VERSION'] = self::API_VERSION;
219
+
220
+ return $this->_request( $params );
221
+ }
222
+
223
+ public function get_transaction_details( $params ) {
224
+ $params['METHOD'] = 'GetTransactionDetails';
225
+ $params['VERSION'] = self::API_VERSION;
226
+
227
+ return $this->_request( $params );
228
+ }
229
+
230
+ /**
231
+ * Obtain your Pal ID, which is the PayPal–assigned merchant account number,
232
+ * and other informaton about your account.
233
+ *
234
+ * @see https://developer.paypal.com/docs/classic/api/merchant/GetPalDetails_API_Operation_NVP/
235
+ *
236
+ * @return array NVP response
237
+ */
238
+ public function get_pal_details() {
239
+ $params['METHOD'] = 'GetPalDetails';
240
+ $params['VERSION'] = self::API_VERSION;
241
+
242
+ return $this->_request( $params );
243
+ }
244
+
245
+ /**
246
+ * Issues a refund to the PayPal account holder associated with a transaction.
247
+ *
248
+ * @see https://developer.paypal.com/docs/classic/api/merchant/RefundTransaction_API_Operation_NVP/
249
+ *
250
+ * @param array $params NVP params
251
+ * @return array NVP response
252
+ */
253
+ public function refund_transaction( $params ) {
254
+ $params['METHOD'] = 'RefundTransaction';
255
+ $params['VERSION'] = self::API_VERSION;
256
+
257
+ return $this->_request( $params );
258
+ }
259
+
260
+ public function test_api_credentials( $credentials, $environment = 'sandbox' ) {
261
+ $this->set_credential( $credentials );
262
+ $this->set_environment( $environment );
263
+
264
+ $result = $this->get_pal_details();
265
+
266
+ if ( 'Success' != $result['ACK'] && 'SuccessWithWarning' != $result['ACK'] ) {
267
+ // Look at the result a little more closely to make sure it's a credentialing issue.
268
+ $found_10002 = false;
269
+ foreach ( $result as $index => $value ) {
270
+ if ( preg_match( '/^L_ERRORCODE\d+$/', $index ) ) {
271
+ if ( '10002' == $value ) {
272
+ $found_10002 = true;
273
+ }
274
+ }
275
+ }
276
+
277
+ if ( $found_10002 ) {
278
+ return false;
279
+ } else {
280
+ // Call failed for some other reason.
281
+ throw new PayPal_API_Exception( $result );
282
+ }
283
+ }
284
+
285
+ return $result['PAL'];
286
+ }
287
+
288
+ // Probe to see whether the merchant has the billing address feature enabled. We do this
289
+ // by running a SetExpressCheckout call with REQBILLINGADDRESS set to 1; if the merchant has
290
+ // this feature enabled, the call will complete successfully; if they do not, the call will
291
+ // fail with error code 11601.
292
+ public function test_for_billing_address_enabled( $credentials, $environment = 'sandbox' ) {
293
+ $this->set_credential( $credentials );
294
+ $this->set_environment( $environment );
295
+
296
+ $req = array(
297
+ 'RETURNURL' => home_url( '/' ),
298
+ 'CANCELURL' => home_url( '/' ),
299
+ 'REQBILLINGADDRESS' => '1',
300
+ 'AMT' => '1.00'
301
+ );
302
+ $result = $this->set_express_checkout( $req );
303
+
304
+ if ( 'Success' != $result['ACK'] && 'SuccessWithWarning' != $result['ACK'] ) {
305
+ $found_11601 = false;
306
+ foreach ( $result as $index => $value ) {
307
+ if ( preg_match( '/^L_ERRORCODE\d+$/', $index ) ) {
308
+ if ( '11601' == $value ) {
309
+ $found_11601 = true;
310
+ }
311
+ }
312
+ }
313
+
314
+ if ( $found_11601 ) {
315
+ return false;
316
+ } else {
317
+ throw new PayPal_API_Exception( $result );
318
+ }
319
+ }
320
+
321
+ return true;
322
+ }
323
+ }
includes/class-wc-gateway-ppec-gateway-loader.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin bootstrapper.
4
+ */
5
+
6
+ if ( ! defined( 'ABSPATH' ) ) {
7
+ exit; // Exit if accessed directly
8
+ }
9
+
10
+ class WC_Gateway_PPEC_Gateway_Loader {
11
+
12
+ public function __construct() {
13
+ $includes_path = wc_gateway_ppec()->includes_path;
14
+
15
+ require_once( $includes_path . 'class-wc-gateway-ppec-refund.php' );
16
+ require_once( $includes_path . 'abstracts/abstract-wc-gateway-ppec.php' );
17
+ require_once( $includes_path . 'class-wc-gateway-ppec-with-paypal.php' );
18
+ require_once( $includes_path . 'class-wc-gateway-ppec-with-paypal-credit.php' );
19
+
20
+ add_filter( 'woocommerce_payment_gateways', array( $this, 'payment_gateways' ) );
21
+ }
22
+
23
+ public function payment_gateways( $methods ) {
24
+ // Defer this check for next release when ppec is ready.
25
+ // If the buyer already went through the PP checkout, then filter out the
26
+ // option they didn't select.
27
+ // $session = is_admin() ? false : WC()->session->get( 'paypal' );
28
+ // if ( ( is_checkout() || is_ajax() ) && $session && is_a( $session, 'WC_Gateway_PPEC_Session_Data' ) &&
29
+ // $session->checkout_completed && $session->expiry_time >= time() &&
30
+ // $session->payerID ) {
31
+ // if ( $session->using_ppc ) {
32
+ // $methods[] = 'WC_Gateway_PPEC_With_PayPal_Credit';
33
+ // } else {
34
+ // $methods[] = 'WC_Gateway_PPEC_With_PayPal';
35
+ // }
36
+ // } else {
37
+ // $methods[] = 'WC_Gateway_PPEC_With_PayPal';
38
+ // $methods[] = 'WC_Gateway_PPEC_With_PayPal_Credit';
39
+ // }
40
+
41
+ $methods[] = 'WC_Gateway_PPEC_With_PayPal';
42
+
43
+ return $methods;
44
+ }
45
+ }
includes/class-wc-gateway-ppec-ips-handler.php ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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';
13
+
14
+ /**
15
+ * Countries that support IPS.
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',
22
+ 'BG', 'BF', 'BI', 'KH', 'CA', 'CV', 'KY', 'TD', 'CL', 'CN', 'C2', 'CO',
23
+ 'KM', 'CG', 'CK', 'CR', 'HR', 'CY', 'CZ', 'CD', 'DK', 'DJ', 'DM', 'DO',
24
+ 'EC', 'EG', 'SV', 'ER', 'EE', 'ET', 'FK', 'FM', 'FJ', 'FI', 'FR', 'GF',
25
+ 'PF', 'GA', 'GM', 'GE', 'DE', 'GI', 'GR', 'GL', 'GD', 'GP', 'GU', 'GT',
26
+ 'GN', 'GW', 'GY', 'VA', 'HN', 'HK', 'HU', 'IS', 'ID', 'IE', 'IT', 'JM',
27
+ 'JO', 'KZ', 'KE', 'KI', 'KW', 'KG', 'LA', 'LV', 'LS', 'LI', 'LT', 'LU',
28
+ 'MG', 'MW', 'MY', 'MV', 'ML', 'MT', 'MH', 'MQ', 'MR', 'MU', 'YT', 'MX',
29
+ 'MN', 'MS', 'MA', 'MZ', 'NA', 'NR', 'NP', 'NL', 'AN', 'NC', 'NZ', 'NI',
30
+ 'NE', 'NU', 'NF', 'NO', 'OM', 'PW', 'PA', 'PG', 'PE', 'PH', 'PN', 'PL',
31
+ 'PT', 'QA', 'RE', 'RO', 'RU', 'RW', 'SH', 'KN', 'LC', 'PM', 'VC', 'WS',
32
+ 'SM', 'ST', 'SA', 'SN', 'RS', 'SC', 'SL', 'SG', 'SK', 'SI', 'SB', 'SO',
33
+ 'ZA', 'KR', 'ES', 'LK', 'SR', 'SJ', 'SZ', 'SE', 'CH', 'TW', 'TJ', 'TH',
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.
40
+ *
41
+ * This is store URL that will be redirected from middleware.
42
+ *
43
+ * @param string $env Environment
44
+ *
45
+ * @return string Redirect URL
46
+ */
47
+ public function get_redirect_url( $env ) {
48
+ if ( ! in_array( $env, array( 'live', 'sandbox' ) ) ) {
49
+ $env = 'live';
50
+ }
51
+
52
+ return add_query_arg(
53
+ array(
54
+ 'env' => $env,
55
+ 'wc_ppec_ips_admin_nonce' => wp_create_nonce( 'wc_ppec_ips' ),
56
+ ),
57
+ wc_gateway_ppec()->admin->gateway_admin_url( 'WC_Gateway_PPEC_With_PayPal' )
58
+ );
59
+ }
60
+
61
+ /**
62
+ * Get login URL to WC middleware.
63
+ *
64
+ * @param string $env Environment
65
+ *
66
+ * @return string Signup URL
67
+ */
68
+ public function get_middleware_login_url( $env ) {
69
+ $service = 'ppe';
70
+ if ( 'sandbox' === $env ) {
71
+ $service = 'ppesandbox';
72
+ }
73
+
74
+ return self::MIDDLEWARE_BASE_URL . '/login/' . $service;
75
+ }
76
+
77
+ /**
78
+ * Get signup URL to WC middleware.
79
+ *
80
+ * @param string $env Environment
81
+ *
82
+ * @return string Signup URL
83
+ */
84
+ public function get_signup_url( $env ) {
85
+ $query_args = array(
86
+ 'redirect' => urlencode( $this->get_redirect_url( $env ) ),
87
+ 'countryCode' => WC()->countries->get_base_country(),
88
+ 'merchantId' => md5( site_url( '/' ) . time() ),
89
+ );
90
+
91
+ return add_query_arg( $query_args, $this->get_middleware_login_url( $env ) );
92
+ }
93
+
94
+ /**
95
+ * Check if base location country supports IPS.
96
+ *
97
+ * @return bool Returns true of base country in supported countries
98
+ */
99
+ public function is_supported() {
100
+ return in_array( WC()->countries->get_base_country(), $this->_supported_countries );
101
+ }
102
+
103
+ /**
104
+ * Redirect with messages.
105
+ *
106
+ * @return void
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
+ }
116
+
117
+ add_option( 'woo_pp_admin_error', $error_msgs );
118
+ wp_safe_redirect( wc_gateway_ppec()->admin->gateway_admin_url( 'WC_Gateway_PPEC_With_PayPal' ) );
119
+ exit;
120
+ }
121
+
122
+ /**
123
+ * Maybe received credentials after successfully returned from IPS flow.
124
+ *
125
+ * @return mixed
126
+ */
127
+ public function maybe_received_credentials() {
128
+ if ( ! is_admin() || ! is_user_logged_in() ) {
129
+ return false;
130
+ }
131
+
132
+ // Require the nonce.
133
+ if ( empty( $_GET['wc_ppec_ips_admin_nonce'] ) || empty( $_GET['env'] ) ) {
134
+ return false;
135
+ }
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
+
143
+ wc_gateway_ppec_log( sprintf( '%s: returned back from IPS flow with parameters: %s', __METHOD__, print_r( $_GET, true ) ) );
144
+
145
+ // Check if error.
146
+ if ( ! empty( $_GET['error'] ) ) {
147
+ $error_message = ! empty( $_GET['error_message'] ) ? $_GET['error_message'] : '';
148
+ wc_gateway_ppec_log( sprintf( '%s: returned back from IPS flow with error: %s', __METHOD__, $error_message ) );
149
+
150
+ $this->_redirect_with_messages( __( 'Sorry, Easy Setup encountered an error. Please try again.', 'woocommerce-gateway-paypal-express-checkout' ) );
151
+ }
152
+
153
+ // Make sure credentials present in query string.
154
+ foreach ( array( 'api_style', 'api_username', 'api_password', 'signature' ) as $param ) {
155
+ if ( empty( $_GET[ $param ] ) ) {
156
+ wc_gateway_ppec_log( sprintf( '%s: returned back from IPS flow but missing parameter %s', __METHOD__, $param ) );
157
+
158
+ $this->_redirect_with_messages( __( 'Sorry, Easy Setup encountered an error. Please try again.', 'woocommerce-gateway-paypal-express-checkout' ) );
159
+ }
160
+ }
161
+
162
+ // TODO: Check for IPS certificate-style.
163
+ $creds = new WC_Gateway_PPEC_Client_Credential_Signature(
164
+ $_GET['api_username'],
165
+ $_GET['api_password'],
166
+ $_GET['signature']
167
+ );
168
+
169
+ $error_msgs = array();
170
+ try {
171
+ $payer_id = wc_gateway_ppec()->client->test_api_credentials( $creds, $env );
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
+ $creds->set_payer_id( $payer_id );
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
+ $is_enabled_for_billing_address = false;
183
+ try {
184
+ $is_enabled_for_billing_address = wc_gateway_ppec()->client->test_for_billing_address_enabled( $creds, $env );
185
+ } catch( PayPal_API_Exception $ex ) {
186
+ $error_msgs[] = array(
187
+ 'warning' => __( 'Easy Setup encountered an error while trying to determine which features are enabled on your PayPal account. You may not see all of the features below that are enabled for your PayPal account. To try again, click "Save Changes".', 'woocommerce-gateway-paypal-express-checkout' )
188
+ );
189
+ }
190
+
191
+ $error_msgs[] = array(
192
+ 'success' => __( 'Success! Your PayPal account has been set up successfully.', 'woocommerce-gateway-paypal-express-checkout' )
193
+ );
194
+
195
+ $settings = wc_gateway_ppec()->settings->loadSettings();
196
+
197
+ if ( ! $settings->enabled ) {
198
+ $error_msgs[] = array(
199
+ 'warning' => __( 'PayPal Express Checkout is not enabled. To allow your buyers to pay with PayPal, make sure "Enable PayPal Express Checkout" is checked.', 'woocommerce-gateway-paypal-express-checkout' )
200
+ );
201
+ }
202
+
203
+ if ( ! empty( $error_msgs ) ) {
204
+ wc_gateway_ppec_log( sprintf( '%s: returned back from IPS flow but with warnings/errors: %s', __METHOD__, print_r( $error_msgs, true ) ) );
205
+ }
206
+
207
+ $settings->environment = $env;
208
+ if ( 'live' == $env ) {
209
+ $settings->liveApiCredentials = $creds;
210
+ } else {
211
+ $settings->sandboxApiCredentials = $creds;
212
+ }
213
+
214
+ $settings->saveSettings();
215
+
216
+ $this->_redirect_with_messages( $error_msgs );
217
+ }
218
+ }
includes/class-wc-gateway-ppec-payment-details.php ADDED
@@ -0,0 +1,451 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * TODO: Move each class into its own file and group them under one dir, payment-details.
5
+ */
6
+
7
+ if ( ! defined( 'ABSPATH' ) ) {
8
+ exit; // Exit if accessed directly
9
+ }
10
+
11
+ class PayPal_Payment_Details {
12
+ public $token = false;
13
+ public $billing_agreement_id = false;
14
+ public $redirect_required = false;
15
+ public $redirect_requested = false;
16
+ public $note = false;
17
+
18
+ public $payments = false;
19
+
20
+ public $shipping_option_details = false;
21
+
22
+ public function loadFromDoECResponse( $doECResponse ) {
23
+ $map = array(
24
+ 'TOKEN' => 'token',
25
+ 'BILLINGAGREEMENTID' => 'billing_agreement_id',
26
+ 'REDIRECTREQUIRED' => 'redirect_required',
27
+ 'SUCCESSPAGEREDIRECTREQUESTED' => 'redirect_requested',
28
+ 'NOTE' => 'note'
29
+ );
30
+
31
+ $max_payment_num = -1;
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 ) ) {
39
+ if ( $matches[1] > $max_payment_num ) {
40
+ $max_payment_num = $matches[1];
41
+ }
42
+ }
43
+ }
44
+
45
+ if ( $max_payment_num >= 0 ) {
46
+ $this->payments = array();
47
+ for ( $i = 0; $i <= $max_payment_num; $i++ ) {
48
+ $this->payments[ $i ] = new PayPal_Payment_Payment_Details();
49
+ $this->payments[ $i ]->loadFromDoECResponse( $doECResponse, $i );
50
+ }
51
+ }
52
+
53
+ $this->shipping_option_details = new PayPal_Payment_Shipping_Option_Details();
54
+ if ( ! $this->shipping_option_details->loadFromDoECResponse( $doECResponse ) ) {
55
+ $this->shipping_option_details = false;
56
+ }
57
+
58
+ }
59
+ }
60
+
61
+ class PayPal_Payment_Payment_FMF_Details {
62
+ public $filters = false;
63
+
64
+ function loadFromDoECResponse( $doECResponse, $bucketNum ) {
65
+ $max_filter_num = array(
66
+ 'PENDING' => -1,
67
+ 'REPORT' => -1,
68
+ 'DENY' => -1,
69
+ 'ACCEPT' => -1
70
+ );
71
+
72
+ $found_any = false;
73
+ foreach ( $doECResponse as $index => $value ) {
74
+ if ( preg_match( '/^L_PAYMENTINFO_' . $bucketNum . '_FMF(PENDING|REPORT|DENY|ACCEPT)(ID|NAME)(\d+)$/', $index, $matches ) ) {
75
+ $found_any = true;
76
+ if ( $matches[3] > $max_filter_num[ $matches[1] ] ) {
77
+ $max_filter_num[ $matches[1] ] = $matches[3];
78
+ }
79
+ }
80
+ }
81
+
82
+ // If we didn't find anything in the initial scan, bail out now.
83
+ if ( ! $found_any ) {
84
+ return false;
85
+ }
86
+
87
+ $this->filters = array();
88
+ foreach ( $max_filter_num as $index => $value ) {
89
+ for ( $i = 0; $i <= $value; $i++ ) {
90
+ $prefix = 'L_PAYMENTINFO_' . $bucketNum . '_FMF' . $index;
91
+ if ( array_key_exists( $prefix . 'NAME' . $i, $doECResponse ) && array_key_exists( $prefix . 'ID' . $i, $doECResponse ) ) {
92
+ $filters[] = new PayPal_Payment_Fraud_Management_Filter( $doECResponse[ $prefix . 'NAME' . $i ], $doECResponse[ $prefix . 'ID' . $i ], $index );
93
+ }
94
+ }
95
+ }
96
+
97
+ return true;
98
+ }
99
+ }
100
+
101
+ class PayPal_Payment_Fraud_Management_Filter {
102
+ public $name;
103
+ public $id;
104
+ public $status;
105
+
106
+ const FraudManagementFilterPending = 'PENDING';
107
+ const FraudManagementFilterReport = 'REPORT';
108
+ const FraudManagementFilterDeny = 'DENY';
109
+ const FraudManagementFilterAccept = 'ACCEPT';
110
+
111
+ public function __construct( $name, $id, $status ) {
112
+ $this->name = $name;
113
+ $this->id = $id;
114
+ $this->status = $status;
115
+ }
116
+ }
117
+
118
+ class PayPal_Payment_Shipping_Option_Details {
119
+ public $calculation_mode = false;
120
+ public $insurance_option_selected = false;
121
+ public $shipping_option_is_default = false;
122
+ public $shipping_option_amount = false;
123
+ public $shipping_option_name = false;
124
+
125
+ public function loadFromDoECResponse( $doECResponse ) {
126
+ $map = array(
127
+ 'SHIPPINGCALCULATIONMODE' => 'calculation_mode',
128
+ 'INSURANCEOPTIONSELECTED' => 'insurance_option_selected',
129
+ 'SHIPPINGOPTIONISDEFAULT' => 'shipping_option_is_default',
130
+ 'SHIPPINGOPTIONAMOUNT' => 'shipping_option_amount',
131
+ 'SHIPPINGOPTIONNAME' => 'shipping_option_name'
132
+ );
133
+
134
+ $found_any = false;
135
+ foreach ( $map as $index => $value ) {
136
+ if ( array_key_exists( $index, $doECResponse ) ) {
137
+ $this->$value = $doECResponse[ $index ];
138
+ $found_any = true;
139
+ }
140
+ }
141
+
142
+ return $found_any;
143
+ }
144
+ }
145
+
146
+ class PayPal_Payment_Payment_Details {
147
+ public $transaction_id = false;
148
+ public $ebay_item_auction_transaction_id = false;
149
+ public $parent_transaction_id = false;
150
+ public $receipt_id = false;
151
+ public $transaction_type = false;
152
+
153
+ const TransactionTypeCart = 'cart';
154
+ const TransactionTypeExpressCheckout = 'express-checkout';
155
+
156
+ public $payment_type = false;
157
+
158
+ const PaymentTypeNone = 'none';
159
+ const PaymentTypeEcheck = 'echeck';
160
+ const PaymentTypeInstant = 'instant';
161
+
162
+ public $expected_echeck_clear_date = false;
163
+ public $order_time = false;
164
+ public $amount = false;
165
+ public $currency_code = false;
166
+ public $fee_amount = false;
167
+ public $settlement_amount = false;
168
+ public $tax_amount = false;
169
+ public $exchange_rate = false;
170
+ public $payment_status = false;
171
+
172
+ const PaymentStatusNone = 'None';
173
+ const PaymentStatusCanceledReversal = 'Canceled-Reversal';
174
+ const PaymentStatusCompleted = 'Completed';
175
+ const PaymentStatusDenied = 'Denied';
176
+ const PaymentStatusExpired = 'Expired';
177
+ const PaymentStatusFailed = 'Failed';
178
+ const PaymentStatusInProgress = 'In-Progress';
179
+ const PaymentStatusPartiallyRefunded = 'Partially-Refunded';
180
+ const PaymentStatusPending = 'Pending';
181
+ const PaymentStatusRefunded = 'Refunded';
182
+ const PaymentStatusReversed = 'Reversed';
183
+ const PaymentStatusProcessed = 'Processed';
184
+ const PaymentStatusVoided = 'Voided';
185
+ const PaymentStatusCompletedFundsHeld = 'Completed-Funds-Held';
186
+
187
+ public $pending_reason = false;
188
+
189
+ const PendingReasonNone = 'none';
190
+ const PendingReasonAddress = 'address';
191
+ const PendingReasonAuthorization = 'authorization';
192
+ const PendingReasonEcheck = 'echeck';
193
+ const PendingReasonInternational = 'intl';
194
+ const PendingReasonMultiCurrency = 'multi-currency';
195
+ const PendingReasonOrder = 'order';
196
+ const PendingReasonPaymentReview = 'payment-review';
197
+ const PendingReasonRegulatoryReview = 'regulatory-review';
198
+ const PendingReasonUnilateral = 'unilateral';
199
+ const PendingReasonVerify = 'verify';
200
+ const PendingReasonOther = 'other';
201
+
202
+ public $reason_code = false;
203
+
204
+ const ReasonCodeNone = 'none';
205
+ const ReasonCodeChargeback = 'chargeback';
206
+ const ReasonCodeGuarantee = 'guarantee';
207
+ const ReasonCodeBuyerComplaint = 'buyer-complaint';
208
+ const ReasonCodeRefund = 'refund';
209
+ const ReasonCodeOther = 'other';
210
+
211
+ public $hold_decision = false;
212
+
213
+ const HoldDecisionNewSellerPaymentHold = 'newsellerpaymenthold';
214
+ const HoldDecisionPaymentHold = 'paymenthold';
215
+
216
+ public $shipping_method = false;
217
+
218
+ public $protection_eligibility_details = false;
219
+ public $receipt_reference_number = false;
220
+ public $shipping_amount = false;
221
+
222
+ public $handling_amount = false;
223
+
224
+ public $payment_request_id = false;
225
+ public $instrument_details = false;
226
+
227
+ public $offer_details = false;
228
+ public $error_details = false;
229
+ public $seller_details = false;
230
+ public $fmf_details = false;
231
+
232
+ public function loadFromDoECResponse( $doECResponse, $bucketNum ) {
233
+ $map = array(
234
+ 'TRANSACTIONID' => 'transaction_id',
235
+ 'EBAYITEMAUCTIONTXNID' => 'ebay_item_auction_transaction_id',
236
+ 'PARENTTRANSACTIONID' => 'parent_transaction_id',
237
+ 'RECEIPTID' => 'receipt_id',
238
+ 'TRANSACTIONTYPE' => 'transaction_type',
239
+ 'PAYMENTTYPE' => 'payment_type',
240
+ 'EXPECTEDECHECKCLEARDATE' => 'expected_echeck_clear_date',
241
+ 'ORDERTIME' => 'order_time',
242
+ 'AMT' => 'amount',
243
+ 'CURRENCYCODE' => 'currency_code',
244
+ 'FEEAMT' => 'fee_amount',
245
+ 'SETTLEAMT' => 'settlement_amount',
246
+ 'TAXAMT' => 'tax_amount',
247
+ 'EXCHANGERATE' => 'exchange_rate',
248
+ 'PAYMENTSTATUS' => 'payment_status',
249
+ 'PENDINGREASON' => 'pending_reason',
250
+ 'REASONCODE' => 'reason_code',
251
+ 'HOLDDECISION' => 'hold_decision',
252
+ 'SHIPPINGMETHOD' => 'shipping_method',
253
+ 'RECEIPTREFERENCENUMBER' => 'receipt_reference_number',
254
+ 'SHIPPINGAMT' => 'shipping_amount',
255
+ 'HANDLINGAMT' => 'handling_amount',
256
+ 'PAYMENTREQUESTID' => 'payment_request_id'
257
+ );
258
+
259
+ $found_any = false;
260
+ foreach ( $map as $index => $value ) {
261
+ $var_name = 'PAYMENTINFO_' . $bucketNum . '_' . $index;
262
+ if ( array_key_exists( $var_name, $doECResponse ) ) {
263
+ $this->$value = $doECResponse[ $var_name ];
264
+ $found_any = true;
265
+ }
266
+ }
267
+
268
+ $this->protection_eligibility_details = new PayPal_Payment_Payment_Protection_Eligibility_Details();
269
+ if ( ! $this->protection_eligibility_details->loadFromDoECResponse( $doECResponse, $bucketNum ) ) {
270
+ $this->protection_eligibility_details = false;
271
+ }
272
+
273
+ $this->instrument_details = new PayPal_Payment_Payment_Instrument_Details();
274
+ if ( ! $this->instrument_details->loadFromDoECResponse( $doECResponse, $bucketNum ) ) {
275
+ $this->instrument_details = false;
276
+ }
277
+
278
+ $this->offer_details = new PayPal_Payment_Payment_Offer_Details();
279
+ if ( ! $this->offer_details->loadFromDoECResponse( $doECResponse, $bucketNum ) ) {
280
+ $this->offer_details = false;
281
+ }
282
+
283
+ $this->error_details = new PayPal_Payment_Payment_Error_Details();
284
+ if ( ! $this->error_details->loadFromDoECResponse( $doECResponse, $bucketNum ) ) {
285
+ $this->error_details = false;
286
+ }
287
+
288
+ $this->seller_details = new PayPal_Payment_Payment_Seller_Details();
289
+ if ( ! $this->seller_details->loadFromDoECResponse( $doECResponse, $bucketNum ) ) {
290
+ $this->seller_details = false;
291
+ }
292
+
293
+ $this->fmf_details = new PayPal_Payment_Payment_FMF_Details();
294
+ if ( ! $this->fmf_details->loadFromDoECResponse( $doECResponse, $bucketNum ) ) {
295
+ $this->fmf_details = false;
296
+ }
297
+ }
298
+
299
+ }
300
+
301
+ class PayPal_Payment_Payment_Protection_Eligibility_Details {
302
+ public $protection_eligibility = false;
303
+
304
+ const ProtectionEligibilityEligible = 'Eligible';
305
+ const ProtectionEligibilityPartiallyEligible = 'PartiallyEligible';
306
+ const ProtectionEligibilityIneligible = 'Ineligible';
307
+
308
+ public $protection_eligibility_type = false;
309
+
310
+ const ProtectionEligibilityTypeItemNotReceivedEligible = 'ItemNotReceivedEligible';
311
+ const ProtectionEligibilityTypeUnauthorizedPaymentEligible = 'UnauthorizedPaymentEligible';
312
+ const ProtectionEligibilityTypeIneligible = 'Ineligible';
313
+
314
+ public function isItemNotReceivedEligible() {
315
+ $types = explode( ',', $this->protection_eligibility_type );
316
+ foreach ( $types as $value ) {
317
+ if ( self::ProtectionEligibilityTypeItemNotReceivedEligible == $value ) {
318
+ return true;
319
+ }
320
+ }
321
+ return false;
322
+ }
323
+
324
+ public function isUnauthorizedPaymentEligible() {
325
+ $types = explode( ',', $this->protection_eligibility_type );
326
+ foreach ( $types as $value ) {
327
+ if ( self::ProtectionEligibilityTypeUnauthorizedPaymentEligible == $value ) {
328
+ return true;
329
+ }
330
+ }
331
+ return false;
332
+ }
333
+
334
+ public function loadFromDoECResponse( $doECResponse, $bucketNum ) {
335
+ $map = array(
336
+ 'PROTECTIONELIGIBILITY' => 'protection_eligibility',
337
+ 'PROTECTIONELIGIBILITYTYPE' => 'protection_eligibility_type'
338
+ );
339
+
340
+ $found_any = false;
341
+
342
+ foreach ( $map as $index => $value ) {
343
+ $var_name = 'PAYMENTINFO_' . $bucketNum . '_' . $index;
344
+ if ( array_key_exists( $var_name, $doECResponse ) ) {
345
+ $this->$value = $doECResponse[ $var_name ];
346
+ $found_any = true;
347
+ }
348
+ }
349
+
350
+ return $found_any;
351
+ }
352
+ }
353
+
354
+ class PayPal_Payment_Payment_Instrument_Details {
355
+ public $instrument_category = false;
356
+
357
+ const InstrumentCategoryPayPalCredit = '1';
358
+ const InstrumentCategoryPrivateCard = '2';
359
+
360
+ public $instrument_id = false;
361
+
362
+ // Returns true to indicate that the getECResponse array contained variables that were pertinent to this object.
363
+ // If not, it returns false to indicate that the caller can destroy this object.
364
+ public function loadFromDoECResponse( $doECResponse, $bucketNum ) {
365
+ $map = array(
366
+ 'INSTRUMENTCATEGORY' => 'instrument_category',
367
+ 'INSTRUMENTID' => 'instrument_id'
368
+ );
369
+ $found_any = false;
370
+
371
+ foreach ( $map as $index => $value ) {
372
+ $var_name = 'PAYMENTINFO_' . $bucketNum . '_' . $index;
373
+ if ( array_key_exists( $var_name, $doECResponse ) ) {
374
+ $this->$value = $doECResponse[ $var_name ];
375
+ $found_any = true;
376
+ }
377
+ }
378
+
379
+ return $found_any;
380
+ }
381
+ }
382
+
383
+ class PayPal_Payment_Payment_Offer_Details {
384
+ public $offer_code = false;
385
+ public $offer_tracking_id = false;
386
+
387
+ public function loadFromDoECResponse( $doECResponse, $bucketNum ) {
388
+ $map = array(
389
+ 'OFFERCODE' => 'offer_code',
390
+ 'OFFERTRACKINGID' => 'offer_tracking_id'
391
+ );
392
+ }
393
+ }
394
+
395
+ class PayPal_Payment_Payment_Error_Details {
396
+ public $short_message = false;
397
+ public $long_message = false;
398
+ public $error_code = false;
399
+ public $severity_code = false;
400
+ public $ack = false;
401
+
402
+ public function loadFromDoECResponse( $doECResponse, $bucketNum ) {
403
+ $map = array(
404
+ 'SHORTMESSAGE' => 'short_message',
405
+ 'LONGMESSAGE' => 'long_message',
406
+ 'ERRORCODE' => 'error_code',
407
+ 'SEVERITYCODE' => 'severity_code',
408
+ 'ACK' => 'ack'
409
+ );
410
+
411
+ $found_any = false;
412
+ foreach ( $map as $index => $value ) {
413
+ $var_name = 'PAYMENTINFO_' . $bucketNum . '_' . $index;
414
+ if ( array_key_exists( $var_name, $doECResponse ) ) {
415
+ $this->$value = $doECResponse[ $var_name ];
416
+ $found_any = true;
417
+ }
418
+ }
419
+
420
+ return $found_any;
421
+ }
422
+ }
423
+
424
+ class PayPal_Payment_Payment_Seller_Details {
425
+ public $paypal_account_id = false;
426
+ public $secure_merchant_account_id = false;
427
+ public $seller_id = false;
428
+ public $user_name = false;
429
+ public $registration_date = false;
430
+
431
+ public function loadFromDoECResponse( $doECResponse, $bucketNum ) {
432
+ $map = array(
433
+ 'SELLERPAYPALACCOUNTID' => 'paypal_account_id',
434
+ 'SECUREMERCHANTACCOUNTID' => 'secure_merchant_account_id',
435
+ 'SELLERID' => 'seller_id',
436
+ 'SELLERUSERNAME' => 'user_name',
437
+ 'SELLERREGISTRATIONDATE' => 'registration_date'
438
+ );
439
+
440
+ $found_any = false;
441
+ foreach ( $map as $index => $value ) {
442
+ $var_name = 'PAYMENTINFO_' . $bucketNum . '_' . $index;
443
+ if ( array_key_exists( $var_name, $doECResponse ) ) {
444
+ $this->$value = $doECResponse[ $var_name ];
445
+ $found_any = true;
446
+ }
447
+ }
448
+
449
+ return $found_any;
450
+ }
451
+ }
includes/class-wc-gateway-ppec-plugin.php ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * PayPal Express Checkout Plugin.
4
+ */
5
+
6
+ if ( ! defined( 'ABSPATH' ) ) {
7
+ exit; // Exit if accessed directly
8
+ }
9
+
10
+ class WC_Gateway_PPEC_Plugin {
11
+
12
+ /**
13
+ * Filepath of main plugin file.
14
+ *
15
+ * @var string
16
+ */
17
+ public $file;
18
+
19
+ /**
20
+ * Plugin version.
21
+ *
22
+ * @var string
23
+ */
24
+ public $version;
25
+
26
+ /**
27
+ * Absolute plugin path.
28
+ *
29
+ * @var string
30
+ */
31
+ public $plugin_path;
32
+
33
+ /**
34
+ * Absolute plugin URL.
35
+ *
36
+ * @var string
37
+ */
38
+ public $plugin_url;
39
+
40
+ /**
41
+ * Absolute path to plugin includes dir.
42
+ *
43
+ * @var string
44
+ */
45
+ public $includes_path;
46
+
47
+ /**
48
+ * Flag to indicate the plugin has been boostrapped.
49
+ *
50
+ * @var bool
51
+ */
52
+ private $_bootstrapped = false;
53
+
54
+ /**
55
+ * Instance of WC_Gateway_PPEC_Settings.
56
+ *
57
+ * @var WC_Gateway_PPEC_Settings
58
+ */
59
+ public $settings;
60
+
61
+ /**
62
+ * Constructor.
63
+ *
64
+ * @param string $file Filepath of main plugin file
65
+ * @param string $version Plugin version
66
+ */
67
+ public function __construct( $file, $version ) {
68
+ $this->file = $file;
69
+ $this->version = $version;
70
+
71
+ // Path.
72
+ $this->plugin_path = trailingslashit( plugin_dir_path( $this->file ) );
73
+ $this->plugin_url = trailingslashit( plugin_dir_url( $this->file ) );
74
+ $this->includes_path = $this->plugin_path . trailingslashit( 'includes' );
75
+ }
76
+
77
+ /**
78
+ * Maybe run the plugin.
79
+ */
80
+ public function maybe_run() {
81
+ register_activation_hook( $this->file, array( $this, 'activate' ) );
82
+
83
+ add_action( 'plugins_loaded', array( $this, 'bootstrap' ) );
84
+ add_filter( 'plugin_action_links_' . plugin_basename( $this->file ), array( $this, 'plugin_action_links' ) );
85
+ add_filter( 'allowed_redirect_hosts' , array( $this, 'whitelist_paypal_domains_for_redirect' ) );
86
+ }
87
+
88
+ public function bootstrap() {
89
+ try {
90
+ if ( $this->_bootstrapped ) {
91
+ throw new Exception( __( '%s in WooCommerce Gateway PayPal Express Checkout plugin can only be called once', 'woocommerce-gateway-paypal-express-checkout' ) );
92
+ }
93
+
94
+ $this->_check_dependencies();
95
+ $this->_run();
96
+
97
+ $this->_bootstrapped = true;
98
+ delete_option( 'wc_gateway_ppce_bootstrap_warning_message' );
99
+ } catch ( Exception $e ) {
100
+ update_option( 'wc_gateway_ppce_bootstrap_warning_message', $e->getMessage() );
101
+ add_action( 'admin_notices', array( $this, 'show_bootstrap_warning' ) );
102
+ }
103
+ }
104
+
105
+ public function show_bootstrap_warning() {
106
+ $dependencies_message = get_option( 'wc_gateway_ppce_bootstrap_warning_message', '' );
107
+ if ( ! empty( $dependencies_message ) ) {
108
+ ?>
109
+ <div class="error fade">
110
+ <p>
111
+ <strong><?php echo esc_html( $dependencies_message ); ?></strong>
112
+ </p>
113
+ </div>
114
+ <?php
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Check dependencies.
120
+ *
121
+ * @throws Exception
122
+ */
123
+ protected function _check_dependencies() {
124
+ if ( ! function_exists( 'WC' ) ) {
125
+ throw new Exception( __( 'WooCommerce Gateway PayPal Express Checkout requires WooCommerce to be activated', 'woocommerce-gateway-paypal-express-checkout' ) );
126
+ }
127
+
128
+ if ( version_compare( WC()->version, '2.5', '<' ) ) {
129
+ throw new Exception( __( 'WooCommerce Gateway PayPal Express Checkout requires WooCommerce version 2.5 or greater', 'woocommerce-gateway-paypal-express-checkout' ) );
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Run the plugin.
135
+ */
136
+ protected function _run() {
137
+ require_once( $this->includes_path . 'functions.php' );
138
+ $this->_load_handlers();
139
+ }
140
+
141
+ /**
142
+ * Callback for activation hook.
143
+ */
144
+ public function activate() {
145
+ // Enable some options that we recommend for all merchants.
146
+ add_option( 'pp_woo_allowGuestCheckout', true );
147
+ add_option( 'pp_woo_enableInContextCheckout', true );
148
+ /* defer ppc for next next release.
149
+ add_option( 'pp_woo_ppc_enabled', true );
150
+ */
151
+
152
+ if ( ! isset( $this->setings ) ) {
153
+ require_once( $this->includes_path . 'class-wc-gateway-ppec-settings.php' );
154
+ $settings = new WC_Gateway_PPEC_Settings();
155
+ } else {
156
+ $settings = $this->settings;
157
+ }
158
+
159
+ // Force zero decimal on specific currencies.
160
+ if ( $settings->currency_has_decimal_restriction() ) {
161
+ update_option( 'woocommerce_price_num_decimals', 0 );
162
+ update_option( 'wc_gateway_ppce_display_decimal_msg', true );
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Load handlers.
168
+ */
169
+ protected function _load_handlers() {
170
+ // Client.
171
+ require_once( $this->includes_path . 'abstracts/abstract-wc-gateway-ppec-client-credential.php' );
172
+ require_once( $this->includes_path . 'class-wc-gateway-ppec-client-credential-certificate.php' );
173
+ require_once( $this->includes_path . 'class-wc-gateway-ppec-client-credential-signature.php' );
174
+ require_once( $this->includes_path . 'class-wc-gateway-ppec-client.php' );
175
+
176
+ // Load handlers.
177
+ require_once( $this->includes_path . 'class-wc-gateway-ppec-settings.php' );
178
+ require_once( $this->includes_path . 'class-wc-gateway-ppec-gateway-loader.php' );
179
+ require_once( $this->includes_path . 'class-wc-gateway-ppec-admin-handler.php' );
180
+ require_once( $this->includes_path . 'class-wc-gateway-ppec-checkout-handler.php' );
181
+ require_once( $this->includes_path . 'class-wc-gateway-ppec-cart-handler.php' );
182
+ require_once( $this->includes_path . 'class-wc-gateway-ppec-ips-handler.php' );
183
+
184
+ $this->settings = new WC_Gateway_PPEC_Settings();
185
+ $this->settings->loadSettings();
186
+
187
+ $this->gateway_loader = new WC_Gateway_PPEC_Gateway_Loader();
188
+ $this->admin = new WC_Gateway_PPEC_Admin_Handler();
189
+ $this->checkout = new WC_Gateway_PPEC_Checkout_Handler();
190
+ $this->cart = new WC_Gateway_PPEC_Cart_Handler();
191
+ $this->ips = new WC_Gateway_PPEC_IPS_Handler();
192
+
193
+ $this->client = new WC_Gateway_PPEC_Client( $this->settings->getActiveApiCredentials(), $this->settings->environment );
194
+ }
195
+
196
+ /**
197
+ * Adds plugin action links
198
+ *
199
+ * @since 1.0.0
200
+ */
201
+ public function plugin_action_links( $links ) {
202
+
203
+ $section_slug = strtolower( 'WC_Gateway_PPEC_With_PayPal' );
204
+
205
+ $plugin_links = array(
206
+ '<a href="' . admin_url( 'admin.php?page=wc-settings&tab=checkout&section=' . $section_slug ) . '">' . __( 'Settings', 'woocommerce-gateway-paypal-express-checkout' ) . '</a>',
207
+ '<a href="http://docs.woothemes.com/document/woocommerce-gateway-paypal-express-checkout/">' . __( 'Docs', 'woocommerce-gateway-paypal-express-checkout' ) . '</a>',
208
+ '<a href="http://support.woothemes.com/">' . __( 'Support', 'woocommerce-gateway-paypal-express-checkout' ) . '</a>',
209
+ );
210
+ return array_merge( $plugin_links, $links );
211
+ }
212
+
213
+ /**
214
+ * Allow PayPal domains for redirect.
215
+ *
216
+ * @since 1.0.0
217
+ *
218
+ * @param array $domains Whitelisted domains for `wp_safe_redirect`
219
+ *
220
+ * @return array $domains Whitelisted domains for `wp_safe_redirect`
221
+ */
222
+ public function whitelist_paypal_domains_for_redirect( $domains ) {
223
+ $domains[] = 'www.paypal.com';
224
+ $domains[] = 'paypal.com';
225
+ $domains[] = 'www.sandbox.paypal.com';
226
+ $domains[] = 'sandbox.paypal.com';
227
+
228
+ return $domains;
229
+ }
230
+
231
+ }
includes/class-wc-gateway-ppec-refund.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ class WC_Gateway_PPEC_Refund {
8
+
9
+ public static function refund_order( $order, $amount, $refundType, $reason, $currency ) {
10
+
11
+ // add refund params
12
+ $params['TRANSACTIONID'] = $order->get_transaction_id();
13
+ $params['REFUNDTYPE'] = $refundType;
14
+ $params['AMT'] = $amount;
15
+ $params['CURRENCYCODE'] = $currency;
16
+ $params['NOTE'] = $reason;
17
+
18
+ // do API call
19
+ $response = wc_gateway_ppec()->client->refund_transaction( $params );
20
+
21
+ // look at ACK to see if success or failure
22
+ // if success return the transaction ID of the refund
23
+ // if failure then do 'throw new PayPal_API_Exception( $response );'
24
+
25
+ if ( 'Success' == $response['ACK'] || 'SuccessWithWarning' == $response['ACK'] ) {
26
+ return $response['REFUNDTRANSACTIONID'];
27
+ } else {
28
+ throw new PayPal_API_Exception( $response );
29
+ }
30
+ }
31
+
32
+ }
includes/class-wc-gateway-ppec-session-data.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ class WC_Gateway_PPEC_Session_Data {
8
+
9
+ public $leftFrom = false; // 'cart' or 'order'
10
+ public $order_id = false; // if $leftFrom is 'order', this should be the order ID
11
+ public $billingAgreementRequested = false; // true if a billing agreement was requested in the SetEC call, false otherwise
12
+ public $checkout_details = false; // Will be populated with the GetEC details later
13
+ public $shipping_required = false; // True if a shipping address is required for this transaction, false otherwise
14
+ public $checkout_completed = false; // True if the buyer has just returned from PayPal and we should select PayPal as the payment method
15
+ public $token = false; // The EC token
16
+ public $payerID = false; // The buyer's payer ID, once they come back from PayPal
17
+ public $expiry_time = false; // The time at which the token will expire
18
+ public $using_ppc = false; // Whether the buyer is checking out with PayPal Credit
19
+
20
+ public function __construct( $token, $leftFrom = 'cart', $order_id = false, $shipping_required = true, $billingAgreementRequested = false, $expires_in = 10800, $using_ppc = false ) {
21
+ if ( 'cart' == $leftFrom || 'order' == $leftFrom ) {
22
+ $this->leftFrom = $leftFrom;
23
+ }
24
+ if ( 'order' == $leftFrom ) {
25
+ $this->order_id = $order_id;
26
+ }
27
+
28
+ $this->token = $token;
29
+ $this->shipping_required = $shipping_required;
30
+ $this->billingAgreementRequested = $billingAgreementRequested;
31
+
32
+ $this->expiry_time = time() + $expires_in;
33
+
34
+ $this->using_ppc = $using_ppc;
35
+ }
36
+ }
includes/class-wc-gateway-ppec-settings.php ADDED
@@ -0,0 +1,405 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ class WC_Gateway_PPEC_Settings {
8
+
9
+ protected $params;
10
+ protected $validParams = array(
11
+ 'enabled',
12
+ 'logging_enabled',
13
+ 'ppcEnabled',
14
+ 'environment',
15
+ 'liveApiCredentials',
16
+ 'sandboxApiCredentials',
17
+ 'enableInContextCheckout',
18
+ 'buttonSize',
19
+ 'markSize',
20
+ 'logoImageUrl',
21
+ 'paymentAction',
22
+ 'allowGuestCheckout',
23
+ 'zeroSubtotalBehavior',
24
+ 'subtotalMismatchBehavior',
25
+ 'ipnUrl',
26
+ 'blockEChecks',
27
+ 'requireBillingAddress',
28
+ 'liveAccountIsEnabledForBillingAddress',
29
+ 'sbAccountIsEnabledForBillingAddress',
30
+ );
31
+
32
+ protected $_supportedLocale = array(
33
+ 'da_DK', 'de_DE', 'en_AU', 'en_GB', 'en_US', 'es_ES', 'fr_CA', 'fr_FR',
34
+ 'he_IL', 'id_ID', 'it_IT', 'ja_JP', 'nl_NL', 'no_NO', 'pl_PL', 'pt_BR',
35
+ 'pt_PT', 'ru_RU', 'sv_SE', 'th_TH', 'tr_TR', 'zh_CN', 'zh_HK', 'zh_TW',
36
+ );
37
+
38
+ const PaymentActionSale = 'Sale';
39
+ const PaymentActionAuthorization = 'Authorization';
40
+
41
+ const zeroSubtotalBehaviorModifyItems = 'modifyItems';
42
+ const zeroSubtotalBehaviorOmitLineItems = 'omitLineItems';
43
+ const zeroSubtotalBehaviorPassCouponsAsShippingDiscount = 'passCouponsAsShippingDiscount';
44
+
45
+ const subtotalMismatchBehaviorAddLineItem = 'addLineItem';
46
+ const subtotalMismatchBehaviorDropLineItems = 'dropLineItems';
47
+
48
+ const buttonSizeSmall = 'small';
49
+ const buttonSizeMedium = 'medium';
50
+
51
+ const markSizeSmall = 'small';
52
+ const markSizeMedium = 'medium';
53
+ const markSizeLarge = 'large';
54
+
55
+ /**
56
+ * Flag to indicate setting has been loaded from DB.
57
+ *
58
+ * @var bool
59
+ */
60
+ private $_is_setting_loaded = false;
61
+
62
+ public function __get( $name ) {
63
+ if ( in_array( $name, $this->validParams ) ) {
64
+ // Run the value through sanitization functions, if they exist
65
+ $func_name = '_sanitize_' . $name;
66
+ if ( method_exists( $this, $func_name ) ) {
67
+ return $this->$func_name( $this->params[ $name ] );
68
+ } else if ( array_key_exists( $name, $this->params ) ) {
69
+ return $this->params[ $name ];
70
+ } else {
71
+ return null;
72
+ }
73
+ }
74
+
75
+ return null;
76
+ }
77
+
78
+ public function __set( $name, $value ) {
79
+ if ( in_array( $name, $this->validParams ) ) {
80
+ // Run the value through sanitization and validation functions, if they exist
81
+ $func_name = '_sanitize_' . $name;
82
+ if ( method_exists( $this, $func_name ) ) {
83
+ $value = $this->$func_name( $value );
84
+ }
85
+
86
+ $func_name = '_validate_' . $name;
87
+ if ( method_exists( $this, $func_name ) ) {
88
+ if ( $this->$func_name( $value ) ) {
89
+ $this->params[ $name ] = $value;
90
+ }
91
+ } else {
92
+ $this->params[ $name ] = $value;
93
+ }
94
+ }
95
+ }
96
+
97
+ public function __isset( $name ) {
98
+ if ( in_array( $name, $this->validParams ) ) {
99
+ return true;
100
+ } else {
101
+ return false;
102
+ }
103
+ }
104
+
105
+ public function setApiSignatureCredentials( $username, $password, $signature, $subject = false, $environment = 'sandbox' ) {
106
+ if ( 'live' == $environment ) {
107
+ $this->liveApiCredentials = new WC_Gateway_PPEC_Client_Credential_Signature( $username, $password, $signature, $subject );
108
+ } else {
109
+ $this->sandboxApiCredentials = new WC_Gateway_PPEC_Client_Credential_Signature( $username, $password, $signature, $subject );
110
+ }
111
+ }
112
+
113
+ public function setApiCertificateCredentialsFromFile( $username, $password, $certFile, $subject = false, $environment = 'sandbox' ) {
114
+ $certString = file_get_contents( $certFile );
115
+ if ( FALSE === $cert ) {
116
+ // Failed to load the certificate
117
+ // TODO: Add some logging
118
+ return false;
119
+ }
120
+
121
+ $this->setApiCertificateCredentialsFromString( $username, $password, $certString, $subject, $environment );
122
+
123
+ return true;
124
+
125
+ }
126
+
127
+ public function setApiCertificateCredentialsFromString( $username, $password, $certString, $subject = false, $environment = 'sandbox' ) {
128
+ if ( 'live' == $environment ) {
129
+ $this->liveApiCredentials = new WC_Gateway_PPEC_Client_Credential_Certificate( $username, $password, $certString, $subject );
130
+ } else {
131
+ $this->sandboxApiCredentials = new WC_Gateway_PPEC_Client_Credential_Certificate( $username, $password, $certString, $subject );
132
+ }
133
+ }
134
+
135
+ public function getActiveApiCredentials() {
136
+ if ( $this->environment == 'live' ) {
137
+ return $this->liveApiCredentials;
138
+ } else {
139
+ return $this->sandboxApiCredentials;
140
+ }
141
+ }
142
+
143
+ public function getPayPalRedirectUrl( $token, $commit = false ) {
144
+ $url = 'https://www.';
145
+
146
+ if ( $this->environment != 'live' ) {
147
+ $url .= 'sandbox.';
148
+ }
149
+
150
+ $url .= 'paypal.com/';
151
+
152
+ if ( $this->enableInContextCheckout ) {
153
+ $url .= 'checkoutnow?';
154
+ } else {
155
+ $url .= 'cgi-bin/webscr?cmd=_express-checkout&';
156
+ }
157
+
158
+ $url .= 'token=' . urlencode( $token );
159
+
160
+ if ( $commit ) {
161
+ $url .= '&useraction=commit';
162
+ }
163
+
164
+ return $url;
165
+ }
166
+
167
+ public function getSetECShortcutParameters() {
168
+ return $this->getBaseSetECShortcutParameters();
169
+ }
170
+
171
+ public function getSetECMarkParameters() {
172
+ return $this->getBaseSetECMarkParameters();
173
+ }
174
+ public function getDoECParameters() {
175
+ return $this->getBaseDoECParameters();
176
+ }
177
+
178
+ /**
179
+ * TODO: Probably merge with getSetECShortcutParameters
180
+ */
181
+ protected function getBaseSetECShortcutParameters( $buckets = 1 ) {
182
+ $params = array();
183
+
184
+ if ( $this->logoImageUrl ) {
185
+ $params['LOGOIMG'] = $this->logoImageUrl;
186
+ }
187
+
188
+ if ( $this->allowGuestCheckout ) {
189
+ $params['SOLUTIONTYPE'] = 'Sole';
190
+ }
191
+
192
+ if ( ! is_array( $buckets ) ) {
193
+ $numBuckets = $buckets;
194
+ $buckets = array();
195
+ for ( $i = 0; $i < $numBuckets; $i++ ) {
196
+ $buckets[] = $i;
197
+ }
198
+ }
199
+
200
+ if ( $this->requireBillingAddress ) {
201
+ $params['REQBILLINGADDRESS'] = '1';
202
+ }
203
+
204
+ foreach ( $buckets as $bucketNum ) {
205
+ $params[ 'PAYMENTREQUEST_' . $bucketNum . '_PAYMENTACTION' ] = $this->paymentAction;
206
+ if ( $this->blockEChecks ) $params[ 'PAYMENTREQUEST_' . $bucketNum . '_ALLOWEDPAYMENTMETHOD' ] = 'InstantPaymentOnly';
207
+ }
208
+
209
+ return $params;
210
+ }
211
+
212
+ /**
213
+ * TODO: Probably merge with getSetECMarkParameters
214
+ */
215
+ protected function getBaseSetECMarkParameters( $buckets = 1 ) {
216
+ $params = array();
217
+
218
+ if ( $this->logoImageUrl ) {
219
+ $params['LOGOIMG'] = $this->logoImageUrl;
220
+ }
221
+
222
+ if ( $this->allowGuestCheckout ) {
223
+ $params['SOLUTIONTYPE'] = 'Sole';
224
+ }
225
+
226
+ if ( ! is_array( $buckets ) ) {
227
+ $numBuckets = $buckets;
228
+ $buckets = array();
229
+ for ( $i = 0; $i < $numBuckets; $i++ ) {
230
+ $buckets[] = $i;
231
+ }
232
+ }
233
+
234
+ if ( $this->requireBillingAddress ) {
235
+ $params['REQBILLINGADDRESS'] = '1';
236
+ }
237
+
238
+ foreach ( $buckets as $bucketNum ) {
239
+ $params[ 'PAYMENTREQUEST_' . $bucketNum . '_PAYMENTACTION' ] = $this->paymentAction;
240
+ if ( $this->blockEChecks ) {
241
+ $params[ 'PAYMENTREQUEST_' . $bucketNum . '_ALLOWEDPAYMENTMETHOD' ] = 'InstantPaymentOnly';
242
+ }
243
+ }
244
+
245
+ return $params;
246
+ }
247
+
248
+ /**
249
+ * TODO: Probably merge with getDoECParameters
250
+ */
251
+ protected function getBaseDoECParameters( $buckets = 1 ) {
252
+ $params = array();
253
+ if ( ! is_array( $buckets ) ) {
254
+ $numBuckets = $buckets;
255
+ $buckets = array();
256
+ for ( $i = 0; $i < $numBuckets; $i++ ) {
257
+ $buckets[] = $i;
258
+ }
259
+ }
260
+
261
+ foreach ( $buckets as $bucketNum ) {
262
+ $params[ 'PAYMENTREQUEST_' . $bucketNum . '_NOTIFYURL' ] = $this->ipnUrl;
263
+ $params[ 'PAYMENTREQUEST_' . $bucketNum . '_PAYMENTACTION' ] = $this->paymentAction;
264
+ }
265
+
266
+ return $params;
267
+ }
268
+
269
+ protected function _sanitize_zeroSubtotalBehavior( $behavior ) {
270
+ if ( self::zeroSubtotalBehaviorModifyItems == $behavior ||
271
+ self::zeroSubtotalBehaviorOmitLineItems == $behavior ||
272
+ self::zeroSubtotalBehaviorPassCouponsAsShippingDiscount == $behavior ) {
273
+ return $behavior;
274
+ } else {
275
+ return self::zeroSubtotalBehaviorModifyItems;
276
+ }
277
+ }
278
+
279
+ protected function _sanitize_subtotalMismatchBehavior( $behavior ) {
280
+ if ( self::subtotalMismatchBehaviorAddLineItem == $behavior ||
281
+ self::subtotalMismatchBehaviorDropLineItems == $behavior ) {
282
+ return $behavior;
283
+ } else {
284
+ return self::subtotalMismatchBehaviorAddLineItem;
285
+ }
286
+ }
287
+
288
+ protected function _sanitize_buttonSize( $size ) {
289
+ if ( in_array( $size, array( self::buttonSizeSmall, self::buttonSizeMedium ) ) ) {
290
+ return $size;
291
+ } else {
292
+ return self::buttonSizeMedium;
293
+ }
294
+ }
295
+
296
+ protected function _sanitize_markSize( $size ) {
297
+ if ( self::markSizeSmall == $size ||
298
+ self::markSizeMedium == $size ||
299
+ self::markSizeLarge == $size ) {
300
+ return $size;
301
+ } else {
302
+ return self::markSizeSmall;
303
+ }
304
+ }
305
+
306
+ protected function _validate_paymentAction( $value ) {
307
+ return in_array( $value, array( self::PaymentActionSale, self::PaymentActionAuthorization ) );
308
+ }
309
+
310
+ /**
311
+ * Load settings from DB.
312
+ *
313
+ * @param bool $force_reload Force reload, ignore
314
+ *
315
+ * @return WC_Gateway_PPEC_Settings Instance of WC_Gateway_PPEC_Settings
316
+ */
317
+ public function loadSettings( $force_reload = false ) {
318
+ if ( $this->_is_setting_loaded && ! $force_reload ) {
319
+ return $this;
320
+ }
321
+
322
+ $this->enabled = get_option( 'pp_woo_enabled' );
323
+ // $this->ppcEnabled = get_option( 'pp_woo_ppc_enabled' );
324
+ $this->logging_enabled = get_option( 'pp_woo_logging_enabled' );
325
+ $this->ppcEnabled = false; // defer this for next release.
326
+ $this->buttonSize = get_option( 'pp_woo_button_size' );
327
+ $this->markSize = get_option( 'pp_woo_mark_size' );
328
+ $this->liveApiCredentials = get_option( 'pp_woo_liveApiCredentials' );
329
+ $this->sandboxApiCredentials = get_option( 'pp_woo_sandboxApiCredentials' );
330
+ $this->environment = get_option( 'pp_woo_environment' );
331
+ $this->logoImageUrl = get_option( 'pp_woo_logoImageUrl' );
332
+ $this->ipnUrl = get_option( 'pp_woo_ipnUrl' );
333
+ $this->paymentAction = get_option( 'pp_woo_paymentAction' );
334
+ $this->allowGuestCheckout = get_option( 'pp_woo_allowGuestCheckout' );
335
+ $this->blockEChecks = get_option( 'pp_woo_blockEChecks' );
336
+ $this->requireBillingAddress = get_option( 'pp_woo_requireBillingAddress' );
337
+ $this->zeroSubtotalBehavior = get_option( 'pp_woo_zeroSubtotalBehavior' );
338
+ $this->subtotalMismatchBehavior = get_option( 'pp_woo_subtotalMismatchBehavior' );
339
+ $this->enableInContextCheckout = get_option( 'pp_woo_enableInContextCheckout' );
340
+ $this->liveAccountIsEnabledForBillingAddress = get_option( 'pp_woo_liveAccountIsEnabledForBillingAddress' );
341
+ $this->sbAccountIsEnabledForBillingAddress = get_option( 'pp_woo_sbAccountIsEnabledForBillingAddress' );
342
+
343
+ $this->_is_setting_loaded = true;
344
+
345
+ return $this;
346
+ }
347
+
348
+ public function saveSettings() {
349
+ update_option( 'pp_woo_enabled' , $this->enabled );
350
+ update_option( 'pp_woo_logging_enabled' , $this->logging_enabled );
351
+ update_option( 'pp_woo_ppc_enabled' , $this->ppcEnabled );
352
+ update_option( 'pp_woo_button_size' , $this->buttonSize );
353
+ update_option( 'pp_woo_mark_size' , $this->markSize );
354
+ update_option( 'pp_woo_liveApiCredentials' , $this->liveApiCredentials );
355
+ update_option( 'pp_woo_sandboxApiCredentials' , $this->sandboxApiCredentials );
356
+ update_option( 'pp_woo_environment' , $this->environment );
357
+ update_option( 'pp_woo_logoImageUrl' , $this->logoImageUrl );
358
+ update_option( 'pp_woo_ipnUrl' , $this->ipnUrl );
359
+ update_option( 'pp_woo_paymentAction' , $this->paymentAction );
360
+ update_option( 'pp_woo_allowGuestCheckout' , $this->allowGuestCheckout );
361
+ update_option( 'pp_woo_blockEChecks' , $this->blockEChecks );
362
+ update_option( 'pp_woo_requireBillingAddress' , $this->requireBillingAddress );
363
+ update_option( 'pp_woo_zeroSubtotalBehavior' , $this->zeroSubtotalBehavior );
364
+ update_option( 'pp_woo_subtotalMismatchBehavior' , $this->subtotalMismatchBehavior );
365
+ update_option( 'pp_woo_enableInContextCheckout' , $this->enableInContextCheckout );
366
+ update_option( 'pp_woo_liveAccountIsEnabledForBillingAddress', $this->liveAccountIsEnabledForBillingAddress );
367
+ update_option( 'pp_woo_sbAccountIsEnabledForBillingAddress' , $this->sbAccountIsEnabledForBillingAddress );
368
+ }
369
+
370
+ public function getECTokenSessionLength() {
371
+ // Really, we should map this to a merchant-configurable setting, but for now, we'll just set it to the default (3 hours).
372
+ return 10800;
373
+ }
374
+
375
+ /**
376
+ * Whether currency has decimal restriction for PPCE to functions?
377
+ *
378
+ * @return bool True if it has restriction otherwise false
379
+ */
380
+ public function currency_has_decimal_restriction() {
381
+ // Because PayPal will not accept HUF, TWD, or JPY with any decimal places,
382
+ // we'll have to make sure that Woo uses 0 decimal places if the merchant
383
+ // is using any of these three currencies.
384
+ $currency = get_woocommerce_currency();
385
+ $decimals = absint( get_option( 'woocommerce_price_num_decimals', 2 ) );
386
+ $settings = $this->loadSettings();
387
+
388
+ return (
389
+ $settings->enabled
390
+ &&
391
+ in_array( $currency, array( 'HUF', 'TWD', 'JPY' ) )
392
+ &&
393
+ 0 !== $decimals
394
+ );
395
+ }
396
+
397
+ public function get_paypal_locale() {
398
+ $locale = get_locale();
399
+ if ( ! in_array( $locale, $this->_supportedLocale ) ) {
400
+ $locale = 'en_US';
401
+ }
402
+
403
+ return $locale;
404
+ }
405
+ }
includes/class-wc-gateway-ppec-with-paypal-credit.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Exit if accessed directly
5
+ }
6
+
7
+ class WC_Gateway_PPEC_With_PayPal extends WC_Gateway_PPEC {
8
+ public function __construct() {
9
+
10
+ $this->id = 'ppec_paypal';
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/pp-acceptance-' . $settings->markSize . '.png';
17
+ $this->enabled = $settings->enabled ? 'yes' : 'no';
18
+ $this->title = __( 'PayPal', 'woocommerce-gateway-paypal-express-checkout' );
19
+ $this->description = __( 'A PayPal Account is not necessary. All credit card payments will be processed by PayPal.', 'woocommerce-gateway-paypal-express-checkout' );
20
+ }
21
+ }
22
+
includes/exceptions/class-wc-gateway-ppec-api-exception.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ 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 ) {
19
+ if ( preg_match( '/^L_ERRORCODE(\d+)$/', $index, $matches ) ) {
20
+ $errors[ $matches[1] ]['code'] = $value;
21
+ } elseif ( preg_match( '/^L_SHORTMESSAGE(\d+)$/', $index, $matches ) ) {
22
+ $errors[ $matches[1] ]['message'] = $value;
23
+ } elseif ( preg_match( '/^L_LONGMESSAGE(\d+)$/', $index, $matches ) ) {
24
+ $errors[ $matches[1] ]['long'] = $value;
25
+ } elseif ( preg_match( '/^L_SEVERITYCODE(\d+)$/', $index, $matches ) ) {
26
+ $errors[ $matches[1] ]['severity'] = $value;
27
+ } elseif ( 'CORRELATIONID' == $index ) {
28
+ $this->correlation_id = $value;
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
+ }
includes/exceptions/class-wc-gateway-ppec-missing-session-exception.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ 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
+ }
includes/functions.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ function woo_pp_start_checkout() {
4
+ $checkout = wc_gateway_ppec()->checkout;
5
+
6
+ try {
7
+ $redirect_url = $checkout->startCheckoutFromCart();
8
+ wp_safe_redirect( $redirect_url );
9
+ exit;
10
+ } catch( PayPal_API_Exception $e ) {
11
+ $final_output = '';
12
+ foreach ( $e->errors as $error ) {
13
+ $final_output .= '<li>' . __( $error->mapToBuyerFriendlyError(), 'woocommerce-gateway-paypal-express-checkout' ) . '</li>';
14
+ }
15
+ wc_add_notice( __( 'Payment error:', 'woocommerce-gateway-paypal-express-checkout' ) . $final_output, 'error' );
16
+
17
+ $redirect_url = WC()->cart->get_cart_url();
18
+ $settings = wc_gateway_ppec()->settings->loadSettings();
19
+
20
+ if( 'yes' == $settings->enabled && $settings->enableInContextCheckout && $settings->getActiveApiCredentials()->get_payer_id() ) {
21
+ ob_end_clean();
22
+ ?>
23
+ <script type="text/javascript">
24
+ if( ( window.opener != null ) && ( window.opener !== window ) &&
25
+ ( typeof window.opener.paypal != "undefined" ) &&
26
+ ( typeof window.opener.paypal.checkout != "undefined" ) ) {
27
+ window.opener.location.assign( "<?php echo $redirect_url; ?>" );
28
+ window.close();
29
+ } else {
30
+ window.location.assign( "<?php echo $redirect_url; ?>" );
31
+ }
32
+ </script>
33
+ <?php
34
+ exit;
35
+ } else {
36
+ wp_safe_redirect( $redirect_url );
37
+ exit;
38
+ }
39
+
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Log a message via WC_Logger.
45
+ *
46
+ * @param string $message Message to log
47
+ */
48
+ function wc_gateway_ppec_log( $message ) {
49
+ static $wc_ppec_logger;
50
+
51
+ // No need to write to log file if logging is disabled.
52
+ if ( ! wc_gateway_ppec()->settings->loadSettings()->logging_enabled ) {
53
+ return false;
54
+ }
55
+
56
+ if ( ! isset( $wc_ppec_logger ) ) {
57
+ $wc_ppec_logger = new WC_Logger();
58
+ }
59
+
60
+ $wc_ppec_logger->add( 'wc_gateway_ppec', $message );
61
+ }
includes/views/admin-settings.php ADDED
@@ -0,0 +1,581 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 $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>
readme.txt ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WooCommerce PayPal Express Checkout Payment Gateway ===
2
+ Contributors: automattic, woothemes, akeda, allendav, slash1andy, woosteve, spraveenitpro, mikedmoore, fernashes, shellbeezy
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: 0.1.0
7
+ License: GPLv3
8
+ License URI: http://www.gnu.org/licenses/gpl-3.0.html
9
+
10
+ Accept PayPal, Credit Cards and Debit Cards on your WooCommerce store.
11
+
12
+ == Description ==
13
+
14
+ This is a PayPal Express Payment Gateway for WooCommerce.
15
+
16
+ PayPal Express allows you to securely sell your products and subscriptions online using In-Context Checkout to help you meet security requirements without causing your theme to suffer. In-Context Checkout uses a modal iFrame, hosted on PayPal's servers, that overlays the checkout form and provides a secure means for your customers to enter their account information.
17
+
18
+ Also, with Integrated PayPal Setup (Easy Setup), connecting to PayPal is as simple as clicking a button - no complicated API keys to cut and paste.
19
+
20
+ == Installation ==
21
+
22
+ = Minimum Requirements =
23
+
24
+ * WordPress 4.4 or greater
25
+
26
+ = Automatic installation =
27
+
28
+ Automatic installation is the easiest option as WordPress handles the file transfers itself and you don’t need to leave your web browser. To do an automatic install of, log in to your WordPress dashboard, navigate to the Plugins menu and click Add New.
29
+
30
+ In the search field type "WooCommerce PayPal Express Checkout" and click Search Plugins. Once you’ve found our plugin you can view details about it such as the point release, rating and description. Most importantly of course, you can install it by simply clicking “Install Now”.
31
+
32
+ = Manual installation =
33
+
34
+ The manual installation method involves downloading our plugin and uploading it to your webserver via your favourite FTP application. The
35
+ WordPress codex contains [instructions on how to do this here](http://codex.wordpress.org/Managing_Plugins#Manual_Plugin_Installation).
36
+
37
+ = Updating =
38
+
39
+ Automatic updates should work like a charm; as always though, ensure you backup your site just in case.
40
+
41
+ If on the off-chance you do encounter issues with the shop/category pages after an update you simply need to flush the permalinks by going to WordPress > Settings > Permalinks and hitting 'save'. That should return things to normal.
42
+
43
+ == Frequently Asked Questions ==
44
+
45
+ = Does this plugin work with credit cards or just PayPal? =
46
+
47
+ This plugin supports payments using both credit and debit cards as well as PayPal.
48
+
49
+ = Does this support Checkout with PayPal from the cart view? =
50
+
51
+ Yes!
52
+
53
+ = Does this support both production mode and sandbox mode for testing? =
54
+
55
+ Yes it does - production and sandbox mode is driven by how you connect. You may choose to connect in either mode, and disconnect and reconnect in the other mode whenever you want.
56
+
57
+ = Where can I find documentation? =
58
+
59
+ For help setting up and configuring, please refer to our [user guide](http://docs.woothemes.com/document/woocommerce-gateway-paypal-express-checkout/)
60
+
61
+ = Where can I get support or talk to other users? =
62
+
63
+ If you get stuck, you can ask for help in the Plugin Forum.
64
+
65
+ = Will this plugin work with my theme? =
66
+
67
+ Yes, this plugin will work with any theme, but may require some styling to make it match nicely. Please see
68
+ our [codex](http://docs.woothemes.com/documentation/plugins/woocommerce/woocommerce-codex/) for help. If you're
69
+ looking for a theme with built in WooCommerce integration we recommend [Storefront](http://www.woothemes.com/storefront/).
70
+
71
+ = Where can I request new features or report bugs? =
72
+
73
+ New feature requests and bugs reports can be made in the plugin forum.
74
+
75
+ == Screenshots ==
76
+
77
+ 1. Click the "Click Here to Set Up Your PayPal Account" button. If you want to test before goes live, you can switch the Environment, above the button, to Sandbox.
78
+ 2. API credentials will be set after Easy Setup. Or, you can set that manually.
79
+ 3. Checkout with PayPal directly from the Cart.
80
+
81
+ == Changelog ==
82
+
83
+ = 0.1.0 =
84
+ * Beta release
woocommerce-gateway-paypal-express-checkout.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: WooCommerce PayPal Express Checkout Gateway
4
+ * Plugin URI: https://woothemes.com
5
+ * Description: A payment gateway for PayPal Express Checkout ( https://www.paypal.com/us/webapps/mpp/express-checkout ). Requires WC 2.5+
6
+ * Version: 0.1.0
7
+ * Author: Automattic/WooCommerce
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
20
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
21
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22
+ */
23
+
24
+ if ( ! defined( 'ABSPATH' ) ) {
25
+ exit; // Exit if accessed directly
26
+ }
27
+
28
+ /**
29
+ * Return instance of WC_Gateway_PPEC_Plugin.
30
+ *
31
+ * @return WC_Gateway_PPEC_Plugin
32
+ */
33
+ function wc_gateway_ppec() {
34
+ static $plugin;
35
+
36
+ if ( ! isset( $plugin ) ) {
37
+ require_once( 'includes/class-wc-gateway-ppec-plugin.php' );
38
+
39
+ $plugin = new WC_Gateway_PPEC_Plugin( __FILE__, '0.1.0' );
40
+ }
41
+
42
+ return $plugin;
43
+ }
44
+
45
+ wc_gateway_ppec()->maybe_run();