Stripe Payments For WooCommerce by Checkout Plugins - Version 1.1.0

Version Description

TUESDAY, 21ST DECEMBER 2021 = * New: Express Checkout

Download this release

Release Info

Developer brainstormworg
Plugin Icon 128x128 Stripe Payments For WooCommerce by Checkout Plugins
Version 1.1.0
Comparing to
See all releases

Code changes from version 1.0.0 to 1.1.0

admin/admin-controller.php CHANGED
@@ -10,13 +10,13 @@ namespace CPSW\Admin;
10
 
11
  use CPSW\Gateway\Stripe\Stripe_Api;
12
  use CPSW\Inc\Traits\Get_Instance;
 
13
  use CPSW\Inc\Helper;
14
  use CPSW\Gateway\Stripe\Webhook;
15
  use Stripe\OAuth;
16
  use WC_Admin_Settings;
17
  use Exception;
18
 
19
-
20
  /**
21
  * Admin Controller - This class is used to update or delete stripe settings.
22
  *
@@ -67,15 +67,16 @@ class Admin_Controller {
67
  * @since 0.0.1
68
  */
69
  public function __construct() {
70
-
71
  $this->init();
 
72
  foreach ( $this->settings_keys as $key ) {
73
  $this->settings[ $key ] = get_option( $key );
74
  }
75
 
76
  $this->navigation = [
77
- 'cpsw_api_settings' => __( 'Stripe API Settings', 'checkout-plugins-stripe-woo' ),
78
- 'cpsw_stripe' => __( 'Credit Cards', 'checkout-plugins-stripe-woo' ),
 
79
  ];
80
 
81
  }
