Stripe Payments For WooCommerce by Checkout Plugins - Version 1.2.0

Version Description

TUESDAY, 4TH JANUARY 2022 = * New: Supports Alipay payment method. * New: Supports iDEAL payment method. * Improvement: More customization options for Express Checkout. * Improvement: Webhook integration for multiple events - charge.refunded, charge.dispute.created, charge.dispute.closed, payment_intent.succeeded, payment_intent.amount_capturable_updated, payment_intent.payment_failed, review.opened, review.closed.

Download this release

Release Info

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

Code changes from version 1.1.1 to 1.2.0

admin/admin-controller.php CHANGED
@@ -77,6 +77,8 @@ class Admin_Controller {
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
  }
@@ -106,6 +108,7 @@ class Admin_Controller {
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' ] );
@@ -132,6 +135,7 @@ class Admin_Controller {
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
  ];
@@ -154,8 +158,9 @@ class Admin_Controller {
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
  }
@@ -196,12 +201,15 @@ class Admin_Controller {
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' );
 
 
202
  wp_enqueue_style( 'cpsw-admin-style' );
203
 
204
- wp_register_script( 'cpsw-admin-js', plugins_url( 'assets/js/admin.js', __FILE__ ), [ 'jquery' ], CPSW_VERSION, true );
205
  wp_enqueue_script( 'cpsw-admin-js' );
206
 
207
  wp_localize_script(
@@ -223,18 +231,19 @@ class Admin_Controller {
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' );
@@ -457,6 +466,7 @@ class Admin_Controller {
457
  <?php
458
  }
459
 
 
460
  /**
461
  * This method is used to display block for Stripe webhook url.
462
  *
@@ -1264,10 +1274,8 @@ class Admin_Controller {
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 );
@@ -1335,12 +1343,13 @@ class Admin_Controller {
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' => [
@@ -1354,7 +1363,7 @@ class Admin_Controller {
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' ),
@@ -1366,7 +1375,7 @@ class Admin_Controller {
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' ),
@@ -1380,7 +1389,7 @@ class Admin_Controller {
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' ),
@@ -1393,7 +1402,7 @@ class Admin_Controller {
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
  ],
@@ -1401,7 +1410,7 @@ class Admin_Controller {
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
  ],
@@ -1420,11 +1429,18 @@ class Admin_Controller {
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' ),
@@ -1432,10 +1448,20 @@ class Admin_Controller {
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'],
@@ -1447,7 +1473,7 @@ class Admin_Controller {
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' => [
@@ -1456,12 +1482,25 @@ class Admin_Controller {
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
  ],
@@ -1470,7 +1509,7 @@ class Admin_Controller {
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
  ],
@@ -1488,7 +1527,7 @@ class Admin_Controller {
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' ),
@@ -1502,7 +1541,7 @@ class Admin_Controller {
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
  ],
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
+ 'cpsw_alipay' => __( 'Alipay', 'checkout-plugins-stripe-woo' ),
81
+ 'cpsw_ideal' => __( 'iDEAL', 'checkout-plugins-stripe-woo' ),
82
  ];
83
 
84
  }
108
  add_action( 'wp_ajax_cpsw_test_stripe_connection', [ $this, 'connection_test' ] );
109
  add_action( 'wp_ajax_cpsw_disconnect_account', [ $this, 'disconnect_account' ] );
110
  add_action( 'wp_ajax_cpsw_js_errors', [ $this, 'js_errors' ] );
111
+ add_action( 'wp_ajax_nopriv_cpsw_js_errors', [ $this, 'js_errors' ] );
112
 
113
  add_action( 'woocommerce_settings_save_cpsw_api_settings', [ $this, 'check_connection_on_updates' ] );
114
  add_filter( 'woocommerce_save_settings_checkout_cpsw_express_checkout', [ $this, 'cpsw_express_checkout_option_updates' ] );
135
  $express_checkout = [];
136
  $radio_checkbox = [
137
  'express_checkout_enabled' => 'no',
138
+ 'express_checkout_product_options' => 'no',
139
  'express_checkout_product_sticky_footer' => 'no',
140
  'express_checkout_product_checkout_page' => 'no',
141
  ];
158
  }
159
 
160
  if ( ! empty( $express_checkout ) ) {
161
+ $cpsw_stripe = get_option( 'woocommerce_cpsw_stripe_settings' );
162
+ $cpsw_stripe['express_checkout_location'] = [];
163
+ $cpsw_stripe = array_merge( $cpsw_stripe, $radio_checkbox, $express_checkout );
164
  update_option( 'woocommerce_cpsw_stripe_settings', $cpsw_stripe );
165
  }
166
  }
201
  * @since 0.0.1
202
  */
203
  public function enqueue_scripts() {
204
+ $version = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? time() : CPSW_VERSION;
205
+ $allow_scripts_methods = [ 'cpsw_stripe', 'cpsw_alipay', 'cpsw_ideal' ];
206
 
207
+ if ( isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] && isset( $_GET['tab'] ) && ( 'cpsw_api_settings' === $_GET['tab'] || isset( $_GET['section'] ) && ( in_array( sanitize_text_field( $_GET['section'] ), $allow_scripts_methods, true ) ) ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
208
+
209
+ wp_register_style( 'cpsw-admin-style', plugins_url( 'assets/css/admin.css', __FILE__ ), [], $version, 'all' );
210
  wp_enqueue_style( 'cpsw-admin-style' );
211
 
212
+ wp_register_script( 'cpsw-admin-js', plugins_url( 'assets/js/admin.js', __FILE__ ), [ 'jquery' ], $version, true );
213
  wp_enqueue_script( 'cpsw-admin-js' );
214
 
215
  wp_localize_script(
231
  'is_connected' => $this->is_stripe_connected(),
232
  'is_manually_connected' => isset( $_GET['connect'] ) ? sanitize_text_field( $_GET['connect'] ) : '', //phpcs:ignore WordPress.Security.NonceVerification.Recommended
233
  'cpsw_admin_settings_tab' => isset( $_GET['tab'] ) ? sanitize_text_field( $_GET['tab'] ) : '', //phpcs:ignore WordPress.Security.NonceVerification.Recommended
234
+ 'cpsw_admin_current_page' => isset( $_GET['section'] ) ? sanitize_text_field( $_GET['section'] ) : '', //phpcs:ignore WordPress.Security.NonceVerification.Recommended
235
  ]
236
  );
237
  }
238
 
239
  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
240
+ wp_register_style( 'cpsw-express-checkout-style', plugins_url( 'assets/css/express-checkout.css', __FILE__ ), [], $version, 'all' );
241
  wp_enqueue_style( 'cpsw-express-checkout-style' );
242
 
243
+ wp_register_script( 'cpsw-stripe-external', 'https://js.stripe.com/v3/', [], $version, true );
244
  wp_enqueue_script( 'cpsw-stripe-external' );
245
 
246
+ wp_register_script( 'cpsw-express-checkout-js', plugins_url( 'assets/js/express-checkout.js', __FILE__ ), [ 'jquery', 'cpsw-stripe-external' ], $version, true );
247
  wp_enqueue_script( 'cpsw-express-checkout-js' );
248
 
249
  $public_key = ( 'live' === Helper::get_payment_mode() ) ? Helper::get_setting( 'cpsw_pub_key' ) : Helper::get_setting( 'cpsw_test_pub_key' );
466
  <?php
467
  }
468
 
469
+
470
  /**
471
  * This method is used to display block for Stripe webhook url.
472
  *
1274
  * @return array $settings_tab Settings tabs array returned.
1275
  */
1276
  public function add_settings_links( $settings_tab ) {
1277
+ if ( isset( $_GET['section'] ) && 0 === strpos( $_GET['section'], 'cpsw_' ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
1278
+ $settings_tab = array_merge( $settings_tab, $this->navigation );
 
 
1279
  }
1280
  array_shift( $settings_tab );
1281
  return apply_filters( 'cpsw_setting_tabs', $settings_tab );
1343
  ],
1344
  ];
1345
  } else {
1346
+ // Default values need to be set in Helper class.
1347
  $settings = [
1348
  'section_title' => [
1349
  'name' => __( 'Express Checkout', 'checkout-plugins-stripe-woo' ),
1350
  'type' => 'title',
1351
  /* translators: HTML Markup*/
1352
+ '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>' ),
1353
  'id' => 'cpsw_express_checkout',
1354
  ],
1355
  'enable' => [
1363
  'type' => 'multiselect',
1364
  'class' => 'cpsw_express_checkout_location',
1365
  'id' => 'cpsw_express_checkout_location',
1366
+ 'desc_tip' => __( 'Choose page to display Express Checkout buttons.', 'checkout-plugins-stripe-woo' ),
1367
  'options' => [
1368
  'product' => __( 'Product', 'checkout-plugins-stripe-woo' ),
1369
  'cart' => __( 'Cart', 'checkout-plugins-stripe-woo' ),
1375
  'title' => __( 'Button type', 'checkout-plugins-stripe-woo' ),
1376
  'type' => 'select',
1377
  'id' => 'cpsw_express_checkout_button_type',
1378
+ 'desc' => __( 'Select label for Express Checkout button. Unfortunately, we can\'t customize the label because of the restrictions from Stripe.', 'checkout-plugins-stripe-woo' ),
1379
  'value' => $values['express_checkout_button_type'],
1380
  'options' => [
1381
  'default' => __( 'Default', 'checkout-plugins-stripe-woo' ),
1389
  'title' => __( 'Button theme', 'checkout-plugins-stripe-woo' ),
1390
  'type' => 'select',
1391
  'id' => 'cpsw_express_checkout_button_theme',
1392
+ 'desc' => __( 'Select theme for Express Checkout button.', 'checkout-plugins-stripe-woo' ),
1393
  'value' => $values['express_checkout_button_theme'],
1394
  'options' => [
1395
  'dark' => __( 'Dark', 'checkout-plugins-stripe-woo' ),
1402
  'title' => __( 'Button height', 'checkout-plugins-stripe-woo' ),
1403
  'type' => 'number',
1404
  'id' => 'cpsw_express_checkout_button_height',
1405
+ 'desc' => __( 'Select height for Express Checkout button (in px). Button height can be between 35px to 60px. Default height 40px.', 'checkout-plugins-stripe-woo' ),
1406
  'value' => $values['express_checkout_button_height'],
1407
  'desc_tip' => true,
1408
  ],
1410
  'title' => __( 'Separator text', 'checkout-plugins-stripe-woo' ),
1411
  'type' => 'text',
1412
  'id' => 'cpsw_express_checkout_separator',
1413
+ 'desc' => __( 'Add separator text for the Express Checkout button. This will help to distinguish between Express Checkout and other buttons.', 'checkout-plugins-stripe-woo' ),
1414
  'value' => $values['express_checkout_separator'],
1415
  'desc_tip' => true,
1416
  ],
1429
  'desc' => __( 'Advanced customization options for product page.', 'checkout-plugins-stripe-woo' ),
1430
  'id' => 'cpsw_express_checkout_product_page',
1431
  ],
1432
+ 'product_page_options' => [
1433
+ 'title' => __( 'Advanced options', 'checkout-plugins-stripe-woo' ),
1434
+ 'type' => 'checkbox',
1435
+ 'id' => 'cpsw_express_checkout_product_options',
1436
+ 'value' => $values['express_checkout_product_options'],
1437
+ ],
1438
+ 'product_button_position' => [
1439
  'title' => __( 'Button position', 'checkout-plugins-stripe-woo' ),
1440
  'type' => 'select',
1441
  'id' => 'cpsw_express_checkout_product_page_position',
1442
+ 'class' => 'cpsw_product_options',
1443
+ 'desc' => __( 'Select the position of Express Checkout button. This option will work only for Product page.', 'checkout-plugins-stripe-woo' ),
1444
  'value' => $values['express_checkout_product_page_position'],
1445
  'options' => [
1446
  'above' => __( 'Above Add to Cart', 'checkout-plugins-stripe-woo' ),
1448
  ],
1449
  'desc_tip' => true,
1450
  ],
1451
+ 'product_button_width' => [
1452
+ 'title' => __( 'Button width', 'checkout-plugins-stripe-woo' ),
1453
+ 'type' => 'number',
1454
+ 'class' => 'cpsw_product_options',
1455
+ 'id' => 'cpsw_express_checkout_product_button_width',
1456
+ 'desc' => __( 'Select width for button (in px). Minimum width should be 150px, Default width 100%', 'checkout-plugins-stripe-woo' ),
1457
+ 'value' => $values['express_checkout_product_button_width'],
1458
+ 'desc_tip' => true,
1459
+ ],
1460
  'sticky_footer' => [
1461
  'name' => __( 'Responsive behaviour', 'checkout-plugins-stripe-woo' ),
1462
  /* translators: HTML Markup*/
1463
+ 'desc' => sprintf( __( 'If checked the Express Checkout button will stick%1$1sat bottom of screen on responsive devices.', 'checkout-plugins-stripe-woo' ), '<br/>' ),
1464
+ 'class' => 'cpsw_product_options',
1465
  'type' => 'checkbox',
1466
  'id' => 'cpsw_express_checkout_product_sticky_footer',
1467
  'value' => $values['express_checkout_product_sticky_footer'],
1473
  'checkout_page_section_title' => [
1474
  'name' => __( 'Checkout page options', 'checkout-plugins-stripe-woo' ),
1475
  'type' => 'title',
1476
+ 'desc' => __( 'Advanced customization options for Checkout page.', 'checkout-plugins-stripe-woo' ),
1477
  'id' => 'cpsw_express_checkout_product_page',
1478
  ],
1479
  'checkout_page_options' => [
1482
  'id' => 'cpsw_express_checkout_product_checkout_page',
1483
  'value' => $values['express_checkout_product_checkout_page'],
1484
  ],
1485
+ 'checkout_button_position' => [
1486
+ 'title' => __( 'Button position', 'checkout-plugins-stripe-woo' ),
1487
+ 'type' => 'select',
1488
+ 'class' => 'cpsw_checkout_options',
1489
+ 'id' => 'cpsw_express_checkout_checkout_page_position',
1490
+ 'desc' => __( 'Select the position of Express Checkout button. This option will work only for Checkout page.', 'checkout-plugins-stripe-woo' ),
1491
+ 'value' => $values['express_checkout_checkout_page_position'],
1492
+ 'options' => [
1493
+ 'above-checkout' => __( 'Above checkout form', 'checkout-plugins-stripe-woo' ),
1494
+ 'above-billing' => __( 'Above billing details', 'checkout-plugins-stripe-woo' ),
1495
+ ],
1496
+ 'desc_tip' => true,
1497
+ ],
1498
  'title' => [
1499
  'title' => __( 'Title', 'checkout-plugins-stripe-woo' ),
1500
  'type' => 'text',
1501
  'class' => 'cpsw_checkout_options',
1502
  'id' => 'cpsw_express_checkout_title',
1503
+ 'desc' => __( 'Add a title above Express Checkout button on Checkout page.', 'checkout-plugins-stripe-woo' ),
1504
  'value' => $values['express_checkout_title'],
1505
  'desc_tip' => true,
1506
  ],
1509
  'type' => 'text',
1510
  'class' => 'cpsw_checkout_options',
1511
  'id' => 'cpsw_express_checkout_tagline',
1512
+ 'desc' => __( 'Add a tagline below the title on Checkout page.', 'checkout-plugins-stripe-woo' ),
1513
  'value' => $values['express_checkout_tagline'],
1514
  'desc_tip' => true,
1515
  ],
1527
  'type' => 'select',
1528
  'class' => 'cpsw_checkout_options',
1529
  'id' => 'cpsw_express_checkout_button_alignment',
1530
+ 'desc' => __( 'This setting will align title, tagline and button based on selection on Checkout page.', 'checkout-plugins-stripe-woo' ),
1531
  'value' => $values['express_checkout_button_alignment'],
1532
  'options' => [
1533
  'left' => __( 'Left', 'checkout-plugins-stripe-woo' ),
1541
  'type' => 'text',
1542
  'class' => 'cpsw_checkout_options',
1543
  'id' => 'cpsw_express_checkout_separator_checkout',
1544
+ 'desc' => __( 'Add separator text for Checkout page. If empty will show default separator text.', 'checkout-plugins-stripe-woo' ),
1545
  'value' => $values['express_checkout_separator_checkout'],
1546
  'desc_tip' => true,
1547
  ],
admin/assets/css/express-checkout.css CHANGED
@@ -5,9 +5,6 @@
5
 
6
  .cpsw_floating_preview {
7
  width: 400px;
8
- position: fixed;
9
- left: 900px;
10
- top: 300px;
11
  }
12
 
13
  .cpsw_express_checkout_preview {
@@ -21,7 +18,6 @@
21
  }
22
 
23
  .cpsw_preview_tagline {
24
- font-style: italic;
25
  margin-bottom: 1em;
26
  }
27
 
5
 
6
  .cpsw_floating_preview {
7
  width: 400px;
 
 
 
8
  }
9
 
10
  .cpsw_express_checkout_preview {
18
  }
19
 
20
  .cpsw_preview_tagline {
 
21
  margin-bottom: 1em;
22
  }
23
 
admin/assets/js/admin.js CHANGED
@@ -193,4 +193,67 @@
193
  $( document ).ready( function() {
194
  $( '.cpsw_select_woo' ).selectWoo();
195
  } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  }( jQuery ) );
193
  $( document ).ready( function() {
194
  $( '.cpsw_select_woo' ).selectWoo();
195
  } );
196
+
197
+ const CPSWAdminPaymentSettings = {
198
+ init() {
199
+ $( '[name^="woocommerce_' + cpsw_ajax_object.cpsw_admin_current_page + '_allowed_countries"]' ).on( 'change', this.display_children );
200
+ $( '[name^="cpsw_mode"]' ).on( 'change', this.show_hide_webhook_secret );
201
+
202
+ this.display_children();
203
+ this.show_hide_webhook_secret();
204
+ },
205
+
206
+ /**
207
+ * Show hide webhook secret
208
+ */
209
+ show_hide_webhook_secret() {
210
+ const mode = $( '#cpsw_mode' ).val();
211
+
212
+ if ( 'test' === mode ) {
213
+ $( '#cpsw_test_webhook_secret' ).parents( 'tr' ).show();
214
+ $( '#cpsw_live_webhook_secret' ).parents( 'tr' ).hide();
215
+ } else if ( 'live' === mode ) {
216
+ $( '#cpsw_test_webhook_secret' ).parents( 'tr' ).hide();
217
+ $( '#cpsw_live_webhook_secret' ).parents( 'tr' ).show();
218
+ }
219
+ },
220
+
221
+ /**
222
+ * Show hide country dorpdown
223
+ */
224
+ display_children() {
225
+ $( '[data-show-if]' ).each( function( i, el ) {
226
+ const $this = $( el );
227
+ const values = $this.data( 'show-if' );
228
+ const hidden = [];
229
+ $.each( values, function( k, v ) {
230
+ const $key = $( '#woocommerce_' + cpsw_ajax_object.cpsw_admin_current_page + '_allowed_countries' );
231
+ if ( hidden.indexOf( $this.attr( 'id' ) ) === -1 ) {
232
+ if ( $key.is( ':checkbox' ) ) {
233
+ if ( $key.is( ':checked' ) === v ) {
234
+ $this.closest( 'tr' ).show();
235
+ } else {
236
+ $this.closest( 'tr' ).hide();
237
+ hidden.push( $this.attr( 'id' ) );
238
+ }
239
+ } else {
240
+ if ( $key.val() === v ) {
241
+ $this.closest( 'tr' ).show();
242
+ } else {
243
+ $this.closest( 'tr' ).hide();
244
+ hidden.push( $this.attr( 'id' ) );
245
+ }
246
+ }
247
+ } else {
248
+ $this.closest( 'tr' ).hide();
249
+ hidden.push( $this.attr( 'id' ) );
250
+ }
251
+ }.bind( this ) );
252
+ }.bind( this ) );
253
+ },
254
+ };
255
+
256
+ $( function() {
257
+ CPSWAdminPaymentSettings.init();
258
+ } );
259
  }( jQuery ) );
admin/assets/js/express-checkout.js CHANGED
@@ -44,12 +44,13 @@
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() } );
@@ -68,8 +69,7 @@
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 );
@@ -87,18 +87,28 @@
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
  }
@@ -137,10 +147,15 @@
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 ) {
@@ -148,6 +163,36 @@
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() {
@@ -158,13 +203,17 @@
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
 
44
 
45
  if ( $( '.cpsw_express_checkout_preview_wrapper .cpsw_express_checkout_preview' ).length > 0 ) {
46
  $( '.cpsw_button_preview_label' ).css( { display: 'block' } );
47
+ $( '.cpsw_preview_notice' ).css( { display: 'block' } );
48
  $( '.cpsw_express_checkout_preview_wrapper .cpsw_express_checkout_preview' ).fadeIn();
49
  prButton.mount( '.cpsw_express_checkout_preview_wrapper .cpsw_express_checkout_preview' );
50
  $( '.cpsw_preview_title' ).html( $( '#cpsw_express_checkout_title' ).val() );
51
  $( '.cpsw_preview_tagline' ).html( $( '#cpsw_express_checkout_tagline' ).val() );
52
  if ( $( '#cpsw_express_checkout_product_checkout_page' ).is( ':checked' ) ) {
53
+ const buttonWidth = $( '#cpsw_express_checkout_button_width' ).val() ? $( '#cpsw_express_checkout_button_width' ).val() + 'px' : '100%';
54
  $( '.cpsw_express_checkout_preview' ).width( buttonWidth );
55
 
56
  $( '.cpsw_express_checkout_preview_wrapper' ).css( { textAlign: $( '#cpsw_express_checkout_button_alignment' ).val() } );
69
  function addCheckoutPreviewElement() {
70
  removeCheckoutPreviewElement();
71
  $( '.cpsw_express_checkout_preview_wrapper' ).prepend( '<h3 class="cpsw_preview_title"></h3><p class="cpsw_preview_tagline"></p>' );
72
+ $( '.cpsw_express_checkout_preview_wrapper' ).after( '<p class="cpsw_preview_notice">NOTE: Title and Tagline appears only on Checkout page.</p>' );
 
73
 
74
  const buttonWidth = $( '#cpsw_express_checkout_button_width' ).val() + 'px';
75
  $( '.cpsw_express_checkout_preview' ).width( buttonWidth );
87
  $( '.cpsw_express_checkout_preview' ).css( { margin: '0 auto', float: 'none', width: '100%' } );
88
  }
89
 
90
+ function toggleOptions( page = 'checkout' ) {
91
+ let handler = '#cpsw_express_checkout_product_checkout_page';
92
+ let options = '.cpsw_checkout_options';
93
+ if ( 'product' === page ) {
94
+ handler = '#cpsw_express_checkout_product_options';
95
+ options = '.cpsw_product_options';
96
+ }
97
+ if ( ! $( handler ).is( ':checked' ) ) {
98
+ $( options ).each( function() {
99
  $( this ).parents( 'tr' ).hide();
100
+ if ( 'checkout' === page ) {
101
+ removeCheckoutPreviewElement();
102
+ }
103
  } );
104
  } else {
105
+ $( options ).each( function() {
106
  $( this ).parents( 'tr' ).show();
107
+ if ( 'checkout' === page ) {
108
+ addCheckoutPreviewElement();
109
+ $( '#cpsw_express_checkout_title' ).trigger( 'keyup' );
110
+ $( '#cpsw_express_checkout_tagline' ).trigger( 'keyup' );
111
+ }
112
  } );
113
  }
114
  }
147
  generateExpressCheckoutDemo( style );
148
  } );
149
 
150
+ toggleOptions();
151
+ toggleOptions( 'product' );
152
 
153
  $( '#cpsw_express_checkout_product_checkout_page' ).change( function() {
154
+ toggleOptions();
155
+ } );
156
+
157
+ $( '#cpsw_express_checkout_product_options' ).change( function() {
158
+ toggleOptions( 'product' );
159
  } );
160
 
161
  if ( $( document ).width() > 1200 ) {
163
  buttonPreview.parents( 'tr' ).hide();
164
  $( '.submit' ).after( '<div class="cpsw_floating_preview"><span class="cpsw_button_preview_label">Button preview</div>' );
165
  $( '.cpsw_floating_preview' ).append( buttonPreview );
166
+
167
+ const maxTop = $( '#cpsw_express_checkout-description' ).offset().top;
168
+ const absoluteLeft = 900 - jQuery( '#adminmenuwrap' ).width();
169
+ $( '.cpsw_floating_preview' ).css(
170
+ {
171
+ position: 'absolute',
172
+ top: maxTop,
173
+ left: absoluteLeft,
174
+ },
175
+ );
176
+
177
+ $( window ).scroll( function() {
178
+ if ( maxTop - 110 < $( window ).scrollTop() ) {
179
+ $( '.cpsw_floating_preview' ).css(
180
+ {
181
+ position: 'fixed',
182
+ top: '200px',
183
+ left: 900,
184
+ },
185
+ );
186
+ } else {
187
+ $( '.cpsw_floating_preview' ).css(
188
+ {
189
+ position: 'absolute',
190
+ top: maxTop,
191
+ left: absoluteLeft,
192
+ },
193
+ );
194
+ }
195
+ } );
196
  }
197
 
198
  $( '#cpsw_express_checkout_title' ).keyup( function() {
203
  $( '.cpsw_preview_tagline' ).html( $( this ).val() );
204
  } );
205
 
206
+ $( '#cpsw_express_checkout_button_width, #cpsw_express_checkout_product_button_width' ).change( function() {
207
+ let buttonWidth = $( this ).val();
208
+ if ( buttonWidth < 150 && buttonWidth !== '' ) {
209
+ $( this ).val( 150 );
210
  buttonWidth = 150;
211
  alert( 'Express Checkout button width should not be less than 150px' );
212
  }
213
+
214
+ if ( '' === buttonWidth ) {
215
+ buttonWidth = '100%';
216
+ }
217
  $( '.cpsw_express_checkout_preview' ).width( buttonWidth );
218
  } );
219
 
assets/css/stripe-elements.css CHANGED
@@ -1,7 +1,8 @@
1
  .cpsw-stripe-error,
2
  .cpsw-number-error,
3
  .cpsw-expiry-error,
4
- .cpsw-cvc-error {
 
5
  padding-top: 3px;
6
  font-weight: 500;
7
  color: #f00;
@@ -34,6 +35,10 @@
34
  cursor: not-allowed;
35
  }
36
 
 
 
 
 
37
  #cpsw-payment-request-button {
38
  max-width: 100%;
39
  border-radius: 3px;
@@ -43,8 +48,6 @@
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,
@@ -82,6 +85,10 @@
82
  display: none;
83
  }
84
 
 
 
 
 
85
  #cpsw-payment-request-wrapper.checkout #cpsw-payment-request-title {
86
  font-weight: 600;
87
  margin-bottom: 0.5em;
@@ -92,10 +99,14 @@
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;
@@ -108,19 +119,27 @@
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
  }