@@ -86,7 +87,6 @@ class Admin_Controller {
86
  * @since 0.0.1
87
  */
88
  public function init() {
89
-
90
  add_filter( 'woocommerce_settings_tabs_array', [ $this, 'add_settings_tab' ], 50 );
91
  add_action( 'woocommerce_settings_tabs_cpsw_api_settings', [ $this, 'settings_tab' ] );
92
  add_action( 'woocommerce_update_options_cpsw_api_settings', [ $this, 'update_settings' ] );
@@ -97,14 +97,18 @@ class Admin_Controller {
97
  add_action( 'woocommerce_admin_field_cpsw_stripe_connect', [ $this, 'stripe_connect' ] );
98
  add_action( 'woocommerce_admin_field_cpsw_account_id', [ $this, 'account_id' ] );
99
  add_action( 'woocommerce_admin_field_cpsw_webhook_url', [ $this, 'webhook_url' ] );
 
 
100
 
101
- add_action( 'woocommerce_init', [ $this, 'admin_options' ] );
102
- add_action( 'woocommerce_init', [ $this, 'initialise_warnings' ] );
103
 
104
  add_action( 'wp_ajax_cpsw_test_stripe_connection', [ $this, 'connection_test' ] );
105
  add_action( 'wp_ajax_cpsw_disconnect_account', [ $this, 'disconnect_account' ] );
 
106
 
107
  add_action( 'woocommerce_settings_save_cpsw_api_settings', [ $this, 'check_connection_on_updates' ] );
 
108
  add_filter( 'cpsw_settings', [ $this, 'filter_settings_fields' ], 1 );
109
  add_action( 'update_option_cpsw_mode', [ $this, 'update_mode' ], 10, 3 );
110
 
@@ -114,6 +118,49 @@ class Admin_Controller {
114
 
115
  add_filter( 'woocommerce_get_sections_checkout', [ $this, 'add_settings_links' ] );
116
  add_filter( 'woocommerce_get_sections_cpsw_api_settings', [ $this, 'add_settings_links' ] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  }
118
 
119
  /**
@@ -122,14 +169,13 @@ class Admin_Controller {
122
  * @since 0.0.1
123
  */
124
  public function initialise_warnings() {
125
-
126
  // If keys are not set bail.
127
  if ( ! $this->are_keys_set() ) {
128
  add_action( 'admin_notices', [ $this, 'are_keys_set_check' ] );
129
  }
130
 
131
  // If no SSL bail.
132
- if ( 'live' === get_option( 'cpsw_mode' ) && ! is_ssl() ) {
133
  add_action( 'admin_notices', [ $this, 'ssl_not_connect' ] );
134
  }
135
 
@@ -142,7 +188,6 @@ class Admin_Controller {
142
  if ( isset( $_GET['cpsw_call'] ) && ! empty( $_GET['cpsw_call'] ) && 'failed' === $_GET['cpsw_call'] ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
143
  add_action( 'admin_notices', [ $this, 'connect_failed_notice' ] );
144
  }
145
-
146
  }
147
 
148
  /**
@@ -151,7 +196,6 @@ class Admin_Controller {
151
  * @since 0.0.1
152
  */
153
  public function enqueue_scripts() {
154
-
155
  if ( isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] && isset( $_GET['tab'] ) && ( 'cpsw_api_settings' === $_GET['tab'] || isset( $_GET['section'] ) && 'cpsw_stripe' === $_GET['section'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
156
 
157
  wp_register_style( 'cpsw-admin-style', plugins_url( 'assets/css/admin.css', __FILE__ ), [], CPSW_VERSION, 'all' );
@@ -166,7 +210,7 @@ class Admin_Controller {
166
  [
167
  'site_url' => get_site_url() . '/wp-admin/admin.php?page=wc-settings',
168
  'ajax_url' => admin_url( 'admin-ajax.php' ),
169
- 'cpsw_mode' => get_option( 'cpsw_mode' ),
170
  'admin_nonce' => wp_create_nonce( 'cpsw_admin_nonce' ),
171
  'dashboard_url' => admin_url( 'admin.php?page=wc-settings&tab=cpsw_api_settings' ),
172
  'generic_error' => __( 'Something went wrong! Please reload the page and try again.', 'checkout-plugins-stripe-woo' ),
@@ -178,6 +222,37 @@ class Admin_Controller {
178
  'stripe_connect_other_acc' => __( 'You can connect other Stripe account now.', 'checkout-plugins-stripe-woo' ),
179
  'is_connected' => $this->is_stripe_connected(),
180
  'is_manually_connected' => isset( $_GET['connect'] ) ? sanitize_text_field( $_GET['connect'] ) : '', //phpcs:ignore WordPress.Security.NonceVerification.Recommended
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  ]
182
  );
183
  }
@@ -270,7 +345,6 @@ class Admin_Controller {
270
  * @param string $value Field name in string.
271
  */
272
  public function stripe_connect( $value ) {
273
-
274
  if ( true === $this->is_stripe_connected() ) {
275
  return;
276
  }
@@ -320,7 +394,7 @@ class Admin_Controller {
320
  return;
321
  }
322
 
323
- $option_value = get_option( 'cpsw_account_id' );
324
  ?>
325
  <tr valign="top">
326
  <th scope="row">
@@ -383,7 +457,6 @@ class Admin_Controller {
383
  <?php
384
  }
385
 
386
-
387
  /**
388
  * This method is used to display block for Stripe webhook url.
389
  *
@@ -418,13 +491,58 @@ class Admin_Controller {
418
  <?php
419
  }
420
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
421
  /**
422
  * This method is used to display Stripe Account key information on the settings page.
423
  *
424
  * @param string $value Name of the field.
425
  */
426
  public function account_keys( $value ) {
427
-
428
  if ( empty( Helper::get_setting( 'cpsw_pub_key' ) ) && empty( Helper::get_setting( 'cpsw_test_pub_key' ) ) ) {
429
  return;
430
  }
@@ -463,9 +581,7 @@ class Admin_Controller {
463
  * @param string $value Name of the field.
464
  */
465
  public function connect_button( $value ) {
466
-
467
- $data = WC_Admin_Settings::get_field_description( $value );
468
-
469
  $description = $data['description'];
470
  $tooltip_html = $data['tooltip_html'];
471
  $manual_link = false;
@@ -534,7 +650,6 @@ class Admin_Controller {
534
  woocommerce_admin_fields( $this->get_settings() );
535
  }
536
 
537
-
538
  /**
539
  * Uses the WooCommerce options API to save settings via the @see woocommerce_update_options() function.
540
  */
@@ -568,7 +683,6 @@ class Admin_Controller {
568
  );
569
  }
570
 
571
-
572
  /**
573
  * This method is used to initialize all stripe configuration fields.
574
  *
@@ -685,14 +799,12 @@ class Admin_Controller {
685
  return $settings;
686
  }
687
 
688
-
689
  /**
690
  * Checks for response after stripe onboarding process
691
  *
692
  * @return void
693
  */
694
  public function admin_options() {
695
-
696
  if ( ! isset( $_GET['cpsw_connect_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_GET['cpsw_connect_nonce'] ), 'stripe-connect' ) ) {
697
  return;
698
  }
@@ -748,14 +860,12 @@ class Admin_Controller {
748
  }
749
  }
750
 
751
-
752
  /**
753
  * Perform a connection test
754
  *
755
  * @return $mixed
756
  */
757
  public function connection_test() {
758
-
759
  if ( ! isset( $_GET['_security'] ) || ! wp_verify_nonce( sanitize_text_field( $_GET['_security'] ), 'cpsw_admin_nonce' ) ) {
760
  return wp_send_json_error( [ 'message' => __( 'Error: Sorry, the nonce security check didn’t pass. Please reload the page and try again.', 'checkout-plugins-stripe-woo' ) ] );
761
  }
@@ -870,6 +980,25 @@ class Admin_Controller {
870
  return wp_send_json_success( [ 'message' => __( 'Stripe keys are reset successfully.', 'checkout-plugins-stripe-woo' ) ] );
871
  }
872
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
873
  /**
874
  * This method is used get account information from stripe.
875
  *
@@ -881,13 +1010,13 @@ class Admin_Controller {
881
  return false;
882
  }
883
 
884
- $stripe_api = Stripe_Api::get_instance();
885
  $response = $stripe_api->accounts( 'retrieve', [ $account_id ] );
886
  if ( $response['success'] ) {
887
  $response = $response['data'];
888
  return $response->settings->dashboard->display_name;
889
  } else {
890
- return $response['message'];
891
  }
892
  }
893
 
@@ -898,7 +1027,7 @@ class Admin_Controller {
898
  * @return $array array It returns cpsw_settings array.
899
  */
900
  public function filter_settings_fields( $array = [] ) {
901
- if ( 'success' !== get_option( 'cpsw_con_status' ) && 'success' !== get_option( 'cpsw_test_con_status' ) ) {
902
  unset( $array['test_mode'] );
903
  unset( $array['webhook_url'] );
904
  unset( $array['test_webhook_secret'] );
@@ -952,7 +1081,7 @@ class Admin_Controller {
952
  * @return $mixed
953
  */
954
  public function is_stripe_connected() {
955
- if ( 'success' === get_option( 'cpsw_con_status' ) || 'success' === get_option( 'cpsw_test_con_status' ) ) {
956
  return true;
957
  }
958
  return false;
@@ -964,7 +1093,6 @@ class Admin_Controller {
964
  * @return $mixed
965
  */
966
  public function check_connection_on_updates() {
967
-
968
  if ( 'yes' === Helper::get_setting( 'cpsw_auto_connect' ) ) {
969
  return;
970
  }
@@ -1065,7 +1193,6 @@ class Admin_Controller {
1065
  update_option( 'cpsw_con_status', 'success' );
1066
  update_option( 'cpsw_mode', 'live' );
1067
  }
1068
-
1069
  }
1070
 
1071
  /**
@@ -1073,17 +1200,17 @@ class Admin_Controller {
1073
  *
1074
  * @param string $old_value Old value of the option.
1075
  * @param strign $value New value of the option.
 
1076
  * @return void
1077
  */
1078
  public function update_mode( $old_value, $value ) {
1079
-
1080
- if ( 'yes' === get_option( 'cpsw_auto_connect' ) ) {
1081
  return;
1082
  }
1083
 
1084
- if ( ! empty( get_option( 'cpsw_secret_key' ) ) && empty( get_option( 'cpsw_test_secret_key' ) ) ) {
1085
  update_option( 'cpsw_mode', 'live' );
1086
- } elseif ( ! empty( get_option( 'cpsw_test_secret_key' ) ) && empty( get_option( 'cpsw_secret_key' ) ) ) {
1087
  update_option( 'cpsw_mode', 'test' );
1088
  }
1089
  }
@@ -1105,7 +1232,6 @@ class Admin_Controller {
1105
  * @return void
1106
  */
1107
  public function add_breadcrumb() {
1108
-
1109
  if ( ! empty( $this->navigation ) ) {
1110
  ?>
1111
  <ul class="subsubsub">
@@ -1134,13 +1260,16 @@ class Admin_Controller {
1134
  * Adds settings link to the checkout section.
1135
  *
1136
  * @param array $settings_tab Settings tabs array.
 
1137
  * @return array $settings_tab Settings tabs array returned.
1138
  */
1139
  public function add_settings_links( $settings_tab ) {
1140
- if ( isset( $_GET['section'] ) && 'cpsw_stripe' === $_GET['section'] ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
1141
- $settings_tab['cpsw_api_settings'] = __( 'Stripe API Settings', 'checkout-plugins-stripe-woo' );
1142
- $settings_tab['cpsw_stripe'] = __( 'Credit Cards', 'checkout-plugins-stripe-woo' );
 
1143
  }
 
1144
  return apply_filters( 'cpsw_setting_tabs', $settings_tab );
1145
  }
1146
 
@@ -1148,10 +1277,10 @@ class Admin_Controller {
1148
  * Adds manual api keys links.
1149
  *
1150
  * @param string $links default copyright link with text.
 
1151
  * @return string $links Return customized copyright text with link.
1152
  */
1153
  public function add_manual_connect_link( $links ) {
1154
-
1155
  if ( ! isset( $_GET['page'] ) || ! isset( $_GET['tab'] ) || 'wc-settings' !== $_GET['page'] || 'cpsw_api_settings' !== $_GET['tab'] ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
1156
  return $links;
1157
  }
@@ -1181,4 +1310,210 @@ class Admin_Controller {
1181
  }
1182
  }
1183
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1184
  }
10
 
11
  use CPSW\Gateway\Stripe\Stripe_Api;
12
  use CPSW\Inc\Traits\Get_Instance;
13
+ use CPSW\Inc\Logger;
14
  use CPSW\Inc\Helper;
15
  use CPSW\Gateway\Stripe\Webhook;
16
  use Stripe\OAuth;
17
  use WC_Admin_Settings;
18
  use Exception;
19
 
 
20
  /**
21
  * Admin Controller - This class is used to update or delete stripe settings.
22
  *
67
  * @since 0.0.1
68
  */
69
  public function __construct() {
 
70
  $this->init();
71
+
72
  foreach ( $this->settings_keys as $key ) {
73
  $this->settings[ $key ] = get_option( $key );
74
  }
75
 
76
  $this->navigation = [
77
+ 'cpsw_api_settings' => __( 'Stripe API Settings', 'checkout-plugins-stripe-woo' ),
78
+ 'cpsw_stripe' => __( 'Credit Cards', 'checkout-plugins-stripe-woo' ),
79
+ 'cpsw_express_checkout' => __( 'Express Checkout', 'checkout-plugins-stripe-woo' ),
80
  ];
81
 
82
  }
87
  * @since 0.0.1
88
  */
89
  public function init() {
 
90
  add_filter( 'woocommerce_settings_tabs_array', [ $this, 'add_settings_tab' ], 50 );
91
  add_action( 'woocommerce_settings_tabs_cpsw_api_settings', [ $this, 'settings_tab' ] );
92
  add_action( 'woocommerce_update_options_cpsw_api_settings', [ $this, 'update_settings' ] );
97
  add_action( 'woocommerce_admin_field_cpsw_stripe_connect', [ $this, 'stripe_connect' ] );
98
  add_action( 'woocommerce_admin_field_cpsw_account_id', [ $this, 'account_id' ] );
99
  add_action( 'woocommerce_admin_field_cpsw_webhook_url', [ $this, 'webhook_url' ] );
100
+ add_action( 'woocommerce_admin_field_cpsw_express_checkout_preview', [ $this, 'express_checkout_preview' ] );
101
+ add_action( 'woocommerce_admin_field_express_checkout_notice', [ $this, 'express_checkout_notice' ] );
102
 
103
+ add_action( 'admin_init', [ $this, 'admin_options' ] );
104
+ add_action( 'admin_init', [ $this, 'initialise_warnings' ] );
105
 
106
  add_action( 'wp_ajax_cpsw_test_stripe_connection', [ $this, 'connection_test' ] );
107
  add_action( 'wp_ajax_cpsw_disconnect_account', [ $this, 'disconnect_account' ] );
108
+ add_action( 'wp_ajax_cpsw_js_errors', [ $this, 'js_errors' ] );
109
 
110
  add_action( 'woocommerce_settings_save_cpsw_api_settings', [ $this, 'check_connection_on_updates' ] );
111
+ add_filter( 'woocommerce_save_settings_checkout_cpsw_express_checkout', [ $this, 'cpsw_express_checkout_option_updates' ] );
112
  add_filter( 'cpsw_settings', [ $this, 'filter_settings_fields' ], 1 );
113
  add_action( 'update_option_cpsw_mode', [ $this, 'update_mode' ], 10, 3 );
114
 
118
 
119
  add_filter( 'woocommerce_get_sections_checkout', [ $this, 'add_settings_links' ] );
120
  add_filter( 'woocommerce_get_sections_cpsw_api_settings', [ $this, 'add_settings_links' ] );
121
+ add_filter( 'woocommerce_get_settings_checkout', [ $this, 'checkout_settings' ], 10, 2 );
122
+ }
123
+
124
+ /**
125
+ * Saves section cpsw_express_checkout_data to cpsw_stripe settings
126
+ *
127
+ * @return array
128
+ * @since 1.1.0
129
+ */
130
+ public function cpsw_express_checkout_option_updates() {
131
+ if ( isset( $_POST['save'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing
132
+ $express_checkout = [];
133
+ $radio_checkbox = [
134
+ 'express_checkout_enabled' => 'no',
135
+ 'express_checkout_product_sticky_footer' => 'no',
136
+ 'express_checkout_product_checkout_page' => 'no',
137
+ ];
138
+ foreach ( $_POST as $key => $value ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing
139
+ if ( 0 === strpos( $key, 'cpsw_express_checkout' ) ) {
140
+ $k = sanitize_text_field( str_replace( 'cpsw_', '', $key ) );
141
+ if ( ! empty( $radio_checkbox ) && in_array( $k, array_keys( $radio_checkbox ), true ) ) {
142
+ $express_checkout[ $k ] = 'yes';
143
+ unset( $radio_checkbox[ $k ] );
144
+ } else {
145
+ if ( is_array( $value ) ) {
146
+ $express_checkout[ $k ] = array_map( 'sanitize_text_field', $value );
147
+ } else {
148
+ $express_checkout[ $k ] = sanitize_text_field( $value );
149
+ }
150
+ }
151
+
152
+ unset( $_POST[ $key ] ); //phpcs:ignore WordPress.Security.NonceVerification.Missing
153
+ }
154
+ }
155
+
156
+ if ( ! empty( $express_checkout ) ) {
157
+ $cpsw_stripe = get_option( 'woocommerce_cpsw_stripe_settings' );
158
+ $cpsw_stripe = array_merge( $cpsw_stripe, $radio_checkbox, $express_checkout );
159
+ update_option( 'woocommerce_cpsw_stripe_settings', $cpsw_stripe );
160
+ }
161
+ }
162
+
163
+ return false;
164
  }
165
 
166
  /**
169
  * @since 0.0.1
170
  */
171
  public function initialise_warnings() {
 
172
  // If keys are not set bail.
173
  if ( ! $this->are_keys_set() ) {
174
  add_action( 'admin_notices', [ $this, 'are_keys_set_check' ] );
175
  }
176
 
177
  // If no SSL bail.
178
+ if ( 'live' === Helper::get_payment_mode() && ! is_ssl() ) {
179
  add_action( 'admin_notices', [ $this, 'ssl_not_connect' ] );
180
  }
181
 
188
  if ( isset( $_GET['cpsw_call'] ) && ! empty( $_GET['cpsw_call'] ) && 'failed' === $_GET['cpsw_call'] ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
189
  add_action( 'admin_notices', [ $this, 'connect_failed_notice' ] );
190
  }
 
191
  }
192
 
193
  /**
196
  * @since 0.0.1
197
  */
198
  public function enqueue_scripts() {
 
199
  if ( isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] && isset( $_GET['tab'] ) && ( 'cpsw_api_settings' === $_GET['tab'] || isset( $_GET['section'] ) && 'cpsw_stripe' === $_GET['section'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
200
 
201
  wp_register_style( 'cpsw-admin-style', plugins_url( 'assets/css/admin.css', __FILE__ ), [], CPSW_VERSION, 'all' );
210
  [
211
  'site_url' => get_site_url() . '/wp-admin/admin.php?page=wc-settings',
212
  'ajax_url' => admin_url( 'admin-ajax.php' ),
213
+ 'cpsw_mode' => Helper::get_payment_mode(),
214
  'admin_nonce' => wp_create_nonce( 'cpsw_admin_nonce' ),
215
  'dashboard_url' => admin_url( 'admin.php?page=wc-settings&tab=cpsw_api_settings' ),
216
  'generic_error' => __( 'Something went wrong! Please reload the page and try again.', 'checkout-plugins-stripe-woo' ),
222
  'stripe_connect_other_acc' => __( 'You can connect other Stripe account now.', 'checkout-plugins-stripe-woo' ),
223
  'is_connected' => $this->is_stripe_connected(),
224
  'is_manually_connected' => isset( $_GET['connect'] ) ? sanitize_text_field( $_GET['connect'] ) : '', //phpcs:ignore WordPress.Security.NonceVerification.Recommended
225
+ 'cpsw_admin_settings_tab' => isset( $_GET['tab'] ) ? sanitize_text_field( $_GET['tab'] ) : '', //phpcs:ignore WordPress.Security.NonceVerification.Recommended
226
+ ]
227
+ );
228
+ }
229
+
230
+ if ( isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] && isset( $_GET['tab'] ) && 'checkout' === $_GET['tab'] && isset( $_GET['section'] ) && 'cpsw_express_checkout' === $_GET['section'] ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
231
+ wp_register_style( 'cpsw-express-checkout-style', plugins_url( 'assets/css/express-checkout.css', __FILE__ ), [], CPSW_VERSION, 'all' );
232
+ wp_enqueue_style( 'cpsw-express-checkout-style' );
233
+
234
+ wp_register_script( 'cpsw-stripe-external', 'https://js.stripe.com/v3/', [], CPSW_VERSION, true );
235
+ wp_enqueue_script( 'cpsw-stripe-external' );
236
+
237
+ wp_register_script( 'cpsw-express-checkout-js', plugins_url( 'assets/js/express-checkout.js', __FILE__ ), [ 'jquery', 'cpsw-stripe-external' ], CPSW_VERSION, true );
238
+ wp_enqueue_script( 'cpsw-express-checkout-js' );
239
+
240
+ $public_key = ( 'live' === Helper::get_payment_mode() ) ? Helper::get_setting( 'cpsw_pub_key' ) : Helper::get_setting( 'cpsw_test_pub_key' );
241
+
242
+ wp_localize_script(
243
+ 'cpsw-express-checkout-js',
244
+ 'cpsw_express_checkout',
245
+ [
246
+ 'public_key' => $public_key,
247
+ 'style' => [
248
+ 'type' => Helper::get_setting( 'express_checkout_button_type', 'cpsw_stripe' ),
249
+ 'theme' => Helper::get_setting( 'express_checkout_button_theme', 'cpsw_stripe' ),
250
+ 'height' => (int) Helper::get_setting( 'express_checkout_button_height', 'cpsw_stripe' ),
251
+ ],
252
+ 'messages' => [
253
+ /* translators: Html Markup*/
254
+ 'no_method' => sprintf( __( 'No payment method detected. Either your browser is not supported or you do not have save cards. For more details read %1$1sdocument$2$2s.', 'checkout-plugins-stripe-woo' ), '<a href="https://stripe.com/docs/stripe-js/elements/payment-request-button#html-js-testing" target="_blank">', '</a>' ),
255
+ ],
256
  ]
257
  );
258
  }
345
  * @param string $value Field name in string.
346
  */
347
  public function stripe_connect( $value ) {
 
348
  if ( true === $this->is_stripe_connected() ) {
349
  return;
350
  }
394
  return;
395
  }
396
 
397
+ $option_value = Helper::get_setting( 'cpsw_account_id' );
398
  ?>
399
  <tr valign="top">
400
  <th scope="row">
457
  <?php
458
  }
459
 
 
460
  /**
461
  * This method is used to display block for Stripe webhook url.
462
  *
491
  <?php
492
  }
493
 
494
+ /**
495
+ * Displays express checkout button preview
496
+ *
497
+ * @param array $value settings data.
498
+ *
499
+ * @return void
500
+ */
501
+ public function express_checkout_preview( $value ) {
502
+ ?>
503
+ <tr valign="top">
504
+ <th scope="row">
505
+ <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?> </label>
506
+ </th>
507
+ <td class="form-wc form-wc-<?php echo esc_attr( $value['class'] ); ?>">
508
+ <fieldset>
509
+ <div class="cpsw_express_checkout_preview_wrapper">
510
+ <div class="cpsw_express_checkout_preview"></div>
511
+ </div>
512
+ </fieldset>
513
+ <?php if ( ! empty( $value['desc'] ) ) { ?>
514
+ <fieldset class="cpsw_express_checkout_preview_description">
515
+ <div class="notice inline notice-warning cpsw_inline_notice" style="margin: 5px 0 -10px;padding:10px 20px;">
516
+ <?php echo wp_kses_post( $value['desc'] ); ?>
517
+ </div>
518
+ </fieldset>
519
+ <?php } ?>
520
+ </td>
521
+ </tr>
522
+ <?php
523
+ }
524
+
525
+ /**
526
+ * Displays express checkout button preview
527
+ *
528
+ * @param array $value settings data.
529
+ *
530
+ * @return void
531
+ */
532
+ public function express_checkout_notice( $value ) {
533
+ ?>
534
+ <div class="notice inline notice-error cpsw_inline_notice" style="margin: 5px 0 -10px;padding:10px 20px;">
535
+ <?php echo wp_kses_post( $value['desc'] ); ?>
536
+ </div>
537
+ <?php
538
+ }
539
+
540
  /**
541
  * This method is used to display Stripe Account key information on the settings page.
542
  *
543
  * @param string $value Name of the field.
544
  */
545
  public function account_keys( $value ) {
 
546
  if ( empty( Helper::get_setting( 'cpsw_pub_key' ) ) && empty( Helper::get_setting( 'cpsw_test_pub_key' ) ) ) {
547
  return;
548
  }
581
  * @param string $value Name of the field.
582
  */
583
  public function connect_button( $value ) {
584
+ $data = WC_Admin_Settings::get_field_description( $value );
 
 
585
  $description = $data['description'];
586
  $tooltip_html = $data['tooltip_html'];
587
  $manual_link = false;
650
  woocommerce_admin_fields( $this->get_settings() );
651
  }
652
 
 
653
  /**
654
  * Uses the WooCommerce options API to save settings via the @see woocommerce_update_options() function.
655
  */
683
  );
684
  }
685
 
 
686
  /**
687
  * This method is used to initialize all stripe configuration fields.
688
  *
799
  return $settings;
800
  }
801
 
 
802
  /**
803
  * Checks for response after stripe onboarding process
804
  *
805
  * @return void
806
  */
807
  public function admin_options() {
 
808
  if ( ! isset( $_GET['cpsw_connect_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_GET['cpsw_connect_nonce'] ), 'stripe-connect' ) ) {
809
  return;
810
  }
860
  }
861
  }
862
 
 
863
  /**
864
  * Perform a connection test
865
  *
866
  * @return $mixed
867
  */
868
  public function connection_test() {
 
869
  if ( ! isset( $_GET['_security'] ) || ! wp_verify_nonce( sanitize_text_field( $_GET['_security'] ), 'cpsw_admin_nonce' ) ) {
870
  return wp_send_json_error( [ 'message' => __( 'Error: Sorry, the nonce security check didn’t pass. Please reload the page and try again.', 'checkout-plugins-stripe-woo' ) ] );
871
  }
980
  return wp_send_json_success( [ 'message' => __( 'Stripe keys are reset successfully.', 'checkout-plugins-stripe-woo' ) ] );
981
  }
982
 
983
+ /**
984
+ * Logs js errors
985
+ *
986
+ * @return json
987
+ */
988
+ public function js_errors() {
989
+ if ( ! isset( $_POST['_security'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['_security'] ), 'cpsw_js_error_nonce' ) ) {
990
+ return wp_send_json_error( [ 'message' => __( 'Invalid Nonce', 'checkout-plugins-stripe-woo' ) ] );
991
+ }
992
+
993
+ if ( isset( $_POST['error'] ) ) {
994
+ $error = $_POST['error'];
995
+ $error_message = $error['message'] . ' (' . $error['type'] . ')';
996
+ Logger::error( $error_message, true );
997
+ return wp_send_json_success( [ 'message' => $error_message ] );
998
+ }
999
+ exit();
1000
+ }
1001
+
1002
  /**
1003
  * This method is used get account information from stripe.
1004
  *
1010
  return false;
1011
  }
1012
 
1013
+ $stripe_api = new Stripe_Api();
1014
  $response = $stripe_api->accounts( 'retrieve', [ $account_id ] );
1015
  if ( $response['success'] ) {
1016
  $response = $response['data'];
1017
  return $response->settings->dashboard->display_name;
1018
  } else {
1019
+ return '';
1020
  }
1021
  }
1022
 
1027
  * @return $array array It returns cpsw_settings array.
1028
  */
1029
  public function filter_settings_fields( $array = [] ) {
1030
+ if ( 'success' !== Helper::get_setting( 'cpsw_con_status' ) && 'success' !== Helper::get_setting( 'cpsw_test_con_status' ) ) {
1031
  unset( $array['test_mode'] );
1032
  unset( $array['webhook_url'] );
1033
  unset( $array['test_webhook_secret'] );
1081
  * @return $mixed
1082
  */
1083
  public function is_stripe_connected() {
1084
+ if ( 'success' === Helper::get_setting( 'cpsw_con_status' ) || 'success' === Helper::get_setting( 'cpsw_test_con_status' ) ) {
1085
  return true;
1086
  }
1087
  return false;
1093
  * @return $mixed
1094
  */
1095
  public function check_connection_on_updates() {
 
1096
  if ( 'yes' === Helper::get_setting( 'cpsw_auto_connect' ) ) {
1097
  return;
1098
  }
1193
  update_option( 'cpsw_con_status', 'success' );
1194
  update_option( 'cpsw_mode', 'live' );
1195
  }
 
1196
  }
1197
 
1198
  /**
1200
  *
1201
  * @param string $old_value Old value of the option.
1202
  * @param strign $value New value of the option.
1203
+ *
1204
  * @return void
1205
  */
1206
  public function update_mode( $old_value, $value ) {
1207
+ if ( 'yes' === Helper::get_setting( 'cpsw_auto_connect' ) ) {
 
1208
  return;
1209
  }
1210
 
1211
+ if ( ! empty( Helper::get_setting( 'cpsw_secret_key' ) ) && empty( Helper::get_setting( 'cpsw_test_secret_key' ) ) ) {
1212
  update_option( 'cpsw_mode', 'live' );
1213
+ } elseif ( ! empty( Helper::get_setting( 'cpsw_test_secret_key' ) ) && empty( Helper::get_setting( 'cpsw_secret_key' ) ) ) {
1214
  update_option( 'cpsw_mode', 'test' );
1215
  }
1216
  }
1232
  * @return void
1233
  */
1234
  public function add_breadcrumb() {
 
1235
  if ( ! empty( $this->navigation ) ) {
1236
  ?>
1237
  <ul class="subsubsub">
1260
  * Adds settings link to the checkout section.
1261
  *
1262
  * @param array $settings_tab Settings tabs array.
1263
+ *
1264
  * @return array $settings_tab Settings tabs array returned.
1265
  */
1266
  public function add_settings_links( $settings_tab ) {
1267
+ if ( isset( $_GET['section'] ) && ( 'cpsw_stripe' === $_GET['section'] || 'cpsw_express_checkout' === $_GET['section'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
1268
+ $settings_tab['cpsw_api_settings'] = __( 'Stripe API Settings', 'checkout-plugins-stripe-woo' );
1269
+ $settings_tab['cpsw_stripe'] = __( 'Credit Cards', 'checkout-plugins-stripe-woo' );
1270
+ $settings_tab['cpsw_express_checkout'] = __( 'Express Checkout', 'checkout-plugins-stripe-woo' );
1271
  }
1272
+ array_shift( $settings_tab );
1273
  return apply_filters( 'cpsw_setting_tabs', $settings_tab );
1274
  }
1275
 
1277
  * Adds manual api keys links.
1278
  *
1279
  * @param string $links default copyright link with text.
1280
+ *
1281
  * @return string $links Return customized copyright text with link.
1282
  */
1283
  public function add_manual_connect_link( $links ) {
 
1284
  if ( ! isset( $_GET['page'] ) || ! isset( $_GET['tab'] ) || 'wc-settings' !== $_GET['page'] || 'cpsw_api_settings' !== $_GET['tab'] ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
1285
  return $links;
1286
  }
1310
  }
1311
  }
1312
 
1313
+ /**
1314
+ * Add settings for section checkout
1315
+ *
1316
+ * @param array $settings existing settings.
1317
+ * @param string $current_section section.
1318
+ * @return array
1319
+ */
1320
+ public function checkout_settings( $settings, $current_section ) {
1321
+ if ( 'cpsw_api_settings' === $current_section ) {
1322
+ header( 'Location: ' . admin_url() . 'admin.php?page=wc-settings&tab=cpsw_api_settings' );
1323
+ exit();
1324
+ }
1325
+ if ( 'cpsw_express_checkout' === $current_section ) {
1326
+ $settings = [];
1327
+ $values = Helper::get_gateway_settings();
1328
+
1329
+ if ( 'no' === $values['enabled'] ) {
1330
+ $settings = [
1331
+ 'notice' => [
1332
+ 'title' => '',
1333
+ 'type' => 'express_checkout_notice',
1334
+ 'desc' => __( 'Express Checkout is a feature of Card Payments. Enable Card Payments to use Express Checkout', 'checkout-plugins-stripe-woo' ),
1335
+ ],
1336
+ ];
1337
+ } else {
1338
+ $settings = [
1339
+ 'section_title' => [
1340
+ 'name' => __( 'Express Checkout', 'checkout-plugins-stripe-woo' ),
1341
+ 'type' => 'title',
1342
+ /* translators: HTML Markup*/
1343
+ 'desc' => sprintf( __( 'Accept payment using Apple Pay, Google Pay, Browser Payment Method.%1$1sExpress checkout uses Payment Request API which is based on client\'s browser and saved cards.%1$1sPlease check %2$2sprerequisite%3$3s for Apple Pay, Google Pay and Browser Payment Method.', 'checkout-plugins-stripe-woo' ), '<br/>', '<a href="https://stripe.com/docs/stripe-js/elements/payment-request-button#html-js-testing" target="_blank">', '</a>' ),
1344
+ 'id' => 'cpsw_express_checkout',
1345
+ ],
1346
+ 'enable' => [
1347
+ 'name' => __( 'Enable Express Checkout', 'checkout-plugins-stripe-woo' ),
1348
+ 'id' => 'cpsw_express_checkout_enabled',
1349
+ 'type' => 'checkbox',
1350
+ 'value' => $values['express_checkout_enabled'],
1351
+ ],
1352
+ 'button_location' => [
1353
+ 'title' => __( 'Show button on', 'checkout-plugins-stripe-woo' ),
1354
+ 'type' => 'multiselect',
1355
+ 'class' => 'cpsw_express_checkout_location',
1356
+ 'id' => 'cpsw_express_checkout_location',
1357
+ 'desc_tip' => __( 'Choose page to display express checkout buttons. By default displayed in all pages.', 'checkout-plugins-stripe-woo' ),
1358
+ 'options' => [
1359
+ 'product' => __( 'Product', 'checkout-plugins-stripe-woo' ),
1360
+ 'cart' => __( 'Cart', 'checkout-plugins-stripe-woo' ),
1361
+ 'checkout' => __( 'Checkout', 'checkout-plugins-stripe-woo' ),
1362
+ ],
1363
+ 'value' => $values['express_checkout_location'],
1364
+ ],
1365
+ 'button_type' => [
1366
+ 'title' => __( 'Button type', 'checkout-plugins-stripe-woo' ),
1367
+ 'type' => 'select',
1368
+ 'id' => 'cpsw_express_checkout_button_type',
1369
+ 'desc' => __( 'Select label for express checkout button.', 'checkout-plugins-stripe-woo' ),
1370
+ 'value' => $values['express_checkout_button_type'],
1371
+ 'options' => [
1372
+ 'default' => __( 'Default', 'checkout-plugins-stripe-woo' ),
1373
+ 'book' => __( 'Book', 'checkout-plugins-stripe-woo' ),
1374
+ 'buy' => __( 'Buy', 'checkout-plugins-stripe-woo' ),
1375
+ 'donate' => __( 'Donate', 'checkout-plugins-stripe-woo' ),
1376
+ ],
1377
+ 'desc_tip' => true,
1378
+ ],
1379
+ 'button_theme' => [
1380
+ 'title' => __( 'Button theme', 'checkout-plugins-stripe-woo' ),
1381
+ 'type' => 'select',
1382
+ 'id' => 'cpsw_express_checkout_button_theme',
1383
+ 'desc' => __( 'Select theme for express checkout button.', 'checkout-plugins-stripe-woo' ),
1384
+ 'value' => $values['express_checkout_button_theme'],
1385
+ 'options' => [
1386
+ 'dark' => __( 'Dark', 'checkout-plugins-stripe-woo' ),
1387
+ 'light' => __( 'Light', 'checkout-plugins-stripe-woo' ),
1388
+ 'light-outline' => __( 'Light Outline', 'checkout-plugins-stripe-woo' ),
1389
+ ],
1390
+ 'desc_tip' => true,
1391
+ ],
1392
+ 'button_height' => [
1393
+ 'title' => __( 'Button height', 'checkout-plugins-stripe-woo' ),
1394
+ 'type' => 'number',
1395
+ 'id' => 'cpsw_express_checkout_button_height',
1396
+ 'desc' => __( 'Select height for express checkout button (in px). Button height can be between 35px to 60px. Default height 40px.', 'checkout-plugins-stripe-woo' ),
1397
+ 'value' => $values['express_checkout_button_height'],
1398
+ 'desc_tip' => true,
1399
+ ],
1400
+ 'separator_text' => [
1401
+ 'title' => __( 'Separator text', 'checkout-plugins-stripe-woo' ),
1402
+ 'type' => 'text',
1403
+ 'id' => 'cpsw_express_checkout_separator',
1404
+ 'desc' => __( 'Add express checkout button separator text. This will separate express checkout button with other buttons.', 'checkout-plugins-stripe-woo' ),
1405
+ 'value' => $values['express_checkout_separator'],
1406
+ 'desc_tip' => true,
1407
+ ],
1408
+ 'preview' => [
1409
+ 'title' => __( 'Button Preview', 'checkout-plugins-stripe-woo' ),
1410
+ 'type' => 'cpsw_express_checkout_preview',
1411
+ 'id' => 'cpsw_express_checkout_preview',
1412
+ ],
1413
+ 'section_end' => [
1414
+ 'type' => 'sectionend',
1415
+ 'id' => 'cpsw_express_checkout',
1416
+ ],
1417
+ 'product_page_section_title' => [
1418
+ 'name' => __( 'Product page options', 'checkout-plugins-stripe-woo' ),
1419
+ 'type' => 'title',
1420
+ 'desc' => __( 'Advanced customization options for product page.', 'checkout-plugins-stripe-woo' ),
1421
+ 'id' => 'cpsw_express_checkout_product_page',
1422
+ ],
1423
+ 'above_woocommerce_button' => [
1424
+ 'title' => __( 'Button position', 'checkout-plugins-stripe-woo' ),
1425
+ 'type' => 'select',
1426
+ 'id' => 'cpsw_express_checkout_product_page_position',
1427
+ 'desc' => __( 'Select the position of Express Checkout button. This option will work only for product page.', 'checkout-plugins-stripe-woo' ),
1428
+ 'value' => $values['express_checkout_product_page_position'],
1429
+ 'options' => [
1430
+ 'above' => __( 'Above Add to Cart', 'checkout-plugins-stripe-woo' ),
1431
+ 'below' => __( 'Below Add to Cart', 'checkout-plugins-stripe-woo' ),
1432
+ ],
1433
+ 'desc_tip' => true,
1434
+ ],
1435
+ 'sticky_footer' => [
1436
+ 'name' => __( 'Responsive behaviour', 'checkout-plugins-stripe-woo' ),
1437
+ /* translators: HTML Markup*/
1438
+ 'desc' => sprintf( __( 'If checked the express checkout button will stick%1$1sat bottom of screen on responsive devices.', 'checkout-plugins-stripe-woo' ), '<br/>' ),
1439
+ 'type' => 'checkbox',
1440
+ 'id' => 'cpsw_express_checkout_product_sticky_footer',
1441
+ 'value' => $values['express_checkout_product_sticky_footer'],
1442
+ ],
1443
+ 'product_page_section_end' => [
1444
+ 'type' => 'sectionend',
1445
+ 'id' => 'cpsw_express_checkout',
1446
+ ],
1447
+ 'checkout_page_section_title' => [
1448
+ 'name' => __( 'Checkout page options', 'checkout-plugins-stripe-woo' ),
1449
+ 'type' => 'title',
1450
+ 'desc' => __( 'Advanced customization options for checkout page.', 'checkout-plugins-stripe-woo' ),
1451
+ 'id' => 'cpsw_express_checkout_product_page',
1452
+ ],
1453
+ 'checkout_page_options' => [
1454
+ 'title' => __( 'Advanced options', 'checkout-plugins-stripe-woo' ),
1455
+ 'type' => 'checkbox',
1456
+ 'id' => 'cpsw_express_checkout_product_checkout_page',
1457
+ 'value' => $values['express_checkout_product_checkout_page'],
1458
+ ],
1459
+ 'title' => [
1460
+ 'title' => __( 'Title', 'checkout-plugins-stripe-woo' ),
1461
+ 'type' => 'text',
1462
+ 'class' => 'cpsw_checkout_options',
1463
+ 'id' => 'cpsw_express_checkout_title',
1464
+ 'desc' => __( 'Add a title above express checkout button on checkout page.', 'checkout-plugins-stripe-woo' ),
1465
+ 'value' => $values['express_checkout_title'],
1466
+ 'desc_tip' => true,
1467
+ ],
1468
+ 'tagline' => [
1469
+ 'title' => __( 'Tagline', 'checkout-plugins-stripe-woo' ),
1470
+ 'type' => 'text',
1471
+ 'class' => 'cpsw_checkout_options',
1472
+ 'id' => 'cpsw_express_checkout_tagline',
1473
+ 'desc' => __( 'Add a tagline just above express checkout button on checkout page.', 'checkout-plugins-stripe-woo' ),
1474
+ 'value' => $values['express_checkout_tagline'],
1475
+ 'desc_tip' => true,
1476
+ ],
1477
+ 'checkout_button_width' => [
1478
+ 'title' => __( 'Button width', 'checkout-plugins-stripe-woo' ),
1479
+ 'type' => 'number',
1480
+ 'class' => 'cpsw_checkout_options',
1481
+ 'id' => 'cpsw_express_checkout_button_width',
1482
+ 'desc' => __( 'Select width for button (in px). Minimum width should be 150px, Default width 100%', 'checkout-plugins-stripe-woo' ),
1483
+ 'value' => $values['express_checkout_button_width'],
1484
+ 'desc_tip' => true,
1485
+ ],
1486
+ 'checkout_button_alignment' => [
1487
+ 'title' => __( 'Alignment', 'checkout-plugins-stripe-woo' ),
1488
+ 'type' => 'select',
1489
+ 'class' => 'cpsw_checkout_options',
1490
+ 'id' => 'cpsw_express_checkout_button_alignment',
1491
+ 'desc' => __( 'This setting will align title, tagline and button based on selection on checkout page.', 'checkout-plugins-stripe-woo' ),
1492
+ 'value' => $values['express_checkout_button_alignment'],
1493
+ 'options' => [
1494
+ 'left' => __( 'Left', 'checkout-plugins-stripe-woo' ),
1495
+ 'center' => __( 'Center', 'checkout-plugins-stripe-woo' ),
1496
+ 'right' => __( 'Right', 'checkout-plugins-stripe-woo' ),
1497
+ ],
1498
+ 'desc_tip' => true,
1499
+ ],
1500
+ 'checkout_separator_text' => [
1501
+ 'title' => __( 'Separator text', 'checkout-plugins-stripe-woo' ),
1502
+ 'type' => 'text',
1503
+ 'class' => 'cpsw_checkout_options',
1504
+ 'id' => 'cpsw_express_checkout_separator_checkout',
1505
+ 'desc' => __( 'Add separator text for checkout page. If empty will show default separator text.', 'checkout-plugins-stripe-woo' ),
1506
+ 'value' => $values['express_checkout_separator_checkout'],
1507
+ 'desc_tip' => true,
1508
+ ],
1509
+ 'checkout_page_section_end' => [
1510
+ 'type' => 'sectionend',
1511
+ 'id' => 'cpsw_express_checkout',
1512
+ ],
1513
+ ];
1514
+ }
1515
+ }
1516
+
1517
+ return $settings;
1518
+ }
1519
  }
admin/assets/css/admin.css CHANGED
@@ -1,128 +1,128 @@
1
- /*
2
- * Admin Style
3
- */
4
-
5
- .form_overlay {
6
- opacity: 0.5;
7
- pointer-events: none;
8
- }
9
-
10
- .form-wc-wc_cpsw_connect_btn fieldset {
11
- margin: 20px 0 20px 0;
12
- }
13
-
14
- .form-wc-wc_cpsw_connect_btn img {
15
- margin: 5px 10px 0 10px;
16
- width: 20px;
17
- float: left;
18
- }
19
-
20
- .form-wc-wc_cpsw_connect_btn .cpsw_connect_btn,
21
- .form-wc-account_id .cpsw_connect_btn {
22
- background: #6772e5;
23
- display: inline-block;
24
- height: 38px;
25
- text-decoration: none;
26
- width: 240px;
27
-
28
- border-radius: 4px;
29
- -moz-border-radius: 4px;
30
- -webkit-border-radius: 4px;
31
-
32
- user-select: none;
33
- -moz-user-select: none;
34
- -webkit-user-select: none;
35
- -ms-user-select: none;
36
-
37
- -webkit-font-smoothing: antialiased;
38
- }
39
-
40
- .form-wc-account_id .cpsw_connect_btn {
41
- margin: 10px;
42
- }
43
-
44
- .form-wc-wc_cpsw_connect_btn .cpsw_connect_btn:hover {
45
- background: #3f4ddf;
46
- }
47
-
48
- .form-wc-wc_cpsw_connect_btn .cpsw_connect_btn span,
49
- .form-wc-account_id .cpsw_connect_btn span {
50
- color: #fff;
51
- display: block;
52
- font-family: sohne-var, "Helvetica Neue", Arial, sans-serif;
53
- font-size: 15px;
54
- font-weight: 400;
55
- line-height: 14px;
56
- padding: 11px 0 0 65px;
57
- position: relative;
58
- text-align: left;
59
- }
60
-
61
- .form-wc-wc_cpsw_connect_btn .cpsw_connect_btn span::before,
62
- .form-wc-account_id .cpsw_connect_btn span::before {
63
- background-repeat: no-repeat;
64
- content: "";
65
- height: 20px;
66
- left: 16%;
67
- position: absolute;
68
- top: 30%;
69
- width: 49.58px;
70
- background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 468 222.5' style='enable-background:new 0 0 468 222.5;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill-rule:evenodd;clip-rule:evenodd;fill:%23FFFFFF;%7D%0A%3C/style%3E%3Cg%3E%3Cpath class='st0' d='M6.796 7.639c0-1.03.845-1.427 2.245-1.427 2.008 0 4.544.608 6.552 1.691V1.695C13.4.823 11.234.479 9.041.479 3.678.48.111 3.28.111 7.956c0 7.292 10.04 6.13 10.04 9.273 0 1.215-1.057 1.611-2.536 1.611-2.193 0-4.993-.898-7.213-2.113v6.288a18.315 18.315 0 007.213 1.506c5.495 0 9.273-2.722 9.273-7.45-.027-7.873-10.092-6.473-10.092-9.432z'/%3E%3Cpath class='st0' d='M6.796 7.639c0-1.03.845-1.427 2.245-1.427 2.008 0 4.544.608 6.552 1.691V1.695C13.4.823 11.234.479 9.041.479 3.678.48.111 3.28.111 7.956c0 7.292 10.04 6.13 10.04 9.273 0 1.215-1.057 1.611-2.536 1.611-2.193 0-4.993-.898-7.213-2.113v6.288a18.315 18.315 0 007.213 1.506c5.495 0 9.273-2.722 9.273-7.45-.027-7.873-10.092-6.473-10.092-9.432z'/%3E%3C/g%3E%3C/svg%3E"); /* stylelint-disable-line function-url-quotes */
71
- background-size: 380px;
72
- }
73
-
74
- .wc-connect-stripe-help {
75
- margin-top: 10px;
76
- }
77
-
78
- .form-wc-wc_cpsw_connect_btn .st_connect_mn_dv {
79
- margin-top: 14px;
80
- }
81
-
82
- .form-wc-wc_cpsw_test_button fieldset {
83
- margin: 20px 0 20px 0;
84
- }
85
-
86
- .form-wc-wc_cpsw_test_button a {
87
- border: 1px solid rgb(103, 114, 229);
88
- }
89
-
90
- .form-wc-wc_cpsw_test_button a,
91
- .form-wc-wc_cpsw_test_button a > span {
92
- padding: 5px;
93
- color: rgb(103, 114, 229);
94
- font-weight: 500;
95
- background: #fff;
96
- border-radius: 5px;
97
- text-decoration: none;
98
- }
99
-
100
- .stipe-connect-active {
101
- color: #008000;
102
- font-weight: 500;
103
- }
104
-
105
- .form-wc-wc_stripe_acc_keys fieldset {
106
- margin-top: 10px !important;
107
- }
108
-
109
- .cpsw_connect_mn_btn,
110
- .cpsw_connect_hide_btn {
111
- text-decoration: udnerline;
112
- color: inherit;
113
- font-size: 12px;
114
- }
115
-
116
- .account_status .cpsw_connect_mn_btn,
117
- .account_status .cpsw_connect_hide_btn {
118
- color: #2271b1;
119
- font-size: 14px;
120
- }
121
-
122
- #wpfooter {
123
- display: block !important;
124
- }
125
-
126
- .form-wc-account_id .cpsw_connect_hide_btn {
127
- display: none;
128
- }
1
+ /*
2
+ * Admin Style
3
+ */
4
+
5
+ .form_overlay {
6
+ opacity: 0.5;
7
+ pointer-events: none;
8
+ }
9
+
10
+ .form-wc-wc_cpsw_connect_btn fieldset {
11
+ margin: 20px 0 20px 0;
12
+ }
13
+
14
+ .form-wc-wc_cpsw_connect_btn img {
15
+ margin: 5px 10px 0 10px;
16
+ width: 20px;
17
+ float: left;
18
+ }
19
+
20
+ .form-wc-wc_cpsw_connect_btn .cpsw_connect_btn,
21
+ .form-wc-account_id .cpsw_connect_btn {
22
+ background: #6772e5;
23
+ display: inline-block;
24
+ height: 38px;
25
+ text-decoration: none;
26
+ width: 240px;
27
+
28
+ border-radius: 4px;
29
+ -moz-border-radius: 4px;
30
+ -webkit-border-radius: 4px;
31
+
32
+ user-select: none;
33
+ -moz-user-select: none;
34
+ -webkit-user-select: none;
35
+ -ms-user-select: none;
36
+
37
+ -webkit-font-smoothing: antialiased;
38
+ }
39
+
40
+ .form-wc-account_id .cpsw_connect_btn {
41
+ margin: 10px;
42
+ }
43
+
44
+ .form-wc-wc_cpsw_connect_btn .cpsw_connect_btn:hover {
45
+ background: #3f4ddf;
46
+ }
47
+
48
+ .form-wc-wc_cpsw_connect_btn .cpsw_connect_btn span,
49
+ .form-wc-account_id .cpsw_connect_btn span {
50
+ color: #fff;
51
+ display: block;
52
+ font-family: sohne-var, "Helvetica Neue", Arial, sans-serif;
53
+ font-size: 15px;
54
+ font-weight: 400;
55
+ line-height: 14px;
56
+ padding: 11px 0 0 65px;
57
+ position: relative;
58
+ text-align: left;
59
+ }
60
+
61
+ .form-wc-wc_cpsw_connect_btn .cpsw_connect_btn span::before,
62
+ .form-wc-account_id .cpsw_connect_btn span::before {
63
+ background-repeat: no-repeat;
64
+ content: "";
65
+ height: 20px;
66
+ left: 16%;
67
+ position: absolute;
68
+ top: 30%;
69
+ width: 49.58px;
70
+ background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 468 222.5' style='enable-background:new 0 0 468 222.5;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill-rule:evenodd;clip-rule:evenodd;fill:%23FFFFFF;%7D%0A%3C/style%3E%3Cg%3E%3Cpath class='st0' d='M6.796 7.639c0-1.03.845-1.427 2.245-1.427 2.008 0 4.544.608 6.552 1.691V1.695C13.4.823 11.234.479 9.041.479 3.678.48.111 3.28.111 7.956c0 7.292 10.04 6.13 10.04 9.273 0 1.215-1.057 1.611-2.536 1.611-2.193 0-4.993-.898-7.213-2.113v6.288a18.315 18.315 0 007.213 1.506c5.495 0 9.273-2.722 9.273-7.45-.027-7.873-10.092-6.473-10.092-9.432z'/%3E%3Cpath class='st0' d='M6.796 7.639c0-1.03.845-1.427 2.245-1.427 2.008 0 4.544.608 6.552 1.691V1.695C13.4.823 11.234.479 9.041.479 3.678.48.111 3.28.111 7.956c0 7.292 10.04 6.13 10.04 9.273 0 1.215-1.057 1.611-2.536 1.611-2.193 0-4.993-.898-7.213-2.113v6.288a18.315 18.315 0 007.213 1.506c5.495 0 9.273-2.722 9.273-7.45-.027-7.873-10.092-6.473-10.092-9.432z'/%3E%3C/g%3E%3C/svg%3E"); /* stylelint-disable-line function-url-quotes */
71
+ background-size: 380px;
72
+ }
73
+
74
+ .wc-connect-stripe-help {
75
+ margin-top: 10px;
76
+ }
77
+
78
+ .form-wc-wc_cpsw_connect_btn .st_connect_mn_dv {
79
+ margin-top: 14px;
80
+ }
81
+
82
+ .form-wc-wc_cpsw_test_button fieldset {
83
+ margin: 20px 0 20px 0;
84
+ }
85
+
86
+ .form-wc-wc_cpsw_test_button a {
87
+ border: 1px solid rgb(103, 114, 229);
88
+ }
89
+
90
+ .form-wc-wc_cpsw_test_button a,
91
+ .form-wc-wc_cpsw_test_button a > span {
92
+ padding: 5px;
93
+ color: rgb(103, 114, 229);
94
+ font-weight: 500;
95
+ background: #fff;
96
+ border-radius: 5px;
97
+ text-decoration: none;
98
+ }
99
+
100
+ .stipe-connect-active {
101
+ color: #008000;
102
+ font-weight: 500;
103
+ }
104
+
105
+ .form-wc-wc_stripe_acc_keys fieldset {
106
+ margin-top: 10px !important;
107
+ }
108
+
109
+ .cpsw_connect_mn_btn,
110
+ .cpsw_connect_hide_btn {
111
+ text-decoration: udnerline;
112
+ color: inherit;
113
+ font-size: 12px;
114
+ }
115
+
116
+ .account_status .cpsw_connect_mn_btn,
117
+ .account_status .cpsw_connect_hide_btn {
118
+ color: #2271b1;
119
+ font-size: 14px;
120
+ }
121
+
122
+ #wpfooter {
123
+ display: block !important;
124
+ }
125
+
126
+ .form-wc-account_id .cpsw_connect_hide_btn {
127
+ display: none;
128
+ }
admin/assets/css/express-checkout.css ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .cpsw_express_checkout_preview_wrapper {
2
+ max-width: 380px;
3
+ width: 100%;
4
+ }
5
+
6
+ .cpsw_floating_preview {
7
+ width: 400px;
8
+ position: fixed;
9
+ left: 900px;
10
+ top: 300px;
11
+ }
12
+
13
+ .cpsw_express_checkout_preview {
14
+ max-width: 100%;
15
+ }
16
+
17
+ .cpsw_preview_title {
18
+ font-weight: 600;
19
+ margin-bottom: 0.5em;
20
+ margin-top: 0;
21
+ }
22
+
23
+ .cpsw_preview_tagline {
24
+ font-style: italic;
25
+ margin-bottom: 1em;
26
+ }
27
+
28
+ .cpsw_preview_notice {
29
+ font-style: italic;
30
+ font-size: 12px;
31
+ clear: both;
32
+ margin-top: 1em;
33
+ display: none;
34
+ }
35
+
36
+ .cpsw_button_preview_label {
37
+ font-weight: 600;
38
+ margin-bottom: 1em;
39
+ font-size: 15px;
40
+ display: none;
41
+ }
42
+
admin/assets/js/admin.js CHANGED
@@ -24,7 +24,7 @@
24
  $( '#cpsw_live_webhook_secret' ).closest( 'tr' ).hide();
25
  }
26
 
27
- if ( cpsw_ajax_object.is_connected === '' ) {
28
  $( '.woocommerce-save-button' ).hide();
29
  }
30
 
@@ -191,6 +191,6 @@
191
  } );
192
  } );
193
  $( document ).ready( function() {
194
- $( '.cpsw_allowed_cards_field' ).selectWoo();
195
  } );
196
  }( jQuery ) );
24
  $( '#cpsw_live_webhook_secret' ).closest( 'tr' ).hide();
25
  }
26
 
27
+ if ( cpsw_ajax_object.is_connected === '' && 'cpsw_api_settings' === cpsw_ajax_object.cpsw_admin_settings_tab ) {
28
  $( '.woocommerce-save-button' ).hide();
29
  }
30
 
191
  } );
192
  } );
193
  $( document ).ready( function() {
194
+ $( '.cpsw_select_woo' ).selectWoo();
195
  } );
196
  }( jQuery ) );
admin/assets/js/express-checkout.js ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ ( function( $ ) {
3
+ const pubKey = cpsw_express_checkout.public_key;
4
+ let style = cpsw_express_checkout.style;
5
+
6
+ const stripe = Stripe( pubKey );
7
+
8
+ function generateExpressCheckoutDemo() {
9
+ try {
10
+ const data = {
11
+ country: 'US',
12
+ currency: 'usd',
13
+ total: {
14
+ label: 'Demo total',
15
+ amount: 1099,
16
+ },
17
+ requestPayerName: true,
18
+ requestPayerEmail: true,
19
+ };
20
+
21
+ const paymentRequest = stripe.paymentRequest( data );
22
+
23
+ const elements = stripe.elements();
24
+
25
+ const prButton = elements.create( 'paymentRequestButton', {
26
+ paymentRequest,
27
+ style: {
28
+ paymentRequestButton: {
29
+ type: style.type,
30
+ theme: style.theme,
31
+ height: style.height + 'px',
32
+ },
33
+ },
34
+ } );
35
+
36
+ paymentRequest.canMakePayment().then( function( result ) {
37
+ if ( ! result ) {
38
+ return;
39
+ }
40
+
41
+ prButton.on( 'click', function( e ) {
42
+ e.preventDefault();
43
+ } );
44
+
45
+ if ( $( '.cpsw_express_checkout_preview_wrapper .cpsw_express_checkout_preview' ).length > 0 ) {
46
+ $( '.cpsw_button_preview_label' ).css( { display: 'block' } );
47
+ $( '.cpsw_express_checkout_preview_wrapper .cpsw_express_checkout_preview' ).fadeIn();
48
+ prButton.mount( '.cpsw_express_checkout_preview_wrapper .cpsw_express_checkout_preview' );
49
+ $( '.cpsw_preview_title' ).html( $( '#cpsw_express_checkout_title' ).val() );
50
+ $( '.cpsw_preview_tagline' ).html( $( '#cpsw_express_checkout_tagline' ).val() );
51
+ if ( $( '#cpsw_express_checkout_product_checkout_page' ).is( ':checked' ) ) {
52
+ const buttonWidth = $( '#cpsw_express_checkout_button_width' ).val() + 'px';
53
+ $( '.cpsw_express_checkout_preview' ).width( buttonWidth );
54
+
55
+ $( '.cpsw_express_checkout_preview_wrapper' ).css( { textAlign: $( '#cpsw_express_checkout_button_alignment' ).val() } );
56
+ if ( $( '#cpsw_express_checkout_button_alignment' ).val() === 'center' ) {
57
+ $( '.cpsw_express_checkout_preview' ).css( { margin: '0 auto', float: 'none' } );
58
+ } else {
59
+ $( '.cpsw_express_checkout_preview' ).css( { marginBottom: '1em', float: $( '#cpsw_express_checkout_button_alignment' ).val() } );
60
+ }
61
+ }
62
+ }
63
+ } );
64
+ } catch ( e ) {
65
+ }
66
+ }
67
+
68
+ function addCheckoutPreviewElement() {
69
+ removeCheckoutPreviewElement();
70
+ $( '.cpsw_express_checkout_preview_wrapper' ).prepend( '<h3 class="cpsw_preview_title"></h3><p class="cpsw_preview_tagline"></p>' );
71
+ $( '.cpsw_express_checkout_preview_wrapper' ).after( '<p class="cpsw_preview_notice">NOTE: Title and Tagline appears only on checkout page</p>' );
72
+ $( '.cpsw_preview_notice' ).css( { display: 'block' } );
73
+
74
+ const buttonWidth = $( '#cpsw_express_checkout_button_width' ).val() + 'px';
75
+ $( '.cpsw_express_checkout_preview' ).width( buttonWidth );
76
+
77
+ $( '.cpsw_express_checkout_preview_wrapper' ).css( { textAlign: $( '#cpsw_express_checkout_button_alignment' ).val() } );
78
+ if ( $( '#cpsw_express_checkout_button_alignment' ).val() === 'center' ) {
79
+ $( '.cpsw_express_checkout_preview' ).css( { margin: '0 auto', float: 'none' } );
80
+ } else {
81
+ $( '.cpsw_express_checkout_preview' ).css( { marginBottom: '1em', float: $( '#cpsw_express_checkout_button_alignment' ).val() } );
82
+ }
83
+ }
84
+
85
+ function removeCheckoutPreviewElement() {
86
+ $( '.cpsw_preview_title, .cpsw_preview_tagline, .cpsw_preview_notice' ).remove();
87
+ $( '.cpsw_express_checkout_preview' ).css( { margin: '0 auto', float: 'none', width: '100%' } );
88
+ }
89
+
90
+ function toggleCheckoutOptions() {
91
+ if ( ! $( '#cpsw_express_checkout_product_checkout_page' ).is( ':checked' ) ) {
92
+ $( '.cpsw_checkout_options' ).each( function() {
93
+ $( this ).parents( 'tr' ).hide();
94
+ removeCheckoutPreviewElement();
95
+ } );
96
+ } else {
97
+ $( '.cpsw_checkout_options' ).each( function() {
98
+ $( this ).parents( 'tr' ).show();
99
+ addCheckoutPreviewElement();
100
+ $( '#cpsw_express_checkout_title' ).trigger( 'keyup' );
101
+ $( '#cpsw_express_checkout_tagline' ).trigger( 'keyup' );
102
+ } );
103
+ }
104
+ }
105
+
106
+ $( document ).ready( function() {
107
+ $( '.cpsw_express_checkout_location' ).selectWoo();
108
+ generateExpressCheckoutDemo( style );
109
+
110
+ $( '#cpsw_express_checkout_button_type, #cpsw_express_checkout_button_theme' ).change( function() {
111
+ style = {
112
+ type: $( '#cpsw_express_checkout_button_type' ).val(),
113
+ theme: $( '#cpsw_express_checkout_button_theme' ).val(),
114
+ height: $( '#cpsw_express_checkout_button_height' ).val(),
115
+
116
+ };
117
+ $( '.cpsw_express_checkout_preview_wrapper .cpsw_express_checkout_preview' ).hide();
118
+ generateExpressCheckoutDemo( style );
119
+ } );
120
+
121
+ $( '#cpsw_express_checkout_button_height' ).change( function() {
122
+ if ( $( this ).val() > 60 ) {
123
+ $( this ).val( 60 );
124
+ }
125
+
126
+ if ( $( this ).val() < 35 ) {
127
+ $( this ).val( 35 );
128
+ }
129
+
130
+ style = {
131
+ type: $( '#cpsw_express_checkout_button_type' ).val(),
132
+ theme: $( '#cpsw_express_checkout_button_theme' ).val(),
133
+ height: $( '#cpsw_express_checkout_button_height' ).val(),
134
+
135
+ };
136
+ $( '.cpsw_express_checkout_preview_wrapper .cpsw_express_checkout_preview' ).hide();
137
+ generateExpressCheckoutDemo( style );
138
+ } );
139
+
140
+ toggleCheckoutOptions();
141
+
142
+ $( '#cpsw_express_checkout_product_checkout_page' ).change( function() {
143
+ toggleCheckoutOptions();
144
+ } );
145
+
146
+ if ( $( document ).width() > 1200 ) {
147
+ const buttonPreview = $( '.cpsw_express_checkout_preview_wrapper' ).parents( 'fieldset' );
148
+ buttonPreview.parents( 'tr' ).hide();
149
+ $( '.submit' ).after( '<div class="cpsw_floating_preview"><span class="cpsw_button_preview_label">Button preview</div>' );
150
+ $( '.cpsw_floating_preview' ).append( buttonPreview );
151
+ }
152
+
153
+ $( '#cpsw_express_checkout_title' ).keyup( function() {
154
+ $( '.cpsw_preview_title' ).html( $( this ).val() );
155
+ } );
156
+
157
+ $( '#cpsw_express_checkout_tagline' ).keyup( function() {
158
+ $( '.cpsw_preview_tagline' ).html( $( this ).val() );
159
+ } );
160
+
161
+ $( '#cpsw_express_checkout_button_width' ).change( function() {
162
+ let buttonWidth = $( '#cpsw_express_checkout_button_width' ).val();
163
+ if ( buttonWidth < 150 ) {
164
+ $( '#cpsw_express_checkout_button_width' ).val( 150 );
165
+ buttonWidth = 150;
166
+ alert( 'Express Checkout button width should not be less than 150px' );
167
+ }
168
+ $( '.cpsw_express_checkout_preview' ).width( buttonWidth );
169
+ } );
170
+
171
+ $( '#cpsw_express_checkout_button_alignment' ).change( function() {
172
+ $( '.cpsw_express_checkout_preview_wrapper' ).css( { textAlign: $( this ).val() } );
173
+ if ( $( this ).val() === 'center' ) {
174
+ $( '.cpsw_express_checkout_preview' ).css( { margin: '0 auto', float: 'none' } );
175
+ } else {
176
+ $( '.cpsw_express_checkout_preview' ).css( { marginBottom: '1em', float: $( this ).val() } );
177
+ }
178
+ } );
179
+ } );
180
+ }( jQuery ) );
assets/css/stripe-elements.css CHANGED
@@ -28,3 +28,104 @@
28
  .payment_method_cpsw_stripe .wc-saved-payment-methods {
29
  display: none;
30
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  .payment_method_cpsw_stripe .wc-saved-payment-methods {
29
  display: none;
30
  }
31
+
32
+ .payment-method-disabled {
33
+ opacity: 0.2;
34
+ cursor: not-allowed;
35
+ }
36
+
37
+ #cpsw-payment-request-button {
38
+ max-width: 100%;
39
+ border-radius: 3px;
40
+ box-shadow: 0 1px 2px rgb(0 0 0 / 50%);
41
+ }
42
+
43
+ #cpsw-payment-request-wrapper {
44
+ clear: both;
45
+ display: none;
46
+
47
+ /* padding-top: 1em; */
48
+ }
49
+
50
+ #cpsw-payment-request-wrapper.checkout.center #cpsw-payment-request-title,
51
+ #cpsw-payment-request-wrapper.checkout.center #cpsw-payment-request-tagline,
52
+ #cpsw-payment-request-separator.checkout.center {
53
+ text-align: center;
54
+ }
55
+
56
+ #cpsw-payment-request-wrapper.checkout.center div {
57
+ margin: 0 auto;
58
+ }
59
+
60
+ #cpsw-payment-request-wrapper.checkout.right #cpsw-payment-request-title,
61
+ #cpsw-payment-request-wrapper.checkout.right #cpsw-payment-request-tagline,
62
+ #cpsw-payment-request-separator.checkout.right {
63
+ text-align: right;
64
+ }
65
+
66
+ #cpsw-payment-request-wrapper.checkout.right #cpsw-payment-request-button {
67
+ float: right;
68
+ }
69
+
70
+ #cpsw-payment-request-separator.checkout.left {
71
+ text-align: left;
72
+ }
73
+
74
+ #cpsw-payment-request-wrapper.below {
75
+ padding-top: 0;
76
+ }
77
+
78
+ #cpsw-payment-request-separator {
79
+ clear: both;
80
+ padding: 1em 0;
81
+ text-align: center;
82
+ display: none;
83
+ }
84
+
85
+ #cpsw-payment-request-wrapper.checkout #cpsw-payment-request-title {
86
+ font-weight: 600;
87
+ margin-bottom: 0.5em;
88
+ }
89
+
90
+ #cpsw-payment-request-wrapper.checkout #cpsw-payment-request-button {
91
+ margin-top: 5px;
92
+ }
93
+
94
+ #cpsw-payment-request-wrapper.checkout #cpsw-payment-request-tagline {
95
+ font-style: italic;
96
+ margin-bottom: 1em;
97
+ }
98
+
99
+ #cpsw-payment-request-wrapper.product #cpsw-payment-request-title,
100
+ #cpsw-payment-request-wrapper.cart #cpsw-payment-request-title {
101
+ display: none;
102
+ }
103
+
104
+ #cpsw-payment-request-wrapper.product #cpsw-payment-request-tagline,
105
+ #cpsw-payment-request-wrapper.cart #cpsw-payment-request-tagline {
106
+ display: none;
107
+ }
108
+
109
+ @media only screen and (max-width: 600px) {
110
+
111
+ #cpsw-payment-request-wrapper.product.sticky {
112
+ position: fixed;
113
+ bottom: 0;
114
+ left: 0;
115
+ z-index: 1000000;
116
+ clear: both;
117
+ padding: 5px 0.5em 0.5em 0.5em;
118
+ border: none;
119
+ width: 100%;
120
+ margin: 0;
121
+ background-color: #88888880;
122
+ }
123
+
124
+ #cpsw-payment-request-wrapper.product #cpsw-payment-request-title {
125
+ display: none;
126
+ }
127
+
128
+ #cpsw-payment-request-wrapper.product #cpsw-payment-request-tagline {
129
+ display: none;
130
+ }
131
+ }
assets/js/payment-request.js ADDED
@@ -0,0 +1,566 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ( function( $ ) {
2
+ const pubKey = cpsw_payment_request.public_key;
3
+ const mode = cpsw_payment_request.mode;
4
+ const ajaxUrl = cpsw_payment_request.ajax_url;
5
+ const nonce = cpsw_payment_request.nonce;
6
+ const isProductPage = cpsw_payment_request.is_product_page;
7
+ const currencyCode = cpsw_payment_request.currency_code;
8
+ const countryCode = cpsw_payment_request.country_code;
9
+ const style = cpsw_payment_request.style;
10
+ const jsNonce = cpsw_payment_request.nonce.js_nonce;
11
+ const endpoint = cpsw_payment_request.ajax_endpoint;
12
+ let smallScreen = true;
13
+
14
+ let requestType = '';
15
+
16
+ if ( '' === pubKey || ( 'live' === mode ) ) {
17
+ return;
18
+ }
19
+
20
+ const stripe = Stripe( pubKey );
21
+
22
+ function getAjaxEndpoint( param ) {
23
+ return endpoint.toString().replace( '%%endpoint%%', param );
24
+ }
25
+
26
+ function initializePaymentButton() {
27
+ let data;
28
+ if ( isProductPage ) {
29
+ data = {
30
+ total: cpsw_payment_request.product.total,
31
+ currency: cpsw_payment_request.currency_code,
32
+ country: cpsw_payment_request.country_code,
33
+ requestPayerName: true,
34
+ requestPayerEmail: true,
35
+ requestPayerPhone: true,
36
+ requestShipping: cpsw_payment_request.product.requestShipping,
37
+ displayItems: cpsw_payment_request.product.displayItems,
38
+ };
39
+ generatePaymentButton( data );
40
+ } else {
41
+ const params = {
42
+ cart_nonce: nonce.payment,
43
+ };
44
+
45
+ $.ajax( {
46
+ type: 'POST',
47
+ data: params,
48
+ url: getAjaxEndpoint( 'cpsw_get_cart_details' ),
49
+ success( response ) {
50
+ data = {
51
+ total: response.order_data.total,
52
+ currency: currencyCode,
53
+ country: countryCode,
54
+ requestPayerName: true,
55
+ requestPayerEmail: true,
56
+ requestPayerPhone: true,
57
+ requestShipping: response.shipping_required,
58
+ displayItems: response.order_data.displayItems,
59
+ };
60
+ generatePaymentButton( data );
61
+ },
62
+ } );
63
+ }
64
+ }
65
+
66
+ function generatePaymentButton( data ) {
67
+ try {
68
+ const paymentRequest = stripe.paymentRequest( data );
69
+
70
+ const elements = stripe.elements();
71
+
72
+ let height = style.height;
73
+
74
+ if ( $( document ).width() < 660 ) {
75
+ height = 40;
76
+ smallScreen = true;
77
+ } else {
78
+ smallScreen = false;
79
+ }
80
+
81
+ const prButton = elements.create( 'paymentRequestButton', {
82
+ paymentRequest,
83
+ style: {
84
+ paymentRequestButton: {
85
+ type: style.type,
86
+ theme: style.theme,
87
+ height: height + 'px',
88
+ },
89
+ },
90
+ } );
91
+
92
+ paymentRequest.canMakePayment().then( function( result ) {
93
+ if ( ! result ) {
94
+ return;
95
+ }
96
+
97
+ if ( result.applePay ) {
98
+ requestType = 'apple_pay';
99
+ } else if ( result.googlePay ) {
100
+ requestType = 'google_pay';
101
+ } else {
102
+ requestType = 'payment_request_api';
103
+ }
104
+
105
+ attachPaymentRequestButtonEventListeners( prButton, paymentRequest );
106
+
107
+ $( '#cpsw-payment-request-wrapper' ).show();
108
+ if ( $( '#cpsw-payment-request-wrapper #cpsw-payment-request-button' ).length > 0 ) {
109
+ prButton.mount( '#cpsw-payment-request-wrapper #cpsw-payment-request-button' );
110
+ }
111
+
112
+ if ( $( '#cpsw-payment-request-separator' ).hasClass( 'cart' ) ) {
113
+ $( '#cpsw-payment-request-separator' ).show();
114
+ }
115
+
116
+ if ( $( '#cpsw-payment-request-separator' ).hasClass( 'checkout' ) ) {
117
+ $( '#cpsw-payment-request-separator' ).show();
118
+ }
119
+
120
+ if ( $( '#cpsw-payment-request-separator' ).hasClass( 'product' ) ) {
121
+ if ( smallScreen && $( '#cpsw-payment-request-wrapper' ).hasClass( 'sticky' ) ) {
122
+ $( '#cpsw-payment-request-separator' ).hide();
123
+ } else {
124
+ $( '#cpsw-payment-request-separator' ).show();
125
+ }
126
+ }
127
+ } );
128
+
129
+ paymentRequest.on( 'shippingaddresschange', function( e ) {
130
+ $.when( updateShippingAddress( e.shippingAddress ) ).then( function( response ) {
131
+ if ( 'success' === response.result ) {
132
+ e.updateWith( { status: response.result, shippingOptions: response.shipping_options, total: response.total, displayItems: response.displayItems } );
133
+ }
134
+ if ( 'fail' === response.result ) {
135
+ e.updateWith( { status: 'fail' } );
136
+ }
137
+ } );
138
+ } );
139
+
140
+ paymentRequest.on( 'shippingoptionchange', function( e ) {
141
+ $.when( updateShippingOption( e.shippingOption ) ).then( function( response ) {
142
+ if ( 'success' === response.result ) {
143
+ e.updateWith( { status: 'success', total: response.total, displayItems: response.displayItems } );
144
+ }
145
+
146
+ if ( 'fail' === response.result ) {
147
+ e.updateWith( { status: 'fail' } );
148
+ }
149
+ } );
150
+ } );
151
+
152
+ paymentRequest.on( 'paymentmethod', function( e ) {
153
+ $.when( createPaymentMethod( e, requestType ) ).then( function( response ) {
154
+ if ( 'success' === response.result ) {
155
+ confirmPaymentIntent( e, response.redirect );
156
+ } else {
157
+ abortPayment( e, response.messages );
158
+ }
159
+ } );
160
+ } );
161
+ } catch ( e ) {
162
+ logError( e );
163
+ }
164
+ }
165
+
166
+ function updateShippingAddress( address ) {
167
+ const data = {
168
+ shipping_address_nonce: nonce.shipping,
169
+ country: address.country,
170
+ state: address.region,
171
+ postcode: address.postalCode,
172
+ city: address.city,
173
+ address: typeof address.addressLine[ 0 ] === 'undefined' ? '' : address.addressLine[ 0 ],
174
+ address_2: typeof address.addressLine[ 1 ] === 'undefined' ? '' : address.addressLine[ 1 ],
175
+ payment_request_type: requestType,
176
+ is_product_page: isProductPage,
177
+ };
178
+
179
+ return $.ajax( {
180
+ type: 'POST',
181
+ data,
182
+ url: getAjaxEndpoint( 'cpsw_update_shipping_address' ),
183
+ } );
184
+ }
185
+
186
+ function updateShippingOption( shippingOption ) {
187
+ const data = {
188
+ shipping_option_nonce: nonce.shipping_option,
189
+ shipping_method: [ shippingOption.id ],
190
+ payment_request_type: requestType,
191
+ is_product_page: isProductPage,
192
+ };
193
+
194
+ return $.ajax( {
195
+ type: 'POST',
196
+ data,
197
+ url: getAjaxEndpoint( 'cpsw_update_shipping_option' ),
198
+ } );
199
+ }
200
+
201
+ function attachPaymentRequestButtonEventListeners( prButton, paymentRequest ) {
202
+ prButton.on( 'click', function() {
203
+ $( 'body' ).addClass( 'cpsw-prButton-clicked' );
204
+ } );
205
+
206
+ if ( isProductPage ) {
207
+ listenProductPageEvents( prButton, paymentRequest );
208
+ }
209
+ }
210
+
211
+ function addProductToCart() {
212
+ let productId = $( '.single_add_to_cart_button' ).val();
213
+
214
+ // Check if product is a variable product.
215
+ if ( $( '.single_variation_wrap' ).length ) {
216
+ productId = $( '.single_variation_wrap' ).find( 'input[name="product_id"]' ).val();
217
+ }
218
+
219
+ const data = {
220
+ add_to_cart_nonce: nonce.add_to_cart,
221
+ action: 'add_to_cart',
222
+ product_id: productId,
223
+ qty: $( '.quantity .qty' ).val(),
224
+ attributes: $( '.variations_form' ).length ? getAttributes().data : [],
225
+ };
226
+
227
+ // add addons data to the POST body
228
+ const formData = $( 'form.cart' ).serializeArray();
229
+ $.each( formData, function( i, field ) {
230
+ if ( /^addon-/.test( field.name ) ) {
231
+ if ( /\[\]$/.test( field.name ) ) {
232
+ const fieldName = field.name.substring( 0, field.name.length - 2 );
233
+ if ( data[ fieldName ] ) {
234
+ data[ fieldName ].push( field.value );
235
+ } else {
236
+ data[ fieldName ] = [ field.value ];
237
+ }
238
+ } else {
239
+ data[ field.name ] = field.value;
240
+ }
241
+ }
242
+ } );
243
+
244
+ return $.ajax( {
245
+ type: 'POST',
246
+ data,
247
+ url: getAjaxEndpoint( 'cpsw_add_to_cart' ),
248
+ } );
249
+ }
250
+
251
+ function listenProductPageEvents( prButton, paymentRequest ) {
252
+ const addToCart = $( '.single_add_to_cart_button' );
253
+
254
+ prButton.on( 'click', function( event ) {
255
+ // First check if product can be added to cart.
256
+ if ( addToCart.is( '.disabled' ) ) {
257
+ event.preventDefault();
258
+ addToCart.trigger( 'click' );
259
+ return;
260
+ }
261
+
262
+ addProductToCart();
263
+ } );
264
+
265
+ $( document.body ).on( 'woocommerce_variation_has_changed', function() {
266
+ if ( addToCart.is( '.disabled' ) ) {
267
+ $( '#cpsw-payment-request-button' ).addClass( 'payment-method-disabled' );
268
+ return;
269
+ }
270
+ $( '#cpsw-payment-request-button' ).removeClass( 'payment-method-disabled' );
271
+ blockPaymentRequestButton();
272
+
273
+ $.when( getSelectedProductData() ).then( function( response ) {
274
+ if ( response.error ) {
275
+ displayErrorMessage( response.error );
276
+ } else {
277
+ $.when(
278
+ paymentRequest.update( {
279
+ total: response.total,
280
+ displayItems: response.displayItems,
281
+ } ),
282
+ ).then( function() {
283
+ unblockPaymentRequestButton();
284
+ } );
285
+ }
286
+ } );
287
+ } );
288
+
289
+ $( 'form.cart .quantity' ).on( 'input', '.qty', function() {
290
+ if ( addToCart.is( '.disabled' ) ) {
291
+ return;
292
+ }
293
+ blockPaymentRequestButton();
294
+ } );
295
+
296
+ $( 'form.cart .quantity' ).on( 'input', '.qty', debounce( 250, function() {
297
+ if ( addToCart.is( '.disabled' ) ) {
298
+ return;
299
+ }
300
+ blockPaymentRequestButton();
301
+
302
+ $.when( getSelectedProductData() ).then( function( response ) {
303
+ if ( response.error ) {
304
+ displayErrorMessage( response.error );
305
+ } else {
306
+ $.when(
307
+ paymentRequest.update( {
308
+ total: response.total,
309
+ displayItems: response.displayItems,
310
+ } ),
311
+ ).then( function() {
312
+ unblockPaymentRequestButton();
313
+ } );
314
+ }
315
+ } );
316
+ } ) );
317
+ }
318
+
319
+ function debounce( wait, func, immediate ) {
320
+ let timeout;
321
+
322
+ return function() {
323
+ const context = this;
324
+ const args = arguments;
325
+
326
+ const later = function() {
327
+ timeout = null;
328
+ if ( ! immediate ) {
329
+ func.apply( context, args );
330
+ }
331
+ };
332
+
333
+ const callNow = immediate && ! timeout;
334
+
335
+ clearTimeout( timeout );
336
+
337
+ timeout = setTimeout( later, wait );
338
+
339
+ if ( callNow ) {
340
+ func.apply( context, args );
341
+ }
342
+ };
343
+ }
344
+
345
+ function getSelectedProductData() {
346
+ let productId = $( '.single_add_to_cart_button' ).val();
347
+
348
+ // Check if product is a variable product.
349
+ if ( $( '.single_variation_wrap' ).length ) {
350
+ productId = $( '.single_variation_wrap' ).find( 'input[name="product_id"]' ).val();
351
+ }
352
+
353
+ const addons = $( '#product-addons-total' ).data( 'price_data' ) || [];
354
+ const addonValue = addons.reduce( function( sum, addon ) {
355
+ return sum + addon.cost;
356
+ }, 0 );
357
+
358
+ const data = {
359
+ selected_product_nonce: nonce.selected_product_data,
360
+ product_id: productId,
361
+ qty: $( '.quantity .qty' ).val(),
362
+ attributes: $( '.variations_form' ).length ? getAttributes().data : [],
363
+ addon_value: addonValue,
364
+ };
365
+
366
+ return $.ajax( {
367
+ type: 'POST',
368
+ data,
369
+ url: getAjaxEndpoint( 'cpsw_selected_product_data' ),
370
+ } );
371
+ }
372
+
373
+ function blockPaymentRequestButton( ) {
374
+ if ( $( '#cpsw-payment-request-button' ).data( 'blockUI.isBlocked' ) ) {
375
+ return;
376
+ }
377
+
378
+ $( '#cpsw-payment-request-button' ).addClass( 'cpsw_request_button_blocked' ).block( { message: null } );
379
+ }
380
+
381
+ function unblockPaymentRequestButton() {
382
+ $( '#cpsw-payment-request-button' ).removeClass( 'cpsw_request_button_blocked' ).unblock();
383
+ }
384
+
385
+ function getAttributes() {
386
+ const select = $( '.variations_form' ).find( '.variations select' ),
387
+ data = {};
388
+ let count = 0,
389
+ chosen = 0;
390
+
391
+ select.each( function() {
392
+ const attributeName = $( this ).data( 'attribute_name' ) || $( this ).attr( 'name' );
393
+ const value = $( this ).val() || '';
394
+
395
+ if ( value.length > 0 ) {
396
+ chosen++;
397
+ }
398
+
399
+ count++;
400
+ data[ attributeName ] = value;
401
+ } );
402
+
403
+ return {
404
+ count,
405
+ chosenCount: chosen,
406
+ data,
407
+ };
408
+ }
409
+
410
+ function preparePaymentMethod( event ) {
411
+ const paymentMethod = event.paymentMethod;
412
+ const billingDetails = paymentMethod.billing_details;
413
+ const email = billingDetails.email;
414
+ const phone = billingDetails.phone;
415
+ const billing = billingDetails.address;
416
+ const name = billingDetails.name;
417
+ const shipping = event.shippingAddress;
418
+ const data = {
419
+ checkout_nonce: nonce.checkout,
420
+ billing_first_name: null !== name ? name.split( ' ' ).slice( 0, 1 ).join( ' ' ) : '',
421
+ billing_last_name: null !== name ? name.split( ' ' ).slice( 1 ).join( ' ' ) : '',
422
+ billing_company: '',
423
+ billing_email: null !== email ? email : event.payerEmail,
424
+ billing_phone: null !== phone ? phone : event.payerPhone && event.payerPhone.replace( '/[() -]/g', '' ),
425
+ billing_country: null !== billing ? billing.country : '',
426
+ billing_address_1: null !== billing ? billing.line1 : '',
427
+ billing_address_2: null !== billing ? billing.line2 : '',
428
+ billing_city: null !== billing ? billing.city : '',
429
+ billing_state: null !== billing ? billing.state : '',
430
+ billing_postcode: null !== billing ? billing.postal_code : '',
431
+ shipping_first_name: '',
432
+ shipping_last_name: '',
433
+ shipping_company: '',
434
+ shipping_country: '',
435
+ shipping_address_1: '',
436
+ shipping_address_2: '',
437
+ shipping_city: '',
438
+ shipping_state: '',
439
+ shipping_postcode: '',
440
+ shipping_method: [ null === event.shippingOption ? null : event.shippingOption.id ],
441
+ order_comments: '',
442
+ payment_method: 'cpsw_stripe',
443
+ ship_to_different_address: 1,
444
+ terms: 1,
445
+ payment_method_created: paymentMethod.id,
446
+ payment_request_type: requestType,
447
+ };
448
+
449
+ if ( shipping ) {
450
+ data.shipping_first_name = shipping.recipient.split( ' ' ).slice( 0, 1 ).join( ' ' );
451
+ data.shipping_last_name = shipping.recipient.split( ' ' ).slice( 1 ).join( ' ' );
452
+ data.shipping_company = shipping.organization;
453
+ data.shipping_country = shipping.country;
454
+ data.shipping_address_1 = typeof shipping.addressLine[ 0 ] === 'undefined' ? '' : shipping.addressLine[ 0 ];
455
+ data.shipping_address_2 = typeof shipping.addressLine[ 1 ] === 'undefined' ? '' : shipping.addressLine[ 1 ];
456
+ data.shipping_city = shipping.city;
457
+ data.shipping_state = shipping.region;
458
+ data.shipping_postcode = shipping.postalCode;
459
+ }
460
+
461
+ return data;
462
+ }
463
+
464
+ function createPaymentMethod( event ) {
465
+ const data = preparePaymentMethod( event );
466
+ return $.ajax( {
467
+ type: 'POST',
468
+ data,
469
+ dataType: 'json',
470
+ url: getAjaxEndpoint( 'cpsw_payment_request_checkout' ),
471
+ } );
472
+ }
473
+
474
+ function confirmPaymentIntent( event, hash ) {
475
+ const partials = hash.match(
476
+ /^#?confirm-(pi|si)-([^:]+):(.+)$/,
477
+ );
478
+
479
+ if ( ! partials || 4 > partials.length ) {
480
+ return;
481
+ }
482
+
483
+ const intentClientSecret = partials[ 2 ];
484
+ const redirectURL = decodeURIComponent( partials[ 3 ] );
485
+
486
+ confirmPayment( event, intentClientSecret, redirectURL );
487
+ }
488
+
489
+ function confirmPayment( event, clientSecret, redirectURL ) {
490
+ stripe.confirmCardPayment( clientSecret, {} ).then( function( result ) {
491
+ if ( result.error ) {
492
+ // Show error to your customer
493
+ $( '.woocommerce-error' ).remove();
494
+ $( 'form.woocommerce-checkout' ).unblock();
495
+ logError( result.error );
496
+ displayErrorMessage( result.error );
497
+ } else {
498
+ // The payment has been processed!
499
+ if ( result.paymentIntent.status === 'succeeded' || result.paymentIntent.status === 'requires_capture' ) {
500
+ $.blockUI( {
501
+ message: null,
502
+ overlayCSS: {
503
+ background: '#fff',
504
+ opacity: 0.6,
505
+ },
506
+ } );
507
+ event.complete( 'success' );
508
+ window.location = redirectURL;
509
+ }
510
+ }
511
+ } );
512
+ }
513
+
514
+ function logError( error ) {
515
+ $.ajax( {
516
+ type: 'POST',
517
+ dataType: 'json',
518
+ url: ajaxUrl,
519
+ data: { action: 'cpsw_js_errors', _security: jsNonce, error },
520
+ beforeSend: () => {
521
+ $( 'body' ).css( 'cursor', 'progress' );
522
+ },
523
+ success( response ) {
524
+ if ( response.success === true ) {
525
+ } else if ( response.success === false ) {
526
+ return response.message;
527
+ }
528
+ $( 'body' ).css( 'cursor', 'default' );
529
+ },
530
+ error() {
531
+ $( 'body' ).css( 'cursor', 'default' );
532
+ alert( 'Something went wrong!' );
533
+ },
534
+ } );
535
+ }
536
+
537
+ function displayErrorMessage( message ) {
538
+ $( '.woocommerce-error' ).remove();
539
+
540
+ if ( isProductPage ) {
541
+ const element = $( '.product' ).first();
542
+ element.before( message );
543
+ window.scrollTo( { top: 100, behavior: 'smooth' } );
544
+ } else {
545
+ const $form = $( '.shop_table.cart, #cpsw-payment-request-wrapper.checkout' ).closest( 'form' );
546
+ $form.before( message );
547
+ window.scrollTo( { top: 100, behavior: 'smooth' } );
548
+ }
549
+ }
550
+
551
+ function abortPayment( event, message ) {
552
+ event.complete( 'fail' );
553
+ displayErrorMessage( message );
554
+ }
555
+
556
+ initializePaymentButton();
557
+
558
+ $( document.body ).on( 'updated_cart_totals', function() {
559
+ initializePaymentButton();
560
+ } );
561
+
562
+ // We need to refresh payment request data when total is updated.
563
+ $( document.body ).on( 'updated_checkout', function() {
564
+ initializePaymentButton();
565
+ } );
566
+ }( jQuery ) );
autoloader.php CHANGED
@@ -8,8 +8,9 @@
8
 