1
  .cpsw-stripe-error,
2
  .cpsw-number-error,
3
  .cpsw-expiry-error,
4
+ .cpsw-cvc-error,
5
+ .cpsw_stripe_ideal_error {
6
  padding-top: 3px;
7
  font-weight: 500;
8
  color: #f00;
35
  cursor: not-allowed;
36
  }
37
 
38
+ .cpsw_stripe_ideal_form .cpsw_stripe_ideal_select {
39
+ margin-top: 10px;
40
+ }
41
+
42
  #cpsw-payment-request-button {
43
  max-width: 100%;
44
  border-radius: 3px;
48
  #cpsw-payment-request-wrapper {
49
  clear: both;
50
  display: none;
 
 
51
  }
52
 
53
  #cpsw-payment-request-wrapper.checkout.center #cpsw-payment-request-title,
85
  display: none;
86
  }
87
 
88
+ #cpsw-payment-request-separator.product {
89
+ padding: 0.5em 0;
90
+ }
91
+
92
  #cpsw-payment-request-wrapper.checkout #cpsw-payment-request-title {
93
  font-weight: 600;
94
  margin-bottom: 0.5em;
99
  }
100
 
101
  #cpsw-payment-request-wrapper.checkout #cpsw-payment-request-tagline {
 
102
  margin-bottom: 1em;
103
  }
104
 
105
+ #cpsw-payment-request-wrapper.product.above .cpsw-payment-request-button-wrapper {
106
+ display: block;
107
+ padding-top: 1.5em;
108
+ }
109
+
110
  #cpsw-payment-request-wrapper.product #cpsw-payment-request-title,
111
  #cpsw-payment-request-wrapper.cart #cpsw-payment-request-title {
112
  display: none;
119
 
120
  @media only screen and (max-width: 600px) {
121
 
122
+ #cpsw-payment-request-wrapper.product.above.sticky .cpsw-payment-request-button-wrapper {
123
+ padding-top: 0;
124
+ }
125
+
126
  #cpsw-payment-request-wrapper.product.sticky {
127
  position: fixed;
128
  bottom: 0;
129
  left: 0;
130
  z-index: 1000000;
131
  clear: both;
132
+ padding: 0.5em;
133
  border: none;
134
+ width: 100% !important;
135
  margin: 0;
136
  background-color: #88888880;
137
  }
138
 
139
+ #cpsw-payment-request-wrapper.product.sticky #cpsw-payment-request-separator {
140
+ display: none;
141
+ }
142
+
143
  #cpsw-payment-request-wrapper.product #cpsw-payment-request-title {
144
  display: none;
145
  }
assets/icon/alipay.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 374" preserveAspectRatio="xMidYMid meet"><defs><style>.cls-1{fill:#ec6c00}.cls-2{fill:#003f96}</style></defs><path class="cls-1" d="M326 287l-8 2-7 3-5 4a10 10 0 00-3 5l2 5c2 2 5 2 5 2l6-2 2-4-2-7 4-2 6-1 6 2 4 4 1 3 1 4v5l-12 4-12 4-5 3-4 3a16 16 0 00-4 4l-1 7c0 6 5 10 5 10 5 4 12 4 12 4l9-1 3-1 9-7a10 10 0 004 6c3 3 8 3 8 3l5-1 6-2v-3h-4l-4-1-2-2-1-4v-29l-1-10-6-6-7-3-10-1zm12 28v21l-6 4-6 3h-3a10 10 0 01-7-3c-2-2-2-7-2-7a13 13 0 011-6l6-5 7-4 10-3zm23-26h29v4l-4 1-3 2v1l16 35 16-34-3-3-6-2v-4h24v4l-5 2a14 14 0 00-5 5l-13 30-17 32-7 8-5 3-5 1s-5 0-8-2a6 6 0 01-2-5l1-5a8 8 0 015-2l3 1 3 1 4 5 8-8s6-10 4-18l-19-42-5-4-6-2v-4m-100-1l-8 2-9 6v-7l-1-1-22 2v4h4l3 1 3 3 1 3v63l-1 3-3 2-3 1h-4v4h36v-4l-9-2-3-2-1-3v-18l7 3 9 1h1l11-2 9-7 7-9 2-13-2-12a33 33 0 00-6-10 27 27 0 00-8-6l-11-2zm0 8c9 1 13 8 13 8 5 5 4 14 4 14a26 26 0 01-4 14c-5 7-13 8-13 8-7 1-17-5-17-5v-34a43 43 0 0117-5zM95 262l-30 71-3 5-4 3-5 2h-4v4h35v-4l-9-1c-4-2-4-4-4-4v-2l6-18h33l7 19 1 2v1c0 2-3 2-3 2l-9 1v4h39v-4h-4l-4-1-4-4a7 7 0 01-2-3l-31-73zm-2 19h1l13 32H80zm102 0a8 8 0 01-6-3 8 8 0 01-3-5 9 9 0 013-6 9 9 0 016-2 10 10 0 016 2l2 5-2 6a10 10 0 01-6 3m11 9v47l1 3 3 2 6 2v3h-31v-3l5-1 3-2 1-3v-36l-1-4-3-3-3-1h-4v-4l22-2 1 1m-40-33l-23 1h-1v4h1l4 1 4 1 3 3 1 4v68l-1 3-3 2-6 1v3h31v-3l-3-1-3-1-2-1-1-4v-80l-1-1m424 34l22-2 1 2v8l4-4 5-3a23 23 0 015-3h7l11 3 7 7 4-4 5-3a24 24 0 015-3h8s8 0 13 5c0 0 5 5 5 14v33l3 3h3l4 1v3h-32v-3l6-1 3-2v-33s0-7-3-10a12 12 0 00-7-3h-7l-5 3-3 3-2 3v33l1 3 3 3h3l3 1v3h-31v-3l5-1 3-2 1-3v-30s0-7-3-10a11 11 0 00-8-3h-6l-5 3-3 3-2 2v34l1 3 2 2 3 1 3 1v3h-31v-3l6-1 3-2 1-3v-37l-1-3-3-3-3-1h-4v-4m-126 31s0 11 6 18c0 0 6 8 15 8l12-3 9-9 4 3a37 37 0 01-12 11 36 36 0 01-16 4s-15 0-23-8a35 35 0 01-9-24 35 35 0 012-12 36 36 0 017-10 38 38 0 0110-7 40 40 0 0114-3s10 0 17 4c0 0 6 5 6 12l-2 5-5 3-7-2-2-4 1-6 1-4-4-3h-5l-7 1-6 5-4 8a44 44 0 00-2 13m85-32a40 40 0 00-24 9c-9 9-9 23-9 23a37 37 0 002 14l8 10a39 39 0 0010 7l13 2 15-3a34 34 0 0010-7l7-10 3-13a33 33 0 00-10-23c-9-9-24-9-24-9h-1zm0 5h1l9 2 6 6 3 9 1 10c0 13-5 21-5 21a19 19 0 01-14 7h-1l-8-2-6-7-4-9a61 61 0 01-1-11l1-9 3-9a26 26 0 017-6 21 21 0 018-2zm-118 55a5 5 0 005-5s0-6-5-6c0 0-6 0-6 6 0 0 0 5 6 5"/><path d="M701 0v2h5v16h3V2h5V0zm16 0v18h2V3l6 15h2l5-15v15h2V0h-3l-5 15-6-15z"/><path class="cls-2" d="M586 0l-9 1s-10 2-10 6v14l-41 1v-7h-19s-18 1-18 7v51h78v35h-54v14h54v80l40-5v-75h58v-14h-58V73h68v-7h2s5 0 8-9l3-23s0-17-18-17l-63 3V0zm62 32a6 6 0 016 6l-3 21H526V36l41-1 40-1z"/><path class="cls-1" d="M342 108l12 15 15 13s5 4 9 0l14-13 13-15s5-4 0-9l-12-15-15-13s-4-4-9 0l-14 13-13 15s-4 5 0 9m335 46a6 6 0 000-7l-10-11-12-10a5 5 0 00-6 0l-11 10-10 11s-3 3 0 7l10 11 11 10s3 3 6 0l12-10 10-11"/><path class="cls-2" d="M78 91h120a166 166 0 01-13 31s-15 29-38 51c0 0-43 40-71 40 0 0-20 0-34-7 0 0-16-10-16-31 0 0 0-28 42-36 0 0-27-2-41 13 0 0-14 12-14 34 0 0 0 21 19 34a79 79 0 0042 11h6s47-2 91-39c0 0 38-31 57-76a188 188 0 009-28l2-11h-65V46h82V33h-82V1h-21l-9 1s-10 2-10 6v25H56v13h78v31H78v14M346 0v7l-5 16a95 95 0 01-12 20v158l-40-5V71l-28 12-5-8a238 238 0 0027-23s27-27 27-45c0 0 0-4 9-6l9-1h18"/><path class="cls-2" d="M335 60h76v146h40V60h21V46h-21V0h-22l-9 1s-9 2-9 6v39h-76v14"/><path class="cls-1" d="M153 145s-51-22-83-23c0 0-26-2-47 13 0 0-22 16-23 39 0 0 0 27 22 43 0 0 20 14 52 14h3-5s-24 0-40-12c0 0-18-13-18-34 0 0 0-23 16-35 0 0 13-10 35-10l31 4 25 9 57 29s40 21 77 33c0 0 132 44 425 15l16-3s6-2 10-9c0 0 4-6 30-30 0 0 19-17 13-22l-9 1s-96 22-243 28c0 0-164 6-246-17 0 0-56-16-98-33"/></svg>
assets/icon/ideal.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 661"><path d="M0 0v661h385c255 0 365-142 365-331C750 142 640 0 385 0z" fill="#fff"/><path d="M42 42h344c233 0 322 127 322 288 0 193-125 289-322 289H42zm33 33v511h311c187 0 289-87 289-256 0-174-111-255-289-255H75z"/><path d="M117 368h99v176h-99z"/><circle cx="166" cy="278" r="62"/><path d="M425 307v29h-71V221h68v29h-40v13h38v29h-38v15zm12 29l35-115h40l35 115h-30l-6-22h-38l-6 22zm45-51h20l-9-32h-2l-9 32zm79-64h29v86h42c-11-156-134-190-246-190H266v104h18c32 0 52 22 52 57 0 36-20 58-52 58h-18v209h120c182 0 245-85 247-209h-72V221zm-295 29v57h18c12 0 23-3 23-29 0-25-12-28-23-28z" fill="#d50072"/></svg>
assets/js/payment-request.js CHANGED
@@ -8,6 +8,8 @@
8
  const style = cpsw_payment_request.style;
9
  const jsNonce = cpsw_payment_request.nonce.js_nonce;
10
  const endpoint = cpsw_payment_request.ajax_endpoint;
 
 
11
  let smallScreen = true;
12
 
13
  let requestType = '';
@@ -562,4 +564,22 @@
562
  $( document.body ).on( 'updated_checkout', function() {
563
  initializePaymentButton();
564
  } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
565
  }( jQuery ) );
8
  const style = cpsw_payment_request.style;
9
  const jsNonce = cpsw_payment_request.nonce.js_nonce;
10
  const endpoint = cpsw_payment_request.ajax_endpoint;
11
+ const isResponsive = cpsw_payment_request.is_responsive;
12
+
13
  let smallScreen = true;
14
 
15
  let requestType = '';
564
  $( document.body ).on( 'updated_checkout', function() {
565
  initializePaymentButton();
566
  } );
567
+
568
+ // Responsive behaviour for product page.
569
+ $( window ).on( 'resize', function() {
570
+ if ( $( '#cpsw-payment-request-wrapper' ).hasClass( 'product' ) ) {
571
+ if ( $( document ).width() > 660 ) {
572
+ $( '#cpsw-payment-request-wrapper' ).removeClass( 'sticky' );
573
+ $( '#cpsw-payment-request-separator' ).show();
574
+ } else {
575
+ if ( 'no' === cpsw_payment_request.product_advanced_options ) {
576
+ return;
577
+ }
578
+ if ( 'yes' === isResponsive ) {
579
+ $( '#cpsw-payment-request-wrapper' ).addClass( 'sticky' );
580
+ $( '#cpsw-payment-request-separator' ).hide();
581
+ }
582
+ }
583
+ }
584
+ } );
585
  }( jQuery ) );
assets/js/stripe-elements.js CHANGED
@@ -1,15 +1,15 @@
1
  ( function( $ ) {
2
  const pubKey = cpsw_global_settings.public_key;
3
  const inlineCC = cpsw_global_settings.inline_cc;
4
- const isSsl = cpsw_global_settings.is_ssl;
5
  const mode = cpsw_global_settings.mode;
6
  const ajaxUrl = cpsw_global_settings.ajax_url;
7
  const jsNonce = cpsw_global_settings.js_nonce;
8
  const allowedCards = cpsw_global_settings.allowed_cards;
9
  const notAllowedString = cpsw_global_settings.not_allowed_string;
10
  const defaultCards = cpsw_global_settings.default_cards;
 
11
 
12
- if ( '' === pubKey || ( 'live' === mode && ! isSsl ) ) {
13
  return;
14
  }
15
 
@@ -22,7 +22,9 @@
22
  let cardCvc = null;
23
  let paymentForm = null;
24
  let paymentMethod = '';
25
- let isAllowedCard = false;
 
 
26
 
27
  const style = {
28
  base: {
@@ -115,6 +117,28 @@
115
  } );
116
  }
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  function mountCard() {
119
  $( '.cpsw-stripe-elements-form' ).show();
120
  if ( 0 === $( '.cpsw-stripe-elements-form' ).length ) {
@@ -134,16 +158,19 @@
134
  }
135
  }
136
 
137
- function createStripePaymentMethod() {
138
- let paymentObject = {};
139
- let selectedPaymentMethod = '';
140
- if ( $( '.wc_payment_method input' ).length ) {
141
- selectedPaymentMethod = $( '.wc_payment_method input:checked' ).val();
142
- } else if ( 0 < $( '#payment_method_cpsw_stripe' ).length && $( '#payment_method_cpsw_stripe' ).is( ':checked' ) ) {
143
- selectedPaymentMethod = $( '#payment_method_cpsw_stripe' ).val();
144
  }
145
 
146
- switch ( selectedPaymentMethod ) {
 
 
 
 
 
 
147
  case 'cpsw_stripe':
148
  if ( 'no' === inlineCC ) {
149
  card = cardNumber;
@@ -182,7 +209,11 @@
182
  paymentForm.trigger( 'submit' );
183
  }
184
  } else if ( result.error ) {
 
 
185
  logError( result.error );
 
 
186
  return false;
187
  }
188
  } );
@@ -191,21 +222,46 @@
191
  }
192
 
193
  function confirmStripePayment( clientSecret, redirectURL ) {
194
- stripe.confirmCardPayment( clientSecret, {} ).then( function( result ) {
195
- if ( result.error ) {
196
- // Show error to your customer (e.g., insufficient funds)
197
- $( '.woocommerce-error' ).remove();
198
- $( 'form.woocommerce-checkout' ).unblock();
199
- logError( result.error );
200
- $( '.woocommerce-notices-wrapper:first-child' ).html( '<div class="woocommerce-error cpsw-errors">' + result.error.message + '</div>' ).show();
201
- window.scrollTo( { top: 0, behavior: 'smooth' } );
202
- } else {
203
- // The payment has been processed!
204
- if ( result.paymentIntent.status === 'succeeded' || result.paymentIntent.status === 'requires_capture' ) {
205
- window.location = redirectURL;
206
- }
207
- }
208
- } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  }
210
 
211
  function confirmCardSetup() {
@@ -237,8 +293,11 @@
237
 
238
  function isAllowedGateway() {
239
  if ( 0 < $( '.wc_payment_method' ).length ) {
240
- const selectedPaymentMethod = $( '.wc_payment_method input:checked' ).val();
241
- return ( -1 === $.inArray( selectedPaymentMethod, [ 'cpsw_stripe' ] ) ) ? false : true;
 
 
 
242
  } else if ( 0 < $( '#payment_method_cpsw_stripe' ).length && $( '#payment_method_cpsw_stripe' ).is( ':checked' ) && 'cpsw_stripe' === $( '#payment_method_cpsw_stripe' ).val() ) {
243
  return true;
244
  }
@@ -279,7 +338,7 @@
279
  const redirectURL = decodeURIComponent( partials[ 3 ] );
280
 
281
  // Cleanup the URL
282
- window.location.hash = '';
283
  confirmStripePayment( intentClientSecret, redirectURL );
284
  }
285
 
@@ -323,8 +382,8 @@
323
  }
324
 
325
  const processingSubmit = function( e ) {
326
- if ( isAllowedGateway() && ! savedCard && '' === paymentMethod ) {
327
- if ( ! isAllowedCard ) {
328
  return false;
329
  }
330
  e.preventDefault();
@@ -332,6 +391,7 @@
332
 
333
  return false;
334
  }
 
335
  return true;
336
  };
337
 
@@ -339,6 +399,21 @@
339
  paymentForm = $( 'form.woocommerce-checkout' );
340
  }
341
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  $( 'form.woocommerce-checkout' ).on( 'submit checkout_place_order_cpsw_stripe', processingSubmit );
343
 
344
  if ( $( 'form#order_review' ).length ) {
@@ -362,6 +437,7 @@
362
  hideShowElements();
363
  } );
364
  mountCard();
 
365
  $( "input[name='wc-cpsw-payment-token']" ).click( function() {
366
  hideShowCard();
367
  } );
@@ -374,4 +450,5 @@
374
 
375
  window.addEventListener( 'hashchange', onHashChange );
376
  mountCard();
 
377
  }( jQuery ) );
1
  ( function( $ ) {
2
  const pubKey = cpsw_global_settings.public_key;
3
  const inlineCC = cpsw_global_settings.inline_cc;
 
4
  const mode = cpsw_global_settings.mode;
5
  const ajaxUrl = cpsw_global_settings.ajax_url;
6
  const jsNonce = cpsw_global_settings.js_nonce;
7
  const allowedCards = cpsw_global_settings.allowed_cards;
8
  const notAllowedString = cpsw_global_settings.not_allowed_string;
9
  const defaultCards = cpsw_global_settings.default_cards;
10
+ const homeURL = cpsw_global_settings.get_home_url;
11
 
12
+ if ( '' === pubKey || ( 'live' === mode && ! cpsw_global_settings.is_ssl ) ) {
13
  return;
14
  }
15
 
22
  let cardCvc = null;
23
  let paymentForm = null;
24
  let paymentMethod = '';
25
+ let isAllowedCard = '';
26
+ let selectedGateway = '';
27
+ let selectedBank = '';
28
 
29
  const style = {
30
  base: {
117
  } );
118
  }
119
 
120
+ const options = {
121
+ style: {
122
+ base: {
123
+ padding: '10px 12px',
124
+ color: '#32325d',
125
+ fontSize: '16px',
126
+ '::placeholder': {
127
+ color: '#aab7c4',
128
+ backgroundColor: '#fff',
129
+ },
130
+ },
131
+ },
132
+ };
133
+
134
+ // Create an instance of the idealBank Element
135
+ const ideal = elements.create( 'idealBank', options );
136
+
137
+ ideal.on( 'change', function( event ) {
138
+ selectedBank = event.value;
139
+ $( '.cpsw_stripe_ideal_error' ).html( '' );
140
+ } );
141
+
142
  function mountCard() {
143
  $( '.cpsw-stripe-elements-form' ).show();
144
  if ( 0 === $( '.cpsw-stripe-elements-form' ).length ) {
158
  }
159
  }
160
 
161
+ function mountIdeal() {
162
+ $( '.cpsw_stripe_ideal_form' ).show();
163
+ if ( 0 === $( '.cpsw_stripe_ideal_form' ).length ) {
164
+ return;
 
 
 
165
  }
166
 
167
+ ideal.mount( '.cpsw_stripe_ideal_form .cpsw_stripe_ideal_select' );
168
+ $( '.cpsw_stripe_ideal_form .cpsw_stripe_ideal_select' ).css( { backgroundColor: '#fff', borderRadius: '3px' } );
169
+ }
170
+
171
+ function createStripePaymentMethod() {
172
+ let paymentObject = {};
173
+ switch ( selectedGateway ) {
174
  case 'cpsw_stripe':
175
  if ( 'no' === inlineCC ) {
176
  card = cardNumber;
209
  paymentForm.trigger( 'submit' );
210
  }
211
  } else if ( result.error ) {
212
+ $( '.woocommerce-error' ).remove();
213
+ $( 'form.woocommerce-checkout' ).unblock();
214
  logError( result.error );
215
+ $( '.woocommerce-notices-wrapper:first-child' ).html( '<div class="woocommerce-error cpsw-errors">' + result.error.message + '</div>' ).show();
216
+ window.scrollTo( { top: 0, behavior: 'smooth' } );
217
  return false;
218
  }
219
  } );
222
  }
223
 