9
  namespace CPSW;
10
 
11
- use CPSW\Inc\Helper;
12
  use CPSW\Gateway\Stripe\Card_Payments;
 
 
13
  use CPSW\Admin\Admin_Controller;
14
  use CPSW\Gateway\Stripe\Webhook;
15
 
@@ -89,9 +90,9 @@ class CPSW_Loader {
89
  * @return void
90
  */
91
  public function setup_classes() {
92
- Helper::get_instance();
93
  Admin_Controller::get_instance();
94
  Webhook::get_instance();
 
95
  }
96
 
97
  /**
@@ -118,6 +119,7 @@ class CPSW_Loader {
118
 
119
  if ( class_exists( 'woocommerce' ) ) {
120
  Card_Payments::get_instance();
 
121
  } else {
122
  add_action( 'admin_notices', [ $this, 'wc_is_not_active' ] );
123
  }
8
 
9
  namespace CPSW;
10
 
 
11
  use CPSW\Gateway\Stripe\Card_Payments;
12
+ use CPSW\Gateway\Stripe\Payment_Request_Api;
13
+ use CPSW\Compatibility\Apple_Pay;
14
  use CPSW\Admin\Admin_Controller;
15
  use CPSW\Gateway\Stripe\Webhook;
16
 
90
  * @return void
91
  */
92
  public function setup_classes() {
 
93
  Admin_Controller::get_instance();
94
  Webhook::get_instance();
95
+ Apple_Pay::get_instance();
96
  }
97
 
98
  /**
119
 
120
  if ( class_exists( 'woocommerce' ) ) {
121
  Card_Payments::get_instance();
122
+ Payment_Request_Api::get_instance();
123
  } else {
124
  add_action( 'admin_notices', [ $this, 'wc_is_not_active' ] );
125
  }
checkout-plugins-stripe-woo.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Checkout Plugins - Stripe for WooCommerce
4
  * Plugin URI: https://www.checkoutplugins.com/
5
  * Description: Stripe for WooCommerce delivers a simple, secure way to accept credit card payments in your WooCommerce store. Reduce payment friction and boost conversions using this free plugin!
6
- * Version: 1.0.0
7
  * Author: Brainstorm Force
8
  * Author URI: https://brainstormforce.com/
9
  * License: GPLv2 or later
@@ -20,6 +20,6 @@ define( 'CPSW_FILE', __FILE__ );
20
  define( 'CPSW_BASE', plugin_basename( CPSW_FILE ) );
21
  define( 'CPSW_DIR', plugin_dir_path( CPSW_FILE ) );
22
  define( 'CPSW_URL', plugins_url( '/', CPSW_FILE ) );
23
- define( 'CPSW_VERSION', '1.0.0' );
24
 
25
  require_once 'autoloader.php';
3
  * Plugin Name: Checkout Plugins - Stripe for WooCommerce
4
  * Plugin URI: https://www.checkoutplugins.com/
5
  * Description: Stripe for WooCommerce delivers a simple, secure way to accept credit card payments in your WooCommerce store. Reduce payment friction and boost conversions using this free plugin!
6
+ * Version: 1.1.0
7
  * Author: Brainstorm Force
8
  * Author URI: https://brainstormforce.com/
9
  * License: GPLv2 or later
20
  define( 'CPSW_BASE', plugin_basename( CPSW_FILE ) );
21
  define( 'CPSW_DIR', plugin_dir_path( CPSW_FILE ) );
22
  define( 'CPSW_URL', plugins_url( '/', CPSW_FILE ) );
23
+ define( 'CPSW_VERSION', '1.1.0' );
24
 
25
  require_once 'autoloader.php';
compatibility/apple-developer-merchantid-domain-association ADDED
@@ -0,0 +1 @@
 
1
+ 7B227073704964223A2239373943394538343346343131343044463144313834343232393232313734313034353044314339464446394437384337313531303944334643463542433731222C2276657273696F6E223A312C22637265617465644F6E223A313536363233343735303036312C227369676E6174757265223A22333038303036303932613836343838366637306430313037303261303830333038303032303130313331306633303064303630393630383634383031363530333034303230313035303033303830303630393261383634383836663730643031303730313030303061303830333038323033653333303832303338386130303330323031303230323038346333303431343935313964353433363330306130363038326138363438636533643034303330323330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333031653137306433313339333033353331333833303331333333323335333735613137306433323334333033353331333633303331333333323335333735613330356633313235333032333036303335353034303330633163363536333633326437333664373032643632373236663662363537323264373336393637366535663535343333343264353035323466343433313134333031323036303335353034306230633062363934663533323035333739373337343635366437333331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333035393330313330363037326138363438636533643032303130363038326138363438636533643033303130373033343230303034633231353737656465626436633762323231386636386464373039306131323138646337623062643666326332383364383436303935643934616634613534313162383334323065643831316633343037653833333331663163353463336637656233323230643662616435643465666634393238393839336537633066313361333832303231313330383230323064333030633036303335353164313330313031666630343032333030303330316630363033353531643233303431383330313638303134323366323439633434663933653465663237653663346636323836633366613262626664326534623330343530363038326230363031303530353037303130313034333933303337333033353036303832623036303130353035303733303031383632393638373437343730336132663266366636333733373032653631373037303663363532653633366636643266366636333733373033303334326436313730373036633635363136393633363133333330333233303832303131643036303335353164323030343832303131343330383230313130333038323031306330363039326138363438383666373633363430353031333038316665333038316333303630383262303630313035303530373032303233303831623630633831623335323635366336393631366536333635323036663665323037343638363937333230363336353732373436393636363936333631373436353230363237393230363136653739323037303631373237343739323036313733373337353664363537333230363136333633363537303734363136653633363532303666363632303734363836353230373436383635366532303631373037303663363936333631363236633635323037333734363136653634363137323634323037343635373236643733323036313665363432303633366636653634363937343639366636653733323036663636323037353733363532633230363336353732373436393636363936333631373436353230373036663663363936333739323036313665363432303633363537323734363936363639363336313734363936663665323037303732363136333734363936333635323037333734363137343635366436353665373437333265333033363036303832623036303130353035303730323031313632613638373437343730336132663266373737373737326536313730373036633635326536333666366432663633363537323734363936363639363336313734363536313735373436383666373236393734373932663330333430363033353531643166303432643330326233303239613032376130323538363233363837343734373033613266326636333732366332653631373037303663363532653633366636643266363137303730366336353631363936333631333332653633373236633330316430363033353531643065303431363034313439343537646236666435373438313836383938393736326637653537383530376537396235383234333030653036303335353164306630313031666630343034303330323037383033303066303630393261383634383836663736333634303631643034303230353030333030613036303832613836343863653364303430333032303334393030333034363032323130306265303935373166653731653165373335623535653561666163623463373266656234343566333031383532323263373235313030326236316562643666353530323231303064313862333530613564643664643665623137343630333562313165623263653837636661336536616636636264383338303839306463383263646461613633333038323032656533303832303237356130303330323031303230323038343936643266626633613938646139373330306130363038326138363438636533643034303330323330363733313162333031393036303335353034303330633132343137303730366336353230353236663666373432303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330316531373064333133343330333533303336333233333334333633333330356131373064333233393330333533303336333233333334333633333330356133303761333132653330326330363033353530343033306332353431373037303663363532303431373037303663363936333631373436393666366532303439366537343635363737323631373436393666366532303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030346630313731313834313964373634383564353161356532353831303737366538383061326566646537626165346465303864666334623933653133333536643536363562333561653232643039373736306432323465376262613038666437363137636538386362373662623636373062656338653832393834666635343435613338316637333038316634333034363036303832623036303130353035303730313031303433613330333833303336303630383262303630313035303530373330303138363261363837343734373033613266326636663633373337303265363137303730366336353265363336663664326636663633373337303330333432643631373037303663363537323666366637343633363136373333333031643036303335353164306530343136303431343233663234396334346639336534656632376536633466363238366333666132626266643265346233303066303630333535316431333031303166663034303533303033303130316666333031663036303335353164323330343138333031363830313462626230646561313538333338383961613438613939646562656264656261666461636232346162333033373036303335353164316630343330333032653330326361303261613032383836323636383734373437303361326632663633373236633265363137303730366336353265363336663664326636313730373036633635373236663666373436333631363733333265363337323663333030653036303335353164306630313031666630343034303330323031303633303130303630613261383634383836663736333634303630323065303430323035303033303061303630383261383634386365336430343033303230333637303033303634303233303361636637323833353131363939623138366662333563333536636136326266663431376564643930663735346461323865626566313963383135653432623738396638393866373962353939663938643534313064386639646539633266653032333033323264643534343231623061333035373736633564663333383362393036376664313737633263323136643936346663363732363938323132366635346638376137643162393963623962303938393231363130363939306630393932316430303030333138323031386233303832303138373032303130313330383138363330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533303230383463333034313439353139643534333633303064303630393630383634383031363530333034303230313035303061303831393533303138303630393261383634383836663730643031303930333331306230363039326138363438383666373064303130373031333031633036303932613836343838366637306430313039303533313066313730643331333933303338333133393331333733313332333333303561333032613036303932613836343838366637306430313039333433313164333031623330306430363039363038363438303136353033303430323031303530306131306130363038326138363438636533643034303330323330326630363039326138363438383666373064303130393034333132323034323062303731303365313430613462386231376262613230316130336163643036396234653431366232613263383066383661383338313435633239373566633131333030613036303832613836343863653364303430333032303434363330343430323230343639306264636637626461663833636466343934396534633035313039656463663334373665303564373261313264376335666538633033303033343464663032323032363764353863393365626233353031333836363062353730373938613064643731313734316262353864626436613138363633353038353431656565393035303030303030303030303030227D
compatibility/apple-pay.php ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Apple Pay domain association
4
+ *
5
+ * @package checkout-plugins-stripe-woo
6
+ * @since 1.1.0
7
+ */
8
+
9
+ namespace CPSW\Compatibility;
10
+
11
+ use CPSW\Inc\Traits\Get_Instance;
12
+ use CPSW\Gateway\Stripe\Stripe_Api;
13
+ use CPSW\Inc\Helper;
14
+
15
+ /**
16
+ * Apple Pay Domain Verification class
17
+ */
18
+ class Apple_Pay {
19
+
20
+ use Get_Instance;
21
+
22
+ const APPLE_PAY_FILE = 'apple-developer-merchantid-domain-association';
23
+ const APPLE_PAY_DIR = '.well-known';
24
+
25
+ /**
26
+ * Domain verification flag
27
+ *
28
+ * @var bool
29
+ */
30
+ public $domain_is_verfied;
31
+
32
+ /**
33
+ * Verified domain stored in database
34
+ *
35
+ * @var string
36
+ */
37
+ public $verified_domain;
38
+
39
+ /**
40
+ * Current domain
41
+ *
42
+ * @var string
43
+ */
44
+ public $domain;
45
+
46
+ /**
47
+ * Stores apple pay domain verification failure message.
48
+ *
49
+ * @var string
50
+ */
51
+ private $failure_message;
52
+
53
+ /**
54
+ * Constructor
55
+ */
56
+ public function __construct() {
57
+ add_action( 'init', [ $this, 'apple_pay_domain_association_rewrite_rule' ] );
58
+ add_action( 'admin_init', [ $this, 'is_domain_verified' ] );
59
+ add_filter( 'query_vars', [ $this, 'add_domain_association_query_var' ], 10, 1 );
60
+ add_action( 'parse_request', [ $this, 'parse_domain_association_request' ], 10, 1 );
61
+
62
+ $this->domain_is_verfied = get_option( 'cpsw_apple_pay_domain_is_verfied' );
63
+ $this->verified_domain = get_option( 'cpsw_apple_pay_verified_domain' );
64
+ $this->secret_key = Helper::get_setting( 'cpsw_secret_key' );
65
+ $this->failure_message = '';
66
+ $this->domain = isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : str_replace( array( 'https://', 'http://' ), '', get_site_url() );
67
+ }
68
+
69
+ /**
70
+ * Rewrite rules for apple pay domain association.
71
+ *
72
+ * @return void
73
+ */
74
+ public function apple_pay_domain_association_rewrite_rule() {
75
+ $regex = '^\\' . self::APPLE_PAY_DIR . '\/' . self::APPLE_PAY_FILE . '$';
76
+ $redirect = 'index.php?' . self::APPLE_PAY_FILE . '=1';
77
+
78
+ add_rewrite_rule( $regex, $redirect, 'top' );
79
+ }
80
+
81
+ /**
82
+ * Add domain association query var
83
+ *
84
+ * @param array $query_vars existing query vars.
85
+ * @return array
86
+ */
87
+ public function add_domain_association_query_var( $query_vars ) {
88
+ $query_vars[] = self::APPLE_PAY_FILE;
89
+ return $query_vars;
90
+ }
91
+
92
+ /**
93
+ * Parse current domain should serve apple pay domain association file or not.
94
+ *
95
+ * @param object $wp query parameters.
96
+ * @return void
97
+ */
98
+ public function parse_domain_association_request( $wp ) {
99
+ if (
100
+ self::APPLE_PAY_DIR . '/' . self::APPLE_PAY_FILE !== $wp->request ||
101
+ self::APPLE_PAY_FILE !== $wp->query_vars['attachment']
102
+ ) {
103
+ return;
104
+ }
105
+
106
+ $path = CPSW_DIR . 'compatibility/' . self::APPLE_PAY_FILE;
107
+ header( 'Content-Type: text/plain;charset=utf-8' );
108
+ echo esc_html( @file_get_contents( $path ) ); // @codingStandardsIgnoreLine
109
+ exit;
110
+ }
111
+
112
+ /**
113
+ * Checks if current domain is verified or not else verifys current domain.
114
+ *
115
+ * @return boolean
116
+ */
117
+ public function is_domain_verified() {
118
+ if ( ! empty( $this->verified_domain ) && $this->domain === $this->verified_domain && $this->domain_is_verfied ) {
119
+ return;
120
+ }
121
+
122
+ $settings = Helper::get_gateway_settings();
123
+ $this->express_checkout = $settings['express_checkout_enabled'];
124
+ if ( 'yes' !== $this->express_checkout || 'yes' !== $settings['enabled'] ) {
125
+ return;
126
+ }
127
+
128
+ if ( isset( $_GET['page'] ) && isset( $_GET['tab'] ) && isset( $_GET['section'] ) && 'wc-settings' === $_GET['page'] && 'checkout' === $_GET['tab'] && 'cpsw_express_checkout' === $_GET['section'] ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
129
+ if ( ! is_ssl() ) {
130
+ add_action( 'admin_notices', [ $this, 'no_ssl_notice' ] );
131
+ return;
132
+ }
133
+
134
+ flush_rewrite_rules();
135
+
136
+ $response = $this->move_file_to_apple_dir();
137
+ if ( ! $response['success'] ) {
138
+ $this->failure_message = $response['message'];
139
+ add_action( 'admin_notices', [ $this, 'apple_pay_verification_failed' ] );
140
+ return;
141
+ }
142
+
143
+ $this->verify_domain_for_apple_pay();
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Moves domain association file to required directory
149
+ *
150
+ * @return array
151
+ */
152
+ public function move_file_to_apple_dir() {
153
+ if ( $this->check_hosted_file() ) {
154
+ return [
155
+ 'success' => true,
156
+ ];
157
+ }
158
+
159
+ $well_known_dir = untrailingslashit( ABSPATH ) . '/' . self::APPLE_PAY_DIR;
160
+ $fullpath = $well_known_dir . '/' . self::APPLE_PAY_FILE;
161
+
162
+ if ( ! file_exists( $well_known_dir ) ) {
163
+ if ( ! @mkdir( $well_known_dir, 0755 ) ) { // @codingStandardsIgnoreLine
164
+ return [
165
+ 'success' => false,
166
+ /* translators: 1 - 4 html entities */
167
+ 'message' => sprintf( __( 'Unable to create domain association folder to domain root due to file permissions. Please create %1$1s.well-known%2$2s directory under domain root and place %3$3sdomain verification file%4$4s under it and refresh.', 'checkout-plugins-stripe-woo' ), '<code>', '</code>', '<a href="https://stripe.com/files/apple-pay/apple-developer-merchantid-domain-association" target="_blank">', '</a>' ),
168
+ ];
169
+ }
170
+ }
171
+
172
+ if ( ! @copy( CPSW_DIR . 'compatibility/' . self::APPLE_PAY_FILE, $fullpath ) ) { // @codingStandardsIgnoreLine
173
+ return [
174
+ 'success' => false,
175
+ 'message' => __( 'Unable to copy domain association file to domain root.', 'checkout-plugins-stripe-woo' ),
176
+ ];
177
+ }
178
+
179
+ return [
180
+ 'success' => true,
181
+ ];
182
+ }
183
+
184
+ /**
185
+ * Checks if hosted domain verification file is correct or not, updates if required.
186
+ *
187
+ * @return bool
188
+ */
189
+ public function check_hosted_file() {
190
+ $new_contents = @file_get_contents( CPSW_DIR . 'compatibility/' . self::APPLE_PAY_FILE ); // @codingStandardsIgnoreLine
191
+ $fullpath = untrailingslashit( ABSPATH ) . '/' . self::APPLE_PAY_DIR . '/' . self::APPLE_PAY_FILE;
192
+ $local_contents = @file_get_contents( $fullpath ); // @codingStandardsIgnoreLine
193
+ $url = get_site_url() . '/' . self::APPLE_PAY_DIR . '/' . self::APPLE_PAY_FILE;
194
+ $response = @wp_remote_get( $url ); // @codingStandardsIgnoreLine
195
+ $remote_contents = @wp_remote_retrieve_body( $response ); // @codingStandardsIgnoreLine
196
+
197
+ return $local_contents === $new_contents || $remote_contents === $new_contents;
198
+ }
199
+
200
+ /**
201
+ * Automatic verification for apple pay using stripe api
202
+ *
203
+ * @return void
204
+ */
205
+ public function verify_domain_for_apple_pay() {
206
+
207
+ if ( empty( $this->secret_key ) ) {
208
+ add_action( 'admin_notices', [ $this, 'no_live_secret_key' ] );
209
+ return;
210
+ }
211
+
212
+ add_filter( 'cpsw_get_secret_key', [ $this, 'get_live_secret_key' ], 10, 1 );
213
+ $stripe = new Stripe_Api();
214
+
215
+ $response = $stripe->apple_pay_domains(
216
+ 'create',
217
+ [
218
+ [
219
+ 'domain_name' => $this->domain,
220
+ ],
221
+ ]
222
+ );
223
+
224
+ $verification_response = $response['success'] ? $response['data'] : false;
225
+
226
+ if ( $verification_response ) {
227
+ update_option( 'cpsw_apple_pay_verified_domain', $this->domain );
228
+ update_option( 'cpsw_apple_pay_domain_is_verfied', true );
229
+ add_action( 'admin_notices', [ $this, 'apple_pay_verification_success' ] );
230
+ } else {
231
+ $this->failure_message = $response['message'];
232
+ delete_option( 'cpsw_apple_pay_domain_is_verfied' );
233
+ add_action( 'admin_notices', [ $this, 'apple_pay_verification_failed' ] );
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Generates admin notice if no live secret key is found
239
+ *
240
+ * @return void
241
+ */
242
+ public function no_live_secret_key() {
243
+ echo wp_kses_post( '<div class="notice notice-error is-dismissible"><p>' . __( 'We cannot find live secret key in database, Live secret key is required for Apple Pay domain verification. ', 'checkout-plugins-stripe-woo' ) . '</p></div>' );
244
+ }
245
+
246
+ /**
247
+ * Generates admin notice for apple pay success
248
+ *
249
+ * @return void
250
+ */
251
+ public function apple_pay_verification_success() {
252
+ echo wp_kses_post( '<div class="notice notice-success is-dismissible"><p>' . __( 'Apple Pay domain verification successful.', 'checkout-plugins-stripe-woo' ) . '</p></div>' );
253
+ }
254
+
255
+ /**
256
+ * Generates admin notice for SSL requirment for payment request Api
257
+ *
258
+ * @return void
259
+ */
260
+ public function no_ssl_notice() {
261
+ echo wp_kses_post( '<div class="notice notice-error is-dismissible"><p>' . __( 'SSL is required for Express Pay Checkout.', 'checkout-plugins-stripe-woo' ) . '</p></div>' );
262
+ }
263
+
264
+ /**
265
+ * Generates admin notice for apple pay registration failure
266
+ *
267
+ * @return void
268
+ */
269
+ public function apple_pay_verification_failed() {
270
+ /* translators: %1s - %3s HTML Entities, %4s Error Message */
271
+ echo wp_kses_post( '<div class="notice notice-warning is-dismissible"><p>' . sprintf( __( '%1$1sApple Pay domain verification failed. %2$2sReason%3$3s: %4$4s', 'checkout-plugins-stripe-woo' ), '<b>', '<br/>', '</b>', $this->failure_message ) . '</p></div>' );
272
+ }
273
+
274
+ /**
275
+ * Returns live secret key for apple pay Verification
276
+ *
277
+ * @param string $secret_key current secret key as per mode.
278
+ * @return string
279
+ */
280
+ public function get_live_secret_key( $secret_key ) {
281
+ return $this->secret_key;
282
+ }
283
+ }
gateway/stripe/card-payments.php CHANGED
@@ -15,14 +15,12 @@ use CPSW\Inc\Traits\Subscriptions;
15
  use CPSW\Gateway\Abstract_Payment_Gateway;
16
  use CPSW\Gateway\Stripe\Frontend_Scripts;
17
  use CPSW\Gateway\Stripe\Stripe_Api;
18
- use WC_Order;
19
  use WC_AJAX;
20
  use WC_HTTPS;
21
  use WC_Payment_Tokens;
22
  use WC_Payment_Token_CC;
23
  use Exception;
24
  use WP_Error;
25
-
26
  /**
27
  * Card_Payments
28
  *
@@ -46,6 +44,13 @@ class Card_Payments extends Abstract_Payment_Gateway {
46
  * @var array
47
  */
48
 
 
 
 
 
 
 
 
49
  /**
50
  * Constructor
51
  *
@@ -54,9 +59,7 @@ class Card_Payments extends Abstract_Payment_Gateway {
54
  public function __construct() {
55
  add_filter( 'woocommerce_payment_gateways', [ $this, 'add_gateway_class' ] );
56
 
57
- $this->id = 'cpsw_stripe';
58
- $this->stripe_api = Stripe_Api::get_instance();
59
- $this->method_title = __( 'Stripe Card Payments', 'checkout-plugins-stripe-woo' );
60
  $this->method_description = __( 'Accepts payments via Credit/Debit Cards', 'checkout-plugins-stripe-woo' );
61
  $this->has_fields = true;
62
  $this->init_supports();
@@ -78,7 +81,8 @@ class Card_Payments extends Abstract_Payment_Gateway {
78
  add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, [ $this, 'process_admin_options' ] );
79
  add_filter( 'woocommerce_payment_successful_result', [ $this, 'modify_successful_payment_result' ], 999, 2 );
80
  add_action( 'wc_ajax_cpsw_verify_payment_intent', [ $this, 'verify_intent' ] );
81
- add_action( 'woocommerce_admin_field_status', [ $this, 'connection_status' ] );
 
82
  }
83
 
84
  /**
@@ -208,7 +212,7 @@ class Card_Payments extends Abstract_Payment_Gateway {
208
  'allowed_cards' => [
209
  'title' => __( 'Allowed Cards', 'checkout-plugins-stripe-woo' ),
210
  'type' => 'multiselect',
211
- 'class' => 'cpsw_allowed_cards_field',
212
  'desc_tip' => __( 'Accepts payments using selected cards. If empty all stripe cards are accepted.', 'checkout-plugins-stripe-woo' ),
213
  'options' => [
214
  'mastercard' => 'MasterCard',
@@ -221,6 +225,20 @@ class Card_Payments extends Abstract_Payment_Gateway {
221
  ],
222
  'default' => [],
223
  ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  'order_button_text' => [
225
  'title' => __( 'Order Button Label', 'checkout-plugins-stripe-woo' ),
226
  'type' => 'text',
@@ -266,7 +284,7 @@ class Card_Payments extends Abstract_Payment_Gateway {
266
  $data['statement_descriptor'] = $this->statement_descriptor;
267
  }
268
 
269
- if ( isset( $_POST['enable_saved_card'] ) || $this->has_subscription( $order_id ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing
270
  $data['setup_future_usage'] = 'off_session';
271
  }
272
 
@@ -316,7 +334,8 @@ class Card_Payments extends Abstract_Payment_Gateway {
316
  try {
317
  $order = wc_get_order( $order_id );
318
  $token = $this->get_token_from_request( $_POST ); //phpcs:ignore WordPress.Security.NonceVerification.Missing
319
- $response = $this->stripe_api->payment_methods( 'retrieve', [ $token->get_token() ] );
 
320
  $payment_method = $response['success'] ? $response['data'] : false;
321
  $prepared_payment_method = $this->prepare_payment_method( $payment_method, $token );
322
 
@@ -339,7 +358,7 @@ class Card_Payments extends Abstract_Payment_Gateway {
339
  $request['statement_descriptor'] = $this->statement_descriptor;
340
  }
341
 
342
- $response = $this->stripe_api->payment_intents( 'create', [ $request ] );
343
 
344
  if ( $response['success'] ) {
345
  $intent = $response['data'];
@@ -409,6 +428,18 @@ class Card_Payments extends Abstract_Payment_Gateway {
409
  }
410
  }
411
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  /**
413
  * Process payment method functionality
414
  *
@@ -427,7 +458,8 @@ class Card_Payments extends Abstract_Payment_Gateway {
427
  $customer_id = $this->get_customer_id();
428
 
429
  $source = ! empty( $_POST['payment_method_created'] ) ? wc_clean( wp_unslash( $_POST['payment_method_created'] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Missing
430
- $response = $this->stripe_api->payment_methods( 'retrieve', [ $source ] );
 
431
  $source_object = $response['success'] ? $response['data'] : false;
432
 
433
  if ( isset( $source_object ) ) {
@@ -441,10 +473,11 @@ class Card_Payments extends Abstract_Payment_Gateway {
441
 
442
  $source_id = $source_object->id;
443
  }
444
- $response = $this->stripe_api->payment_methods( 'attach', [ $source_id, [ 'customer' => $customer_id ] ] );
445
- $response = $response['success'] ? $response['data'] : false;
446
- $user = wp_get_current_user();
447
- $user_id = ( $user->ID && $user->ID > 0 ) ? $user->ID : false;
 
448
  $this->create_payment_token_for_user( $user_id, $source_object );
449
 
450
  if ( ! $response || is_wp_error( $response ) || ! empty( $response->error ) ) {
@@ -512,12 +545,14 @@ class Card_Payments extends Abstract_Payment_Gateway {
512
 
513
  /* translators: transaction id */
514
  $message = sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'checkout-plugins-stripe-woo' ), $response->id );
 
515
  $order->add_order_note( $message );
516
  }
517
 
518
  if ( 'failed' === $response->status ) {
519
- $localized_message = __( 'Payment processing failed. Please retry.', 'checkout-plugins-stripe-woo' );
520
- $order->add_order_note( $localized_message );
 
521
  }
522
  } else {
523
  $order->set_transaction_id( $response->id );
@@ -620,8 +655,9 @@ class Card_Payments extends Abstract_Payment_Gateway {
620
  $client_secret = '';
621
 
622
  if ( ! empty( $intent_secret ) ) {
623
- $secret = $intent_secret;
624
- $response = $this->stripe_api->payment_intents( 'retrieve', [ $secret['id'] ] );
 
625
  if ( $response['success'] && 'success' === $response['data']->status ) {
626
  wc_add_notice( __( 'An error has occurred internally, due to which you are not redirected to the order received page.', 'checkout-plugins-stripe-woo' ), $notice_type = 'error' );
627
  wp_safe_redirect( wc_get_checkout_url() );
@@ -633,7 +669,8 @@ class Card_Payments extends Abstract_Payment_Gateway {
633
  [ 'idempotency_key' => $idempotency_key ],
634
  ];
635
 
636
- $response = $this->stripe_api->payment_intents( 'create', $args );
 
637
 
638
  if ( $response['success'] ) {
639
  $intent = $response['data'];
@@ -666,7 +703,8 @@ class Card_Payments extends Abstract_Payment_Gateway {
666
  $order = wc_get_order( $order_id );
667
 
668
  $intent_secret = get_post_meta( $order_id, '_cpsw_intent_secret', true );
669
- $response = $this->stripe_api->payment_intents( 'retrieve', [ $intent_secret['id'] ] );
 
670
  $intent = $response['success'] ? $response['data'] : false;
671
 
672
  if ( 'succeeded' === $intent->status || 'requires_capture' === $intent->status ) {
@@ -674,14 +712,13 @@ class Card_Payments extends Abstract_Payment_Gateway {
674
  $user = $order->get_id() ? $order->get_user() : wp_get_current_user();
675
  $user_id = $user->ID;
676
  $payment_method = $intent->payment_method;
677
- $response = $this->stripe_api->payment_methods( 'retrieve', [ $payment_method ] );
678
  $payment_method = $response['success'] ? $response['data'] : false;
679
  $token = $this->create_payment_token_for_user( $user_id, $payment_method );
680
  /* translators: %1$1s order id, %2$2s token id */
681
  Logger::info( sprintf( __( 'Payment method tokenized for Order id - %1$1s with token id - %2$2s', 'checkout-plugins-stripe-woo' ), $order_id, $token->get_id() ) );
682
  $prepared_payment_method = $this->prepare_payment_method( $payment_method, $token );
683
  $this->save_payment_method_to_order( $order, $prepared_payment_method );
684
-
685
  }
686
  $redirect_to = $this->process_order( end( $intent->charges->data ), $order_id );
687
  $redirect_url = apply_filters( 'cpsw_redirect_order_url', ! empty( $redirect ) ? $redirect : $redirect_to, $order );
@@ -836,8 +873,9 @@ class Card_Payments extends Abstract_Payment_Gateway {
836
  ];
837
  }
838
 
839
- $response = $this->stripe_api->customers( 'create', [ $args ] );
840
- $response = $response['success'] ? $response['data'] : false;
 
841
 
842
  if ( empty( $response->id ) ) {
843
  return false;
@@ -855,40 +893,18 @@ class Card_Payments extends Abstract_Payment_Gateway {
855
  * @return bool
856
  */
857
  public function process_refund( $order_id, $amount = null, $reason = '' ) {
858
- $client = $this->get_clients_details();
859
 
860
  if ( 0 >= $amount ) {
861
  return false;
862
  }
863
 
864
  try {
865
- $intent_secret = get_post_meta( $order_id, '_cpsw_intent_secret', true );
866
- $response = $this->stripe_api->payment_intents( 'retrieve', [ $intent_secret['id'] ] );
867
- $status = $response['success'] ? $response['data']->charges->data[0]->captured : false;
868
 
869
- if ( ! $status ) {
870
- Logger::error( __( 'Uncaptured Amount cannot be refunded', 'checkout-plugins-stripe-woo' ), true );
871
- return new WP_Error( 'error', __( 'Uncaptured Amount cannot be refunded', 'checkout-plugins-stripe-woo' ) );
872
- }
873
 
874
- $intent_response = $response['data'];
875
- $order = wc_get_order( $order_id );
876
- $currency = $intent_response->currency;
877
-
878
- $refund_params = [
879
- 'payment_intent' => $intent_secret['id'],
880
- 'amount' => $this->get_formatted_amount( $amount, $currency ),
881
- 'reason' => 'requested_by_customer',
882
- 'metadata' => [
883
- 'order_id' => $order->get_order_number(),
884
- 'Customer IP' => $client['IP'],
885
- 'Agent' => $client['Agent'],
886
- 'Referer' => $client['Referer'],
887
- 'Reason for Refund' => $reason,
888
- ],
889
- ];
890
 
891
- $response = $this->stripe_api->refunds( 'create', [ $refund_params ] );
892
  $refund_response = $response['success'] ? $response['data'] : false;
893
 
894
  if ( $refund_response ) {
@@ -898,14 +914,57 @@ class Card_Payments extends Abstract_Payment_Gateway {
898
  return true;
899
  } else {
900
  $order->add_order_note( __( 'Reason : ', 'checkout-plugins-stripe-woo' ) . $reason . '.<br>' . __( 'Amount : ', 'checkout-plugins-stripe-woo' ) . get_woocommerce_currency_symbol() . $amount . '.<br>' . __( ' Status : Failed ', 'checkout-plugins-stripe-woo' ) );
901
- Logger::error( $refund_response->message, true );
902
- return new WP_Error( 'error', $refund_response->message );
903
  }
904
  } catch ( Exception $e ) {
905
  Logger::error( $e->getMessage(), true );
906
  }
907
  }
908
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
909
  /**
910
  * Get stripe activated payment cards icon.
911
  */
@@ -1054,5 +1113,73 @@ class Card_Payments extends Abstract_Payment_Gateway {
1054
  return $customer->id;
1055
  }
1056
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1057
  }
1058
 
15
  use CPSW\Gateway\Abstract_Payment_Gateway;
16
  use CPSW\Gateway\Stripe\Frontend_Scripts;
17
  use CPSW\Gateway\Stripe\Stripe_Api;
 
18
  use WC_AJAX;
19
  use WC_HTTPS;
20
  use WC_Payment_Tokens;
21
  use WC_Payment_Token_CC;
22
  use Exception;
23
  use WP_Error;
 
24
  /**
25
  * Card_Payments
26
  *
44
  * @var array
45
  */
46
 
47
+ /**
48
+ * Gateway id
49
+ *
50
+ * @var string
51
+ */
52
+ public $id = 'cpsw_stripe';
53
+
54
  /**
55
  * Constructor
56
  *
59
  public function __construct() {
60
  add_filter( 'woocommerce_payment_gateways', [ $this, 'add_gateway_class' ] );
61
 
62
+ $this->method_title = __( 'Stripe Card Processing', 'checkout-plugins-stripe-woo' );
 
 
63
  $this->method_description = __( 'Accepts payments via Credit/Debit Cards', 'checkout-plugins-stripe-woo' );
64
  $this->has_fields = true;
65
  $this->init_supports();
81
  add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, [ $this, 'process_admin_options' ] );
82
  add_filter( 'woocommerce_payment_successful_result', [ $this, 'modify_successful_payment_result' ], 999, 2 );
83
  add_action( 'wc_ajax_cpsw_verify_payment_intent', [ $this, 'verify_intent' ] );
84
+ add_filter( 'woocommerce_payment_complete_order_status', [ $this, 'cpsw_payment_complete_order_status' ], 10, 3 );
85
+
86
  }
87
 
88
  /**
212
  'allowed_cards' => [
213
  'title' => __( 'Allowed Cards', 'checkout-plugins-stripe-woo' ),
214
  'type' => 'multiselect',
215
+ 'class' => 'cpsw_select_woo',
216
  'desc_tip' => __( 'Accepts payments using selected cards. If empty all stripe cards are accepted.', 'checkout-plugins-stripe-woo' ),
217
  'options' => [
218
  'mastercard' => 'MasterCard',
225
  ],
226
  'default' => [],
227
  ],
228
+ 'order_status' => [
229
+ 'type' => 'select',
230
+ 'title' => __( 'Order Status', 'checkout-plugins-stripe-woo' ),
231
+ 'class' => 'cpsw_select_woo',
232
+ 'options' => [
233
+ '' => __( 'Default', 'checkout-plugins-stripe-woo' ),
234
+ 'wc-processing' => __( 'Processing', 'checkout-plugins-stripe-woo' ),
235
+ 'wc-on-hold' => __( 'On Hold', 'checkout-plugins-stripe-woo' ),
236
+ 'wc-completed' => __( 'Completed', 'checkout-plugins-stripe-woo' ),
237
+ ],
238
+ 'default' => '',
239
+ 'tool_tip' => true,
240
+ 'description' => __( 'This is the status of the order once payment is complete. If <b>Default</b> is selected, then WooCommerce will set the order status automatically based on internal logic which states if a product is virtual and downloadable then status is set to complete. Products that require shipping are set to Processing. Default is the recommended setting as it allows standard WooCommerce code to process the order status.', 'checkout-plugins-stripe-woo' ),
241
+ ],
242
  'order_button_text' => [
243
  'title' => __( 'Order Button Label', 'checkout-plugins-stripe-woo' ),
244
  'type' => 'text',
284
  $data['statement_descriptor'] = $this->statement_descriptor;
285
  }
286
 
287
+ if ( $this->should_save_card( $order_id ) ) {
288
  $data['setup_future_usage'] = 'off_session';
289
  }
290
 
334
  try {
335
  $order = wc_get_order( $order_id );
336
  $token = $this->get_token_from_request( $_POST ); //phpcs:ignore WordPress.Security.NonceVerification.Missing
337
+ $stripe_api = new Stripe_Api();
338
+ $response = $stripe_api->payment_methods( 'retrieve', [ $token->get_token() ] );
339
  $payment_method = $response['success'] ? $response['data'] : false;
340
  $prepared_payment_method = $this->prepare_payment_method( $payment_method, $token );
341
 
358
  $request['statement_descriptor'] = $this->statement_descriptor;
359
  }
360
 
361
+ $response = $this->create_payment_for_saved_payment_method( $request );
362
 
363
  if ( $response['success'] ) {
364
  $intent = $response['data'];
428
  }
429
  }
430
 
431
+ /**
432
+ * Create payment for saved payment method.
433
+ *
434
+ * @param array $request_args arguments.
435
+ *
436
+ * @return array
437
+ */
438
+ public function create_payment_for_saved_payment_method( $request_args ) {
439
+ $stripe_api = new Stripe_Api();
440
+ return $stripe_api->payment_intents( 'create', [ $request_args ] );
441
+ }
442
+
443
  /**
444
  * Process payment method functionality
445
  *
458
  $customer_id = $this->get_customer_id();
459
 
460
  $source = ! empty( $_POST['payment_method_created'] ) ? wc_clean( wp_unslash( $_POST['payment_method_created'] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Missing
461
+ $stripe_api = new Stripe_Api();
462
+ $response = $stripe_api->payment_methods( 'retrieve', [ $source ] );
463
  $source_object = $response['success'] ? $response['data'] : false;
464
 
465
  if ( isset( $source_object ) ) {
473
 
474
  $source_id = $source_object->id;
475
  }
476
+ $stripe_api = new Stripe_Api();
477
+ $response = $stripe_api->payment_methods( 'attach', [ $source_id, [ 'customer' => $customer_id ] ] );
478
+ $response = $response['success'] ? $response['data'] : false;
479
+ $user = wp_get_current_user();
480
+ $user_id = ( $user->ID && $user->ID > 0 ) ? $user->ID : false;
481
  $this->create_payment_token_for_user( $user_id, $source_object );
482
 
483
  if ( ! $response || is_wp_error( $response ) || ! empty( $response->error ) ) {
545
 
546
  /* translators: transaction id */
547
  $message = sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'checkout-plugins-stripe-woo' ), $response->id );
548
+ Logger::info( $message, true );
549
  $order->add_order_note( $message );
550
  }
551
 
552
  if ( 'failed' === $response->status ) {
553
+ $message = __( 'Payment processing failed. Please retry.', 'checkout-plugins-stripe-woo' );
554
+ Logger::error( $message, true );
555
+ $order->add_order_note( $message );
556
  }
557
  } else {
558
  $order->set_transaction_id( $response->id );
655
  $client_secret = '';
656
 
657
  if ( ! empty( $intent_secret ) ) {
658
+ $secret = $intent_secret;
659
+ $stripe_api = new Stripe_Api();
660
+ $response = $stripe_api->payment_intents( 'retrieve', [ $secret['id'] ] );
661
  if ( $response['success'] && 'success' === $response['data']->status ) {
662
  wc_add_notice( __( 'An error has occurred internally, due to which you are not redirected to the order received page.', 'checkout-plugins-stripe-woo' ), $notice_type = 'error' );
663
  wp_safe_redirect( wc_get_checkout_url() );
669
  [ 'idempotency_key' => $idempotency_key ],
670
  ];
671
 
672
+ $stripe_api = new Stripe_Api();
673
+ $response = $stripe_api->payment_intents( 'create', $args );
674
 
675
  if ( $response['success'] ) {
676
  $intent = $response['data'];
703
  $order = wc_get_order( $order_id );
704
 
705
  $intent_secret = get_post_meta( $order_id, '_cpsw_intent_secret', true );
706
+ $stripe_api = new Stripe_Api();
707
+ $response = $stripe_api->payment_intents( 'retrieve', [ $intent_secret['id'] ] );
708
  $intent = $response['success'] ? $response['data'] : false;
709
 
710
  if ( 'succeeded' === $intent->status || 'requires_capture' === $intent->status ) {
712
  $user = $order->get_id() ? $order->get_user() : wp_get_current_user();
713
  $user_id = $user->ID;
714
  $payment_method = $intent->payment_method;
715
+ $response = $stripe_api->payment_methods( 'retrieve', [ $payment_method ] );
716
  $payment_method = $response['success'] ? $response['data'] : false;
717
  $token = $this->create_payment_token_for_user( $user_id, $payment_method );
718
  /* translators: %1$1s order id, %2$2s token id */
719
  Logger::info( sprintf( __( 'Payment method tokenized for Order id - %1$1s with token id - %2$2s', 'checkout-plugins-stripe-woo' ), $order_id, $token->get_id() ) );
720
  $prepared_payment_method = $this->prepare_payment_method( $payment_method, $token );
721
  $this->save_payment_method_to_order( $order, $prepared_payment_method );
 
722
  }
723
  $redirect_to = $this->process_order( end( $intent->charges->data ), $order_id );
724
  $redirect_url = apply_filters( 'cpsw_redirect_order_url', ! empty( $redirect ) ? $redirect : $redirect_to, $order );
873
  ];
874
  }
875
 
876
+ $stripe_api = new Stripe_Api();
877
+ $response = $stripe_api->customers( 'create', [ $args ] );
878
+ $response = $response['success'] ? $response['data'] : false;
879
 
880
  if ( empty( $response->id ) ) {
881
  return false;
893
  * @return bool
894
  */
895
  public function process_refund( $order_id, $amount = null, $reason = '' ) {
 
896
 
897
  if ( 0 >= $amount ) {
898
  return false;
899
  }
900
 
901
  try {
 
 
 
902
 
903
+ $order = wc_get_order( $order_id );
904
+ $intent_secret = $order->get_meta( '_cpsw_intent_secret', true );
 
 
905
 
906
+ $response = $this->create_refund_request( $order, $amount, $reason, $intent_secret['id'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
907
 
 
908
  $refund_response = $response['success'] ? $response['data'] : false;
909
 
910
  if ( $refund_response ) {
914
  return true;
915
  } else {
916
  $order->add_order_note( __( 'Reason : ', 'checkout-plugins-stripe-woo' ) . $reason . '.<br>' . __( 'Amount : ', 'checkout-plugins-stripe-woo' ) . get_woocommerce_currency_symbol() . $amount . '.<br>' . __( ' Status : Failed ', 'checkout-plugins-stripe-woo' ) );
917
+ Logger::error( $response['message'], true );
918
+ return new WP_Error( 'error', $response['message'] );
919
  }
920
  } catch ( Exception $e ) {
921
  Logger::error( $e->getMessage(), true );
922
  }
923
  }
924
 
925
+ /**
926
+ * Create refund request.
927
+ *
928
+ * @param object $order order.
929
+ * @param string $amount refund amount.
930
+ * @param string $reason reason of refund.
931
+ * @param string $intent_secret_id secret key.
932
+ *
933
+ * @return array
934
+ */
935
+ public function create_refund_request( $order, $amount, $reason, $intent_secret_id ) {
936
+
937
+ $client = $this->get_clients_details();
938
+
939
+ $stripe_api = new Stripe_Api();
940
+ $response = $stripe_api->payment_intents( 'retrieve', [ $intent_secret_id ] );
941
+ $status = $response['success'] ? $response['data']->charges->data[0]->captured : false;
942
+
943
+ if ( ! $status ) {
944
+ Logger::error( __( 'Uncaptured Amount cannot be refunded', 'checkout-plugins-stripe-woo' ), true );
945
+ return new WP_Error( 'error', __( 'Uncaptured Amount cannot be refunded', 'checkout-plugins-stripe-woo' ) );
946
+ }
947
+
948
+ $intent_response = $response['data'];
949
+ $currency = $intent_response->currency;
950
+
951
+ $refund_params = [
952
+ 'payment_intent' => $intent_secret_id,
953
+ 'amount' => $this->get_formatted_amount( $amount, $currency ),
954
+ 'reason' => 'requested_by_customer',
955
+ 'metadata' => [
956
+ 'order_id' => $order->get_order_number(),
957
+ 'Customer IP' => $client['IP'],
958
+ 'Agent' => $client['Agent'],
959
+ 'Referer' => $client['Referer'],
960
+ 'Reason for Refund' => $reason,
961
+ ],
962
+ ];
963
+
964
+ $stripe_api = new Stripe_Api();
965
+ return $stripe_api->refunds( 'create', [ $refund_params ] );
966
+ }
967
+
968
  /**
969
  * Get stripe activated payment cards icon.
970
  */
1113
  return $customer->id;
1114
  }
1115
  }
1116
+
1117
+ /**
1118
+ * Updates order status as per option 'order_status' set in card payment settings
1119
+ *
1120
+ * @param string $order_status default order status.
1121
+ * @param id $order_id current order id.
1122
+ * @param WC_Order $order current order.
1123
+ * @return string
1124
+ */
1125
+ public function cpsw_payment_complete_order_status( $order_status, $order_id, $order = null ) {
1126
+ if ( $order && $order->get_payment_method() ) {
1127
+ $gateway = $order->get_payment_method();
1128
+ if ( 'cpsw_stripe' === $gateway && ! empty( $this->get_option( 'order_status' ) ) ) {
1129
+ $order_status = $this->get_option( 'order_status' );
1130
+ }
1131
+ }
1132
+
1133
+ return apply_filters( 'cpsw_payment_complete_order_status', $order_status );
1134
+ }
1135
+
1136
+ /**
1137
+ * Checks whether current page is a product pare or not
1138
+ *
1139
+ * @return boolean
1140
+ */
1141
+ public function is_product() {
1142
+ return is_product() || wc_post_content_has_shortcode( 'product_page' );
1143
+ }
1144
+
1145
+ /**
1146
+ * Prepare Sorce for current order
1147
+ *
1148
+ * @param WC_Order $order Current order.
1149
+ * @return object
1150
+ */
1151
+ public function prepare_order_source( $order = null ) {
1152
+ $stripe_customer = false;
1153
+ $stripe_source = false;
1154
+ $source_object = false;
1155
+
1156
+ if ( $order ) {
1157
+
1158
+ $stripe_customer_id = $this->get_cpsw_customer_id( $order );
1159
+
1160
+ if ( $stripe_customer_id ) {
1161
+
1162
+ $stripe_customer = [];
1163
+ $stripe_customer['id'] = $stripe_customer_id;
1164
+ }
1165
+
1166
+ $source_id = $order->get_meta( '_cpsw_source_id', true );
1167
+
1168
+ if ( $source_id ) {
1169
+ $stripe_source = $source_id;
1170
+ $stripe_api = new Stripe_Api();
1171
+ $response = $stripe_api->payment_methods( 'retrieve', [ $stripe_source ] );
1172
+ $source_object = $response['success'] ? $response['data'] : false;
1173
+ } elseif ( apply_filters( 'cpsw_use_default_customer_source', true ) ) {
1174
+ $stripe_source = '';
1175
+ }
1176
+ }
1177
+
1178
+ return (object) [
1179
+ 'customer' => $stripe_customer ? $stripe_customer['id'] : false,
1180
+ 'source' => $stripe_source,
1181
+ 'source_object' => $source_object,
1182
+ ];
1183
+ }
1184
  }
1185
 
gateway/stripe/frontend-scripts.php CHANGED
@@ -10,6 +10,7 @@ namespace CPSW\Gateway\Stripe;
10
 
11
  use CPSW\Inc\Traits\Get_Instance;
12
  use CPSW\Inc\Helper;
 
13
 
14
  /**
15
  * Consists frontend scripts for payment gateways
@@ -47,9 +48,15 @@ class Frontend_Scripts {
47
  public function enqueue_scripts() {
48
 
49
  $public_key = ( 'live' === Helper::get_payment_mode() ) ? Helper::get_setting( 'cpsw_pub_key' ) : Helper::get_setting( 'cpsw_test_pub_key' );
50
- wp_enqueue_script( $this->prefix . 'stripe-external', 'https://js.stripe.com/v3/', [], CPSW_VERSION, true );
51
- wp_enqueue_script( $this->prefix . 'stripe-elements', $this->assets_url . 'js/stripe-elements.js', [ 'jquery', $this->prefix . 'stripe-external' ], CPSW_VERSION, true );
52
- wp_enqueue_style( $this->prefix . 'stripe-elements', $this->assets_url . 'css/stripe-elements.css', [], CPSW_VERSION );
 
 
 
 
 
 
53
  wp_localize_script(
54
  $this->prefix . 'stripe-elements',
55
  'cpsw_global_settings',
@@ -73,5 +80,48 @@ class Frontend_Scripts {
73
  'not_allowed_string' => __( 'is not allowed', 'checkout-plugins-stripe-woo' ),
74
  ]
75
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  }
77
  }
10
 
11
  use CPSW\Inc\Traits\Get_Instance;
12
  use CPSW\Inc\Helper;
13
+ use WC_AJAX;
14
 
15
  /**
16
  * Consists frontend scripts for payment gateways
48
  public function enqueue_scripts() {
49
 
50
  $public_key = ( 'live' === Helper::get_payment_mode() ) ? Helper::get_setting( 'cpsw_pub_key' ) : Helper::get_setting( 'cpsw_test_pub_key' );
51
+ wp_register_script( $this->prefix . 'stripe-external', 'https://js.stripe.com/v3/', [], CPSW_VERSION, true );
52
+ wp_enqueue_script( $this->prefix . 'stripe-external' );
53
+
54
+ wp_register_script( $this->prefix . 'stripe-elements', $this->assets_url . 'js/stripe-elements.js', [ 'jquery', $this->prefix . 'stripe-external' ], CPSW_VERSION, true );
55
+ wp_enqueue_script( $this->prefix . 'stripe-elements' );
56
+
57
+ wp_register_style( $this->prefix . 'stripe-elements', $this->assets_url . 'css/stripe-elements.css', [], CPSW_VERSION );
58
+ wp_enqueue_style( $this->prefix . 'stripe-elements' );
59
+
60
  wp_localize_script(
61
  $this->prefix . 'stripe-elements',
62
  'cpsw_global_settings',
80
  'not_allowed_string' => __( 'is not allowed', 'checkout-plugins-stripe-woo' ),
81
  ]
82
  );
83
+
84
+ if ( 'yes' === Helper::get_setting( 'express_checkout_enabled', 'cpsw_stripe' ) ) {
85
+ wp_register_script( $this->prefix . 'payment-request', $this->assets_url . 'js/payment-request.js', [ 'jquery', $this->prefix . 'stripe-external', $this->prefix . 'stripe-elements' ], CPSW_VERSION, true );
86
+ wp_enqueue_script( $this->prefix . 'payment-request' );
87
+
88
+ $needs_shipping = false;
89
+ if ( ! is_null( WC()->cart ) && WC()->cart->needs_shipping() ) {
90
+ $needs_shipping = true;
91
+ }
92
+
93
+ wp_localize_script(
94
+ $this->prefix . 'payment-request',
95
+ 'cpsw_payment_request',
96
+ apply_filters(
97
+ 'cpsw_payment_request_localization',
98
+ [
99
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
100
+ 'ajax_endpoint' => WC_AJAX::get_endpoint( '%%endpoint%%' ),
101
+ 'public_key' => $public_key,
102
+ 'mode' => Helper::get_payment_mode(),
103
+ 'currency_code' => strtolower( get_woocommerce_currency() ),
104
+ 'country_code' => substr( get_option( 'woocommerce_default_country' ), 0, 2 ),
105
+ 'needs_shipping' => $needs_shipping,
106
+ 'phone_required' => 'required' === get_option( 'woocommerce_checkout_phone_field', 'required' ),
107
+ 'nonce' => [
108
+ 'checkout' => wp_create_nonce( 'cpsw_checkout' ),
109
+ 'payment' => wp_create_nonce( 'cpsw_payment_request' ),
110
+ 'add_to_cart' => wp_create_nonce( 'cpsw_add_to_cart' ),
111
+ 'selected_product_data' => wp_create_nonce( 'cpsw_selected_product_data' ),
112
+ 'shipping' => wp_create_nonce( 'cpsw_shipping_address' ),
113
+ 'shipping_option' => wp_create_nonce( 'cpsw_shipping_option' ),
114
+ 'js_nonce' => wp_create_nonce( 'cpsw_js_error_nonce' ),
115
+ ],
116
+ 'style' => [
117
+ 'type' => Helper::get_setting( 'express_checkout_button_type', 'cpsw_stripe' ),
118
+ 'theme' => Helper::get_setting( 'express_checkout_button_theme', 'cpsw_stripe' ),
119
+ 'height' => (int) Helper::get_setting( 'express_checkout_button_height', 'cpsw_stripe' ),
120
+ ],
121
+ 'is_product_page' => is_product() || wc_post_content_has_shortcode( 'product_page' ),
122
+ ]
123
+ )
124
+ );
125
+ }
126
  }
127
  }
gateway/stripe/payment-request-api.php ADDED
@@ -0,0 +1,869 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Stripe Gateway
4
+ *
5
+ * @package checkout-plugins-stripe-woo
6
+ * @since 1.1.0
7
+ */
8
+
9
+ namespace CPSW\Gateway\Stripe;
10
+
11
+ use CPSW\Gateway\Stripe\Card_Payments;
12
+ use CPSW\Inc\Helper;
13
+ use CPSW\Inc\Traits\Get_Instance;
14
+ use WC_Data_Store;
15
+ use WC_Subscriptions_Product;
16
+ use WC_Validation;
17
+
18
+ /**
19
+ * Payment Request Api.
20
+ */
21
+ class Payment_Request_Api extends Card_Payments {
22
+
23
+ use Get_Instance;
24
+
25
+ /**
26
+ * Constructor
27
+ */
28
+ public function __construct() {
29
+ $settings = Helper::get_gateway_settings();
30
+ $this->express_checkout = $settings['express_checkout_enabled'];
31
+ if ( 'yes' !== $this->express_checkout || 'yes' !== $settings['enabled'] ) {
32
+ return;
33
+ }
34
+ $this->statement_descriptor = $this->clean_statement_descriptor( $this->get_option( 'statement_descriptor' ) );
35
+
36
+ $this->capture_method = Helper::get_setting( 'charge_type', 'cpsw_stripe' );
37
+
38
+ if ( 'above' === Helper::get_setting( 'express_checkout_product_page_position', 'cpsw_stripe' ) ) {
39
+ add_action( 'woocommerce_after_add_to_cart_quantity', [ $this, 'payment_request_button' ], 10 );
40
+ } else {
41
+ add_action( 'woocommerce_after_add_to_cart_button', [ $this, 'payment_request_button' ], 1 );
42
+ }
43
+
44
+ add_action( 'woocommerce_proceed_to_checkout', [ $this, 'payment_request_button' ], 1 );
45
+
46
+ add_action( 'woocommerce_checkout_before_customer_details', [ $this, 'payment_request_button' ], 5 );
47
+
48
+ add_filter( 'cpsw_payment_request_localization', [ $this, 'localize_product_data' ] );
49
+
50
+ add_action( 'wc_ajax_cpsw_payment_request_checkout', [ $this, 'ajax_checkout' ] );
51
+ add_action( 'wc_ajax_cpsw_get_cart_details', [ $this, 'ajax_get_cart_details' ] );
52
+ add_action( 'wc_ajax_cpsw_add_to_cart', [ $this, 'ajax_add_to_cart' ] );
53
+ add_action( 'wc_ajax_cpsw_selected_product_data', [ $this, 'ajax_selected_product_data' ] );
54
+ add_action( 'wc_ajax_cpsw_update_shipping_address', [ $this, 'ajax_update_shipping_address' ] );
55
+ add_action( 'wc_ajax_cpsw_update_shipping_option', [ $this, 'ajax_update_shipping_option' ] );
56
+
57
+ }
58
+
59
+ /**
60
+ * Checks wheter current page is supported for express checkout
61
+ *
62
+ * @return boolean
63
+ */
64
+ private function is_page_supported() {
65
+ return $this->is_product()
66
+ || is_cart()
67
+ || is_checkout()
68
+ || isset( $_GET['pay_for_order'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
69
+ }
70
+
71
+ /**
72
+ * Checks if current location is chosen to display express checkout button
73
+ *
74
+ * @return boolean
75
+ */
76
+ private function is_selected_location() {
77
+ $location = Helper::get_setting( 'express_checkout_location', 'cpsw_stripe' );
78
+ if ( is_array( $location ) ) {
79
+ if ( empty( $location ) ) {
80
+ return true;
81
+ }
82
+
83
+ if ( $this->is_product() && in_array( 'product', $location, true ) ) {
84
+ return true;
85
+ }
86
+
87
+ if ( is_cart() && in_array( 'cart', $location, true ) ) {
88
+ return true;
89
+ }
90
+
91
+ if ( is_checkout() && in_array( 'checkout', $location, true ) ) {
92
+ return true;
93
+ }
94
+ }
95
+
96
+ return false;
97
+ }
98
+
99
+ /**
100
+ * Creates container for payment request button
101
+ *
102
+ * @return void
103
+ */
104
+ public function payment_request_button() {
105
+ $gateways = WC()->payment_gateways->get_available_payment_gateways();
106
+
107
+ if ( ! isset( $gateways['cpsw_stripe'] ) ) {
108
+ return;
109
+ }
110
+
111
+ if ( ! $this->is_page_supported() ) {
112
+ return;
113
+ }
114
+
115
+ if ( ! $this->is_selected_location() ) {
116
+ return;
117
+ }
118
+
119
+ if ( 'yes' === $this->express_checkout ) {
120
+ $container_class = '';
121
+ if ( $this->is_product() ) {
122
+ $container_class = 'product';
123
+ } elseif ( is_checkout() ) {
124
+ $container_class = 'checkout';
125
+ } elseif ( is_cart() ) {
126
+ $container_class = 'cart';
127
+ }
128
+
129
+ $separator_below = true;
130
+ $position_class = 'above';
131
+
132
+ if ( 'below' === Helper::get_setting( 'express_checkout_product_page_position', 'cpsw_stripe' ) && 'product' === $container_class ) {
133
+ $this->payment_request_button_separator();
134
+ $separator_below = false;
135
+ $position_class = 'below';
136
+ }
137
+
138
+ $options = Helper::get_gateway_settings( 'cpsw_stripe' );
139
+
140
+ $alignment_class = '';
141
+ $button_width = '';
142
+
143
+ if ( 'checkout' === $container_class ) {
144
+ if ( 'yes' === $options['express_checkout_product_checkout_page'] ) {
145
+ $alignment_class = $options['express_checkout_button_alignment'];
146
+ if ( ! empty( $options['express_checkout_button_width'] && $options['express_checkout_button_width'] > 0 ) ) {
147
+ $button_width = 'width:' . (int) $options['express_checkout_button_width'] . 'px';
148
+ }
149
+ }
150
+ }
151
+
152
+ if ( 'product' === $container_class ) {
153
+ if ( 'yes' === $options['express_checkout_product_sticky_footer'] ) {
154
+ $container_class .= ' sticky';
155
+ }
156
+ }
157
+
158
+ ?>
159
+ <div id="cpsw-payment-request-wrapper" class="<?php echo esc_attr( $container_class . ' ' . $position_class . ' ' . $alignment_class ); ?>">
160
+ <div class="cpsw-payment-request-button-wrapper">
161
+ <?php
162
+ if ( ! empty( trim( $options['express_checkout_title'] ) && 'yes' === $options['express_checkout_product_checkout_page'] ) ) {
163
+ ?>
164
+ <h3 id="cpsw-payment-request-title"><?php echo esc_html( Helper::get_setting( 'express_checkout_title', 'cpsw_stripe' ) ); ?></h3>
165
+ <?php
166
+ }
167
+ if ( ! empty( trim( $options['express_checkout_tagline'] ) && 'yes' === $options['express_checkout_product_checkout_page'] ) ) {
168
+ ?>
169
+ <p id="cpsw-payment-request-tagline"><?php echo wp_kses_post( Helper::get_setting( 'express_checkout_tagline', 'cpsw_stripe' ) ); ?></p>
170
+ <?php
171
+ }
172
+ ?>
173
+
174
+ <div id="cpsw-payment-request-button" style="<?php echo esc_html( $button_width ); ?>">
175
+ <!-- A Stripe Element will be inserted here. -->
176
+ </div>
177
+ </div>
178
+ <?php
179
+ if ( $separator_below ) {
180
+ $this->payment_request_button_separator();
181
+ }
182
+ ?>
183
+ </div>
184
+ <?php
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Creates separator for payment request button
190
+ *
191
+ * @return void
192
+ */
193
+ public function payment_request_button_separator() {
194
+ $gateways = WC()->payment_gateways->get_available_payment_gateways();
195
+
196
+ if ( ! isset( $gateways['cpsw_stripe'] ) ) {
197
+ return;
198
+ }
199
+
200
+ if ( ! $this->is_page_supported() ) {
201
+ return;
202
+ }
203
+
204
+ if ( ! $this->is_selected_location() ) {
205
+ return;
206
+ }
207
+
208
+ if ( 'yes' === $this->express_checkout ) {
209
+ $container_class = '';
210
+ if ( $this->is_product() ) {
211
+ $container_class = 'product';
212
+ } elseif ( is_checkout() ) {
213
+ $container_class = 'checkout';
214
+ } elseif ( is_cart() ) {
215
+ $container_class = 'cart';
216
+ }
217
+
218
+ $options = Helper::get_gateway_settings( 'cpsw_stripe' );
219
+
220
+ $alignment_class = '';
221
+ $separator_text = $options['express_checkout_separator'];
222
+
223
+ if ( 'checkout' === $container_class ) {
224
+ if ( 'yes' === $options['express_checkout_product_checkout_page'] ) {
225
+ $alignment_class = $options['express_checkout_button_alignment'];
226
+ if ( ! empty( $options['express_checkout_separator_checkout'] ) ) {
227
+ $separator_text = $options['express_checkout_separator_checkout'];
228
+ }
229
+ }
230
+ }
231
+
232
+ ?>
233
+ <div id="cpsw-payment-request-separator" class="<?php echo esc_html( $container_class . ' ' . $alignment_class ); ?>">
234
+ <?php echo esc_html( $separator_text ); ?>
235
+ </div>
236
+ <?php
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Process chekout on payment request button click
242
+ *
243
+ * @return void
244
+ */
245
+ public function ajax_checkout() {
246
+ check_ajax_referer( 'cpsw_checkout', 'checkout_nonce' );
247
+
248
+ if ( WC()->cart->is_empty() ) {
249
+ wp_send_json_error( __( 'Empty cart', 'checkout-plugins-stripe-woo' ) );
250
+ }
251
+
252
+ if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
253
+ define( 'WOOCOMMERCE_CHECKOUT', true );
254
+ }
255
+ // setting the checkout nonce to avoid exception.
256
+ $_REQUEST['_wpnonce'] = wp_create_nonce( 'woocommerce-process_checkout' );
257
+ $_POST['_wpnonce'] = $_REQUEST['_wpnonce'];
258
+
259
+ WC()->checkout()->process_checkout();
260
+ exit();
261
+ }
262
+
263
+ /**
264
+ * Gets product data either form product page or page where shortcode is used
265
+ *
266
+ * @return object
267
+ */
268
+ public function get_product() {
269
+ global $post;
270
+ if ( is_product() ) {
271
+ return wc_get_product( $post->ID );
272
+ } elseif ( wc_post_content_has_shortcode( 'product_page' ) ) {
273
+ // Get id from product_page shortcode.
274
+ preg_match( '/\[product_page id="(?<id>\d+)"\]/', $post->post_content, $shortcode_match );
275
+
276
+ if ( ! isset( $shortcode_match['id'] ) ) {
277
+ return false;
278
+ }
279
+
280
+ return wc_get_product( $shortcode_match['id'] );
281
+ }
282
+
283
+ return false;
284
+ }
285
+
286
+ /**
287
+ * Get price of selected product
288
+ *
289
+ * @param object $product Selected product data.
290
+ * @return string
291
+ */
292
+ public function get_product_price( $product ) {
293
+ $product_price = $product->get_price();
294
+ // Add subscription sign-up fees to product price.
295
+ if ( 'subscription' === $product->get_type() && class_exists( 'WC_Subscriptions_Product' ) ) {
296
+ $product_price = $product->get_price() + WC_Subscriptions_Product::get_sign_up_fee( $product );
297
+ }
298
+
299
+ return $product_price;
300
+ }
301
+
302
+ /**
303
+ * Get data of selected product
304
+ *
305
+ * @return array
306
+ */
307
+ public function get_product_data() {
308
+ if ( ! $this->is_product() ) {
309
+ return false;
310
+ }
311
+
312
+ $product = $this->get_product();
313
+
314
+ if ( 'variable' === $product->get_type() ) {
315
+ $variation_attributes = $product->get_variation_attributes();
316
+ $attributes = [];
317
+
318
+ foreach ( $variation_attributes as $attribute_name => $attribute_values ) {
319
+ $attribute_key = 'attribute_' . sanitize_title( $attribute_name );
320
+
321
+ $attributes[ $attribute_key ] = isset( $_GET[ $attribute_key ] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
322
+ ? wc_clean( wp_unslash( $_GET[ $attribute_key ] ) ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
323
+ : $product->get_variation_default_attribute( $attribute_name );
324
+ }
325
+
326
+ $data_store = WC_Data_Store::load( 'product' );
327
+ $variation_id = $data_store->find_matching_product_variation( $product, $attributes );
328
+
329
+ if ( ! empty( $variation_id ) ) {
330
+ $product = wc_get_product( $variation_id );
331
+ }
332
+ }
333
+
334
+ $data = [];
335
+ $items = [];
336
+
337
+ if ( 'subscription' === $product->get_type() && class_exists( 'WC_Subscriptions_Product' ) ) {
338
+ $items[] = [
339
+ 'label' => $product->get_name(),
340
+ 'amount' => $this->get_formatted_amount( $product->get_price() ),
341
+ ];
342
+
343
+ $items[] = [
344
+ 'label' => __( 'Sign up Fee', 'checkout-plugins-stripe-woo' ),
345
+ 'amount' => $this->get_formatted_amount( WC_Subscriptions_Product::get_sign_up_fee( $product ) ),
346
+ ];
347
+ } else {
348
+ $items[] = [
349
+ 'label' => $product->get_name(),
350
+ 'amount' => $this->get_formatted_amount( $this->get_product_price( $product ) ),
351
+ ];
352
+ }
353
+
354
+ if ( wc_tax_enabled() ) {
355
+ $items[] = [
356
+ 'label' => __( 'Tax', 'checkout-plugins-stripe-woo' ),
357
+ 'amount' => 0,
358
+ 'pending' => true,
359
+ ];
360
+ }
361
+
362
+ if ( wc_shipping_enabled() && $product->needs_shipping() ) {
363
+ $items[] = [
364
+ 'label' => __( 'Shipping', 'checkout-plugins-stripe-woo' ),
365
+ 'amount' => 0,
366
+ 'pending' => true,
367
+ ];
368
+
369
+ $data['shippingOptions'] = [
370
+ 'id' => 'pending',
371
+ 'label' => __( 'Pending', 'checkout-plugins-stripe-woo' ),
372
+ 'detail' => '',
373
+ 'amount' => 0,
374
+ ];
375
+ }
376
+
377
+ $data['displayItems'] = $items;
378
+ $data['total'] = [
379
+ 'label' => apply_filters( 'cpsw_payment_request_total_label', $this->statement_descriptor ),
380
+ 'amount' => $this->get_formatted_amount( $this->get_product_price( $product ) ),
381
+ 'pending' => true,
382
+ ];
383
+ $data['requestShipping'] = ( wc_shipping_enabled() && $product->needs_shipping() && 0 !== wc_get_shipping_method_count( true ) );
384
+ return apply_filters( 'cpsw_payment_request_product_data', $data, $product );
385
+ }
386
+
387
+ /**
388
+ * Adds product data to locatized data via filter
389
+ *
390
+ * @param array $localized_data localized data.
391
+ * @return array
392
+ */
393
+ public function localize_product_data( $localized_data ) {
394
+ return array_merge(
395
+ $localized_data,
396
+ [ 'product' => $this->get_product_data() ]
397
+ );
398
+ }
399
+
400
+ /**
401
+ * Format data to display in payment request cart form
402
+ *
403
+ * @param boolean $display_items show detailed view or not.
404
+ * @return array
405
+ */
406
+ protected function build_display_items( $display_items = true ) {
407
+ if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
408
+ define( 'WOOCOMMERCE_CART', true );
409
+ }
410
+
411
+ $items = [];
412
+ $lines = [];
413
+ $subtotal = 0;
414
+ $discounts = 0;
415
+
416
+ foreach ( WC()->cart->get_cart() as $item ) {
417
+ $subtotal += $item['line_subtotal'];
418
+ $amount = $item['line_subtotal'];
419
+ $quantity_label = 1 < $item['quantity'] ? ' (' . $item['quantity'] . ')' : '';
420
+ $product_name = $item['data']->get_name();
421
+
422
+ $lines[] = [
423
+ 'label' => $product_name . $quantity_label,
424
+ 'amount' => $this->get_formatted_amount( $amount ),
425
+ ];
426
+ }
427
+
428
+ if ( $display_items ) {
429
+ $items = array_merge( $items, $lines );
430
+ } else {
431
+ // Default show only subtotal instead of itemization.
432
+
433
+ $items[] = [
434
+ 'label' => 'Subtotal',
435
+ 'amount' => $this->get_formatted_amount( $subtotal ),
436
+ ];
437
+ }
438
+
439
+ if ( version_compare( WC_VERSION, '3.2', '<' ) ) {
440
+ $discounts = wc_format_decimal( WC()->cart->get_cart_discount_total(), WC()->cart->dp );
441
+ } else {
442
+ $applied_coupons = array_values( WC()->cart->get_coupon_discount_totals() );
443
+
444
+ foreach ( $applied_coupons as $amount ) {
445
+ $discounts += (float) $amount;
446
+ }
447
+ }
448
+
449
+ $discounts = wc_format_decimal( $discounts, WC()->cart->dp );
450
+ $tax = wc_format_decimal( WC()->cart->tax_total + WC()->cart->shipping_tax_total, WC()->cart->dp );
451
+ $shipping = wc_format_decimal( WC()->cart->shipping_total, WC()->cart->dp );
452
+ $items_total = wc_format_decimal( WC()->cart->cart_contents_total, WC()->cart->dp ) + $discounts;
453
+ $order_total = version_compare( WC_VERSION, '3.2', '<' ) ? wc_format_decimal( $items_total + $tax + $shipping - $discounts, WC()->cart->dp ) : WC()->cart->get_total( false );
454
+
455
+ if ( wc_tax_enabled() ) {
456
+ $items[] = [
457
+ 'label' => esc_html( __( 'Tax', 'checkout-plugins-stripe-woo' ) ),
458
+ 'amount' => $this->get_formatted_amount( $tax ),
459
+ ];
460
+ }
461
+
462
+ if ( WC()->cart->needs_shipping() ) {
463
+ $items[] = [
464
+ 'label' => esc_html( __( 'Shipping', 'checkout-plugins-stripe-woo' ) ),
465
+ 'amount' => $this->get_formatted_amount( $shipping ),
466
+ ];
467
+ }
468
+
469
+ if ( WC()->cart->has_discount() ) {
470
+ $items[] = [
471
+ 'label' => esc_html( __( 'Discount', 'checkout-plugins-stripe-woo' ) ),
472
+ 'amount' => $this->get_formatted_amount( $discounts ),
473
+ ];
474
+ }
475
+
476
+ if ( version_compare( WC_VERSION, '3.2', '<' ) ) {
477
+ $cart_fees = WC()->cart->fees;
478
+ } else {
479
+ $cart_fees = WC()->cart->get_fees();
480
+ }
481
+
482
+ // Include fees and taxes as display items.
483
+ foreach ( $cart_fees as $fee ) {
484
+ $items[] = [
485
+ 'label' => $fee->name,
486
+ 'amount' => $this->get_formatted_amount( $fee->amount ),
487
+ ];
488
+ }
489
+
490
+ return [
491
+ 'displayItems' => $items,
492
+ 'total' => [
493
+ 'label' => apply_filters( 'cpsw_payment_request_total_label', $this->statement_descriptor ),
494
+ 'amount' => max( 0, apply_filters( 'cpsw_stripe_calculated_total', $this->get_formatted_amount( $order_total ), $order_total, WC()->cart ) ),
495
+ 'pending' => false,
496
+ ],
497
+ ];
498
+ }
499
+
500
+ /**
501
+ * Fetch cart details
502
+ *
503
+ * @return void
504
+ */
505
+ public function ajax_get_cart_details() {
506
+ check_ajax_referer( 'cpsw_payment_request', 'cart_nonce' );
507
+
508
+ if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
509
+ define( 'WOOCOMMERCE_CART', true );
510
+ }
511
+
512
+ WC()->cart->calculate_totals();
513
+
514
+ $currency = get_woocommerce_currency();
515
+
516
+ // Set mandatory payment details.
517
+ $data = [
518
+ 'shipping_required' => WC()->cart->needs_shipping(),
519
+ 'order_data' => [
520
+ 'currency' => strtolower( $currency ),
521
+ 'country_code' => substr( get_option( 'woocommerce_default_country' ), 0, 2 ),
522
+ ],
523
+ ];
524
+
525
+ $data['order_data'] += $this->build_display_items( true );
526
+
527
+ wp_send_json( $data );
528
+ }
529
+
530
+ /**
531
+ * Updates cart on product variant change
532
+ *
533
+ * @return void
534
+ */
535
+ public function ajax_add_to_cart() {
536
+ check_ajax_referer( 'cpsw_add_to_cart', 'add_to_cart_nonce' );
537
+
538
+ if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
539
+ define( 'WOOCOMMERCE_CART', true );
540
+ }
541
+
542
+ WC()->shipping->reset_shipping();
543
+
544
+ $product_id = isset( $_POST['product_id'] ) ? absint( $_POST['product_id'] ) : 0;
545
+ $qty = ! isset( $_POST['qty'] ) ? 1 : absint( $_POST['qty'] );
546
+ $product = wc_get_product( $product_id );
547
+ $product_type = $product->get_type();
548
+
549
+ // First empty the cart to prevent wrong calculation.
550
+ WC()->cart->empty_cart();
551
+
552
+ if ( ( 'variable' === $product_type || 'variable-subscription' === $product_type ) && isset( $_POST['attributes'] ) ) {
553
+ $attributes = wc_clean( wp_unslash( $_POST['attributes'] ) );
554
+
555
+ $data_store = WC_Data_Store::load( 'product' );
556
+ $variation_id = $data_store->find_matching_product_variation( $product, $attributes );
557
+
558
+ WC()->cart->add_to_cart( $product->get_id(), $qty, $variation_id, $attributes );
559
+ }
560
+
561
+ if ( 'simple' === $product_type || 'subscription' === $product_type ) {
562
+ WC()->cart->add_to_cart( $product->get_id(), $qty );
563
+ }
564
+
565
+ WC()->cart->calculate_totals();
566
+
567
+ $data = [];
568
+ $data += $this->build_display_items( true );
569
+ $data['result'] = 'success';
570
+
571
+ wp_send_json( $data );
572
+ }
573
+
574
+ /**
575
+ * Updates data as per selected product variant
576
+ *
577
+ * @throws Exception Error messages.
578
+ * @return void
579
+ */
580
+ public function ajax_selected_product_data() {
581
+ check_ajax_referer( 'cpsw_selected_product_data', 'selected_product_nonce' );
582
+
583
+ try {
584
+ $product_id = isset( $_POST['product_id'] ) ? absint( $_POST['product_id'] ) : 0;
585
+ $qty = ! isset( $_POST['qty'] ) ? 1 : apply_filters( 'woocommerce_add_to_cart_quantity', absint( $_POST['qty'] ), $product_id );
586
+ $addon_value = isset( $_POST['addon_value'] ) ? max( floatval( $_POST['addon_value'] ), 0 ) : 0;
587
+ $product = wc_get_product( $product_id );
588
+ $variation_id = null;
589
+
590
+ if ( ! is_a( $product, 'WC_Product' ) ) {
591
+ /* translators: %d is the product Id */
592
+ throw new Exception( sprintf( __( 'Product with the ID (%d) cannot be found.', 'checkout-plugins-stripe-woo' ), $product_id ) );
593
+ }
594
+
595
+ $product_type = $product->get_type();
596
+ if ( ( 'variable' === $product_type || 'variable-subscription' === $product_type ) && isset( $_POST['attributes'] ) ) {
597
+ $attributes = wc_clean( wp_unslash( $_POST['attributes'] ) );
598
+
599
+ $data_store = WC_Data_Store::load( 'product' );
600
+ $variation_id = $data_store->find_matching_product_variation( $product, $attributes );
601
+
602
+ if ( ! empty( $variation_id ) ) {
603
+ $product = wc_get_product( $variation_id );
604
+ }
605
+ }
606
+
607
+ if ( $product->is_sold_individually() ) {
608
+ $qty = apply_filters( 'cpsw_payment_request_add_to_cart_sold_individually_quantity', 1, $qty, $product_id, $variation_id );
609
+ }
610
+
611
+ if ( ! $product->has_enough_stock( $qty ) ) {
612
+ /* translators: 1: product name 2: quantity in stock */
613
+ throw new Exception( sprintf( __( 'You cannot add that amount of "%1$s"; to the cart because there is not enough stock (%2$s remaining).', 'checkout-plugins-stripe-woo' ), $product->get_name(), wc_format_stock_quantity_for_display( $product->get_stock_quantity(), $product ) ) );
614
+ }
615
+
616
+ $total = $qty * $this->get_product_price( $product ) + $addon_value;
617
+
618
+ $quantity_label = 1 < $qty ? ' (' . $qty . ')' : '';
619
+
620
+ $data = [];
621
+ $items = [];
622
+
623
+ $items[] = [
624
+ 'label' => $product->get_name() . $quantity_label,
625
+ 'amount' => $this->get_formatted_amount( $total ),
626
+ ];
627
+
628
+ if ( wc_tax_enabled() ) {
629
+ $items[] = [
630
+ 'label' => __( 'Tax', 'checkout-plugins-stripe-woo' ),
631
+ 'amount' => 0,
632
+ 'pending' => true,
633
+ ];
634
+ }
635
+
636
+ if ( wc_shipping_enabled() && $product->needs_shipping() ) {
637
+ $items[] = [
638
+ 'label' => __( 'Shipping', 'checkout-plugins-stripe-woo' ),
639
+ 'amount' => 0,
640
+ 'pending' => true,
641
+ ];
642
+
643
+ $data['shippingOptions'] = [
644
+ 'id' => 'pending',
645
+ 'label' => __( 'Pending', 'checkout-plugins-stripe-woo' ),
646
+ 'detail' => '',
647
+ 'amount' => 0,
648
+ ];
649
+ }
650
+
651
+ $data['displayItems'] = $items;
652
+ $data['total'] = [
653
+ 'label' => apply_filters( 'cpsw_payment_request_total_label', $this->statement_descriptor ),
654
+ 'amount' => $this->get_formatted_amount( $total ),
655
+ 'pending' => true,
656
+ ];
657
+
658
+ $data['requestShipping'] = ( wc_shipping_enabled() && $product->needs_shipping() );
659
+ $data['currency'] = strtolower( get_woocommerce_currency() );
660
+ $data['country_code'] = substr( get_option( 'woocommerce_default_country' ), 0, 2 );
661
+
662
+ wp_send_json( $data );
663
+ } catch ( Exception $e ) {
664
+ wp_send_json( [ 'error' => wp_strip_all_tags( $e->getMessage() ) ] );
665
+ }
666
+ }
667
+
668
+ /**
669
+ * Updates shipping address
670
+ *
671
+ * @return void
672
+ */
673
+ public function ajax_update_shipping_address() {
674
+ check_ajax_referer( 'cpsw_shipping_address', 'shipping_address_nonce' );
675
+
676
+ $shipping_address = filter_input_array(
677
+ INPUT_POST,
678
+ [
679
+ 'country' => FILTER_SANITIZE_STRING,
680
+ 'state' => FILTER_SANITIZE_STRING,
681
+ 'postcode' => FILTER_SANITIZE_STRING,
682
+ 'city' => FILTER_SANITIZE_STRING,
683
+ 'address' => FILTER_SANITIZE_STRING,
684
+ 'address_2' => FILTER_SANITIZE_STRING,
685
+ ]
686
+ );
687
+ $product_view_options = filter_input_array( INPUT_POST, [ 'is_product_page' => FILTER_SANITIZE_STRING ] );
688
+ $should_show_itemized_view = ! isset( $product_view_options['is_product_page'] ) ? true : filter_var( $product_view_options['is_product_page'], FILTER_VALIDATE_BOOLEAN );
689
+
690
+ $data = $this->get_shipping_options( $shipping_address, $should_show_itemized_view );
691
+ wp_send_json( $data );
692
+ }
693
+
694
+ /**
695
+ * Updates shipping option
696
+ *
697
+ * @return void
698
+ */
699
+ public function ajax_update_shipping_option() {
700
+ check_ajax_referer( 'cpsw_shipping_option', 'shipping_option_nonce' );
701
+
702
+ if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
703
+ define( 'WOOCOMMERCE_CART', true );
704
+ }
705
+
706
+ $shipping_methods = filter_input( INPUT_POST, 'shipping_method', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
707
+ $this->update_shipping_method( $shipping_methods );
708
+
709
+ WC()->cart->calculate_totals();
710
+
711
+ $product_view_options = filter_input_array( INPUT_POST, [ 'is_product_page' => FILTER_SANITIZE_STRING ] );
712
+ $should_show_itemized_view = ! isset( $product_view_options['is_product_page'] ) ? true : filter_var( $product_view_options['is_product_page'], FILTER_VALIDATE_BOOLEAN );
713
+
714
+ $data = [];
715
+ $data += $this->build_display_items( $should_show_itemized_view );
716
+ $data['result'] = 'success';
717
+
718
+ wp_send_json( $data );
719
+ }
720
+
721
+ /**
722
+ * Fetch shipping options for selected shipping address
723
+ *
724
+ * @param array $shipping_address selected shipping address.
725
+ * @param boolean $itemized_display_items shows descriptive view.
726
+ * @throws Exception If shipping method is not found.
727
+ * @return array
728
+ */
729
+ public function get_shipping_options( $shipping_address, $itemized_display_items = true ) {
730
+ try {
731
+ $data = [];
732
+
733
+ $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
734
+ $this->calculate_shipping( apply_filters( 'cpsw_payment_request_shipping_posted_values', $shipping_address ) );
735
+
736
+ $packages = WC()->shipping->get_packages();
737
+ $shipping_rate_ids = [];
738
+
739
+ if ( ! empty( $packages ) && WC()->customer->has_calculated_shipping() ) {
740
+ foreach ( $packages as $package ) {
741
+ if ( empty( $package['rates'] ) ) {
742
+ throw new Exception( __( 'Unable to find shipping method for address.', 'checkout-plugins-stripe-woo' ) );
743
+ }
744
+
745
+ foreach ( $package['rates'] as $key => $rate ) {
746
+ if ( in_array( $rate->id, $shipping_rate_ids, true ) ) {
747
+ throw new Exception( __( 'Unable to provide shipping options for Payment Requests.', 'checkout-plugins-stripe-woo' ) );
748
+ }
749
+ $shipping_rate_ids[] = $rate->id;
750
+ $data['shipping_options'][] = [
751
+ 'id' => $rate->id,
752
+ 'label' => $rate->label,
753
+ 'detail' => '',
754
+ 'amount' => $this->get_formatted_amount( $rate->cost ),
755
+ ];
756
+ }
757
+ }
758
+ } else {
759
+ throw new Exception( __( 'Unable to find shipping method for address.', 'checkout-plugins-stripe-woo' ) );
760
+ }
761
+
762
+ if ( isset( $data['shipping_options'][0] ) ) {
763
+ if ( isset( $chosen_shipping_methods[0] ) ) {
764
+ $chosen_method_id = $chosen_shipping_methods[0];
765
+ $compare_shipping_options = function ( $a, $b ) use ( $chosen_method_id ) {
766
+ if ( $a['id'] === $chosen_method_id ) {
767
+ return -1;
768
+ }
769
+
770
+ if ( $b['id'] === $chosen_method_id ) {
771
+ return 1;
772
+ }
773
+
774
+ return 0;
775
+ };
776
+ usort( $data['shipping_options'], $compare_shipping_options );
777
+ }
778
+
779
+ $first_shipping_method_id = $data['shipping_options'][0]['id'];
780
+ $this->update_shipping_method( [ $first_shipping_method_id ] );
781
+ }
782
+
783
+ WC()->cart->calculate_totals();
784
+
785
+ $data += $this->build_display_items( $itemized_display_items );
786
+ $data['result'] = 'success';
787
+ } catch ( Exception $e ) {
788
+ $data += $this->build_display_items( $itemized_display_items );
789
+ $data['result'] = 'invalid_shipping_address';
790
+ }
791
+
792
+ return $data;
793
+ }
794
+
795
+ /**
796
+ * Calculated shipping charges
797
+ *
798
+ * @param array $address updated address.
799
+ * @return void
800
+ */
801
+ protected function calculate_shipping( $address = [] ) {
802
+ $country = $address['country'];
803
+ $state = $address['state'];
804
+ $postcode = $address['postcode'];
805
+ $city = $address['city'];
806
+ $address_1 = $address['address'];
807
+ $address_2 = $address['address_2'];
808
+
809
+ WC()->shipping->reset_shipping();
810
+
811
+ if ( $postcode && WC_Validation::is_postcode( $postcode, $country ) ) {
812
+ $postcode = wc_format_postcode( $postcode, $country );
813
+ }
814
+
815
+ if ( $country ) {
816
+ WC()->customer->set_location( $country, $state, $postcode, $city );
817
+ WC()->customer->set_shipping_location( $country, $state, $postcode, $city );
818
+ } else {
819
+ WC()->customer->set_billing_address_to_base();
820
+ WC()->customer->set_shipping_address_to_base();
821
+ }
822
+
823
+ WC()->customer->set_calculated_shipping( true );
824
+ WC()->customer->save();
825
+
826
+ $packages = [];
827
+
828
+ $packages[0]['contents'] = WC()->cart->get_cart();
829
+ $packages[0]['contents_cost'] = 0;
830
+ $packages[0]['applied_coupons'] = WC()->cart->applied_coupons;
831
+ $packages[0]['user']['ID'] = get_current_user_id();
832
+ $packages[0]['destination']['country'] = $country;
833
+ $packages[0]['destination']['state'] = $state;
834
+ $packages[0]['destination']['postcode'] = $postcode;
835
+ $packages[0]['destination']['city'] = $city;
836
+ $packages[0]['destination']['address'] = $address_1;
837
+ $packages[0]['destination']['address_2'] = $address_2;
838
+
839
+ foreach ( WC()->cart->get_cart() as $item ) {
840
+ if ( $item['data']->needs_shipping() ) {
841
+ if ( isset( $item['line_total'] ) ) {
842
+ $packages[0]['contents_cost'] += $item['line_total'];
843
+ }
844
+ }
845
+ }
846
+
847
+ $packages = apply_filters( 'woocommerce_cart_shipping_packages', $packages );
848
+
849
+ WC()->shipping->calculate_shipping( $packages );
850
+ }
851
+
852
+ /**
853
+ * Updates shipping method
854
+ *
855
+ * @param array $shipping_methods available shipping methods array.
856
+ * @return void
857
+ */
858
+ public function update_shipping_method( $shipping_methods ) {
859
+ $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
860
+
861
+ if ( is_array( $shipping_methods ) ) {
862
+ foreach ( $shipping_methods as $i => $value ) {
863
+ $chosen_shipping_methods[ $i ] = wc_clean( $value );
864
+ }
865
+ }
866
+
867
+ WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods );
868
+ }
869
+ }
gateway/stripe/stripe-api.php CHANGED
@@ -8,9 +8,7 @@
8
 
9
  namespace CPSW\Gateway\Stripe;
10
 
11
- use CPSW\Inc\Traits\Get_Instance;
12
  use CPSW\Inc\Logger;
13
- use CPSW\Inc\Helper;
14
  use \Stripe\StripeClient;
15
 
16
  /**
@@ -18,8 +16,6 @@ use \Stripe\StripeClient;
18
  */
19
  class Stripe_Api {
20
 
21
- use Get_Instance;
22
-
23
  /**
24
  * Instance of Stripe
25
  *
@@ -33,7 +29,7 @@ class Stripe_Api {
33
  * @since 0.0.1
34
  */
35
  public function __construct() {
36
- $secret_key = ( 'live' === Helper::get_payment_mode() ) ? Helper::get_setting( 'cpsw_secret_key' ) : Helper::get_setting( 'cpsw_test_secret_key' );
37
  if ( ! empty( $secret_key ) ) {
38
  $this->stripe = new StripeClient(
39
  [
@@ -53,7 +49,19 @@ class Stripe_Api {
53
  * @return array
54
  */
55
  private function execute( $api, $method, $args ) {
 
 
 
 
 
 
 
 
 
 
56
  $error_message = false;
 
 
57
  try {
58
  $response = $this->stripe->{$api}->{$method}( ...$args );
59
  } catch ( \Stripe\Exception\CardException $e ) {
@@ -74,8 +82,8 @@ class Stripe_Api {
74
  $error_message = $e->getError()->message;
75
  } catch ( \Stripe\Exception\ApiConnectionException $e ) {
76
  // Network communication with Stripe failed.
77
- Logger::error( $e->getError()->message, true );
78
- $error_message = $e->getError()->message;
79
  } catch ( \Stripe\Exception\ApiErrorException $e ) {
80
  Logger::error( $e->getError()->message, true );
81
  $error_message = $e->getError()->message;
@@ -170,4 +178,15 @@ class Stripe_Api {
170
  public function accounts( $method, $args ) {
171
  return $this->execute( 'accounts', $method, $args );
172
  }
 
 
 
 
 
 
 
 
 
 
 
173
  }
8
 
9
  namespace CPSW\Gateway\Stripe;
10
 
 
11
  use CPSW\Inc\Logger;
 
12
  use \Stripe\StripeClient;
13
 
14
  /**
16
  */
17
  class Stripe_Api {
18
 
 
 
19
  /**
20
  * Instance of Stripe
21
  *
29
  * @since 0.0.1
30
  */
31
  public function __construct() {
32
+ $secret_key = apply_filters( 'cpsw_get_secret_key', ( 'live' === get_option( 'cpsw_mode' ) ) ? get_option( 'cpsw_secret_key' ) : get_option( 'cpsw_test_secret_key' ) );
33
  if ( ! empty( $secret_key ) ) {
34
  $this->stripe = new StripeClient(
35
  [
49
  * @return array
50
  */
51
  private function execute( $api, $method, $args ) {
52
+
53
+ if ( is_null( $this->stripe ) ) {
54
+ $error_message = __( 'Stripe not initialized', 'checkout-plugins-stripe-woo' );
55
+ Logger::error( $error_message, true );
56
+ return [
57
+ 'success' => false,
58
+ 'message' => $error_message,
59
+ ];
60
+ }
61
+
62
  $error_message = false;
63
+ $response = false;
64
+
65
  try {
66
  $response = $this->stripe->{$api}->{$method}( ...$args );
67
  } catch ( \Stripe\Exception\CardException $e ) {
82
  $error_message = $e->getError()->message;
83
  } catch ( \Stripe\Exception\ApiConnectionException $e ) {
84
  // Network communication with Stripe failed.
85
+ $error_message = is_null( $e->getError() ) ? $e->getMessage() : $e->getError()->message;
86
+ Logger::error( $error_message, true );
87
  } catch ( \Stripe\Exception\ApiErrorException $e ) {
88
  Logger::error( $e->getError()->message, true );
89
  $error_message = $e->getError()->message;
178
  public function accounts( $method, $args ) {
179
  return $this->execute( 'accounts', $method, $args );
180
  }
181
+
182
+ /**
183
+ * Executes Stripe apple pay domains query
184
+ *
185
+ * @param string $method method to be used.
186
+ * @param array $args parameter.
187
+ * @return array
188
+ */
189
+ public function apple_pay_domains( $method, $args ) {
190
+ return $this->execute( 'applePayDomains', $method, $args );
191
+ }
192
  }
inc/helper.php CHANGED
@@ -8,48 +8,22 @@
8
 
9
  namespace CPSW\Inc;
10
 
11
- use CPSW\Inc\Traits\Get_Instance;
12
- use CPSW\Inc\Logger;
13
- use \Stripe\StripeClient;
14
-
15
  /**
16
  * Stripe Webhook.
17
  */
18
  class Helper {
19
 
20
- use Get_Instance;
21
-
22
-
23
- /**
24
- * Instance of Stripe
25
- *
26
- * @var \Stripe\StripeClient
27
- */
28
- public static $stripe;
29
-
30
- /**
31
- * Options
32
- *
33
- * @var array
34
- */
35
- public static $options;
36
-
37
- /**
38
- * Options
39
- *
40
- * @var array
41
- */
42
- public static $global_settings;
43
-
44
  /**
45
- * Default values
46
  *
47
  * @var array
48
  */
49
- private static $defaults = [
50
  'woocommerce_cpsw_stripe_settings' => [
51
- 'inline_cc' => 'yes',
52
- 'allowed_cards' => [
 
 
53
  'mastercard',
54
  'visa',
55
  'diners',
@@ -58,18 +32,44 @@ class Helper {
58
  'jcb',
59
  'unionpay',
60
  ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  ],
62
- 'cpsw_test_pub_key' => '',
63
- 'cpsw_pub_key' => '',
64
- 'cpsw_test_secret_key' => '',
65
- 'cpsw_secret_key' => '',
66
- 'cpsw_test_con_status' => '',
67
- 'cpsw_con_status' => '',
68
- 'cpsw_mode' => '',
69
- 'cpsw_live_webhook_secret' => '',
70
- 'cpsw_test_webhook_secret' => '',
71
- 'cpsw_account_id' => '',
72
- 'cpsw_debug_log' => 'no',
 
 
 
 
 
 
 
 
73
  ];
74
 
75
  /**
@@ -78,56 +78,69 @@ class Helper {
78
  * @since 0.0.1
79
  */
80
  public function __construct() {
81
- foreach ( self::$defaults as $key => $default_data ) {
82
- $db_data = get_option( $key, [] );
83
- $settings_array = true;
84
- switch ( $key ) {
85
- case 'woocommerce_cpsw_stripe_settings':
86
- // check if empty secret key saved manually.
87
- if ( empty( $db_data['inline_cc'] ) ) {
88
- $db_data = [];
89
- }
90
- break;
91
- default:
92
- $settings_array = false;
93
- break;
94
- }
95
-
96
- if ( $settings_array ) {
97
- static::$options[ $key ] = array_merge( $default_data, $db_data );
98
- } else {
99
- static::$global_settings[ $key ] = empty( $db_data ) ? $default_data : $db_data;
100
- }
 
 
 
 
 
 
 
 
101
  }
102
 
103
- add_action( 'plugins_loaded', [ $this, 'load_stripe' ] );
104
- add_action( 'wp_ajax_cpsw_js_errors', [ $this, 'js_errors' ] );
 
105
  }
106
 
107
  /**
108
- * Stripe Instance
109
  *
110
- * @return void
 
 
111
  */
112
- public static function load_stripe() {
113
- $secret_key = ( 'live' === self::get_payment_mode() ) ? self::get_setting( 'cpsw_secret_key' ) : self::get_setting( 'cpsw_test_secret_key' );
114
- if ( ! empty( $secret_key ) ) {
115
- self::$stripe = new StripeClient(
116
- [
117
- 'api_key' => $secret_key,
118
- 'stripe_version' => '2020-08-27',
119
- ]
120
- );
121
  }
 
 
122
  }
123
 
124
  /**
125
- * Stripe get all settings
126
  *
127
- * @return $global_settings array It returns all stripe settings in an array.
 
128
  */
129
- public static function get_settings() {
130
- return apply_filters( 'cpsw_settings', self::$global_settings );
 
131
  }
132
 
133
  /**
@@ -140,17 +153,11 @@ class Helper {
140
  public static function get_setting( $key = '', $gateway = false ) {
141
  $result = false;
142
  if ( false !== $gateway ) {
143
- $options = self::$options; // To get all store settings with default options value like you already did in constructor.
144
- if ( $options && array_key_exists( $key, $options[ 'woocommerce_' . $gateway . '_settings' ] ) ) {
145
- $result = $options[ 'woocommerce_' . $gateway . '_settings' ][ $key ];
146
- }
147
  } else {
148
- $settings = self::get_settings(); // To get all store settings with default value like you already did in constructor.
149
- if ( $settings && array_key_exists( $key, $settings ) ) {
150
- $result = $settings[ $key ];
151
- }
152
  }
153
- return $result ? apply_filters( $key, $result ) : false;
154
  }
155
 
156
  /**
@@ -161,24 +168,4 @@ class Helper {
161
  public static function get_payment_mode() {
162
  return apply_filters( 'cpsw_payment_mode', self::get_setting( 'cpsw_mode' ) );
163
  }
164
-
165
- /**
166
- * Logs js errors
167
- *
168
- * @return json
169
- */
170
- public function js_errors() {
171
- if ( ! isset( $_POST['_security'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['_security'] ), 'cpsw_js_error_nonce' ) ) {
172
- return wp_send_json_error( [ 'message' => __( 'Invalid Nonce', 'checkout-plugins-stripe-woo' ) ] );
173
- }
174
-
175
- if ( isset( $_POST['error'] ) ) {
176
- $error = $_POST['error'];
177
- $error_message = $error['message'] . ' (' . $error['type'] . ')';
178
- Logger::error( $error_message, true );
179
- return wp_send_json_success( [ 'message' => $error_message ] );
180
- }
181
- exit();
182
- }
183
-
184
  }
8
 
9
  namespace CPSW\Inc;
10
 
 
 
 
 
11
  /**
12
  * Stripe Webhook.
13
  */
14
  class Helper {
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  /**
17
+ * Default gateway values
18
  *
19
  * @var array
20
  */
21
+ private static $gateway_defaults = [
22
  'woocommerce_cpsw_stripe_settings' => [
23
+ 'enabled' => 'no',
24
+ 'inline_cc' => 'yes',
25
+ 'order_status' => '',
26
+ 'allowed_cards' => [
27
  'mastercard',
28
  'visa',
29
  'diners',
32
  'jcb',
33
  'unionpay',
34
  ],
35
+ 'express_checkout_location' => [
36
+ 'product',
37
+ 'cart',
38
+ 'checkout',
39
+ ],
40
+ 'express_checkout_enabled' => 'no',
41
+ 'express_checkout_button_type' => 'default',
42
+ 'express_checkout_button_theme' => 'dark',
43
+ 'express_checkout_button_height' => '40',
44
+ 'express_checkout_title' => 'Express Checkout',
45
+ 'express_checkout_tagline' => 'Checkout quickly to use you favourite tool.',
46
+ 'express_checkout_product_page_position' => 'above',
47
+ 'express_checkout_product_sticky_footer' => 'yes',
48
+ 'express_checkout_separator' => 'OR',
49
+ 'express_checkout_product_checkout_page' => 'no',
50
+ 'express_checkout_button_width' => '',
51
+ 'express_checkout_button_alignment' => 'left',
52
+ 'express_checkout_separator_checkout' => 'OR',
53
  ],
54
+ ];
55
+
56
+ /**
57
+ * Default global values
58
+ *
59
+ * @var array
60
+ */
61
+ private static $global_defaults = [
62
+ 'cpsw_test_pub_key' => '',
63
+ 'cpsw_pub_key' => '',
64
+ 'cpsw_test_secret_key' => '',
65
+ 'cpsw_secret_key' => '',
66
+ 'cpsw_test_con_status' => '',
67
+ 'cpsw_con_status' => '',
68
+ 'cpsw_mode' => 'test',
69
+ 'cpsw_live_webhook_secret' => '',
70
+ 'cpsw_test_webhook_secret' => '',
71
+ 'cpsw_account_id' => '',
72
+ 'cpsw_debug_log' => 'no',
73
  ];
74
 
75
  /**
78
  * @since 0.0.1
79
  */
80
  public function __construct() {
81
+ }
82
+
83
+ /**
84
+ * Stripe get all settings
85
+ *
86
+ * @return $global_settings array It returns all stripe settings in an array.
87
+ */
88
+ public static function get_settings() {
89
+ $response = [];
90
+ foreach ( self::$global_defaults as $key => $default_data ) {
91
+ $response[ $key ] = self::get_global_setting( $key );
92
+ }
93
+ return apply_filters( 'cpsw_settings', $response );
94
+ }
95
+
96
+ /**
97
+ * Get all settings of a particular gateway
98
+ *
99
+ * @param string $gateway gateway id.
100
+ * @return array
101
+ */
102
+ public static function get_gateway_settings( $gateway = 'cpsw_stripe' ) {
103
+ $default_settings = [];
104
+ $setting_name = 'woocommerce_' . $gateway . '_settings';
105
+ $saved_settings = get_option( $setting_name, [] );
106
+
107
+ if ( isset( self::$gateway_defaults[ $setting_name ] ) ) {
108
+ $default_settings = self::$gateway_defaults[ $setting_name ];
109
  }
110
 
111
+ $settings = array_merge( $default_settings, $saved_settings );
112
+
113
+ return apply_filters( 'cpsw_gateway_settings', $settings );
114
  }
115
 
116
  /**
117
+ * Get value of gateway option parameter
118
  *
119
+ * @param string $key key name.
120
+ * @param string $gateway gateway id.
121
+ * @return mixed
122
  */
123
+ public static function get_gateway_setting( $key = '', $gateway = 'cpsw_stripe' ) {
124
+ $settings = self::get_gateway_settings( $gateway );
125
+
126
+ $value = false;
127
+
128
+ if ( isset( $settings[ $key ] ) ) {
129
+ $value = $settings[ $key ];
 
 
130
  }
131
+
132
+ return $value;
133
  }
134
 
135
  /**
136
+ * Get value of global option
137
  *
138
+ * @param string $key value of global setting.
139
+ * @return mixed
140
  */
141
+ public static function get_global_setting( $key ) {
142
+ $db_data = get_option( $key );
143
+ return $db_data ? $db_data : self::$global_defaults[ $key ];
144
  }
145
 
146
  /**
153
  public static function get_setting( $key = '', $gateway = false ) {
154
  $result = false;
155
  if ( false !== $gateway ) {
156
+ $result = self::get_gateway_setting( $key, $gateway );
 
 
 
157
  } else {
158
+ $result = self::get_global_setting( $key );
 
 
 
159
  }
160
+ return is_array( $result ) || $result ? apply_filters( $key, $result ) : false;
161
  }
162
 
163
  /**
168
  public static function get_payment_mode() {
169
  return apply_filters( 'cpsw_payment_mode', self::get_setting( 'cpsw_mode' ) );
170
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  }
inc/traits/subscriptions.php CHANGED
@@ -8,9 +8,8 @@
8
  namespace CPSW\Inc\Traits;
9
 
10
  use CPSW\Inc\Traits\Subscription_Helper as SH;
11
- use CPSW\Inc\Helper;
12
  use CPSW\Inc\Logger;
13
- use WC_Payment_Tokens;
14
  use WC_Emails;
15
  use WC_Subscriptions_Change_Payment_Gateway;
16
  use Exception;
@@ -316,8 +315,9 @@ trait Subscriptions {
316
 
317
  $order_id = $order->get_id();
318
  Logger::info( "Stripe Payment initiated for order $order_id" );
319
- $response = $this->stripe_api->payment_intents( 'create', [ $request ] );
320
- $intent = $response['success'] ? $response['data'] : false;
 
321
 
322
  if ( $intent ) {
323
  $intent_data = [
@@ -409,8 +409,9 @@ trait Subscriptions {
409
  }
410
 
411
  if ( isset( $_POST['paymentMethod'] ) ) {
412
- $response = $this->stripe_api->setup_intents( 'create', [ [ 'payment_method_types' => [ 'card' ] ] ] );
413
- $response = $response['success'] ? $response['data'] : false;
 
414
  return wp_send_json_success( [ 'client_secret' => $response->client_secret ] );
415
  }
416
  exit();
@@ -452,7 +453,8 @@ trait Subscriptions {
452
 
453
  if ( $source_id ) {
454
  $stripe_source = $source_id;
455
- $response = $this->stripe_api->payment_methods( 'retrieve', [ $stripe_source ] );
 
456
  $source_object = $response['success'] ? $response['data'] : false;
457
  } elseif ( apply_filters( 'cpsw_use_default_customer_source', true ) ) {
458
  // Attempting with empty source id.
@@ -805,7 +807,8 @@ trait Subscriptions {
805
  // New CC info was entered and we have a new source to process.
806
  if ( ! empty( $_POST['payment_method_created'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing
807
  $stripe_source = wc_clean( wp_unslash( $_POST['payment_method_created'] ) ); //phpcs:ignore WordPress.Security.NonceVerification.Missing
808
- $response = $this->stripe_api->payment_methods( 'retrieve', [ $stripe_source ] );
 
809
  $source_object = $response['success'] ? $response['data'] : false;
810
  $source_id = $source_object->id;
811
 
@@ -814,7 +817,7 @@ trait Subscriptions {
814
 
815
  // Either saved card is enabled or forced by flow.
816
  if ( ( $user_id && $this->enable_saved_cards && $maybe_saved_card && 'reusable' === $source_object->usage ) || $force_save_source ) {
817
- $this->stripe_api->payment_methods( 'attach', [ $source_id, [ 'customer' => $customer_id ] ] );
818
  $this->create_payment_token_for_user( $user_id, $source_object );
819
  if ( ! empty( $response->error ) ) {
820
  throw new Exception( print_r( $response, true ), $this->get_localized_error_message_from_response( $response ) ); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
8
  namespace CPSW\Inc\Traits;
9
 
10
  use CPSW\Inc\Traits\Subscription_Helper as SH;
11
+ use CPSW\Gateway\Stripe\Stripe_Api;
12
  use CPSW\Inc\Logger;
 
13
  use WC_Emails;
14
  use WC_Subscriptions_Change_Payment_Gateway;
15
  use Exception;
315
 
316
  $order_id = $order->get_id();
317
  Logger::info( "Stripe Payment initiated for order $order_id" );
318
+ $stripe_api = new Stripe_Api();
319
+ $response = $stripe_api->payment_intents( 'create', [ $request ] );
320
+ $intent = $response['success'] ? $response['data'] : false;
321
 
322
  if ( $intent ) {
323
  $intent_data = [
409
  }
410
 
411
  if ( isset( $_POST['paymentMethod'] ) ) {
412
+ $stripe_api = new Stripe_Api();
413
+ $response = $stripe_api->setup_intents( 'create', [ [ 'payment_method_types' => [ 'card' ] ] ] );
414
+ $response = $response['success'] ? $response['data'] : false;
415
  return wp_send_json_success( [ 'client_secret' => $response->client_secret ] );
416
  }
417
  exit();
453
 
454
  if ( $source_id ) {
455
  $stripe_source = $source_id;
456
+ $stripe_api = new Stripe_Api();
457
+ $response = $stripe_api->payment_methods( 'retrieve', [ $stripe_source ] );
458
  $source_object = $response['success'] ? $response['data'] : false;
459
  } elseif ( apply_filters( 'cpsw_use_default_customer_source', true ) ) {
460
  // Attempting with empty source id.
807
  // New CC info was entered and we have a new source to process.
808
  if ( ! empty( $_POST['payment_method_created'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing
809
  $stripe_source = wc_clean( wp_unslash( $_POST['payment_method_created'] ) ); //phpcs:ignore WordPress.Security.NonceVerification.Missing
810
+ $stripe_api = new Stripe_Api();
811
+ $response = $stripe_api->payment_methods( 'retrieve', [ $stripe_source ] );
812
  $source_object = $response['success'] ? $response['data'] : false;
813
  $source_id = $source_object->id;
814
 
817
 
818
  // Either saved card is enabled or forced by flow.
819
  if ( ( $user_id && $this->enable_saved_cards && $maybe_saved_card && 'reusable' === $source_object->usage ) || $force_save_source ) {
820
+ $stripe_api->payment_methods( 'attach', [ $source_id, [ 'customer' => $customer_id ] ] );
821
  $this->create_payment_token_for_user( $user_id, $source_object );
822
  if ( ! empty( $response->error ) ) {
823
  throw new Exception( print_r( $response, true ), $this->get_localized_error_message_from_response( $response ) ); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: brainstormforce
3
  Tags: stripe, credit card
4
  Requires at least: 5.4
5
  Tested up to: 5.8
6
- Stable tag: 1.0.0
7
  Requires PHP: 5.6
8
  License: GPLv2 or later
9
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -36,12 +36,27 @@ The plugin supports WooCommerce Subscriptions where users can make recurring pay
36
  = Does this plugin work with WooCommerce Subscriptions? =
37
  Yes, the plugin supports all the functionality of WooCommerce Subscriptions.
38
 
 
 
 
 
 
 
 
 
 
 
39
  == Screenshots ==
40
 
41
  1. API Settings
42
  2. Card Payments Settings
 
 
43
 
44
  == Changelog ==
45
 
 
 
 
46
  = 1.0.0 – TUESDAY, 23RD NOVEMBER 2021 =
47
  * Initial release
3
  Tags: stripe, credit card
4
  Requires at least: 5.4
5
  Tested up to: 5.8
6
+ Stable tag: 1.1.0
7
  Requires PHP: 5.6
8
  License: GPLv2 or later
9
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
36
  = Does this plugin work with WooCommerce Subscriptions? =
37
  Yes, the plugin supports all the functionality of WooCommerce Subscriptions.
38
 
39
+ = I have an existing subscription with another Stripe Payment Gateway plugin. How can I switch to your plugin without losing my existing subscriptions? =
40
+ You can easily switch to Stripe for WooCommerce from your existing plugin without losing your subscription.
41
+
42
+ Here are easy steps
43
+ 1. Install our plugin like any other WordPress plugin
44
+ 2. Follow the documentation [here](https://checkoutplugins.com/docs/stripe-api-settings/) to setup the stripe account keys
45
+ 3. Enable Stripe payment method from our plugin. This allows all your future transactions to be processed through the new Stripe gateway you set with our plugin.
46
+ 4. Disable the existing Stripe method from WooCommerce payment settings.
47
+ NOTE: NOT to deactivate your old payment gateway plugin. Your old subscription's renewal will be processed through the old plugin automatically.
48
+
49
  == Screenshots ==
50
 
51
  1. API Settings
52
  2. Card Payments Settings
53
+ 3. Express Checkout - Admin Settings
54
+ 4. Express Checkout Button on Checkout Page
55
 
56
  == Changelog ==
57
 
58
+ = 1.1.0 – TUESDAY, 21ST DECEMBER 2021 =
59
+ * New: Express Checkout
60
+
61
  = 1.0.0 – TUESDAY, 23RD NOVEMBER 2021 =
62
  * Initial release