224
  function confirmStripePayment( clientSecret, redirectURL ) {
225
+ switch ( selectedGateway ) {
226
+ case 'cpsw_stripe':
227
+ stripe.confirmCardPayment( clientSecret, {} ).then( function( result ) {
228
+ if ( result.error ) {
229
+ // Show error to your customer (e.g., insufficient funds)
230
+ $( '.woocommerce-error' ).remove();
231
+ $( 'form.woocommerce-checkout' ).unblock();
232
+ logError( result.error );
233
+ $( '.woocommerce-notices-wrapper:first-child' ).html( '<div class="woocommerce-error cpsw-errors">' + result.error.message + '</div>' ).show();
234
+ window.scrollTo( { top: 0, behavior: 'smooth' } );
235
+ } else {
236
+ // The payment has been processed!
237
+ if ( result.paymentIntent.status === 'succeeded' || result.paymentIntent.status === 'requires_capture' ) {
238
+ window.location = redirectURL;
239
+ }
240
+ }
241
+ } );
242
+ break;
243
+ case 'cpsw_ideal':
244
+ stripe.confirmIdealPayment(
245
+ clientSecret,
246
+ {
247
+ payment_method: {
248
+ ideal,
249
+ },
250
+ return_url: homeURL + redirectURL,
251
+ },
252
+ );
253
+ break;
254
+ case 'cpsw_alipay':
255
+ stripe.confirmAlipayPayment(
256
+ clientSecret,
257
+ {
258
+ return_url: homeURL + redirectURL,
259
+ },
260
+ );
261
+ break;
262
+ default:
263
+ break;
264
+ }
265
  }
266
 
267
  function confirmCardSetup() {
293
 
294
  function isAllowedGateway() {
295
  if ( 0 < $( '.wc_payment_method' ).length ) {
296
+ const selectedPaymentMethod = $( '.wc_payment_method input[name="payment_method"]:checked' ).val();
297
+ if ( -1 === $.inArray( selectedPaymentMethod, [ 'cpsw_stripe', 'cpsw_alipay', 'cpsw_ideal' ] ) ) {
298
+ return false;
299
+ }
300
+ return selectedGateway = selectedPaymentMethod;
301
  } else if ( 0 < $( '#payment_method_cpsw_stripe' ).length && $( '#payment_method_cpsw_stripe' ).is( ':checked' ) && 'cpsw_stripe' === $( '#payment_method_cpsw_stripe' ).val() ) {
302
  return true;
303
  }
338
  const redirectURL = decodeURIComponent( partials[ 3 ] );
339
 
340
  // Cleanup the URL
341
+ history.pushState( {}, '', window.location.pathname );
342
  confirmStripePayment( intentClientSecret, redirectURL );
343
  }
344
 
382
  }
383
 
384
  const processingSubmit = function( e ) {
385
+ if ( 'cpsw_stripe' === isAllowedGateway() && ! savedCard && '' === paymentMethod ) {
386
+ if ( false === isAllowedCard ) {
387
  return false;
388
  }
389
  e.preventDefault();
391
 
392
  return false;
393
  }
394
+
395
  return true;
396
  };
397
 
399
  paymentForm = $( 'form.woocommerce-checkout' );
400
  }
401
 
402
+ $( 'form.woocommerce-checkout' ).on( 'submit checkout_place_order_cpsw_ideal', function() {
403
+ // check for iDEAL.
404
+ if ( 'cpsw_ideal' === isAllowedGateway() ) {
405
+ if ( '' === selectedBank ) {
406
+ $( '.cpsw_stripe_ideal_error' ).html( cpsw_global_settings.empty_ideal_bank_message );
407
+ $( '.woocommerce-error' ).remove();
408
+ $( 'form.woocommerce-checkout' ).unblock();
409
+ $( '.woocommerce-notices-wrapper:first-child' ).html( '<div class="woocommerce-error cpsw-errors">' + cpsw_global_settings.empty_ideal_bank_message + '</div>' ).show();
410
+ window.scrollTo( { top: 0, behavior: 'smooth' } );
411
+ return false;
412
+ }
413
+ $( '.cpsw_stripe_ideal_error' ).html( '' );
414
+ }
415
+ } );
416
+
417
  $( 'form.woocommerce-checkout' ).on( 'submit checkout_place_order_cpsw_stripe', processingSubmit );
418
 
419
  if ( $( 'form#order_review' ).length ) {
437
  hideShowElements();
438
  } );
439
  mountCard();
440
+ mountIdeal();
441
  $( "input[name='wc-cpsw-payment-token']" ).click( function() {
442
  hideShowCard();
443
  } );
450
 
451
  window.addEventListener( 'hashchange', onHashChange );
452
  mountCard();
453
+ mountIdeal();
454
  }( jQuery ) );
autoloader.php CHANGED
@@ -9,10 +9,13 @@
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
 
17
  /**
18
  * CPSW_Loader
@@ -82,6 +85,7 @@ class CPSW_Loader {
82
  $this->setup_classes();
83
  add_action( 'plugins_loaded', [ $this, 'load_classes' ] );
84
  add_filter( 'plugin_action_links_' . CPSW_BASE, [ $this, 'action_links' ] );
 
85
  }
86
 
87
  /**
@@ -95,6 +99,21 @@ class CPSW_Loader {
95
  Apple_Pay::get_instance();
96
  }
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  /**
99
  * Adds links in Plugins page
100
  *
@@ -120,6 +139,8 @@ class CPSW_Loader {
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
  }
9
  namespace CPSW;
10
 
11
  use CPSW\Gateway\Stripe\Card_Payments;
12
+ use CPSW\Gateway\Stripe\Alipay;
13
  use CPSW\Gateway\Stripe\Payment_Request_Api;
14
+ use CPSW\Gateway\Stripe\Ideal;
15
  use CPSW\Compatibility\Apple_Pay;
16
  use CPSW\Admin\Admin_Controller;
17
  use CPSW\Gateway\Stripe\Webhook;
18
+ use CPSW\Gateway\Stripe\Frontend_Scripts;
19
 
20
  /**
21
  * CPSW_Loader
85
  $this->setup_classes();
86
  add_action( 'plugins_loaded', [ $this, 'load_classes' ] );
87
  add_filter( 'plugin_action_links_' . CPSW_BASE, [ $this, 'action_links' ] );
88
+ add_action( 'woocommerce_init', [ $this, 'frontend_scripts' ] );
89
  }
90
 
91
  /**
99
  Apple_Pay::get_instance();
100
  }
101
 
102
+ /**
103
+ * Includes frontend scripts.
104
+ *
105
+ * @since 1.1.0
106
+ *
107
+ * @return void
108
+ */
109
+ public function frontend_scripts() {
110
+ if ( is_admin() ) {
111
+ return;
112
+ }
113
+
114
+ Frontend_Scripts::get_instance();
115
+ }
116
+
117
  /**
118
  * Adds links in Plugins page
119
  *
139
  if ( class_exists( 'woocommerce' ) ) {
140
  Card_Payments::get_instance();
141
  Payment_Request_Api::get_instance();
142
+ Alipay::get_instance();
143
+ Ideal::get_instance();
144
  } else {
145
  add_action( 'admin_notices', [ $this, 'wc_is_not_active' ] );
146
  }
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.1.1
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.1.1' );
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.2.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.2.0' );
24
 
25
  require_once 'autoloader.php';
compatibility/apple-pay.php CHANGED
@@ -268,7 +268,7 @@ class Apple_Pay {
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
  /**
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
  /**
gateway/abstract-payment-gateway.php CHANGED
@@ -10,13 +10,39 @@ namespace CPSW\Gateway;
10
 
11
  use WC_Payment_Gateway;
12
  use CPSW\Inc\Helper;
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  /**
15
- * Abstract Payment Gateway
16
  *
17
- * @since 0.0.1
18
  */
19
- abstract class Abstract_Payment_Gateway extends WC_Payment_Gateway {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  /**
22
  * Adds transaction url in order details page
@@ -44,5 +70,470 @@ abstract class Abstract_Payment_Gateway extends WC_Payment_Gateway {
44
  return apply_filters( 'cpsw_get_order_description', get_bloginfo( 'name' ) . ' - ' . __( 'Order ', 'checkout-plugins-stripe-woo' ) . $order->get_id() );
45
  }
46
 
47
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  use WC_Payment_Gateway;
12
  use CPSW\Inc\Helper;
13
+ use CPSW\Inc\Logger;
14
+ use CPSW\Gateway\Stripe\Stripe_Api;
15
+ use WP_Error;
16
+ use Exception;
17
+
18
+ /**
19
+ * Abstract Payment Gateway
20
+ *
21
+ * @since 0.0.1
22
+ */
23
+ abstract class Abstract_Payment_Gateway extends WC_Payment_Gateway {
24
 
25
  /**
26
+ * Url of assets directory
27
  *
28
+ * @var string
29
  */
30
+ private $assets_url = CPSW_URL . 'assets/';
31
+
32
+ /**
33
+ * Zero currencies accepted by stripe.
34
+ *
35
+ * @var array
36
+ */
37
+ private static $zero_currencies = [ 'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VUV', 'XAF', 'XOF', 'XPF', 'VND' ];
38
+
39
+ /**
40
+ * Constructor
41
+ */
42
+ public function __construct() {
43
+ add_filter( 'woocommerce_payment_gateways', [ $this, 'add_gateway_class' ] );
44
+ add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, [ $this, 'process_admin_options' ] );
45
+ }
46
 
47
  /**
48
  * Adds transaction url in order details page
70
  return apply_filters( 'cpsw_get_order_description', get_bloginfo( 'name' ) . ' - ' . __( 'Order ', 'checkout-plugins-stripe-woo' ) . $order->get_id() );
71
  }
72
 
73
+ /**
74
+ * Registering Gateway to WooCommerce
75
+ *
76
+ * @param array $methods List of registered gateways.
77
+ * @return array
78
+ */
79
+ public function add_gateway_class( $methods ) {
80
+ $methods[] = $this;
81
+ return $methods;
82
+ }
83
+
84
+ /**
85
+ * Get billing countries for gateways
86
+ *
87
+ * @since 1.2.0
88
+ *
89
+ * @return string $billing_country
90
+ */
91
+ public function get_billing_country() {
92
+ if ( isset( $wp->query_vars['order-pay'] ) ) {
93
+ $order = wc_get_order( absint( $wp->query_vars['order-pay'] ) );
94
+ $billing_country = $order->get_billing_country();
95
+ $currency = $order->get_currency();
96
+ } else {
97
+ $currency = get_woocommerce_currency();
98
+ $customer = WC()->customer;
99
+ $billing_country = $customer ? $customer->get_billing_country() : null;
100
+
101
+ if ( ! $billing_country ) {
102
+ $billing_country = WC()->countries->get_base_country();
103
+ }
104
+ }
105
+
106
+ return $billing_country;
107
+ }
108
+
109
+ /**
110
+ * Get WooCommerce currency
111
+ *
112
+ * @since 1.2.0
113
+ *
114
+ * @return string
115
+ */
116
+ public function get_currency() {
117
+ if ( isset( $wp->query_vars['order-pay'] ) ) {
118
+ $order = wc_get_order( absint( $wp->query_vars['order-pay'] ) );
119
+
120
+ return $order->get_currency();
121
+ }
122
+
123
+ return get_woocommerce_currency();
124
+ }
125
+
126
+ /**
127
+ * Checks whether this gateway is available.
128
+ *
129
+ * @since 1.0.0
130
+ *
131
+ * @return boolean
132
+ */
133
+ public function is_available() {
134
+ if ( 'yes' !== $this->enabled ) {
135
+ return false;
136
+ }
137
 
138
+ if ( ! Helper::get_payment_mode() && is_checkout() ) {
139
+ return false;
140
+ }
141
+
142
+ if ( 'test' === Helper::get_payment_mode() ) {
143
+ if ( empty( Helper::get_setting( 'cpsw_test_pub_key' ) ) || empty( Helper::get_setting( 'cpsw_test_secret_key' ) ) ) {
144
+ return false;
145
+ }
146
+ } else {
147
+ if ( empty( Helper::get_setting( 'cpsw_pub_key' ) ) || empty( Helper::get_setting( 'cpsw_secret_key' ) ) ) {
148
+ return false;
149
+ }
150
+ }
151
+
152
+ return true;
153
+ }
154
+
155
+ /**
156
+ * Get/Retrieve stripe customer id if exists
157
+ *
158
+ * @since 1.0.0
159
+ *
160
+ * @param mixed $order current woocommerce order.
161
+ *
162
+ * @return mixed customer id
163
+ */
164
+ public function get_customer_id( $order = false ) {
165
+ $user = wp_get_current_user();
166
+ $user_id = ( $user->ID && $user->ID > 0 ) ? $user->ID : false;
167
+ $customer_id = false;
168
+
169
+ if ( $user_id ) {
170
+ $customer_id = get_user_option( '_cpsw_customer_id', $user_id );
171
+ if ( $customer_id ) {
172
+ return $customer_id;
173
+ }
174
+ }
175
+
176
+ $customer = false;
177
+ if ( ! $customer_id ) {
178
+ $customer = $this->create_stripe_customer( $order, $user->email );
179
+ }
180
+
181
+ if ( $customer ) {
182
+ if ( $user_id ) {
183
+ update_user_option( $user_id, '_cpsw_customer_id', $customer->id, false );
184
+ }
185
+ return $customer->id;
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Creates stripe customer object
191
+ *
192
+ * @since 1.0.0
193
+ *
194
+ * @param object $order woocommerce order object.
195
+ * @param boolean|string $user_email user email id.
196
+ *
197
+ * @return Stripe::Customer
198
+ */
199
+ public function create_stripe_customer( $order = false, $user_email = false ) {
200
+ $args = [
201
+ 'email' => $user_email,
202
+ ];
203
+ if ( $order ) {
204
+ $args = [
205
+ 'description' => 'Customer for Order #' . $order->get_id(),
206
+ 'email' => $user_email ? $user_email : $order->get_billing_email(),
207
+ 'address' => [ // sending name and billing address to stripe to support indian exports.
208
+ 'city' => method_exists( $order, 'get_billing_city' ) ? $order->get_billing_city() : $order->billing_city,
209
+ 'country' => method_exists( $order, 'get_billing_country' ) ? $order->get_billing_country() : $order->billing_country,
210
+ 'line1' => method_exists( $order, 'get_billing_address_1' ) ? $order->get_billing_address_1() : $order->billing_address_1,
211
+ 'line2' => method_exists( $order, 'get_billing_address_2' ) ? $order->get_billing_address_2() : $order->billing_address_2,
212
+ 'postal_code' => method_exists( $order, 'get_billing_postcode' ) ? $order->get_billing_postcode() : $order->billing_postcode,
213
+ 'state' => method_exists( $order, 'get_billing_state' ) ? $order->get_billing_state() : $order->billing_state,
214
+ ],
215
+ 'name' => ( method_exists( $order, 'get_billing_first_name' ) ? $order->get_billing_first_name() : $order->billing_first_name ) . ( method_exists( $order, 'get_billing_last_name' ) ? $order->get_billing_last_name() : $order->billing_last_name ),
216
+ ];
217
+ }
218
+
219
+ $stripe_api = new Stripe_Api();
220
+ $response = $stripe_api->customers( 'create', [ $args ] );
221
+ $response = $response['success'] ? $response['data'] : false;
222
+
223
+ if ( empty( $response->id ) ) {
224
+ return false;
225
+ }
226
+
227
+ return $response;
228
+ }
229
+
230
+ /**
231
+ * Refunds amount from stripe and return true/false as result
232
+ *
233
+ * @param string $order_id order id.
234
+ * @param string $amount refund amount.
235
+ * @param string $reason reason of refund.
236
+ * @return bool
237
+ */
238
+ public function process_refund( $order_id, $amount = null, $reason = '' ) {
239
+ if ( 0 >= $amount ) {
240
+ return false;
241
+ }
242
+
243
+ try {
244
+
245
+ $order = wc_get_order( $order_id );
246
+ $intent_secret = $order->get_meta( '_cpsw_intent_secret', true );
247
+
248
+ $response = $this->create_refund_request( $order, $amount, $reason, $intent_secret['id'] );
249
+
250
+ $refund_response = $response['success'] ? $response['data'] : false;
251
+
252
+ if ( $refund_response ) {
253
+ $refund_time = gmdate( 'Y-m-d H:i:s', time() );
254
+ $order->update_meta_data( '_cpsw_refund_id', $refund_response->id );
255
+ $order->add_order_note( __( 'Reason : ', 'checkout-plugins-stripe-woo' ) . $reason . '.<br>' . __( 'Amount : ', 'checkout-plugins-stripe-woo' ) . get_woocommerce_currency_symbol() . $amount . '.<br>' . __( 'Status : ', 'checkout-plugins-stripe-woo' ) . ( ( 'succeeded' === $refund_response->status ) ? 'Success' : 'Failed' ) . ' [ ' . $refund_time . ' ] ' . ( is_null( $refund_response->id ) ? '' : '<br>' . __( 'Transaction ID : ', 'checkout-plugins-stripe-woo' ) . $refund_response->id ) );
256
+ Logger::info( __( 'Refund initiated: ', 'checkout-plugins-stripe-woo' ) . __( 'Reason : ', 'checkout-plugins-stripe-woo' ) . $reason . __( 'Amount : ', 'checkout-plugins-stripe-woo' ) . get_woocommerce_currency_symbol() . $amount . __( 'Status : ', 'checkout-plugins-stripe-woo' ) . ( ( 'succeeded' === $refund_response->status ) ? 'Success' : 'Failed' ) . ' [ ' . $refund_time . ' ] ' . ( is_null( $refund_response->id ) ? '' : __( 'Transaction ID : ', 'checkout-plugins-stripe-woo' ) . $refund_response->id ), true );
257
+ return true;
258
+ } else {
259
+ $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' ) );
260
+ Logger::error( $response['message'], true );
261
+ return new WP_Error( 'error', $response['message'] );
262
+ }
263
+ } catch ( Exception $e ) {
264
+ Logger::error( $e->getMessage(), true );
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Process response for saved cards
270
+ *
271
+ * @param object $response intent response.
272
+ * @param object $order order response.
273
+ * @return array
274
+ */
275
+ public function process_response( $response, $order ) {
276
+ Logger::info( 'Processing: ' . $response->id );
277
+
278
+ $order_id = $order->get_id();
279
+ $captured = ( isset( $response->captured ) && $response->captured ) ? 'yes' : 'no';
280
+
281
+ // Store charge data.
282
+ $order->update_meta_data( '_cpsw_charge_captured', $captured );
283
+
284
+ if ( 'yes' === $captured ) {
285
+ /**
286
+ * Charge can be captured but in a pending state. Payment methods
287
+ * that are asynchronous may take couple days to clear. Webhook will
288
+ * take care of the status changes.
289
+ */
290
+ if ( 'pending' === $response->status ) {
291
+ $order_stock_reduced = $order->get_meta( '_order_stock_reduced', true );
292
+
293
+ if ( ! $order_stock_reduced ) {
294
+ wc_reduce_stock_levels( $order_id );
295
+ }
296
+
297
+ $order->set_transaction_id( $response->id );
298
+ /* translators: transaction id */
299
+ $order->update_status( 'on-hold', sprintf( __( 'Stripe charge awaiting payment: %s.', 'checkout-plugins-stripe-woo' ), $response->id ) );
300
+ }
301
+
302
+ if ( 'succeeded' === $response->status ) {
303
+ $order->payment_complete( $response->id );
304
+
305
+ /* translators: transaction id */
306
+ $message = sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'checkout-plugins-stripe-woo' ), $response->id );
307
+ Logger::info( $message, true );
308
+ $order->add_order_note( $message );
309
+ }
310
+
311
+ if ( 'failed' === $response->status ) {
312
+ $message = __( 'Payment processing failed. Please retry.', 'checkout-plugins-stripe-woo' );
313
+ Logger::error( $message, true );
314
+ $order->add_order_note( $message );
315
+ }
316
+ } else {
317
+ $order->set_transaction_id( $response->id );
318
+
319
+ if ( $order->has_status( [ 'pending', 'failed' ] ) ) {
320
+ wc_reduce_stock_levels( $order_id );
321
+ }
322
+
323
+ /* translators: transaction id */
324
+ $order->update_status( 'on-hold', sprintf( __( 'Stripe charge authorized (Charge ID: %s). Process order to take payment, or cancel to remove the pre-authorization. Attempting to refund the order in part or in full will release the authorization and cancel the payment.', 'checkout-plugins-stripe-woo' ), $response->id ) );
325
+ }
326
+
327
+ if ( is_callable( [ $order, 'save' ] ) ) {
328
+ $order->save();
329
+ }
330
+
331
+ do_action( 'cpsw_process_response', $response, $order );
332
+
333
+ return $response;
334
+ }
335
+
336
+ /**
337
+ * Basic details of logged in user
338
+ *
339
+ * @since 1.0.0
340
+ *
341
+ * @return array current user data.
342
+ */
343
+ public function get_clients_details() {
344
+ return [
345
+ 'ip' => isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( $_SERVER['REMOTE_ADDR'] ) : '',
346
+ 'agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] ) : '',
347
+ 'referer' => isset( $_SERVER['HTTP_REFERER'] ) ? sanitize_text_field( $_SERVER['HTTP_REFERER'] ) : '',
348
+ ];
349
+ }
350
+
351
+ /**
352
+ * Checks if payment intent available for current order or else creates new payment intent.
353
+ *
354
+ * @since 1.0.0
355
+ *
356
+ * @param int $order_id woocommerce order id.
357
+ * @param string $idempotency_key unique idempotency key.
358
+ * @param array $args payment_intent arguments.
359
+ * @param boolean $return_error_response if true returns complete error response array.
360
+ *
361
+ * @return array intent data.
362
+ */
363
+ public function get_payment_intent( $order_id, $idempotency_key, $args, $return_error_response = false ) {
364
+ $intent_secret = get_post_meta( $order_id, '_cpsw_intent_secret', true );
365
+ $client_secret = '';
366
+
367
+ if ( ! empty( $intent_secret ) ) {
368
+ $secret = $intent_secret;
369
+ $stripe_api = new Stripe_Api();
370
+ $response = $stripe_api->payment_intents( 'retrieve', [ $secret['id'] ] );
371
+
372
+ if ( $response['success'] && 'success' === $response['data']->status ) {
373
+ 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' );
374
+ wp_safe_redirect( wc_get_checkout_url() );
375
+ }
376
+ }
377
+
378
+ $args = [
379
+ [ $args ],
380
+ [ 'idempotency_key' => $idempotency_key ],
381
+ ];
382
+
383
+ $stripe_api = new Stripe_Api();
384
+ $response = $stripe_api->payment_intents( 'create', $args );
385
+
386
+ if ( $response['success'] ) {
387
+ $intent = $response['data'];
388
+ } elseif ( $return_error_response ) {
389
+ return $response;
390
+ } else {
391
+ wc_add_notice( $response['message'], 'error' );
392
+ return false;
393
+ }
394
+
395
+ $intent_data = [
396
+ 'id' => $intent->id,
397
+ 'client_secret' => $intent->client_secret,
398
+ ];
399
+
400
+ update_post_meta( $order_id, '_cpsw_intent_secret', $intent_data, );
401
+ $client_secret = $intent->client_secret;
402
+
403
+ return [
404
+ 'client_secret' => $client_secret,
405
+ ];
406
+ }
407
+
408
+ /**
409
+ * Returns amount as per currency type
410
+ *
411
+ * @since 1.0.0
412
+ *
413
+ * @param string $total amount to be processed.
414
+ * @param string $currency transaction currency.
415
+ *
416
+ * @return int
417
+ */
418
+ public function get_formatted_amount( $total, $currency = '' ) {
419
+ if ( ! $currency ) {
420
+ $currency = get_woocommerce_currency();
421
+ }
422
+
423
+ if ( in_array( strtolower( $currency ), self::$zero_currencies, true ) ) {
424
+ // Zero decimal currencies accepted by stripe.
425
+ return absint( $total );
426
+ } else {
427
+ return absint( wc_format_decimal( ( (float) $total * 100 ), wc_get_price_decimals() ) ); // In cents.
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Add metadata to stripe
433
+ *
434
+ * @since 1.2.0
435
+ *
436
+ * @param int $order_id WooCommerce order Id.
437
+ *
438
+ * @return array
439
+ */
440
+ public function get_metadata( $order_id ) {
441
+ $order = wc_get_order( $order_id );
442
+ $details = [];
443
+ $billing_first_name = $order->get_billing_first_name();
444
+ $billing_last_name = $order->get_billing_last_name();
445
+ $name = $billing_first_name . ' ' . $billing_last_name;
446
+
447
+ if ( ! empty( $name ) ) {
448
+ $details['name'] = $name;
449
+ }
450
+
451
+ if ( ! empty( $order->get_billing_email() ) ) {
452
+ $details['email'] = $order->get_billing_email();
453
+ }
454
+
455
+ if ( ! empty( $order->get_billing_phone() ) ) {
456
+ $details['phone'] = $order->get_billing_phone();
457
+ }
458
+
459
+ if ( ! empty( $order->get_billing_address_1() ) ) {
460
+ $details['address'] = $order->get_billing_address_1();
461
+ }
462
+
463
+ if ( ! empty( $order->get_billing_city() ) ) {
464
+ $details['city'] = $order->get_billing_city();
465
+ }
466
+
467
+ if ( ! empty( $order->get_billing_country() ) ) {
468
+ $details['country'] = $order->get_billing_country();
469
+ }
470
+
471
+ $details['site_url'] = get_site_url();
472
+
473
+ return apply_filters( 'cpsw_metadata_details', $details, $order );
474
+ }
475
+
476
+ /**
477
+ * All payment icons that work with Stripe
478
+ *
479
+ * @since 1.2.0
480
+ *
481
+ * @param string $gateway_id gateway id to fetch icon.
482
+ *
483
+ * @return array
484
+ */
485
+ public function payment_icons( $gateway_id ) {
486
+ $icons = [
487
+ 'cpsw_alipay' => '<img src="' . $this->assets_url . 'icon/alipay.svg" class="cpsw-alipay-icon stripe-icon" alt="Alipay" width="70px" />',
488
+ 'cpsw_ideal' => '<img src="' . $this->assets_url . 'icon/ideal.svg" class="cpsw-ideal-icon stripe-icon" alt="iDEAL" width="32" />',
489
+ ];
490
+
491
+ return apply_filters(
492
+ 'cpsw_payment_icons',
493
+ isset( $icons[ $gateway_id ] ) ? $icons[ $gateway_id ] : ''
494
+ );
495
+ }
496
+
497
+ /**
498
+ * Create refund request.
499
+ *
500
+ * @since 1.0.0
501
+ *
502
+ * @param object $order order.
503
+ * @param string $amount refund amount.
504
+ * @param string $reason reason of refund.
505
+ * @param string $intent_secret_id secret key.
506
+ *
507
+ * @return array
508
+ */
509
+ public function create_refund_request( $order, $amount, $reason, $intent_secret_id ) {
510
+ $client = $this->get_clients_details();
511
+ $stripe_api = new Stripe_Api();
512
+ $response = $stripe_api->payment_intents( 'retrieve', [ $intent_secret_id ] );
513
+ $status = $response['success'] && isset( $response['data']->charges->data[0]->captured ) ? $response['data']->charges->data[0]->captured : false;
514
+
515
+ if ( ! $status ) {
516
+ Logger::error( __( 'Uncaptured Amount cannot be refunded', 'checkout-plugins-stripe-woo' ), true );
517
+ return new WP_Error( 'error', __( 'Uncaptured Amount cannot be refunded', 'checkout-plugins-stripe-woo' ) );
518
+ }
519
+
520
+ $intent_response = $response['data'];
521
+ $currency = $intent_response->currency;
522
+
523
+ $refund_params = [
524
+ 'payment_intent' => $intent_secret_id,
525
+ 'amount' => $this->get_formatted_amount( $amount, $currency ),
526
+ 'reason' => 'requested_by_customer',
527
+ 'metadata' => [
528
+ 'order_id' => $order->get_order_number(),
529
+ 'customer_ip' => $client['ip'],
530
+ 'agent' => $client['agent'],
531
+ 'referer' => $client['referer'],
532
+ 'reason_for_refund' => $reason,
533
+ ],
534
+ ];
535
+
536
+ $stripe_api = new Stripe_Api();
537
+ return $stripe_api->refunds( 'create', [ $refund_params ] );
538
+ }
539
+ }
gateway/local-gateway.php ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Local Gateway
4
+ *
5
+ * @package checkout-plugins-stripe-woo
6
+ * @since 1.2.0
7
+ */
8
+
9
+ namespace CPSW\Gateway;
10
+
11
+ use CPSW\Inc\Traits\Get_Instance;
12
+ use CPSW\Gateway\Abstract_Payment_Gateway;
13
+ use CPSW\Inc\Notice;
14
+ use CPSW\Inc\Helper;
15
+
16
+ /**
17
+ * Local Gateway
18
+ *
19
+ * @since 1.2.0
20
+ */
21
+ class Local_Gateway extends Abstract_Payment_Gateway {
22
+
23
+ use Get_Instance;
24
+
25
+ /**
26
+ * Constructor
27
+ *
28
+ * @since 1.2.0
29
+ */
30
+ public function __construct() {
31
+ parent::__construct();
32
+ add_action( 'admin_init', [ $this, 'add_notice' ] );
33
+ }
34
+
35
+ /**
36
+ * Registers supported filters for payment gateway
37
+ *
38
+ * @since 1.2.0
39
+ *
40
+ * @return void
41
+ */
42
+ public function init_supports() {
43
+ $this->supports = apply_filters( 'cpsw_local_payment_supports_' . $this->id, [ 'products', 'refunds' ] );
44
+ }
45
+
46
+ /**
47
+ * Get gateway form fields
48
+ *
49
+ * @since 1.2.0
50
+ *
51
+ * @return void
52
+ */
53
+ public function init_form_fields() {
54
+ $this->form_fields = apply_filters( 'cpsw_stripe_form_fields_' . $this->id, $this->get_local_payment_settings() );
55
+ }
56
+
57
+ /**
58
+ * Checks whether this gateway is available.
59
+ *
60
+ * @since 1.2.0
61
+ *
62
+ * @return boolean
63
+ */
64
+ public function is_available() {
65
+ if ( ! empty( $this->get_option( 'allowed_countries' ) ) && 'all_except' === $this->get_option( 'allowed_countries' ) ) {
66
+ return ! in_array( $this->get_billing_country(), $this->get_option( 'except_countries', array() ), true );
67
+ } elseif ( ! empty( $this->get_option( 'allowed_countries' ) ) && 'specific' === $this->get_option( 'allowed_countries' ) ) {
68
+ return in_array( $this->get_billing_country(), $this->get_option( 'specific_countries', array() ), true );
69
+ }
70
+
71
+ return parent::is_available();
72
+ }
73
+
74
+ /**
75
+ * Return a description for (for admin sections) describing the required currency & or billing country(s).
76
+ *
77
+ * @since 1.2.0
78
+ *
79
+ * @param string $desc Payment description.
80
+ *
81
+ * @return string
82
+ */
83
+ protected function get_payment_description( $desc ) {
84
+ if ( 'all_except' === $this->get_option( 'allowed_countries' ) ) {
85
+ // translators: %s: except countries.
86
+ $desc .= sprintf( __( ' & billing country is not <strong>%s</strong>', 'checkout-plugins-stripe-woo' ), implode( ', ', $this->get_option( 'except_countries', array() ) ) );
87
+ } elseif ( 'specific' === $this->get_option( 'allowed_countries' ) ) {
88
+ // translators: %s: specificcountries.
89
+ $desc .= sprintf( __( ' & billing country is <strong>%s</strong>', 'checkout-plugins-stripe-woo' ), implode( ', ', $this->get_option( 'specific_countries', array() ) ) );
90
+ }
91
+
92
+ return $desc;
93
+ }
94
+
95
+ /**
96
+ * Return an array of form fields for the gateway.
97
+ *
98
+ * @since 1.2.0
99
+ *
100
+ * @return array
101
+ */
102
+ public function get_local_payment_settings() {
103
+ $method_title = $this->method_title;
104
+
105
+ return [
106
+ 'enabled' => [
107
+ 'label' => ' ',
108
+ 'type' => 'checkbox',
109
+ // translators: %s: Method title.
110
+ 'title' => sprintf( __( 'Enable %s', 'checkout-plugins-stripe-woo' ), $method_title ),
111
+ 'default' => 'no',
112
+ ],
113
+ 'title' => [
114
+ 'title' => __( 'Title', 'checkout-plugins-stripe-woo' ),
115
+ 'type' => 'text',
116
+ // translators: %s: Method title.
117
+ 'description' => sprintf( __( 'Title of the %s gateway.', 'checkout-plugins-stripe-woo' ), $method_title ),
118
+ 'default' => $method_title,
119
+ 'desc_tip' => true,
120
+ ],
121
+ 'description' => [
122
+ 'title' => __( 'Description', 'checkout-plugins-stripe-woo' ),
123
+ 'type' => 'textarea',
124
+ 'css' => 'width:25em',
125
+ /* translators: gateway title */
126
+ 'description' => sprintf( __( 'Description of the %1s gateway.', 'checkout-plugins-stripe-woo' ), $method_title ),
127
+ 'desc_tip' => true,
128
+ ],
129
+ 'order_button_text' => [
130
+ 'title' => __( 'Order button label', 'checkout-plugins-stripe-woo' ),
131
+ 'type' => 'text',
132
+ 'description' => __( 'Customize label for order button.', 'checkout-plugins-stripe-woo' ),
133
+ // translators: %s: Method title.
134
+ 'default' => sprintf( __( 'Pay with %s', 'checkout-plugins-stripe-woo' ), $method_title ),
135
+ 'desc_tip' => true,
136
+ ],
137
+ 'allowed_countries' => [
138
+ 'title' => __( 'Selling location(s)', 'checkout-plugins-stripe-woo' ),
139
+ 'default' => 'all',
140
+ 'type' => 'select',
141
+ 'class' => 'wc-enhanced-select wc-stripe-allowed-countries',
142
+ 'css' => 'min-width: 350px;',
143
+ 'desc_tip' => true,
144
+ /* translators: gateway title */
145
+ 'description' => sprintf( __( 'Choose the countries where you wish to display the %1$s gateway. The gateway will appear when the customer\'s billing country is in the chosen countries.', 'checkout-plugins-stripe-woo' ), $method_title ),
146
+ 'options' => array(
147
+ 'all' => __( 'Sell to all countries', 'checkout-plugins-stripe-woo' ),
148
+ 'all_except' => __( 'Sell to all countries, except for&hellip;', 'checkout-plugins-stripe-woo' ),
149
+ 'specific' => __( 'Sell to specific countries', 'checkout-plugins-stripe-woo' ),
150
+ ),
151
+ ],
152
+ 'except_countries' => [
153
+ 'title' => __( 'Sell to all countries, except for&hellip;', 'checkout-plugins-stripe-woo' ),
154
+ 'type' => 'multi_select_countries',
155
+ 'css' => 'min-width: 350px;',
156
+ 'options' => [],
157
+ 'default' => [],
158
+ 'desc_tip' => true,
159
+ 'description' => __( 'When the billing country matches one of these values, the payment method will be hidden on the Checkout page.', 'checkout-plugins-stripe-woo' ),
160
+ 'custom_attributes' => array( 'data-show-if' => array( 'allowed_countries' => 'all_except' ) ),
161
+ 'sanitize_callback' => function ( $value ) {
162
+ return is_array( $value ) ? $value : array();
163
+ },
164
+ ],
165
+ 'specific_countries' => [
166
+ 'title' => __( 'Sell to specific countries', 'checkout-plugins-stripe-woo' ),
167
+ 'type' => 'multi_select_countries',
168
+ 'css' => 'min-width: 350px;',
169
+ 'options' => [],
170
+ 'default' => [],
171
+ 'desc_tip' => true,
172
+ 'description' => __( 'When the billing country matches one of these values, the payment method will be shown on the Checkout page.', 'checkout-plugins-stripe-woo' ),
173
+ 'custom_attributes' => array( 'data-show-if' => array( 'allowed_countries' => 'specific' ) ),
174
+ 'sanitize_callback' => function ( $value ) {
175
+ return is_array( $value ) ? $value : array();
176
+ },
177
+ ],
178
+ ];
179
+ }
180
+
181
+ /**
182
+ * Generate multi select countries form field
183
+ *
184
+ * @since 1.2.0
185
+ *
186
+ * @param string $key Selling location field key.
187
+ * @param array $data Selling location field data.
188
+ *
189
+ * @return html
190
+ */
191
+ public function generate_multi_select_countries_html( $key, $data ) {
192
+ $field_key = $this->get_field_key( $key );
193
+ $value = (array) $this->get_option( $key );
194
+ $data = wp_parse_args(
195
+ $data,
196
+ array(
197
+ 'title' => '',
198
+ 'class' => '',
199
+ 'style' => '',
200
+ 'description' => '',
201
+ 'desc_tip' => false,
202
+ 'id' => $field_key,
203
+ 'options' => [],
204
+ )
205
+ );
206
+ ob_start();
207
+ $selections = (array) $value;
208
+
209
+ if ( ! empty( $data['options'] ) ) {
210
+ $countries = array_intersect_key( WC()->countries->countries, array_flip( $data['options'] ) );
211
+ } else {
212
+ $countries = WC()->countries->countries;
213
+ }
214
+
215
+ asort( $countries );
216
+ ?>
217
+ <tr valign="top">
218
+ <th scope="row" class="titledesc">
219
+ <label for="<?php echo esc_attr( $data['id'] ); ?>"><?php echo esc_html( $data['title'] ); ?><?php echo $this->get_tooltip_html( $data ); //phpcs:ignore ?></label>
220
+ </th>
221
+ <td class="forminp">
222
+ <select multiple="multiple" name="<?php echo esc_attr( $data['id'] ); ?>[]" style="width:350px"
223
+ data-placeholder="<?php esc_attr_e( 'Choose countries / regions&hellip;', 'checkout-plugins-stripe-woo' ); ?>"
224
+ aria-label="<?php esc_attr_e( 'Country / Region', 'checkout-plugins-stripe-woo' ); ?>" class="wc-enhanced-select"
225
+ <?php echo $this->get_custom_attribute_html( $data ); //phpcs:ignore ?>>
226
+ <?php
227
+ if ( ! empty( $countries ) ) {
228
+ foreach ( $countries as $key => $val ) {
229
+ echo '<option value="' . esc_attr( $key ) . '"' . wc_selected( $key, $selections ) . '>' . esc_html( $val ) . '</option>'; //phpcs:ignore
230
+ }
231
+ }
232
+ ?>
233
+ </select>
234
+ <?php echo $this->get_description_html( $data ); //phpcs:ignore ?>
235
+ <br/>
236
+ <a class="select_all button" href="#"><?php esc_html_e( 'Select all', 'checkout-plugins-stripe-woo' ); ?></a>
237
+ <a class="select_none button" href="#"><?php esc_html_e( 'Select none', 'checkout-plugins-stripe-woo' ); ?></a>
238
+ </td>
239
+ </tr>
240
+ <?php
241
+ return ob_get_clean();
242
+ }
243
+
244
+ /**
245
+ * Validate multi select countries form field
246
+ *
247
+ * @since 1.2.0
248
+ *
249
+ * @param array $attribs Selling location custom attributes.
250
+ *
251
+ * @return array
252
+ */
253
+ public function get_custom_attribute_html( $attribs ) {
254
+ if ( ! empty( $attribs['custom_attributes'] ) && is_array( $attribs['custom_attributes'] ) ) {
255
+ foreach ( $attribs['custom_attributes'] as $k => $v ) {
256
+ if ( is_array( $v ) ) {
257
+ $attribs['custom_attributes'][ $k ] = htmlspecialchars( wp_json_encode( $v ) );
258
+ }
259
+ }
260
+ }
261
+
262
+ return parent::get_custom_attribute_html( $attribs );
263
+ }
264
+
265
+ /**
266
+ * Validate multi select countries form field
267
+ *
268
+ * @since 1.2.0
269
+ *
270
+ * @param string $key Selling location field key.
271
+ * @param array $value Selling location field data.
272
+ *
273
+ * @return array
274
+ */
275
+ public function validate_multi_select_countries_field( $key, $value ) {
276
+ return is_array( $value ) ? array_map( 'wc_clean', array_map( 'stripslashes', $value ) ) : '';
277
+ }
278
+
279
+ /**
280
+ * Gets payment gateway icons for local gateways.
281
+ *
282
+ * @since 1.2.0
283
+ *
284
+ * @return string
285
+ */
286
+ public function get_icon() {
287
+ return $this->payment_icons( $this->id );
288
+ }
289
+
290
+ /**
291
+ * Add notices
292
+ *
293
+ * @since 1.2.0
294
+ *
295
+ * @return void
296
+ */
297
+ public function add_notice() {
298
+ $notice = Notice::get_instance();
299
+
300
+ if ( ! $notice->is_cpsw_section( $this->id ) ) {
301
+ return;
302
+ }
303
+
304
+ // Add notice if missing webhook secret key.
305
+ if (
306
+ 'no' !== get_option( 'cpsw_show_webhook_secret_notice' ) &&
307
+ ! Helper::get_webhook_secret()
308
+ ) {
309
+ /* translators: %1$s Webhook secret page link, %3$s Payment method, %4$s Webhook guide page link */
310
+ $notice->add( 'webhook_secret', 'notice notice-warning', sprintf( __( 'Stripe recommends using the %1$swebhook%2$s for %3$s. %4$sWebhook Guide%5$s ', 'checkout-plugins-stripe-woo' ), '<a href="' . admin_url( 'admin.php?page=wc-settings&tab=cpsw_api_settings' ) . '">', '</a>', $this->method_title, '<a href="https://checkoutplugins.com/docs/stripe-card-payments/#webhook" target="_blank">', '</a>' ), true );
311
+ }
312
+
313
+ // Add notice if currency not supported.
314
+ if (
315
+ method_exists( $this, 'get_supported_currency' ) &&
316
+ ! in_array( get_woocommerce_currency(), $this->get_supported_currency(), true ) &&
317
+ 'no' !== get_option( 'cpsw_show_' . $this->id . '_currency_notice' )
318
+ ) {
319
+ /* translators: %1$s Payment method, %2$s List of supported currencies */
320
+ $notice->add( $this->id . '_currency', 'notice notice-error', sprintf( __( '%1$s is enabled - it requires store currency to be set to %2$s.', 'checkout-plugins-stripe-woo' ), ucfirst( str_replace( 'cpsw_', '', $this->id ) ), implode( ', ', $this->get_supported_currency() ) ), true );
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Return a description for (for admin sections) describing the required currency & or billing country(s).
326
+ *
327
+ * @since 1.2.0
328
+ *
329
+ * @return string
330
+ */
331
+ public function payment_description() {
332
+ $desc = '';
333
+ if ( $this->get_supported_currency() ) {
334
+ // translators: %s: supported currency.
335
+ $desc .= sprintf( __( 'This gateway will appear when store currency is <strong>%s</strong>.', 'checkout-plugins-stripe-woo' ), implode( ', ', $this->get_supported_currency() ) );
336
+ }
337
+
338
+ return $this->get_payment_description( $desc );
339
+ }
340
+
341
+ /**
342
+ * Get test mode description for local gateways
343
+ *
344
+ * @return string
345
+ * @since 1.2.0
346
+ */
347
+ public function get_test_mode_description() {
348
+ /* translators: HTML Entities. */
349
+ return apply_filters( 'cpsw_local_gateway_test_description', sprintf( esc_html__( '%1$1s%2$2sTest Mode Enabled%3$3s : You will be redirected to an authorization page hosted by Stripe.', 'checkout-plugins-stripe-woo' ), '<br/>', '<strong>', '</strong>' ) );
350
+ }
351
+ }
gateway/stripe/alipay.php ADDED
@@ -0,0 +1,336 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Alipay Gateway
4
+ *
5
+ * @package checkout-plugins-stripe-woo
6
+ * @since 1.2.0
7
+ */
8
+
9
+ namespace CPSW\Gateway\Stripe;
10
+
11
+ use CPSW\Inc\Helper;
12
+ use CPSW\Inc\Logger;
13
+ use CPSW\Inc\Traits\Get_Instance;
14
+ use CPSW\Gateway\Local_Gateway;
15
+ use WC_AJAX;
16
+ use Exception;
17
+ use WP_Error;
18
+
19
+ /**
20
+ * Alipay
21
+ *
22
+ * @since 1.2.0
23
+ */
24
+ class Alipay extends Local_Gateway {
25
+
26
+ use Get_Instance;
27
+
28
+ /**
29
+ * Gateway id
30
+ *
31
+ * @var string
32
+ */
33
+ public $id = 'cpsw_alipay';
34
+
35
+ /**
36
+ * Constructor
37
+ *
38
+ * @since 1.2.0
39
+ */
40
+ public function __construct() {
41
+ parent::__construct();
42
+
43
+ $this->method_title = __( 'Alipay', 'checkout-plugins-stripe-woo' );
44
+ $this->method_description = $this->method_description();
45
+ $this->has_fields = true;
46
+ $this->init_supports();
47
+
48
+ $this->init_form_fields();
49
+ $this->init_settings();
50
+ // get_option should be called after init_form_fields().
51
+ $this->title = $this->get_option( 'title' );
52
+ $this->description = $this->get_option( 'description' );
53
+ $this->order_button_text = $this->get_option( 'order_button_text' );
54
+
55
+ add_action( 'wc_ajax_cpsw_verify_alipay_payment_intent', [ $this, 'verify_intent' ] );
56
+ add_filter( 'woocommerce_payment_successful_result', [ $this, 'modify_successful_payment_result' ], 999, 2 );
57
+ }
58
+
59
+ /**
60
+ * Description for alipay gateway
61
+ *
62
+ * @since 1.2.0
63
+ *
64
+ * @return string
65
+ */
66
+ public function method_description() {
67
+ $payment_description = $this->payment_description();
68
+
69
+ return sprintf(
70
+ /* translators: %1$s: Break, %2$s: Gateway appear message, %3$s: Break, %4$s: Gateway appear message currency wise, %4$s: HTML entities */
71
+ __( 'Accept payment using Alipay. %1$s %2$s %3$s %4$s', 'checkout-plugins-stripe-woo' ),
72
+ '<br/>',
73
+ $payment_description,
74
+ '<br/>',
75
+ /* translators: HTML Entities.*/
76
+ sprintf( __( '%1$sEUR%2$s is supported only for billing country %1$sDenmark (DK), Norway (NO), Sweden (SE) & Switzerland (CH)%2$s.', 'checkout-plugins-stripe-woo' ), '<strong>', '</strong>' ),
77
+ );
78
+ }
79
+
80
+ /**
81
+ * Returns all supported currencies for this payment method.
82
+ *
83
+ * @since 1.2.0
84
+ *
85
+ * @return array
86
+ */
87
+ public function get_supported_currency() {
88
+ return apply_filters(
89
+ 'cpsw_alipay_supported_currencies',
90
+ [
91
+ 'EUR',
92
+ 'AUD',
93
+ 'CAD',
94
+ 'CNY',
95
+ 'GBP',
96
+ 'HKD',
97
+ 'JPY',
98
+ 'NZD',
99
+ 'SGD',
100
+ 'USD',
101
+ 'MYR',
102
+ ]
103
+ );
104
+ }
105
+
106
+ /**
107
+ * Checks whether this gateway is available.
108
+ *
109
+ * @since 1.2.0
110
+ *
111
+ * @return boolean
112
+ */
113
+ public function is_available() {
114
+ if ( ! in_array( $this->get_currency(), $this->get_supported_currency(), true ) ) {
115
+ return false;
116
+ }
117
+
118
+ if ( 'EUR' === $this->get_currency() && ! in_array( $this->get_billing_country(), [ 'DK', 'NO', 'SE', 'CH' ], true ) ) {
119
+ return false;
120
+ }
121
+
122
+ return parent::is_available();
123
+ }
124
+
125
+ /**
126
+ * Creates markup for payment form for card payments
127
+ *
128
+ * @since 1.2.0
129
+ *
130
+ * @return void
131
+ */
132
+ public function payment_fields() {
133
+ global $wp;
134
+
135
+ $user = wp_get_current_user();
136
+ $total = WC()->cart->total;
137
+
138
+ // If paying from order, we need to get total from order not cart.
139
+ if ( isset( $_GET['pay_for_order'] ) && ! empty( $_GET['key'] ) ) { // phpcs:ignore
140
+ $order = wc_get_order( wc_clean( $wp->query_vars['order-pay'] ) );
141
+ $total = $order->get_total();
142
+ }
143
+
144
+ if ( is_add_payment_method_page() ) {
145
+ $pay_button_text = __( 'Add Payment', 'checkout-plugins-stripe-woo' );
146
+ $total = '';
147
+ } else {
148
+ $pay_button_text = '';
149
+ }
150
+
151
+ echo '<div
152
+ id="cpsw-alipay-payment-data"
153
+ data-amount="' . esc_attr( $total ) . '"
154
+ data-currency="' . esc_attr( strtolower( $this->get_currency() ) ) . '">';
155
+
156
+ if ( $this->description ) {
157
+ echo wp_kses_post( $this->description );
158
+ }
159
+
160
+ echo '</div>';
161
+ if ( 'test' === Helper::get_payment_mode() ) {
162
+ echo '<div class="cpsw_stripe_alipay_test_description">';
163
+ echo wp_kses_post( $this->get_test_mode_description() );
164
+ echo '</div>';
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Verify intent state and redirect.
170
+ *
171
+ * @since 1.2.0
172
+ *
173
+ * @return void
174
+ */
175
+ public function verify_intent() {
176
+ $order_id = isset( $_GET['order'] ) ? sanitize_text_field( $_GET['order'] ) : 0; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
177
+ $order = wc_get_order( $order_id );
178
+
179
+ $intent_secret = get_post_meta( $order_id, '_cpsw_intent_secret', true );
180
+ $stripe_api = new Stripe_Api();
181
+ $response = $stripe_api->payment_intents( 'retrieve', [ $intent_secret['id'] ] );
182
+ $intent = $response['success'] ? $response['data'] : false;
183
+
184
+ if ( 'succeeded' === $intent->status || 'requires_capture' === $intent->status ) {
185
+ $redirect_to = $this->process_order( end( $intent->charges->data ), $order_id );
186
+ $redirect_url = apply_filters( 'cpsw_redirect_order_url', $redirect_to, $order );
187
+ wp_safe_redirect( $redirect_url );
188
+ } elseif ( isset( $response['data']->last_payment_error ) ) {
189
+ $message = isset( $response['data']->last_payment_error->message ) ? $response['data']->last_payment_error->message : '';
190
+
191
+ // translators: %s: payment fail message.
192
+ wc_add_notice( sprintf( __( 'Payment failed. %s', 'checkout-plugins-stripe-woo' ), $message ), 'error' );
193
+ wp_safe_redirect( wc_get_checkout_url() );
194
+ }
195
+ exit();
196
+ }
197
+
198
+ /**
199
+ * Process order after stripe payment
200
+ *
201
+ * @since 1.2.0
202
+ *
203
+ * @param object $response intent response data.
204
+ * @param string $order_id currnt coocommerce id.
205
+ *
206
+ * @return array return data.
207
+ */
208
+ public function process_order( $response, $order_id ) {
209
+ $order = wc_get_order( $order_id );
210
+
211
+ if ( true === $response->captured ) {
212
+ $order->payment_complete( $response->id );
213
+ /* translators: order id */
214
+ Logger::info( sprintf( __( 'Payment successful Order id - %1s', 'checkout-plugins-stripe-woo' ), $order->get_id() ), true );
215
+ }
216
+ if ( ! $response->captured ) {
217
+ /* translators: transaction id */
218
+ $order->update_status( 'on-hold', sprintf( __( 'Charge authorized (Charge ID: %s). Process order to take payment, or cancel to remove the pre-authorization. Attempting to refund the order in part or in full will release the authorization and cancel the payment.', 'checkout-plugins-stripe-woo' ), $response->id ) );
219
+ /* translators: transaction id */
220
+ Logger::info( sprintf( __( 'Charge authorized Order id - %1s', 'checkout-plugins-stripe-woo' ), $order->get_id() ), true );
221
+ }
222
+
223
+ $order->add_order_note( __( 'Payment Status : ', 'checkout-plugins-stripe-woo' ) . ucfirst( $response->status ) . __( ' Source : ', 'checkout-plugins-stripe-woo' ) . $response->payment_method_details->type );
224
+ WC()->cart->empty_cart();
225
+
226
+ return $this->get_return_url( $order );
227
+ }
228
+
229
+ /**
230
+ * Modify redirect url
231
+ *
232
+ * @since 1.2.0
233
+ *
234
+ * @param array $result redirect url array.
235
+ * @param int $order_id woocommerce order id.
236
+ *
237
+ * @return array modified redirect url array.
238
+ */
239
+ public function modify_successful_payment_result( $result, $order_id ) {
240
+ if ( empty( $order_id ) ) {
241
+ return $result;
242
+ }
243
+
244
+ $order = wc_get_order( $order_id );
245
+
246
+ if ( $this->id !== $order->get_payment_method() ) {
247
+ return $result;
248
+ }
249
+
250
+ if ( ! isset( $result['intent_secret'] ) ) {
251
+ return $result;
252
+ }
253
+
254
+ // Put the final thank you page redirect into the verification URL.
255
+ $verification_url = add_query_arg(
256
+ [
257
+ 'order' => $order_id,
258
+ 'confirm_payment_nonce' => wp_create_nonce( 'cpsw_confirm_payment_intent' ),
259
+ 'redirect_to' => rawurlencode( $result['redirect'] ),
260
+ ],
261
+ WC_AJAX::get_endpoint( 'cpsw_verify_alipay_payment_intent' )
262
+ );
263
+
264
+ // Combine into a hash.
265
+ $redirect = sprintf( '#confirm-pi-%s:%s', $result['intent_secret'], rawurlencode( $verification_url ) );
266
+
267
+ return [
268
+ 'result' => 'success',
269
+ 'redirect' => $redirect,
270
+ ];
271
+ }
272
+
273
+ /**
274
+ * Process woocommerce orders after payment is done
275
+ *
276
+ * @since 1.2.0
277
+ *
278
+ * @param int $order_id wooCommerce order id.
279
+ *
280
+ * @return array data to redirect after payment processing.
281
+ */
282
+ public function process_payment( $order_id ) {
283
+ try {
284
+ $order = wc_get_order( $order_id );
285
+ $customer_id = $this->get_customer_id( $order );
286
+ $idempotency_key = $order->get_order_key() . time();
287
+
288
+ $data = [
289
+ 'amount' => $this->get_formatted_amount( $order->get_total() ),
290
+ 'currency' => $this->get_currency(),
291
+ 'description' => $this->get_order_description( $order ),
292
+ 'metadata' => $this->get_metadata( $order_id ),
293
+ 'payment_method_types' => [ 'alipay' ],
294
+ 'customer' => $customer_id,
295
+ ];
296
+
297
+ /* translators: %1$1s order id, %2$2s order total amount */
298
+ Logger::info( sprintf( __( 'Begin processing payment with Alipay for order %1$1s for the amount of %2$2s', 'checkout-plugins-stripe-woo' ), $order_id, $order->get_total() ) );
299
+ $intent_data = $this->get_payment_intent( $order_id, $idempotency_key, $data, true );
300
+
301
+ if ( $intent_data ) {
302
+ if ( isset( $intent_data['success'] ) && false === $intent_data['success'] ) {
303
+ $error = '';
304
+ if ( 'currency' === $intent_data['type'] ) {
305
+ $error = __( 'Contact seller. ', 'checkout-plugins-stripe-woo' );
306
+
307
+ if ( 'test' === Helper::get_payment_mode() ) {
308
+ $error = __( 'Store currency doesn\'t match stripe currency. ', 'checkout-plugins-stripe-woo' );
309
+ }
310
+ }
311
+
312
+ wc_add_notice( $error . $intent_data['message'], 'error' );
313
+
314
+ return [
315
+ 'result' => 'fail',
316
+ 'redirect' => '',
317
+ ];
318
+ }
319
+
320
+ return [
321
+ 'result' => 'success',
322
+ 'redirect' => false,
323
+ 'intent_secret' => $intent_data['client_secret'],
324
+ ];
325
+ } else {
326
+ return [
327
+ 'result' => 'fail',
328
+ 'redirect' => '',
329
+ ];
330
+ }
331
+ } catch ( Exception $e ) {
332
+ Logger::error( $e->getMessage(), true );
333
+ return new WP_Error( 'order-error', '<div class="woocommerce-error">' . $e->getMessage() . '</div>', [ 'status' => 200 ] );
334
+ }
335
+ }
336
+ }
gateway/stripe/card-payments.php CHANGED
@@ -13,7 +13,6 @@ use CPSW\Inc\Logger;
13
  use CPSW\Inc\Traits\Get_Instance;
14
  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_AJAX;
19
  use WC_HTTPS;
@@ -31,19 +30,6 @@ class Card_Payments extends Abstract_Payment_Gateway {
31
  use Get_Instance;
32
  use Subscriptions;
33
 
34
- /**
35
- * Zero currencies accepted by stripe.
36
- *
37
- * @var array
38
- */
39
- private static $zero_currencies = [ 'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VUV', 'XAF', 'XOF', 'XPF', 'VND' ];
40
-
41
- /**
42
- * Temporary array of allowed cards, further data will be taken from user
43
- *
44
- * @var array
45
- */
46
-
47
  /**
48
  * Gateway id
49
  *
@@ -57,7 +43,7 @@ class Card_Payments extends Abstract_Payment_Gateway {
57
  * @since 0.0.1
58
  */
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' );
@@ -77,12 +63,9 @@ class Card_Payments extends Abstract_Payment_Gateway {
77
  $this->allowed_cards = empty( $this->get_option( 'allowed_cards' ) ) ? [ 'mastercard', 'visa', 'diners', 'discover', 'amex', 'jcb', 'unionpay' ] : $this->get_option( 'allowed_cards' );
78
  $this->statement_descriptor = $this->clean_statement_descriptor( $this->get_option( 'statement_descriptor' ) );
79
 
80
- add_action( 'woocommerce_init', [ $this, 'woocommerce_dependencies' ] );
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
  /**
@@ -100,62 +83,15 @@ class Card_Payments extends Abstract_Payment_Gateway {
100
  ];
101
  }
102
 
103
- /**
104
- * Registering Gateway to WooCommerce
105
- *
106
- * @param array $methods List of registered gateways.
107
- * @return array
108
- */
109
- public function add_gateway_class( $methods ) {
110
- $methods[] = $this;
111
- return $methods;
112
- }
113
-
114
- /**
115
- * Includes woocomerce dependencies.
116
- *
117
- * @return void
118
- */
119
- public function woocommerce_dependencies() {
120
- if ( is_admin() ) {
121
- return;
122
- }
123
- Frontend_Scripts::get_instance();
124
- }
125
-
126
- /**
127
- * Checks whether this gateway is available.
128
- *
129
- * @return boolean
130
- */
131
- public function is_available() {
132
- if ( 'yes' === $this->enabled ) {
133
- if ( ! Helper::get_payment_mode() && is_checkout() ) {
134
- return false;
135
- }
136
- if ( 'test' === Helper::get_payment_mode() ) {
137
- if ( empty( Helper::get_setting( 'cpsw_test_pub_key' ) ) || empty( Helper::get_setting( 'cpsw_test_secret_key' ) ) ) {
138
- return false;
139
- }
140
- } else {
141
- if ( empty( Helper::get_setting( 'cpsw_pub_key' ) ) || empty( Helper::get_setting( 'cpsw_secret_key' ) ) ) {
142
- return false;
143
- }
144
- }
145
- return true;
146
- }
147
- return false;
148
- }
149
-
150
  /**
151
  * Gateway form fields
152
  *
153
  * @return void
154
- */
155
  public function init_form_fields() {
156
  $this->form_fields = [
157
  'enabled' => [
158
- 'label' => __( 'Enable/Disable', 'checkout-plugins-stripe-woo' ),
159
  'type' => 'checkbox',
160
  'title' => __( 'Enable Stripe Gateway', 'checkout-plugins-stripe-woo' ),
161
  'default' => 'no',
@@ -276,6 +212,7 @@ class Card_Payments extends Abstract_Payment_Gateway {
276
  'description' => $this->get_order_description( $order ),
277
  'payment_method_types' => [ 'card' ],
278
  'payment_method' => $payment_method,
 
279
  'customer' => $customer_id,
280
  'capture_method' => $this->capture_method,
281
  ];
@@ -290,12 +227,12 @@ class Card_Payments extends Abstract_Payment_Gateway {
290
 
291
  /* translators: %1$1s order id, %2$2s order total amount */
292
  Logger::info( sprintf( __( 'Begin processing payment with new payment method for order %1$1s for the amount of %2$2s', 'checkout-plugins-stripe-woo' ), $order_id, $order->get_total() ) );
293
- $intent_data = $this->get_payment_intent( $order_id, $payment_method, $customer_id, $idempotency_key, $data );
294
  if ( $intent_data ) {
295
  return [
296
  'result' => 'success',
297
  'redirect' => $this->get_return_url( $order ),
298
- 'payment_method' => $intent_data['payment_method'],
299
  'intent_secret' => $intent_data['client_secret'],
300
  'save_card' => $this->should_save_card( $order_id ),
301
  ];
@@ -350,6 +287,7 @@ class Card_Payments extends Abstract_Payment_Gateway {
350
  'currency' => strtolower( $order->get_currency() ),
351
  'description' => $this->get_order_description( $order ),
352
  'customer' => $payment_method->customer,
 
353
  'confirm' => true,
354
  'capture_method' => $this->capture_method,
355
  ];
@@ -506,74 +444,6 @@ class Card_Payments extends Abstract_Payment_Gateway {
506
  return ( isset( $_POST[ 'wc-' . $payment_method . '-payment-token' ] ) && 'new' !== $_POST[ 'wc-' . $payment_method . '-payment-token' ] ); //phpcs:ignore WordPress.Security.NonceVerification.Missing
507
  }
508
 
509
- /**
510
- * Process response for saved cards
511
- *
512
- * @param object $response intent response.
513
- * @param object $order order response.
514
- * @return array
515
- */
516
- public function process_response( $response, $order ) {
517
- Logger::info( 'Processing: ' . $response->id );
518
-
519
- $order_id = $order->get_id();
520
- $captured = ( isset( $response->captured ) && $response->captured ) ? 'yes' : 'no';
521
-
522
- // Store charge data.
523
- $order->update_meta_data( '_cpsw_charge_captured', $captured );
524
-
525
- if ( 'yes' === $captured ) {
526
- /**
527
- * Charge can be captured but in a pending state. Payment methods
528
- * that are asynchronous may take couple days to clear. Webhook will
529
- * take care of the status changes.
530
- */
531
- if ( 'pending' === $response->status ) {
532
- $order_stock_reduced = $order->get_meta( '_order_stock_reduced', true );
533
-
534
- if ( ! $order_stock_reduced ) {
535
- wc_reduce_stock_levels( $order_id );
536
- }
537
-
538
- $order->set_transaction_id( $response->id );
539
- /* translators: transaction id */
540
- $order->update_status( 'on-hold', sprintf( __( 'Stripe charge awaiting payment: %s.', 'checkout-plugins-stripe-woo' ), $response->id ) );
541
- }
542
-
543
- if ( 'succeeded' === $response->status ) {
544
- $order->payment_complete( $response->id );
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 );
559
-
560
- if ( $order->has_status( [ 'pending', 'failed' ] ) ) {
561
- wc_reduce_stock_levels( $order_id );
562
- }
563
-
564
- /* translators: transaction id */
565
- $order->update_status( 'on-hold', sprintf( __( 'Stripe charge authorized (Charge ID: %s). Process order to take payment, or cancel to remove the pre-authorization. Attempting to refund the order in part or in full will release the authorization and cancel the payment.', 'checkout-plugins-stripe-woo' ), $response->id ) );
566
- }
567
-
568
- if ( is_callable( [ $order, 'save' ] ) ) {
569
- $order->save();
570
- }
571
-
572
- do_action( 'cpsw_process_response', $response, $order );
573
-
574
- return $response;
575
- }
576
-
577
  /**
578
  * Get token object for selected saved card payment
579
  *
@@ -640,58 +510,6 @@ class Card_Payments extends Abstract_Payment_Gateway {
640
  ];
641
  }
642
 
643
- /**
644
- * Checks if payment intent available for current order or else creates new payment intent.
645
- *
646
- * @param int $order_id woocommerce order id.
647
- * @param string $payment_method cc payment method id.
648
- * @param string $customer_id customer id.
649
- * @param string $idempotency_key unique idempotency key.
650
- * @param array $args payment_intent arguments.
651
- * @return array intent data.
652
- */
653
- public function get_payment_intent( $order_id, $payment_method, $customer_id, $idempotency_key, $args ) {
654
- $intent_secret = get_post_meta( $order_id, '_cpsw_intent_secret', true );
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() );
664
- }
665
- }
666
-
667
- $args = [
668
- [ $args ],
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'];
677
- } else {
678
- wc_add_notice( $response['message'], 'error' );
679
- return false;
680
- }
681
-
682
- $intent_data = [
683
- 'id' => $intent->id,
684
- 'client_secret' => $intent->client_secret,
685
- ];
686
- update_post_meta( $order_id, '_cpsw_intent_secret', $intent_data, );
687
- $client_secret = $intent->client_secret;
688
-
689
- return [
690
- 'payment_method' => $payment_method,
691
- 'client_secret' => $client_secret,
692
- ];
693
- }
694
-
695
  /**
696
  * Verify intent state and redirect.
697
  *
@@ -786,7 +604,7 @@ class Card_Payments extends Abstract_Payment_Gateway {
786
  * Process order after stripe payment
787
  *
788
  * @param object $response intent response data.
789
- * @param string $order_id currnt coocommerce id.
790
  * @return array return data.
791
  */
792
  public function process_order( $response, $order_id ) {
@@ -808,10 +626,7 @@ class Card_Payments extends Abstract_Payment_Gateway {
808
  $order->add_order_note( __( 'Payment Status : ', 'checkout-plugins-stripe-woo' ) . ucfirst( $response->status ) . __( ' Source : ', 'checkout-plugins-stripe-woo' ) . $response->payment_method_details->card->brand );
809
  WC()->cart->empty_cart();
810
 
811
- return [
812
- 'result' => 'success',
813
- 'redirect' => $this->get_return_url( $order ),
814
- ];
815
  }
816
 
817
  /**
@@ -822,6 +637,15 @@ class Card_Payments extends Abstract_Payment_Gateway {
822
  * @return array modified redirect url array.
823
  */
824
  public function modify_successful_payment_result( $result, $order_id ) {
 
 
 
 
 
 
 
 
 
825
  if ( ! isset( $result['intent_secret'] ) ) {
826
  return $result;
827
  }
@@ -846,125 +670,6 @@ class Card_Payments extends Abstract_Payment_Gateway {
846
  ];
847
  }
848
 
849
- /**
850
- * Creates stripe customer object
851
- *
852
- * @param object $order woocommerce order object.
853
- * @param boolean|string $user_email user email id.
854
- * @return Stripe::Customer
855
- */
856
- public function create_stripe_customer( $order = false, $user_email = false ) {
857
- $args = [
858
- 'email' => $user_email,
859
- ];
860
- if ( $order ) {
861
- $args = [
862
- 'description' => 'Customer for Order #' . $order->get_id(),
863
- 'email' => $user_email ? $user_email : $order->get_billing_email(),
864
- 'address' => [ // sending name and billing address to stripe to support indian exports.
865
- 'city' => method_exists( $order, 'get_billing_city' ) ? $order->get_billing_city() : $order->billing_city,
866
- 'country' => method_exists( $order, 'get_billing_country' ) ? $order->get_billing_country() : $order->billing_country,
867
- 'line1' => method_exists( $order, 'get_billing_address_1' ) ? $order->get_billing_address_1() : $order->billing_address_1,
868
- 'line2' => method_exists( $order, 'get_billing_address_2' ) ? $order->get_billing_address_2() : $order->billing_address_2,
869
- 'postal_code' => method_exists( $order, 'get_billing_postcode' ) ? $order->get_billing_postcode() : $order->billing_postcode,
870
- 'state' => method_exists( $order, 'get_billing_state' ) ? $order->get_billing_state() : $order->billing_state,
871
- ],
872
- 'name' => ( method_exists( $order, 'get_billing_first_name' ) ? $order->get_billing_first_name() : $order->billing_first_name ) . ( method_exists( $order, 'get_billing_last_name' ) ? $order->get_billing_last_name() : $order->billing_last_name ),
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;
882
- }
883
-
884
- return $response;
885
- }
886
-
887
- /**
888
- * Refunds amount from stripe and return true/false as result
889
- *
890
- * @param string $order_id order id.
891
- * @param string $amount refund amount.
892
- * @param string $reason reason of refund.
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 ) {
911
- $refund_time = gmdate( 'Y-m-d H:i:s', time() );
912
- $order->add_order_note( __( 'Reason : ', 'checkout-plugins-stripe-woo' ) . $reason . '.<br>' . __( 'Amount : ', 'checkout-plugins-stripe-woo' ) . get_woocommerce_currency_symbol() . $amount . '.<br>' . __( 'Status : ', 'checkout-plugins-stripe-woo' ) . ( ( 'succeeded' === $refund_response->status ) ? 'Success' : 'Failed' ) . ' [ ' . $refund_time . ' ] ' . ( is_null( $refund_response->id ) ? '' : '<br>' . __( 'Transaction ID : ', 'checkout-plugins-stripe-woo' ) . $refund_response->id ) );
913
- Logger::info( __( 'Refund initiated: ', 'checkout-plugins-stripe-woo' ) . __( 'Reason : ', 'checkout-plugins-stripe-woo' ) . $reason . __( 'Amount : ', 'checkout-plugins-stripe-woo' ) . get_woocommerce_currency_symbol() . $amount . __( 'Status : ', 'checkout-plugins-stripe-woo' ) . ( ( 'succeeded' === $refund_response->status ) ? 'Success' : 'Failed' ) . ' [ ' . $refund_time . ' ] ' . ( is_null( $refund_response->id ) ? '' : __( 'Transaction ID : ', 'checkout-plugins-stripe-woo' ) . $refund_response->id ), true );
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
  */
@@ -996,41 +701,6 @@ class Card_Payments extends Abstract_Payment_Gateway {
996
  return apply_filters( 'woocommerce_gateway_icon', $icon, $this->id );
997
  }
998
 
999
-
1000
- /**
1001
- * Returns amount as per currency type
1002
- *
1003
- * @param string $total amount to be processed.
1004
- * @param string $currency transaction currency.
1005
- * @return int
1006
- */
1007
- public function get_formatted_amount( $total, $currency = '' ) {
1008
- if ( ! $currency ) {
1009
- $currency = get_woocommerce_currency();
1010
- }
1011
-
1012
- if ( in_array( strtoupper( $currency ), self::$zero_currencies, true ) ) {
1013
- // Zero decimal currencies accepted by stripe.
1014
- $total = absint( $total );
1015
- } else {
1016
- $total = round( $total, 2 ) * 100;
1017
- }
1018
- return $total;
1019
- }
1020
-
1021
- /**
1022
- * Basic details of logged in user
1023
- *
1024
- * @return array current user data.
1025
- */
1026
- public function get_clients_details() {
1027
- return [
1028
- 'IP' => isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( $_SERVER['REMOTE_ADDR'] ) : '',
1029
- 'Agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] ) : '',
1030
- 'Referer' => isset( $_SERVER['HTTP_REFERER'] ) ? sanitize_text_field( $_SERVER['HTTP_REFERER'] ) : '',
1031
- ];
1032
- }
1033
-
1034
  /**
1035
  * Creates markup for payment form for card payments
1036
  *
@@ -1046,12 +716,7 @@ class Card_Payments extends Abstract_Payment_Gateway {
1046
  echo '<div class="status-box"></div>';
1047
  echo '<div class="cpsw-stipe-pay-data">';
1048
  echo '<div class="cpsw-stripe-info">';
1049
- if ( 'live' !== Helper::get_payment_mode() ) {
1050
- /* translators: %1$1s - %6$6s: HTML Markup */
1051
- printf( esc_html__( '%1$1s Test Mode Enabled %2$2s : Use demo card 4242424242424242 with any future date and CVV. %3$3s Check more %4$4sDemo Cards%5$5s %6$6s', 'checkout-plugins-stripe-woo' ), '<b>', '</b>', '</br>', "<a href='https://stripe.com/docs/testing' referrer='noopener' target='_blank'>", '</a>', '</br></br>' );
1052
- } else {
1053
- echo wp_kses_post( wpautop( $this->description ) );
1054
- }
1055
  echo '</div>';
1056
  if ( $display_tokenization ) {
1057
  $this->tokenization_script();
@@ -1081,37 +746,13 @@ class Card_Payments extends Abstract_Payment_Gateway {
1081
  echo ( apply_filters( 'cpsw_display_save_payment_method_checkbox', $display_tokenization ) && $this->enable_saved_cards() ) ? '<span class="cpsw-save-cards"><label><input type="checkbox" name="enable_saved_card" value="on"/>' . wp_kses_post( apply_filters( 'cpsw_saved_cards_label', __( 'Save Card for Future Payments', 'checkout-plugins-stripe-woo' ) ) ) . '</label></span>' : '';
1082
  do_action( 'cpsw_payment_fields_cpsw_stripe', $this->id );
1083
  echo '</div>';
1084
- echo '</div>';
1085
- }
1086
-
1087
- /**
1088
- * Get/Retrieve stripe customer id if exists
1089
- *
1090
- * @param mixed $order current woocommerce order.
1091
- * @return mixed customer id
1092
- */
1093
- public function get_customer_id( $order = false ) {
1094
- $user = wp_get_current_user();
1095
- $user_id = ( $user->ID && $user->ID > 0 ) ? $user->ID : false;
1096
- $customer_id = false;
1097
- if ( $user_id ) {
1098
- $customer_id = get_user_option( '_cpsw_customer_id', $user_id );
1099
- if ( $customer_id ) {
1100
- return $customer_id;
1101
- }
1102
- }
1103
-
1104
- $customer = false;
1105
- if ( ! $customer_id ) {
1106
- $customer = $this->create_stripe_customer( $order, $user->email );
1107
- }
1108
-
1109
- if ( $customer ) {
1110
- if ( $user_id ) {
1111
- update_user_option( $user_id, '_cpsw_customer_id', $customer->id, false );
1112
- }
1113
- return $customer->id;
1114
  }
 
1115
  }
1116
 
1117
  /**
@@ -1182,4 +823,3 @@ class Card_Payments extends Abstract_Payment_Gateway {
1182
  ];
1183
  }
1184
  }
1185
-
13
  use CPSW\Inc\Traits\Get_Instance;
14
  use CPSW\Inc\Traits\Subscriptions;
15
  use CPSW\Gateway\Abstract_Payment_Gateway;
 
16
  use CPSW\Gateway\Stripe\Stripe_Api;
17
  use WC_AJAX;
18
  use WC_HTTPS;
30
  use Get_Instance;
31
  use Subscriptions;
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  /**
34
  * Gateway id
35
  *
43
  * @since 0.0.1
44
  */
45
  public function __construct() {
46
+ parent::__construct();
47
 
48
  $this->method_title = __( 'Stripe Card Processing', 'checkout-plugins-stripe-woo' );
49
  $this->method_description = __( 'Accepts payments via Credit/Debit Cards', 'checkout-plugins-stripe-woo' );
63
  $this->allowed_cards = empty( $this->get_option( 'allowed_cards' ) ) ? [ 'mastercard', 'visa', 'diners', 'discover', 'amex', 'jcb', 'unionpay' ] : $this->get_option( 'allowed_cards' );
64
  $this->statement_descriptor = $this->clean_statement_descriptor( $this->get_option( 'statement_descriptor' ) );
65
 
 
 
66
  add_filter( 'woocommerce_payment_successful_result', [ $this, 'modify_successful_payment_result' ], 999, 2 );
67
  add_action( 'wc_ajax_cpsw_verify_payment_intent', [ $this, 'verify_intent' ] );
68
  add_filter( 'woocommerce_payment_complete_order_status', [ $this, 'cpsw_payment_complete_order_status' ], 10, 3 );
 
69
  }
70
 
71
  /**
83
  ];
84
  }
85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  /**
87
  * Gateway form fields
88
  *
89
  * @return void
90
+ $ */
91
  public function init_form_fields() {
92
  $this->form_fields = [
93
  'enabled' => [
94
+ 'label' => ' ',
95
  'type' => 'checkbox',
96
  'title' => __( 'Enable Stripe Gateway', 'checkout-plugins-stripe-woo' ),
97
  'default' => 'no',
212
  'description' => $this->get_order_description( $order ),
213
  'payment_method_types' => [ 'card' ],
214
  'payment_method' => $payment_method,
215
+ 'metadata' => $this->get_metadata( $order_id ),
216
  'customer' => $customer_id,
217
  'capture_method' => $this->capture_method,
218
  ];
227
 
228
  /* translators: %1$1s order id, %2$2s order total amount */
229
  Logger::info( sprintf( __( 'Begin processing payment with new payment method for order %1$1s for the amount of %2$2s', 'checkout-plugins-stripe-woo' ), $order_id, $order->get_total() ) );
230
+ $intent_data = $this->get_payment_intent( $order_id, $idempotency_key, $data );
231
  if ( $intent_data ) {
232
  return [
233
  'result' => 'success',
234
  'redirect' => $this->get_return_url( $order ),
235
+ 'payment_method' => $payment_method,
236
  'intent_secret' => $intent_data['client_secret'],
237
  'save_card' => $this->should_save_card( $order_id ),
238
  ];
287
  'currency' => strtolower( $order->get_currency() ),
288
  'description' => $this->get_order_description( $order ),
289
  'customer' => $payment_method->customer,
290
+ 'metadata' => $this->get_metadata( $order_id ),
291
  'confirm' => true,
292
  'capture_method' => $this->capture_method,
293
  ];
444
  return ( isset( $_POST[ 'wc-' . $payment_method . '-payment-token' ] ) && 'new' !== $_POST[ 'wc-' . $payment_method . '-payment-token' ] ); //phpcs:ignore WordPress.Security.NonceVerification.Missing
445
  }
446
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
  /**
448
  * Get token object for selected saved card payment
449
  *
510
  ];
511
  }
512
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
513
  /**
514
  * Verify intent state and redirect.
515
  *
604
  * Process order after stripe payment
605
  *
606
  * @param object $response intent response data.
607
+ * @param string $order_id current WooCommerce id.
608
  * @return array return data.
609
  */
610
  public function process_order( $response, $order_id ) {
626
  $order->add_order_note( __( 'Payment Status : ', 'checkout-plugins-stripe-woo' ) . ucfirst( $response->status ) . __( ' Source : ', 'checkout-plugins-stripe-woo' ) . $response->payment_method_details->card->brand );
627
  WC()->cart->empty_cart();
628
 
629
+ return $this->get_return_url( $order );
 
 
 
630
  }
631
 
632
  /**
637
  * @return array modified redirect url array.
638
  */
639
  public function modify_successful_payment_result( $result, $order_id ) {
640
+ if ( empty( $order_id ) ) {
641
+ return $result;
642
+ }
643
+
644
+ $order = wc_get_order( $order_id );
645
+ if ( 'cpsw_stripe' !== $order->get_payment_method() ) {
646
+ return $result;
647
+ }
648
+
649
  if ( ! isset( $result['intent_secret'] ) ) {
650
  return $result;
651
  }
670
  ];
671
  }
672
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
673
  /**
674
  * Get stripe activated payment cards icon.
675
  */
701
  return apply_filters( 'woocommerce_gateway_icon', $icon, $this->id );
702
  }
703
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
704
  /**
705
  * Creates markup for payment form for card payments
706
  *
716
  echo '<div class="status-box"></div>';
717
  echo '<div class="cpsw-stipe-pay-data">';
718
  echo '<div class="cpsw-stripe-info">';
719
+ echo wp_kses_post( wpautop( $this->description ) );
 
 
 
 
 
720
  echo '</div>';
721
  if ( $display_tokenization ) {
722
  $this->tokenization_script();
746
  echo ( apply_filters( 'cpsw_display_save_payment_method_checkbox', $display_tokenization ) && $this->enable_saved_cards() ) ? '<span class="cpsw-save-cards"><label><input type="checkbox" name="enable_saved_card" value="on"/>' . wp_kses_post( apply_filters( 'cpsw_saved_cards_label', __( 'Save Card for Future Payments', 'checkout-plugins-stripe-woo' ) ) ) . '</label></span>' : '';
747
  do_action( 'cpsw_payment_fields_cpsw_stripe', $this->id );
748
  echo '</div>';
749
+ if ( 'test' === Helper::get_payment_mode() ) {
750
+ echo '<div class="cpsw-test-description">';
751
+ /* translators: %1$1s - %6$6s: HTML Markup */
752
+ printf( esc_html__( '%1$1s Test Mode Enabled %2$2s : Use demo card 4242424242424242 with any future date and CVV. %3$3s Check more %4$4sDemo Cards%5$5s', 'checkout-plugins-stripe-woo' ), '<b>', '</b>', '</br>', "<a href='https://stripe.com/docs/testing' referrer='noopener' target='_blank'>", '</a>' );
753
+ echo '</div>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
754
  }
755
+ echo '</div>';
756
  }
757
 
758
  /**
823
  ];
824
  }
825
  }
 
gateway/stripe/frontend-scripts.php CHANGED
@@ -26,6 +26,13 @@ class Frontend_Scripts {
26
  */
27
  private $prefix = 'cpsw-';
28
 
 
 
 
 
 
 
 
29
  /**
30
  * Url of assets directory
31
  *
@@ -37,38 +44,60 @@ class Frontend_Scripts {
37
  * Constructor
38
  */
39
  public function __construct() {
 
40
  add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
41
  }
42
 
43
  /**
44
  * Enqueue scripts
45
  *
 
 
46
  * @return void
47
  */
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',
63
  [
64
- 'public_key' => $public_key,
65
- 'inline_cc' => Helper::get_setting( 'inline_cc', 'cpsw_stripe' ),
66
- 'is_ssl' => is_ssl(),
67
- 'mode' => Helper::get_payment_mode(),
68
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
69
- 'js_nonce' => wp_create_nonce( 'cpsw_js_error_nonce' ),
70
- 'allowed_cards' => Helper::get_setting( 'allowed_cards', 'cpsw_stripe' ),
71
- 'default_cards' => [
72
  'mastercard' => __( 'MasterCard', 'checkout-plugins-stripe-woo' ),
73
  'visa' => __( 'Visa', 'checkout-plugins-stripe-woo' ),
74
  'amex' => __( 'American Express', 'checkout-plugins-stripe-woo' ),
@@ -77,15 +106,18 @@ class Frontend_Scripts {
77
  'diners' => __( 'Diners Club', 'checkout-plugins-stripe-woo' ),
78
  'unionpay' => __( 'UnionPay', 'checkout-plugins-stripe-woo' ),
79
  ],
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
  }
@@ -96,15 +128,15 @@ class Frontend_Scripts {
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' ),
@@ -113,12 +145,14 @@ class Frontend_Scripts {
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
  );
26
  */
27
  private $prefix = 'cpsw-';
28
 
29
+ /**
30
+ * Version
31
+ *
32
+ * @var string
33
+ */
34
+ private $version = '';
35
+
36
  /**
37
  * Url of assets directory
38
  *
44
  * Constructor
45
  */
46
  public function __construct() {
47
+ $this->version = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? time() : CPSW_VERSION;
48
  add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
49
  }
50
 
51
  /**
52
  * Enqueue scripts
53
  *
54
+ * @since 1.0.0
55
+ *
56
  * @return void
57
  */
58
  public function enqueue_scripts() {
 
59
  $public_key = ( 'live' === Helper::get_payment_mode() ) ? Helper::get_setting( 'cpsw_pub_key' ) : Helper::get_setting( 'cpsw_test_pub_key' );
60
+
61
+ wp_register_script( $this->prefix . 'stripe-external', 'https://js.stripe.com/v3/', [], $this->version, true );
62
  wp_enqueue_script( $this->prefix . 'stripe-external' );
63
 
64
+ if (
65
+ 'yes' === Helper::get_setting( 'enabled', 'cpsw_stripe' ) ||
66
+ 'yes' === Helper::get_setting( 'enabled', 'cpsw_alipay' ) ||
67
+ 'yes' === Helper::get_setting( 'enabled', 'cpsw_ideal' )
68
+ ) {
69
+ $this->enqueue_card_payments_scripts( $public_key );
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Enqueue card payments scripts
75
+ *
76
+ * @since 1.0.0
77
+ *
78
+ * @param string $public_key Stripe public key.
79
+ *
80
+ * @return void
81
+ */
82
+ private function enqueue_card_payments_scripts( $public_key ) {
83
+ wp_register_script( $this->prefix . 'stripe-elements', $this->assets_url . 'js/stripe-elements.js', [ 'jquery', $this->prefix . 'stripe-external' ], $this->version, true );
84
  wp_enqueue_script( $this->prefix . 'stripe-elements' );
85
 
86
+ wp_register_style( $this->prefix . 'stripe-elements', $this->assets_url . 'css/stripe-elements.css', [], $this->version );
87
  wp_enqueue_style( $this->prefix . 'stripe-elements' );
88
 
89
  wp_localize_script(
90
  $this->prefix . 'stripe-elements',
91
  'cpsw_global_settings',
92
  [
93
+ 'public_key' => $public_key,
94
+ 'inline_cc' => Helper::get_setting( 'inline_cc', 'cpsw_stripe' ),
95
+ 'is_ssl' => is_ssl(),
96
+ 'mode' => Helper::get_payment_mode(),
97
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
98
+ 'js_nonce' => wp_create_nonce( 'cpsw_js_error_nonce' ),
99
+ 'allowed_cards' => Helper::get_setting( 'allowed_cards', 'cpsw_stripe' ),
100
+ 'default_cards' => [
101
  'mastercard' => __( 'MasterCard', 'checkout-plugins-stripe-woo' ),
102
  'visa' => __( 'Visa', 'checkout-plugins-stripe-woo' ),
103
  'amex' => __( 'American Express', 'checkout-plugins-stripe-woo' ),
106
  'diners' => __( 'Diners Club', 'checkout-plugins-stripe-woo' ),
107
  'unionpay' => __( 'UnionPay', 'checkout-plugins-stripe-woo' ),
108
  ],
109
+ 'not_allowed_string' => __( 'is not allowed', 'checkout-plugins-stripe-woo' ),
110
+ 'get_home_url' => get_home_url(),
111
+ 'empty_ideal_bank_message' => __( 'Please select a bank to proceed.', 'checkout-plugins-stripe-woo' ),
112
  ]
113
  );
114
 
115
  if ( 'yes' === Helper::get_setting( 'express_checkout_enabled', 'cpsw_stripe' ) ) {
116
+ wp_register_script( $this->prefix . 'payment-request', $this->assets_url . 'js/payment-request.js', [ 'jquery', $this->prefix . 'stripe-external', $this->prefix . 'stripe-elements' ], $this->version, true );
117
  wp_enqueue_script( $this->prefix . 'payment-request' );
118
 
119
  $needs_shipping = false;
120
+
121
  if ( ! is_null( WC()->cart ) && WC()->cart->needs_shipping() ) {
122
  $needs_shipping = true;
123
  }
128
  apply_filters(
129
  'cpsw_payment_request_localization',
130
  [
131
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
132
+ 'ajax_endpoint' => WC_AJAX::get_endpoint( '%%endpoint%%' ),
133
+ 'public_key' => $public_key,
134
+ 'mode' => Helper::get_payment_mode(),
135
+ 'currency_code' => strtolower( get_woocommerce_currency() ),
136
+ 'country_code' => substr( get_option( 'woocommerce_default_country' ), 0, 2 ),
137
+ 'needs_shipping' => $needs_shipping,
138
+ 'phone_required' => 'required' === get_option( 'woocommerce_checkout_phone_field', 'required' ),
139
+ 'nonce' => [
140
  'checkout' => wp_create_nonce( 'cpsw_checkout' ),
141
  'payment' => wp_create_nonce( 'cpsw_payment_request' ),
142
  'add_to_cart' => wp_create_nonce( 'cpsw_add_to_cart' ),
145
  'shipping_option' => wp_create_nonce( 'cpsw_shipping_option' ),
146
  'js_nonce' => wp_create_nonce( 'cpsw_js_error_nonce' ),
147
  ],
148
+ 'style' => [
149
  'type' => Helper::get_setting( 'express_checkout_button_type', 'cpsw_stripe' ),
150
  'theme' => Helper::get_setting( 'express_checkout_button_theme', 'cpsw_stripe' ),
151
  'height' => (int) Helper::get_setting( 'express_checkout_button_height', 'cpsw_stripe' ),
152
  ],
153
+ 'is_product_page' => is_product() || wc_post_content_has_shortcode( 'product_page' ),
154
+ 'product_advanced_options' => Helper::get_setting( 'express_checkout_product_options', 'cpsw_stripe' ),
155
+ 'is_responsive' => Helper::get_setting( 'express_checkout_product_sticky_footer', 'cpsw_stripe' ),
156
  ]
157
  )
158
  );
gateway/stripe/ideal.php ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * IDEAL Gateway
4
+ *
5
+ * @package checkout-plugins-stripe-woo
6
+ * @since 1.2.0
7
+ */
8
+
9
+ namespace CPSW\Gateway\Stripe;
10
+
11
+ use CPSW\Inc\Helper;
12
+ use CPSW\Inc\Logger;
13
+ use CPSW\Inc\Traits\Get_Instance;
14
+ use CPSW\Gateway\Local_Gateway;
15
+ use WC_AJAX;
16
+ use Exception;
17
+ use WP_Error;
18
+
19
+ /**
20
+ * IDEAL gateway
21
+ *
22
+ * @since 1.2.0
23
+ */
24
+ class Ideal extends Local_Gateway {
25
+
26
+ use Get_Instance;
27
+
28
+ /**
29
+ * Gateway id
30
+ *
31
+ * @var string
32
+ */
33
+ public $id = 'cpsw_ideal';
34
+
35
+ /**
36
+ * Constructor
37
+ *
38
+ * @since 1.2.0
39
+ */
40
+ public function __construct() {
41
+ parent::__construct();
42
+ $this->method_title = __( 'iDEAL', 'checkout-plugins-stripe-woo' );
43
+ $this->method_description = $this->method_description();
44
+ $this->has_fields = true;
45
+
46
+ $this->init_supports();
47
+ $this->init_form_fields();
48
+ $this->init_settings();
49
+
50
+ $this->title = $this->get_option( 'title' );
51
+ $this->description = $this->get_option( 'description' );
52
+ $this->order_button_text = $this->get_option( 'order_button_text' );
53
+
54
+ add_action( 'wc_ajax_cpsw_verify_ideal_payment_intent', [ $this, 'verify_ideal_intent' ] );
55
+ add_filter( 'woocommerce_payment_successful_result', [ $this, 'modify_successful_payment_result' ], 999, 2 );
56
+ }
57
+
58
+ /**
59
+ * Description for ideal gateway
60
+ *
61
+ * @since 1.2.0
62
+ *
63
+ * @return string
64
+ */
65
+ public function method_description() {
66
+ $payment_description = $this->payment_description();
67
+
68
+ return sprintf(
69
+ /* translators: %1$s: Break, %2$s: HTML entities */
70
+ __( 'Accept payment using iDEAL. %1$s %2$s', 'checkout-plugins-stripe-woo' ),
71
+ '<br/>',
72
+ $payment_description
73
+ );
74
+ }
75
+
76
+ /**
77
+ * Checks whether this gateway is available.
78
+ *
79
+ * @return boolean
80
+ */
81
+ public function is_available() {
82
+ if ( 'eur' !== strtolower( get_woocommerce_currency() ) ) {
83
+ return false;
84
+ }
85
+ return parent::is_available();
86
+ }
87
+
88
+ /**
89
+ * Returns all supported currencies for this payment method.
90
+ *
91
+ * @since 1.2.0
92
+ *
93
+ * @return array
94
+ */
95
+ public function get_supported_currency() {
96
+ return apply_filters(
97
+ 'cpsw_ideal_supported_currencies',
98
+ [
99
+ 'EUR',
100
+ ]
101
+ );
102
+ }
103
+
104
+ /**
105
+ * Creates markup for payment form for iDEAL
106
+ *
107
+ * @return void
108
+ */
109
+ public function payment_fields() {
110
+ echo '<div class="status-box"></div>';
111
+ echo '<div class="cpsw_stripe_ideal_form">';
112
+ if ( $this->description ) {
113
+ echo wp_kses_post( $this->description );
114
+ }
115
+ echo '<div class="cpsw_stripe_ideal_select"></div>';
116
+ echo '<div class="cpsw_stripe_ideal_error"></div>';
117
+ if ( 'test' === Helper::get_payment_mode() ) {
118
+ echo '<div class="cpsw_stripe_ideal_test_description">';
119
+ echo wp_kses_post( $this->get_test_mode_description() );
120
+ echo '</div>';
121
+ }
122
+ echo '</div>';
123
+ }
124
+
125
+ /**
126
+ * Modify redirect url
127
+ *
128
+ * @param array $result redirect url array.
129
+ * @param int $order_id woocommerce order id.
130
+ * @return array modified redirect url array.
131
+ */
132
+ public function modify_successful_payment_result( $result, $order_id ) {
133
+ if ( empty( $order_id ) ) {
134
+ return $result;
135
+ }
136
+
137
+ $order = wc_get_order( $order_id );
138
+ if ( $this->id !== $order->get_payment_method() ) {
139
+ return $result;
140
+ }
141
+
142
+ if ( ! isset( $result['intent_secret'] ) ) {
143
+ return $result;
144
+ }
145
+
146
+ // Put the final thank you page redirect into the verification URL.
147
+ $verification_url = add_query_arg(
148
+ [
149
+ 'order' => $order_id,
150
+ 'confirm_payment_nonce' => wp_create_nonce( 'cpsw_confirm_payment_intent' ),
151
+ 'redirect_to' => rawurlencode( $result['redirect'] ),
152
+ ],
153
+ WC_AJAX::get_endpoint( 'cpsw_verify_ideal_payment_intent' )
154
+ );
155
+
156
+ // Combine into a hash.
157
+ $redirect = sprintf( '#confirm-pi-%s:%s', $result['intent_secret'], rawurlencode( $verification_url ) );
158
+
159
+ return [
160
+ 'result' => 'success',
161
+ 'redirect' => $redirect,
162
+ ];
163
+ }
164
+
165
+ /**
166
+ * Verify intent state and redirect.
167
+ *
168
+ * @return void
169
+ */
170
+ public function verify_ideal_intent() {
171
+ $order_id = isset( $_GET['order'] ) ? sanitize_text_field( $_GET['order'] ) : 0; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
172
+ $order = wc_get_order( $order_id );
173
+
174
+ $intent_secret = get_post_meta( $order_id, '_cpsw_intent_secret', true );
175
+ $stripe_api = new Stripe_Api();
176
+ $response = $stripe_api->payment_intents( 'retrieve', [ $intent_secret['id'] ] );
177
+ $intent = $response['success'] ? $response['data'] : false;
178
+
179
+ if ( 'succeeded' === $intent->status || 'requires_capture' === $intent->status ) {
180
+ $redirect_to = $this->process_order( end( $intent->charges->data ), $order_id );
181
+ $redirect_url = apply_filters( 'cpsw_redirect_order_url', $redirect_to, $order );
182
+ wp_safe_redirect( $redirect_url );
183
+ } elseif ( isset( $response['data']->last_payment_error ) ) {
184
+ $message = isset( $response['data']->last_payment_error->message ) ? $response['data']->last_payment_error->message : '';
185
+ $order->update_status( 'wc-failed' );
186
+ // translators: %s: payment fail message.
187
+ wc_add_notice( sprintf( __( 'Payment failed. %s', 'checkout-plugins-stripe-woo' ), $message ), 'error' );
188
+ wp_safe_redirect( wc_get_checkout_url() );
189
+ }
190
+ exit();
191
+ }
192
+
193
+ /**
194
+ * Process order after stripe payment
195
+ *
196
+ * @param object $response intent response data.
197
+ * @param string $order_id currnt coocommerce id.
198
+ * @return array return data.
199
+ */
200
+ public function process_order( $response, $order_id ) {
201
+
202
+ $order = wc_get_order( $order_id );
203
+
204
+ if ( true === $response->captured ) {
205
+ $order->payment_complete( $response->id );
206
+ /* translators: order id */
207
+ Logger::info( sprintf( __( 'Payment successful Order id - %1s', 'checkout-plugins-stripe-woo' ), $order->get_id() ), true );
208
+ }
209
+ if ( ! $response->captured ) {
210
+ /* translators: transaction id */
211
+ $order->update_status( 'on-hold', sprintf( __( 'Charge authorized (Charge ID: %s). Process order to take payment, or cancel to remove the pre-authorization. Attempting to refund the order in part or in full will release the authorization and cancel the payment.', 'checkout-plugins-stripe-woo' ), $response->id ) );
212
+ /* translators: transaction id */
213
+ Logger::info( sprintf( __( 'Charge authorized Order id - %1s', 'checkout-plugins-stripe-woo' ), $order->get_id() ), true );
214
+ }
215
+
216
+ $order->add_order_note( __( 'Payment Status : ', 'checkout-plugins-stripe-woo' ) . ucfirst( $response->status ) . __( ' Source : ', 'checkout-plugins-stripe-woo' ) . $response->payment_method_details->type );
217
+ WC()->cart->empty_cart();
218
+
219
+ return $this->get_return_url( $order );
220
+ }
221
+
222
+ /**
223
+ * Process woocommerce orders after payment is done
224
+ *
225
+ * @param int $order_id wooCommerce order id.
226
+ * @return array data to redirect after payment processing.
227
+ */
228
+ public function process_payment( $order_id ) {
229
+ try {
230
+ $order = wc_get_order( $order_id );
231
+ $customer_id = $this->get_customer_id( $order );
232
+ $idempotency_key = $order->get_order_key() . time();
233
+
234
+ $data = [
235
+ 'amount' => $this->get_formatted_amount( $order->get_total() ),
236
+ 'currency' => get_woocommerce_currency(),
237
+ 'description' => $this->get_order_description( $order ),
238
+ 'metadata' => $this->get_metadata( $order_id ),
239
+ 'payment_method_types' => [ 'ideal' ],
240
+ 'customer' => $customer_id,
241
+ ];
242
+
243
+ /* translators: %1$1s order id, %2$2s order total amount */
244
+ Logger::info( sprintf( __( 'Begin processing payment with iDEAL for order %1$1s for the amount of %2$2s', 'checkout-plugins-stripe-woo' ), $order_id, $order->get_total() ) );
245
+ $intent_data = $this->get_payment_intent( $order_id, $idempotency_key, $data );
246
+ if ( $intent_data ) {
247
+ return [
248
+ 'result' => 'success',
249
+ 'redirect' => false,
250
+ 'intent_secret' => $intent_data['client_secret'],
251
+ ];
252
+ } else {
253
+ return [
254
+ 'result' => 'fail',
255
+ 'redirect' => '',
256
+ ];
257
+ }
258
+ } catch ( Exception $e ) {
259
+ Logger::error( $e->getMessage(), true );
260
+ return new WP_Error( 'order-error', '<div class="woocommerce-error">' . $e->getMessage() . '</div>', [ 'status' => 200 ] );
261
+ }
262
+ }
263
+ }
264
+
gateway/stripe/payment-request-api.php CHANGED
@@ -35,15 +35,27 @@ class Payment_Request_Api extends Card_Payments {
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
 
@@ -75,11 +87,7 @@ class Payment_Request_Api extends Card_Payments {
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
  }
@@ -126,19 +134,13 @@ class Payment_Request_Api extends Card_Payments {
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'] ) {
@@ -150,13 +152,29 @@ class Payment_Request_Api extends Card_Payments {
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'] ) ) {
@@ -166,7 +184,7 @@ class Payment_Request_Api extends Card_Payments {
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
  ?>
@@ -228,12 +246,13 @@ class Payment_Request_Api extends Card_Payments {
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
 
35
 
36
  $this->capture_method = Helper::get_setting( 'charge_type', 'cpsw_stripe' );
37
 
38
+ $product_page_action = 'woocommerce_after_add_to_cart_quantity';
39
+ $product_page_priority = 10;
40
+
41
+ if ( 'yes' === $settings['express_checkout_product_options'] && 'below' === $settings['express_checkout_product_page_position'] ) {
42
+ $product_page_action = 'woocommerce_after_add_to_cart_button';
43
+ $product_page_priority = 1;
44
  }
45
 
46
+ add_action( $product_page_action, [ $this, 'payment_request_button' ], $product_page_priority );
47
+
48
  add_action( 'woocommerce_proceed_to_checkout', [ $this, 'payment_request_button' ], 1 );
49
 
50
+ $checkout_page_action = 'woocommerce_checkout_before_customer_details';
51
+ $checkout_page_priority = 5;
52
+
53
+ if ( 'yes' === $settings['express_checkout_product_checkout_page'] && 'above-billing' === $settings['express_checkout_checkout_page_position'] ) {
54
+ $checkout_page_action = 'woocommerce_checkout_billing';
55
+ $checkout_page_priority = 1;
56
+ }
57
+
58
+ add_action( $checkout_page_action, [ $this, 'payment_request_button' ], $checkout_page_priority );
59
 
60
  add_filter( 'cpsw_payment_request_localization', [ $this, 'localize_product_data' ] );
61
 
87
  */
88
  private function is_selected_location() {
89
  $location = Helper::get_setting( 'express_checkout_location', 'cpsw_stripe' );
90
+ if ( is_array( $location ) && ! empty( $location ) ) {
 
 
 
 
91
  if ( $this->is_product() && in_array( 'product', $location, true ) ) {
92
  return true;
93
  }
134
  $container_class = 'cart';
135
  }
136
 
 
 
 
 
 
 
 
 
 
137
  $options = Helper::get_gateway_settings( 'cpsw_stripe' );
138
 
139
+ $separator_below = true;
140
+ $position_class = 'above';
141
  $alignment_class = '';
142
  $button_width = '';
143
+ $inline_style = '';
144
 
145
  if ( 'checkout' === $container_class ) {
146
  if ( 'yes' === $options['express_checkout_product_checkout_page'] ) {
152
  }
153
 
154
  if ( 'product' === $container_class ) {
155
+ if ( 'yes' === $options['express_checkout_product_options'] ) {
156
+ if ( 'below' === $options['express_checkout_product_page_position'] ) {
157
+ $separator_below = false;
158
+ $position_class = 'below';
159
+ }
160
+
161
+ if ( ! empty( $options['express_checkout_product_button_width'] ) ) {
162
+ $inline_style = 'width:' . (int) $options['express_checkout_product_button_width'] . 'px';
163
+ }
164
+
165
+ if ( 'yes' === $options['express_checkout_product_sticky_footer'] ) {
166
+ $container_class .= ' sticky';
167
+ }
168
  }
169
  }
170
 
171
  ?>
172
+ <div id="cpsw-payment-request-wrapper" class="<?php echo esc_attr( $container_class . ' ' . $position_class . ' ' . $alignment_class ); ?>" style="<?php echo esc_attr( $inline_style ); ?>">
173
+ <?php
174
+ if ( ! $separator_below ) {
175
+ $this->payment_request_button_separator();
176
+ }
177
+ ?>
178
  <div class="cpsw-payment-request-button-wrapper">
179
  <?php
180
  if ( ! empty( trim( $options['express_checkout_title'] ) && 'yes' === $options['express_checkout_product_checkout_page'] ) ) {
184
  }
185
  if ( ! empty( trim( $options['express_checkout_tagline'] ) && 'yes' === $options['express_checkout_product_checkout_page'] ) ) {
186
  ?>
187
+ <p id="cpsw-payment-request-tagline"><?php echo wp_kses_post( Helper::get_setting( 'express_checkout_tagline', 'cpsw_stripe' ) ); ?></p>
188
  <?php
189
  }
190
  ?>
246
  }
247
  }
248
  }
249
+ if ( ! empty( $separator_text ) ) {
250
+ ?>
251
  <div id="cpsw-payment-request-separator" class="<?php echo esc_html( $container_class . ' ' . $alignment_class ); ?>">
252
+ <?php echo esc_html( $separator_text ); ?>
253
  </div>
254
+ <?php
255
+ }
256
  }
257
  }
258
 
gateway/stripe/stripe-api.php CHANGED
@@ -61,38 +61,46 @@ class Stripe_Api {
61
 
62
  $error_message = false;
63
  $response = false;
 
64
 
65
  try {
66
  $response = $this->stripe->{$api}->{$method}( ...$args );
67
  } catch ( \Stripe\Exception\CardException $e ) {
68
  Logger::error( $e->getError()->message, true );
69
  $error_message = $e->getError()->message;
 
70
  } catch ( \Stripe\Exception\RateLimitException $e ) {
71
  // Too many requests made to the API too quickly.
72
  Logger::error( $e->getError()->message, true );
73
  $error_message = $e->getError()->message;
 
74
  } catch ( \Stripe\Exception\InvalidRequestException $e ) {
75
  // Invalid parameters were supplied to Stripe's API.
76
  Logger::error( $e->getError()->message, true );
77
  $error_message = $e->getError()->message;
 
78
  } catch ( \Stripe\Exception\AuthenticationException $e ) {
79
  // Authentication with Stripe's API failed.
80
  // (maybe you changed API keys recently).
81
  Logger::error( $e->getError()->message, true );
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;
 
90
  // Display a very generic error to the user, and maybe send.
91
  // yourself an email.
92
  } catch ( Exception $e ) {
93
  // Something else happened, completely unrelated to Stripe.
94
  Logger::error( $e->getError()->message, true );
95
  $error_message = $e->getError()->message;
 
96
  }
97
 
98
  if ( is_wp_error( $response ) ) {
@@ -109,6 +117,7 @@ class Stripe_Api {
109
  return [
110
  'success' => false,
111
  'message' => $error_message,
 
112
  ];
113
  }
114
  }
@@ -118,6 +127,7 @@ class Stripe_Api {
118
  *
119
  * @param string $method method to be used.
120
  * @param array $args parameter.
 
121
  * @return array
122
  */
123
  public function payment_intents( $method, $args ) {
61
 
62
  $error_message = false;
63
  $response = false;
64
+ $error_type = '';
65
 
66
  try {
67
  $response = $this->stripe->{$api}->{$method}( ...$args );
68
  } catch ( \Stripe\Exception\CardException $e ) {
69
  Logger::error( $e->getError()->message, true );
70
  $error_message = $e->getError()->message;
71
+ $error_type = $e->getError()->param;
72
  } catch ( \Stripe\Exception\RateLimitException $e ) {
73
  // Too many requests made to the API too quickly.
74
  Logger::error( $e->getError()->message, true );
75
  $error_message = $e->getError()->message;
76
+ $error_type = $e->getError()->param;
77
  } catch ( \Stripe\Exception\InvalidRequestException $e ) {
78
  // Invalid parameters were supplied to Stripe's API.
79
  Logger::error( $e->getError()->message, true );
80
  $error_message = $e->getError()->message;
81
+ $error_type = $e->getError()->param;
82
  } catch ( \Stripe\Exception\AuthenticationException $e ) {
83
  // Authentication with Stripe's API failed.
84
  // (maybe you changed API keys recently).
85
  Logger::error( $e->getError()->message, true );
86
  $error_message = $e->getError()->message;
87
+ $error_type = $e->getError()->param;
88
  } catch ( \Stripe\Exception\ApiConnectionException $e ) {
89
  // Network communication with Stripe failed.
90
  $error_message = is_null( $e->getError() ) ? $e->getMessage() : $e->getError()->message;
91
  Logger::error( $error_message, true );
92
+ $error_type = is_null( $e->getError() ) ? '' : $e->getError()->param;
93
  } catch ( \Stripe\Exception\ApiErrorException $e ) {
94
  Logger::error( $e->getError()->message, true );
95
  $error_message = $e->getError()->message;
96
+ $error_type = $e->getError()->param;
97
  // Display a very generic error to the user, and maybe send.
98
  // yourself an email.
99
  } catch ( Exception $e ) {
100
  // Something else happened, completely unrelated to Stripe.
101
  Logger::error( $e->getError()->message, true );
102
  $error_message = $e->getError()->message;
103
+ $error_type = $e->getError()->param;
104
  }
105
 
106
  if ( is_wp_error( $response ) ) {
117
  return [
118
  'success' => false,
119
  'message' => $error_message,
120
+ 'type' => $error_type,
121
  ];
122
  }
123
  }
127
  *
128
  * @param string $method method to be used.
129
  * @param array $args parameter.
130
+ *
131
  * @return array
132
  */
133
  public function payment_intents( $method, $args ) {
gateway/stripe/webhook.php CHANGED
@@ -152,7 +152,7 @@ class Webhook {
152
  }
153
 
154
  $payload = file_get_contents( 'php://input' );
155
- $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
156
  $event = null;
157
 
158
  try {
@@ -186,6 +186,35 @@ class Webhook {
186
  $charge = $event->data->object;
187
  $this->charge_capture( $charge );
188
  break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
  }
191
  $success = constant( 'self::CPSW_' . strtoupper( $mode ) . '_LAST_SUCCESS_AT' );
@@ -200,9 +229,8 @@ class Webhook {
200
  * @return void
201
  */
202
  public function charge_capture( $charge ) {
203
- global $wpdb;
204
  $payment_intent = sanitize_text_field( $charge->payment_intent );
205
- $order_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id from {$wpdb->prefix}postmeta where meta_key = '_cpsw_intent_secret' and meta_value like %s", '%' . $payment_intent . '%' ) );
206
  if ( ! $order_id ) {
207
  Logger::error( 'Could not find order via charge ID: ' . $charge->id );
208
  return;
@@ -240,12 +268,331 @@ class Webhook {
240
 
241
  }
242
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  /**
244
  * Shows time difference as - XX minutes ago.
245
  *
246
  * @param datetime $datetime time of last event.
247
  * @param boolean $full show full time difference.
248
  * @return string
 
249
  */
250
  public function time_elapsed_string( $datetime, $full = false ) {
251
  $now = new DateTime();
152
  }
153
 
154
  $payload = file_get_contents( 'php://input' );
155
+ $sig_header = isset( $_SERVER['HTTP_STRIPE_SIGNATURE'] ) ? $_SERVER['HTTP_STRIPE_SIGNATURE'] : '';
156
  $event = null;
157
 
158
  try {
186
  $charge = $event->data->object;
187
  $this->charge_capture( $charge );
188
  break;
189
+ case 'charge.refunded':
190
+ $charge = $event->data->object;
191
+ $this->charge_refund( $charge );
192
+ break;
193
+ case 'charge.dispute.created':
194
+ $charge = $event->data->object;
195
+ $this->charge_dispute_created( $charge );
196
+ break;
197
+ case 'charge.dispute.closed':
198
+ $dispute = $event->data->object;
199
+ $this->charge_dispute_closed( $dispute );
200
+ break;
201
+ case 'payment_intent.succeeded':
202
+ case 'payment_intent.amount_capturable_updated':
203
+ $intent = $event->data->object;
204
+ $this->payment_intent_succeeded( $intent );
205
+ break;
206
+ case 'payment_intent.payment_failed':
207
+ $intent = $event->data->object;
208
+ $this->payment_intent_failed( $intent );
209
+ break;
210
+ case 'review.opened':
211
+ $review = $event->data->object;
212
+ $this->review_opened( $review );
213
+ break;
214
+ case 'review.closed':
215
+ $review = $event->data->object;
216
+ $this->review_closed( $review );
217
+ break;
218
 
219
  }
220
  $success = constant( 'self::CPSW_' . strtoupper( $mode ) . '_LAST_SUCCESS_AT' );
229
  * @return void
230
  */
231
  public function charge_capture( $charge ) {
 
232
  $payment_intent = sanitize_text_field( $charge->payment_intent );
233
+ $order_id = $this->get_order_id_from_intent( $payment_intent );
234
  if ( ! $order_id ) {
235
  Logger::error( 'Could not find order via charge ID: ' . $charge->id );
236
  return;
268
 
269
  }
270
 
271
+ /**
272
+ * Refunds WooCommerce order via webhook call
273
+ *
274
+ * @param object $charge Stripe Charge object.
275
+ * @return void
276
+ */
277
+ public function charge_refund( $charge ) {
278
+ $payment_intent = sanitize_text_field( $charge->payment_intent );
279
+ $order_id = $this->get_order_id_from_intent( $payment_intent );
280
+ if ( ! $order_id ) {
281
+ Logger::error( 'Could not find order via charge ID: ' . $charge->id );
282
+ return;
283
+ }
284
+
285
+ $order = wc_get_order( $order_id );
286
+
287
+ if ( 'cpsw_stripe' === $order->get_payment_method() ) {
288
+ $transaction_id = $order->get_transaction_id();
289
+ $captured = $charge->captured;
290
+ $refund_id = $order->get_meta( '_cpsw_refund_id' );
291
+ $currency = strtoupper( $charge->currency );
292
+ $raw_amount = $charge->refunds->data[0]->amount;
293
+
294
+ if ( ! in_array( $currency, self::$zero_currencies, true ) ) {
295
+ $raw_amount = $raw_amount / 100;
296
+ }
297
+
298
+ $amount = wc_price( $raw_amount, [ 'currency' => $currency ] );
299
+
300
+ // If charge wasn't captured, no need to refund.
301
+ if ( ! $captured ) {
302
+ // Handling cancellation of unauthorized charge.
303
+ if ( 'cancelled' !== $order->get_status() ) {
304
+ /* translators: amount (including currency symbol) */
305
+ $order->add_order_note( sprintf( __( 'Pre-Authorization for %s voided from the Stripe Dashboard.', 'checkout-plugins-stripe-woo' ), $amount ) );
306
+ $order->update_status( 'cancelled' );
307
+ }
308
+
309
+ return;
310
+ }
311
+
312
+ // If the refund ID matches, don't continue to prevent double refunding.
313
+ if ( $charge->refunds->data[0]->id === $refund_id ) {
314
+ return;
315
+ }
316
+
317
+ if ( $transaction_id ) {
318
+ $reason = __( 'Refunded via stripe dashboard', 'checkout-plugins-stripe-woo' );
319
+
320
+ // Create the refund.
321
+ $refund = wc_create_refund(
322
+ [
323
+ 'order_id' => $order_id,
324
+ 'amount' => ( $charge->amount_refunded > 0 ) ? $raw_amount : false,
325
+ 'reason' => $reason,
326
+ ]
327
+ );
328
+
329
+ if ( is_wp_error( $refund ) ) {
330
+ Logger::error( $refund->get_error_message() );
331
+ }
332
+
333
+ $refund_id = $charge->refunds->data[0]->id;
334
+ $order->update_meta_data( '_cpsw_refund_id', $refund_id );
335
+
336
+ $refund_time = gmdate( 'Y-m-d H:i:s', time() );
337
+ $order->add_order_note( __( 'Reason : ', 'checkout-plugins-stripe-woo' ) . $reason . '.<br>' . __( 'Amount : ', 'checkout-plugins-stripe-woo' ) . $amount . '.<br>' . __( 'Status : Success', 'checkout-plugins-stripe-woo' ) . ' [ ' . $refund_time . ' ] <br>' . __( 'Transaction ID : ', 'checkout-plugins-stripe-woo' ) . $refund_id );
338
+ Logger::info( $reason . ' : ' . __( 'Amount : ', 'checkout-plugins-stripe-woo' ) . get_woocommerce_currency_symbol() . str_pad( $raw_amount, 2, 0 ) . __( ' Transaction ID : ', 'checkout-plugins-stripe-woo' ) . $refund_id, true );
339
+ }
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Handles charge.dispute.create webhook and changes order status to 'On Hold'
345
+ *
346
+ * @param int $charge stripe webhook object.
347
+ * @return void
348
+ * @since 1.2.0
349
+ */
350
+ public function charge_dispute_created( $charge ) {
351
+ $payment_intent = sanitize_text_field( $charge->payment_intent );
352
+ $order_id = $this->get_order_id_from_intent( $payment_intent );
353
+ if ( ! $order_id ) {
354
+ Logger::error( 'Could not find order via charge ID: ' . $charge->id );
355
+ return;
356
+ }
357
+
358
+ $order = wc_get_order( $order_id );
359
+ $order->update_status( 'on-hold', __( 'This order is under dispute. Please respond via stripe dashboard.', 'checkout-plugins-stripe-woo' ) );
360
+ $order->update_meta_data( 'cpsw_status_before_dispute', $order->get_status() );
361
+ $this->send_failed_order_email( $order_id );
362
+ }
363
+
364
+ /**
365
+ * Handles carge.dispute.closed webhook and update order status accordingly
366
+ *
367
+ * @param object $dispute dispute object recevied from stripe webhook.
368
+ * @return void
369
+ * @since 1.2.0
370
+ */
371
+ public function charge_dispute_closed( $dispute ) {
372
+ $payment_intent = sanitize_text_field( $dispute->payment_intent );
373
+ $order_id = $this->get_order_id_from_intent( $payment_intent );
374
+ if ( ! $order_id ) {
375
+ Logger::error( 'Could not find order for dispute ID: ' . $dispute->id );
376
+ return;
377
+ }
378
+
379
+ $order = wc_get_order( $order_id );
380
+
381
+ switch ( $dispute->status ) {
382
+ case 'lost':
383
+ $message = __( 'The disputed order lost or accepted.', 'checkout-plugins-stripe-woo' );
384
+ break;
385
+
386
+ case 'won':
387
+ $message = __( 'The disputed order resolved in your favour.', 'checkout-plugins-stripe-woo' );
388
+ break;
389
+
390
+ case 'warning_closed':
391
+ $message = __( 'The inquiry or retrieval closed.', 'checkout-plugins-stripe-woo' );
392
+ break;
393
+ }
394
+
395
+ $status = 'lost' === $dispute->status ? 'failed' : $order->get_meta( 'cpsw_status_before_dispute' );
396
+ $order->update_status( $status, $message );
397
+ }
398
+
399
+ /**
400
+ * Handles webhook call of event payment_intent.succeeded
401
+ *
402
+ * @param object $intent intent object received from stripe.
403
+ * @return void
404
+ */
405
+ public function payment_intent_succeeded( $intent ) {
406
+ $payment_intent = sanitize_text_field( $intent->id );
407
+ $order_id = $this->get_order_id_from_intent( $payment_intent );
408
+ if ( ! $order_id ) {
409
+ Logger::error( 'Could not find order via payment intent: ' . $intent->id );
410
+ return;
411
+ }
412
+
413
+ $order = wc_get_order( $order_id );
414
+
415
+ if ( ! $order->has_status(
416
+ [ 'pending', 'failed' ],
417
+ $order
418
+ ) ) {
419
+ return;
420
+ }
421
+
422
+ $charge = end( $intent->charges->data );
423
+ /* translators: transaction id, order id */
424
+ Logger::info( sprintf( __( 'Stripe PaymentIntent %1$1s succeeded for order %2$2s', 'checkout-plugins-stripe-woo' ), $charge->id, $order_id ) );
425
+ $this->process_response( $charge, $order );
426
+ }
427
+
428
+ /**
429
+ * Handles webhook call payment_intent.payment_failed
430
+ *
431
+ * @param object $intent stripe webhook object.
432
+ * @return void
433
+ * @since 1.2.0
434
+ */
435
+ public function payment_intent_failed( $intent ) {
436
+ $payment_intent = sanitize_text_field( $intent->id );
437
+ $order_id = $this->get_order_id_from_intent( $payment_intent );
438
+ if ( ! $order_id ) {
439
+ Logger::error( 'Could not find order via payment intent: ' . $intent->id );
440
+ return;
441
+ }
442
+
443
+ $order = wc_get_order( $order_id );
444
+ /* translators: The error message that was received from Stripe. */
445
+ $error_message = $intent->last_payment_error ? sprintf( __( 'Reason: %s', 'checkout-plugins-stripe-woo' ), $intent->last_payment_error->message ) : '';
446
+ /* translators: The error message that was received from Stripe. */
447
+ $message = sprintf( __( 'Stripe SCA authentication failed. %s', 'checkout-plugins-stripe-woo' ), $error_message );
448
+ $order->update_status( 'failed', $message );
449
+
450
+ $this->send_failed_order_email( $order_id );
451
+ }
452
+
453
+ /**
454
+ * Handles review.opened webhook
455
+ *
456
+ * @param int $review stripe webhook object.
457
+ * @return void
458
+ * @since 1.2.0
459
+ */
460
+ public function review_opened( $review ) {
461
+ $payment_intent = sanitize_text_field( $review->payment_intent );
462
+ $order_id = $this->get_order_id_from_intent( $payment_intent );
463
+ if ( ! $order_id ) {
464
+ Logger::error( 'Could not find order via review ID: ' . $review->id );
465
+ return;
466
+ }
467
+
468
+ $order = wc_get_order( $order_id );
469
+ $order->update_status( 'on-hold', __( 'This order is under review. Please respond via stripe dashboard.', 'checkout-plugins-stripe-woo' ) );
470
+ $order->update_meta_data( 'cpsw_status_before_review', $order->get_status() );
471
+ $this->send_failed_order_email( $order_id );
472
+ }
473
+
474
+ /**
475
+ * Handles review.closed webhook
476
+ *
477
+ * @param int $review stripe webhook object.
478
+ * @return void
479
+ * @since 1.2.0
480
+ */
481
+ public function review_closed( $review ) {
482
+ $payment_intent = sanitize_text_field( $review->payment_intent );
483
+ $order_id = $this->get_order_id_from_intent( $payment_intent );
484
+ if ( ! $order_id ) {
485
+ Logger::error( 'Could not find order via review ID: ' . $review->id );
486
+ return;
487
+ }
488
+
489
+ $order = wc_get_order( $order_id );
490
+ /* translators: Review reson from stripe */
491
+ $message = sprintf( __( 'Review for this order has been resolved. Reason: %s', 'checkout-plugins-stripe-woo' ), $review->reason );
492
+ $order->update_status( $order->get_meta( 'cpsw_status_before_review' ), $message );
493
+ }
494
+
495
+ /**
496
+ * Fetch WooCommerce order id from payment intent
497
+ *
498
+ * @param string $payment_intent payment intent received from stripe.
499
+ * @return int order id.
500
+ * @since 1.2.0
501
+ */
502
+ public function get_order_id_from_intent( $payment_intent ) {
503
+ global $wpdb;
504
+ return $wpdb->get_var( $wpdb->prepare( "SELECT post_id from {$wpdb->prefix}postmeta where meta_key = '_cpsw_intent_secret' and meta_value like %s", '%' . $payment_intent . '%' ) );
505
+ }
506
+
507
+ /**
508
+ * Process response for saved cards
509
+ *
510
+ * @param object $response intent response.
511
+ * @param object $order order response.
512
+ * @return array
513
+ */
514
+ public function process_response( $response, $order ) {
515
+ Logger::info( 'Processing: ' . $response->id );
516
+
517
+ $order_id = $order->get_id();
518
+ $captured = ( isset( $response->captured ) && $response->captured ) ? 'yes' : 'no';
519
+
520
+ // Store charge data.
521
+ $order->update_meta_data( '_cpsw_charge_captured', $captured );
522
+
523
+ if ( 'yes' === $captured ) {
524
+ /**
525
+ * Charge can be captured but in a pending state. Payment methods
526
+ * that are asynchronous may take couple days to clear. Webhook will
527
+ * take care of the status changes.
528
+ */
529
+ if ( 'pending' === $response->status ) {
530
+ $order_stock_reduced = $order->get_meta( '_order_stock_reduced', true );
531
+
532
+ if ( ! $order_stock_reduced ) {
533
+ wc_reduce_stock_levels( $order_id );
534
+ }
535
+
536
+ $order->set_transaction_id( $response->id );
537
+ /* translators: transaction id */
538
+ $order->update_status( 'on-hold', sprintf( __( 'Stripe charge awaiting payment: %s.', 'checkout-plugins-stripe-woo' ), $response->id ) );
539
+ }
540
+
541
+ if ( 'succeeded' === $response->status ) {
542
+ $order->payment_complete( $response->id );
543
+
544
+ /* translators: transaction id */
545
+ $message = sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'checkout-plugins-stripe-woo' ), $response->id );
546
+ Logger::info( $message, true );
547
+ $order->add_order_note( $message );
548
+ }
549
+
550
+ if ( 'failed' === $response->status ) {
551
+ $message = __( 'Payment processing failed. Please retry.', 'checkout-plugins-stripe-woo' );
552
+ Logger::error( $message, true );
553
+ $order->add_order_note( $message );
554
+ }
555
+ } else {
556
+ $order->set_transaction_id( $response->id );
557
+
558
+ if ( $order->has_status( [ 'pending', 'failed' ] ) ) {
559
+ wc_reduce_stock_levels( $order_id );
560
+ }
561
+
562
+ /* translators: transaction id */
563
+ $order->update_status( 'on-hold', sprintf( __( 'Stripe charge authorized (Charge ID: %s). Process order to take payment, or cancel to remove the pre-authorization. Attempting to refund the order in part or in full will release the authorization and cancel the payment.', 'checkout-plugins-stripe-woo' ), $response->id ) );
564
+ }
565
+
566
+ if ( is_callable( [ $order, 'save' ] ) ) {
567
+ $order->save();
568
+ }
569
+
570
+ do_action( 'cpsw_process_response', $response, $order );
571
+
572
+ return $response;
573
+ }
574
+
575
+ /**
576
+ * Sends order failure email.
577
+ *
578
+ * @param int $order_id WooCommerce order id.
579
+ * @return void
580
+ * @since 1.2.0
581
+ */
582
+ public function send_failed_order_email( $order_id ) {
583
+ $emails = WC()->mailer()->get_emails();
584
+ if ( ! empty( $emails ) && ! empty( $order_id ) ) {
585
+ $emails['WC_Email_Failed_Order']->trigger( $order_id );
586
+ }
587
+ }
588
+
589
  /**
590
  * Shows time difference as - XX minutes ago.
591
  *
592
  * @param datetime $datetime time of last event.
593
  * @param boolean $full show full time difference.
594
  * @return string
595
+ * @since 0.0.1
596
  */
597
  public function time_elapsed_string( $datetime, $full = false ) {
598
  $now = new DateTime();
inc/helper.php CHANGED
@@ -8,6 +8,8 @@
8
 
9
  namespace CPSW\Inc;
10
 
 
 
11
  /**
12
  * Stripe Webhook.
13
  */
@@ -20,10 +22,10 @@ class Helper {
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,24 +34,30 @@ class Helper {
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
 
@@ -122,8 +130,7 @@ class Helper {
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 ];
@@ -148,6 +155,7 @@ class Helper {
148
  *
149
  * @param string $key Name of the key to get the value.
150
  * @param mixed $gateway Name of the payment gateway to get options from the database.
 
151
  * @return array $global_settings It returns all stripe settings in an array.
152
  */
153
  public static function get_setting( $key = '', $gateway = false ) {
@@ -168,4 +176,25 @@ class Helper {
168
  public static function get_payment_mode() {
169
  return apply_filters( 'cpsw_payment_mode', self::get_setting( 'cpsw_mode' ) );
170
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  }
8
 
9
  namespace CPSW\Inc;
10
 
11
+ use Exception;
12
+
13
  /**
14
  * Stripe Webhook.
15
  */
22
  */
23
  private static $gateway_defaults = [
24
  'woocommerce_cpsw_stripe_settings' => [
25
+ 'enabled' => 'no',
26
+ 'inline_cc' => 'yes',
27
+ 'order_status' => '',
28
+ 'allowed_cards' => [
29
  'mastercard',
30
  'visa',
31
  'diners',
34
  'jcb',
35
  'unionpay',
36
  ],
37
+ 'express_checkout_location' => [
38
  'product',
39
  'cart',
40
  'checkout',
41
  ],
42
+ 'express_checkout_enabled' => 'no',
43
+ 'express_checkout_button_type' => 'default',
44
+ 'express_checkout_button_theme' => 'dark',
45
+ 'express_checkout_button_height' => '40',
46
+ 'express_checkout_title' => 'Express Checkout',
47
+ 'express_checkout_tagline' => 'Checkout quickly to use you favourite tool.',
48
+ 'express_checkout_product_options' => 'no',
49
+ 'express_checkout_product_page_position' => 'above',
50
+ 'express_checkout_product_button_width' => '',
51
+ 'express_checkout_product_sticky_footer' => 'yes',
52
+ 'express_checkout_separator' => 'OR',
53
+ 'express_checkout_product_checkout_page' => 'no',
54
+ 'express_checkout_button_width' => '',
55
+ 'express_checkout_button_alignment' => 'left',
56
+ 'express_checkout_separator_checkout' => 'OR',
57
+ 'express_checkout_checkout_page_position' => 'above-checkout',
58
+ ],
59
+ 'woocommerce_cpsw_alipay_settings' => [
60
+ 'enabled' => 'no',
61
  ],
62
  ];
63
 
130
  */
131
  public static function get_gateway_setting( $key = '', $gateway = 'cpsw_stripe' ) {
132
  $settings = self::get_gateway_settings( $gateway );
133
+ $value = false;
 
134
 
135
  if ( isset( $settings[ $key ] ) ) {
136
  $value = $settings[ $key ];
155
  *
156
  * @param string $key Name of the key to get the value.
157
  * @param mixed $gateway Name of the payment gateway to get options from the database.
158
+ *
159
  * @return array $global_settings It returns all stripe settings in an array.
160
  */
161
  public static function get_setting( $key = '', $gateway = false ) {
176
  public static function get_payment_mode() {
177
  return apply_filters( 'cpsw_payment_mode', self::get_setting( 'cpsw_mode' ) );
178
  }
179
+
180
+ /**
181
+ * Get webhook secret key.
182
+ *
183
+ * @since 1.2.0
184
+ *
185
+ * @return mixed
186
+ */
187
+ public static function get_webhook_secret() {
188
+ if ( 'live' === self::get_payment_mode() ) {
189
+ $endpoint_secret = self::get_setting( 'cpsw_live_webhook_secret' );
190
+ } elseif ( 'test' === self::get_payment_mode() ) {
191
+ $endpoint_secret = self::get_setting( 'cpsw_test_webhook_secret' );
192
+ }
193
+
194
+ if ( empty( trim( $endpoint_secret ) ) ) {
195
+ return false;
196
+ }
197
+
198
+ return $endpoint_secret;
199
+ }
200
  }
inc/notice.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Notice helper.
4
+ *
5
+ * @package checkout-plugins-stripe-woo
6
+ *
7
+ * @since 1.2.0
8
+ */
9
+
10
+ namespace CPSW\Inc;
11
+
12
+ use CPSW\Inc\Traits\Get_Instance;
13
+
14
+ /**
15
+ * Class that represents admin notices.
16
+ *
17
+ * @since 1.2.0
18
+ */
19
+ class Notice {
20
+
21
+ use Get_Instance;
22
+
23
+ /**
24
+ * Notices (array)
25
+ *
26
+ * @var array
27
+ */
28
+ public $notices = [];
29
+
30
+ /**
31
+ * Constructor
32
+ *
33
+ * @since 1.2.0
34
+ */
35
+ public function __construct() {
36
+ add_action( 'admin_notices', [ $this, 'get_notices' ] );
37
+ add_action( 'wp_loaded', [ $this, 'hide' ] );
38
+ }
39
+
40
+ /**
41
+ * Allow this class and other classes to add slug keyed notices (to avoid duplication).
42
+ *
43
+ * @since 1.2.0
44
+ *
45
+ * @param string $slug Class slug.
46
+ * @param string $class CSS class name.
47
+ * @param string $message Notice message.
48
+ * @param string $dismissible Dismissible icon.
49
+ *
50
+ * @return void
51
+ */
52
+ public function add( $slug, $class, $message, $dismissible = false ) {
53
+ $this->notices[ $slug ] = [
54
+ 'class' => $class,
55
+ 'message' => $message,
56
+ 'dismissible' => $dismissible,
57
+ ];
58
+ }
59
+
60
+ /**
61
+ * Display any notices we've collected thus far.
62
+ *
63
+ * @since 1.2.0
64
+ *
65
+ * @return void
66
+ */
67
+ public function get_notices() {
68
+ if ( ! current_user_can( 'manage_woocommerce' ) ) {
69
+ return;
70
+ }
71
+
72
+ foreach ( (array) $this->notices as $notice_key => $notice ) {
73
+ echo '<div class="' . esc_attr( $notice['class'] ) . '" style="position:relative;">';
74
+
75
+ if ( $notice['dismissible'] ) {
76
+ ?>
77
+ <a href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'cpsw-stripe-hide-notice', $notice_key ), 'cpsw_stripe_hide_notices_nonce', '_cpsw_stripe_notice_nonce' ) ); ?>" class="woocommerce-message-close notice-dismiss" style="position:relative;float:right;padding:9px 0px 9px 9px 9px;text-decoration:none;"></a>
78
+ <?php
79
+ }
80
+
81
+ echo '<p>';
82
+ echo wp_kses(
83
+ $notice['message'],
84
+ [
85
+ 'a' => [
86
+ 'href' => [],
87
+ 'target' => [],
88
+ ],
89
+ ]
90
+ );
91
+ echo '</p></div>';
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Hides any notice.
97
+ *
98
+ * @since 1.2.0
99
+ *
100
+ * @return void
101
+ */
102
+ public function hide() {
103
+ if (
104
+ ! isset( $_GET['_cpsw_stripe_notice_nonce'] )
105
+ || ! wp_verify_nonce( $_GET['_cpsw_stripe_notice_nonce'], 'cpsw_stripe_hide_notices_nonce' )
106
+ ) {
107
+ return;
108
+ }
109
+
110
+ $notice = wc_clean( wp_unslash( $_GET['cpsw-stripe-hide-notice'] ) );
111
+
112
+ update_option( 'cpsw_show_' . $notice . '_notice', 'no' );
113
+ }
114
+
115
+ /**
116
+ * Check current page is cpsw setting page.
117
+ *
118
+ * @since 1.2.0
119
+ *
120
+ * @param string $section gateway section.
121
+ *
122
+ * @return boolean
123
+ */
124
+ public function is_cpsw_section( $section ) {
125
+ if ( isset( $_GET['page'] ) && 'wc-settings' === sanitize_text_field( $_GET['page'] ) && isset( $_GET['tab'] ) && isset( $_GET['section'] ) && sanitize_text_field( $_GET['section'] ) === $section ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
126
+ return true;
127
+ }
128
+
129
+ return false;
130
+ }
131
+ }
inc/traits/subscriptions.php CHANGED
@@ -296,6 +296,8 @@ trait Subscriptions {
296
  * @return array
297
  */
298
  public function create_and_confirm_intent_for_off_session( $order, $source, $amount ) {
 
 
299
  $request = [
300
  'amount' => $amount ? $this->get_formatted_amount( $order->get_total() ) : 0,
301
  'currency' => $order->get_currency(),
@@ -305,6 +307,7 @@ trait Subscriptions {
305
  'confirm' => 'true',
306
  'confirmation_method' => 'automatic',
307
  'customer' => $source->customer,
 
308
  'payment_method' => $source->source,
309
  'capture_method' => $this->capture_method,
310
  ];
@@ -312,8 +315,6 @@ trait Subscriptions {
312
  if ( ! empty( trim( $this->statement_descriptor ) ) ) {
313
  $request['statement_descriptor'] = $this->statement_descriptor;
314
  }
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 ] );
296
  * @return array
297
  */
298
  public function create_and_confirm_intent_for_off_session( $order, $source, $amount ) {
299
+ $order_id = $order->get_id();
300
+
301
  $request = [
302
  'amount' => $amount ? $this->get_formatted_amount( $order->get_total() ) : 0,
303
  'currency' => $order->get_currency(),
307
  'confirm' => 'true',
308
  'confirmation_method' => 'automatic',
309
  'customer' => $source->customer,
310
+ 'metadata' => $this->get_metadata( $order_id ),
311
  'payment_method' => $source->source,
312
  'capture_method' => $this->capture_method,
313
  ];
315
  if ( ! empty( trim( $this->statement_descriptor ) ) ) {
316
  $request['statement_descriptor'] = $this->statement_descriptor;
317
  }
 
 
318
  Logger::info( "Stripe Payment initiated for order $order_id" );
319
  $stripe_api = new Stripe_Api();
320
  $response = $stripe_api->payment_intents( 'create', [ $request ] );
readme.txt CHANGED
@@ -3,27 +3,64 @@ Contributors: brainstormforce
3
  Tags: stripe, credit card
4
  Requires at least: 5.4
5
  Tested up to: 5.8
6
- Stable tag: 1.1.1
7
  Requires PHP: 5.6
8
  License: GPLv2 or later
9
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
10
 
11
- 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!
12
 
13
  == Description ==
14
 
15
  <strong>Accept credit card payments in your store with Stripe for WooCommerce.<strong>
16
 
17
- The smoother the checkout process, the higher the chance of a sale, and offering multiple payment options is a proven way to boost sales. Which is where Stripe for WooCommerce comes in.
18
 
19
- Stripe for WooCommerce is an advanced plugin that delivers a simple, secure way to accept credit card payments using the Stripe API.
20
 
21
  With Stripe you can accept payments from several card brands, from large global networks like Visa and Mastercard to local networks like Cartes Bancaires in France or Interac in Canada. Stripe also supports American Express, Discover, JCB, Diners Club and UnionPay.
22
 
23
- Stripe for WooCommerce will also bring support for Apple pay, Google pay, ACH and iDeal in upcoming updates.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
- The plugin supports WooCommerce Subscriptions where users can make recurring payments for products and services. It’s all the payment plugin you need for your store!
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  == Installation ==
29
 
@@ -46,6 +83,12 @@ Here are easy steps
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
@@ -55,11 +98,17 @@ NOTE: NOT to deactivate your old payment gateway plugin. Your old subscription's
55
 
56
  == Changelog ==
57
 
 
 
 
 
 
 
58
  = 1.1.1 – WEDNESDAY, 22ND DECEMBER 2021 =
59
  * Fix: Express Checkout buttons were not appearing in live mode.
60
 
61
  = 1.1.0 – TUESDAY, 21ST DECEMBER 2021 =
62
- * New: Express Checkout
63
 
64
  = 1.0.0 – TUESDAY, 23RD NOVEMBER 2021 =
65
- * Initial release
3
  Tags: stripe, credit card
4
  Requires at least: 5.4
5
  Tested up to: 5.8
6
+ Stable tag: 1.2.0
7
  Requires PHP: 5.6
8
  License: GPLv2 or later
9
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
10
 
11
+ Stripe for WooCommerce delivers a simple, secure way to accept credit card payments, Apple Pay, and Google Pay on your WooCommerce store. Reduce payment friction and boost conversions using this free plugin!
12
 
13
  == Description ==
14
 
15
  <strong>Accept credit card payments in your store with Stripe for WooCommerce.<strong>
16
 
17
+ The smoother the checkout process, the higher the chance of a sale, and offering multiple payment options is a proven way to boost sales. This is where Stripe for WooCommerce comes in.
18
 
19
+ Stripe for WooCommerce is a payment plugin that delivers a simple, secure way to accept credit card payments using the Stripe service.
20
 
21
  With Stripe you can accept payments from several card brands, from large global networks like Visa and Mastercard to local networks like Cartes Bancaires in France or Interac in Canada. Stripe also supports American Express, Discover, JCB, Diners Club and UnionPay.
22
 
23
+ ## OFFER ONE CLICK CHECKOUT WITH EXPRESS PAY ##
24
+
25
+ The future of ecommerce checkout is express pay options that make it fast to place orders because your buyers don’t need to fill out the checkout form. All your buyers have to do is click one button and their order is complete.
26
+
27
+ Stripe For WooCommerce makes it easy to start offering express payment options such as Apple Pay and Google Pay and fully customize the style, design, and location of these express pay buttons.
28
+
29
+ You will be able to visually style the express pay buttons to match your brand. Next you can choose where you want to show the express pay buttons, on the product page, on the cart page, and on the checkout page.
30
+
31
+ Stripe for WooCommerce offers complete flexibility without needing to understand a single line of code.
32
+
33
+ ### ABOUT CHECKOUT PLUGINS ###
34
+
35
+ ## WE ARE AN OFFICIAL STRIPE PARTNER ##
36
+
37
+ Checkout Plugins is an official Stripe partner!
38
+
39
+ We also make some of the most popular and loved WordPress & WooCommerce products.
40
+
41
+ ## ABOUT US ##
42
+
43
+ Checkout Plugins is part of the Brainstorm Force family of products which are used on millions of websites.
44
 
45
+ Here are some of our products:
46
 
47
+ * **Astra Theme**
48
+ Currently used by nearly 2 million websites, Astra Theme is the most popular WordPress theme and is also the most popular WooCommerce theme. Stripe for WooCommerce was made to work perfectly with Astra Theme. [Visit Astra Theme](https://wpastra.com)
49
+
50
+ * **CartFlows**
51
+ Currently used by nearly 300,000 store owners to get more orders and increase the order value through our conversion optimized checkout replacement for WooCommerce, checkout order bumps, one-click post purchase upsells, and A/B split testing engine. [Visit CartFlows](https://cartflows.com)
52
+
53
+ * **Cart Abandonment Recovery**
54
+ Currently used by nearly 400,000 store owners to capture lost revenue caused by buyers that don’t complete their checkout. Cart Abandonment Recovery captures these lost orders and automatically contacts the lost buyers to get them to complete their order. [Visit Cart Abandonment Recovery](https://wordpress.org/plugins/woo-cart-abandonment-recovery/)
55
+
56
+ * **Starter Templates**
57
+ Currently used by nearly 2 million websites, Starter Templates offers hundreds of complete website templates, including over 50 website designs for WooCommerce stores. [Visit Starter Templates](https://wordpress.org/plugins/astra-sites/)
58
+
59
+ As you can see, we know WooCommerce inside and out and help thousands of store owners build highly profitable stores everyday.
60
+
61
+ Stripe for WooCommerce will also bring support for Apple pay, Google pay, ACH and iDeal in upcoming updates.
62
+
63
+ Stripe for WooCommerce supports WooCommerce Subscriptions where users can make recurring payments for products and services. It’s the only payment plugin you need for your store!
64
 
65
  == Installation ==
66
 
83
  4. Disable the existing Stripe method from WooCommerce payment settings.
84
  NOTE: NOT to deactivate your old payment gateway plugin. Your old subscription's renewal will be processed through the old plugin automatically.
85
 
86
+ = Does this plugin work with my theme? =
87
+ Yes, Stripe for WooCommerce will work with all themes. If you run into any trouble, please let us know and we will be happy to resolve any issues.
88
+
89
+ = Do you offer support for this plugin? =
90
+ Yes, this plugin is fully supported. You can open a request here on the plugin page or visit our website to fill out our support request form.
91
+
92
  == Screenshots ==
93
 
94
  1. API Settings
98
 
99
  == Changelog ==
100
 
101
+ = 1.2.0 – TUESDAY, 4TH JANUARY 2022 =
102
+ * New: Supports Alipay payment method.
103
+ * New: Supports iDEAL payment method.
104
+ * Improvement: More customization options for Express Checkout.
105
+ * Improvement: Webhook integration for multiple events - charge.refunded, charge.dispute.created, charge.dispute.closed, payment_intent.succeeded, payment_intent.amount_capturable_updated, payment_intent.payment_failed, review.opened, review.closed.
106
+
107
  = 1.1.1 – WEDNESDAY, 22ND DECEMBER 2021 =
108
  * Fix: Express Checkout buttons were not appearing in live mode.
109
 
110
  = 1.1.0 – TUESDAY, 21ST DECEMBER 2021 =
111
+ * New: Express Checkout.
112
 
113
  = 1.0.0 – TUESDAY, 23RD NOVEMBER 2021 =
114
+ * Initial release.