-
Notifications
You must be signed in to change notification settings - Fork 42
/
class-sv-wc-payment-gateway.php
executable file
·4538 lines (3577 loc) · 140 KB
/
class-sv-wc-payment-gateway.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?php
/**
* WooCommerce Payment Gateway Framework
*
* This source file is subject to the GNU General Public License v3.0
* that is bundled with this package in the file license.txt.
* It is also available through the world-wide-web at this URL:
* http://www.gnu.org/licenses/gpl-3.0.html
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@skyverge.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade the plugin to newer
* versions in the future. If you wish to customize the plugin for your
* needs please refer to http://www.skyverge.com
*
* @package SkyVerge/WooCommerce/Payment-Gateway/Classes
* @author SkyVerge
* @copyright Copyright (c) 2013-2024, SkyVerge, Inc.
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
*/
namespace SkyVerge\WooCommerce\PluginFramework\v5_15_1;
use Automattic\WooCommerce\Blocks\Integrations\IntegrationInterface;
use SkyVerge\WooCommerce\PluginFramework\v5_15_1\Blocks\Blocks_Handler;
use SkyVerge\WooCommerce\PluginFramework\v5_15_1\Payment_Gateway\Blocks\Gateway_Checkout_Block_Integration;
use stdClass;
defined( 'ABSPATH' ) or exit;
if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_15_1\\SV_WC_Payment_Gateway' ) ) :
/**
* WooCommerce Payment Gateway Framework
*
* @since 1.0.0
*/
#[\AllowDynamicProperties]
abstract class SV_WC_Payment_Gateway extends \WC_Payment_Gateway {
/** Sends through sale and request for funds to be charged to cardholder's credit card. */
const TRANSACTION_TYPE_CHARGE = 'charge';
/** Sends through a request for funds to be "reserved" on the cardholder's credit card. A standard authorization is reserved for 2-5 days. Reservation times are determined by cardholder's bank. */
const TRANSACTION_TYPE_AUTHORIZATION = 'authorization';
/** The production environment identifier */
const ENVIRONMENT_PRODUCTION = 'production';
/** The test environment identifier */
const ENVIRONMENT_TEST = 'test';
/** Debug mode log to file */
const DEBUG_MODE_LOG = 'log';
/** Debug mode display on checkout */
const DEBUG_MODE_CHECKOUT = 'checkout';
/** Debug mode log to file and display on checkout */
const DEBUG_MODE_BOTH = 'both';
/** Debug mode disabled */
const DEBUG_MODE_OFF = 'off';
/** Gateway which supports direct (XML, REST, SOAP, custom, etc) communication */
const GATEWAY_TYPE_DIRECT = 'direct';
/** Gateway which supports redirecting to a gateway server for payment collection, or embedding an iframe on checkout */
const GATEWAY_TYPE_HOSTED = 'hosted';
/** Credit card payment type */
const PAYMENT_TYPE_CREDIT_CARD = 'credit-card';
/** eCheck payment type */
const PAYMENT_TYPE_ECHECK = 'echeck';
/** Gateway with multiple payment options */
const PAYMENT_TYPE_MULTIPLE = 'multiple';
/** Bank transfer gateway */
const PAYMENT_TYPE_BANK_TRANSFER = 'bank_transfer';
/** Products feature */
const FEATURE_PRODUCTS = 'products';
/** Credit card types feature */
const FEATURE_CARD_TYPES = 'card_types';
/** Tokenization feature */
const FEATURE_TOKENIZATION = 'tokenization';
/** Credit Card charge transaction feature */
const FEATURE_CREDIT_CARD_CHARGE = 'charge';
/** Credit Card authorization transaction feature */
const FEATURE_CREDIT_CARD_AUTHORIZATION = 'authorization';
/** Credit Card charge virtual-only orders feature */
const FEATURE_CREDIT_CARD_CHARGE_VIRTUAL = 'charge-virtual';
/** Credit Card capture charge transaction feature */
const FEATURE_CREDIT_CARD_CAPTURE = 'capture_charge';
/** Credit Card partial capture transaction feature */
const FEATURE_CREDIT_CARD_PARTIAL_CAPTURE = 'partial_capture';
/** Display detailed customer decline messages on checkout */
const FEATURE_DETAILED_CUSTOMER_DECLINE_MESSAGES = 'customer_decline_messages';
/** Refunds feature */
const FEATURE_REFUNDS = 'refunds';
/** Voids feature */
const FEATURE_VOIDS = 'voids';
/** Payment Form feature */
const FEATURE_PAYMENT_FORM = 'payment_form';
/** Customer ID feature */
const FEATURE_CUSTOMER_ID = 'customer_id';
/** Add new payment method feature */
const FEATURE_ADD_PAYMENT_METHOD = 'add_payment_method';
/** Apple Pay feature */
const FEATURE_APPLE_PAY = 'apple_pay';
/** Google Pay feature */
const FEATURE_GOOGLE_PAY = 'google_pay';
/** Admin token editor feature */
const FEATURE_TOKEN_EDITOR = 'token_editor';
/** Subscriptions integration ID */
const INTEGRATION_SUBSCRIPTIONS = 'subscriptions';
/** Pre-orders integration ID */
const INTEGRATION_PRE_ORDERS = 'pre_orders';
/** flag used when the checkout context is the shortcode-based checkout */
public const PROCESSING_CONTEXT_SHORTCODE = 'shortcode';
/** flag used when the checkout context is the block-based checkout */
public const PROCESSING_CONTEXT_BLOCK = 'block';
/** @var SV_WC_Payment_Gateway_Plugin the parent plugin class */
private $plugin;
/** @var string payment type, one of 'credit-card' or 'echeck' */
private $payment_type;
/** @var array associative array of environment id to display name, defaults to 'production' => 'Production' */
private $environments;
/** @var array associative array of card type to display name */
private $available_card_types;
/** @var array optional array of currency codes this gateway is allowed for */
protected $currencies;
/** @var string configuration option: the transaction environment, one of $this->environments keys */
private $environment;
/** @var string configuration option: the type of transaction, whether purchase or authorization, defaults to 'charge' */
private $transaction_type;
/** @var string configuration option: whether transactions should always be charged if the order is virtual-only, defaults to 'no' */
private $charge_virtual_orders;
/** @var string configuration option: whether orders can be partially captured multiple times */
private $enable_partial_capture;
/** @var string configuration option: whether orders are captured when switched to a "paid" status */
private $enable_paid_capture;
/** @var array configuration option: card types to show images for */
private $card_types;
/** @var string configuration option: indicates whether a Card Security Code field will be presented on checkout, either 'yes' or 'no' */
private $enable_csc;
/** @var string configuration option: indicates whether a Card Security Code field will be presented for saved cards at checkout, either 'yes' or 'no' */
private $enable_token_csc;
/** @var array configuration option: supported echeck fields, one of 'check_number', 'account_type' */
private $supported_check_fields;
/** @var string configuration option: indicates whether tokenization is enabled, either 'yes' or 'no' */
private $tokenization;
/** @var string configuration option: indicates whether detailed customer decline messages should be displayed at checkout, either 'yes' or 'no' */
private $enable_customer_decline_messages;
/** @var string configuration option: 4 options for debug mode - off, checkout, log, both */
private $debug_mode;
/** @var string configuration option: whether to use a sibling gateway's connection/authentication settings */
private $inherit_settings;
/** @var array of shared setting names, if any. This can be used for instance when a single plugin supports both credit card and echeck payments, and the same credentials can be used for both gateways */
private $shared_settings = array();
/** @var SV_WC_Payment_Gateway_Payment_Tokens_Handler payment tokens handler instance */
protected $payment_tokens_handler;
/** @var Payment_Gateway\Handlers\Capture capture handler instance */
protected $capture_handler;
/** @var array of SV_WC_Payment_Gateway_Integration objects for Subscriptions, Pre-Orders, etc. */
protected $integrations;
/** @var SV_WC_Payment_Gateway_Payment_Form|null payment form instance */
protected $payment_form;
/**
* Initialize the gateway
*
* Args:
*
* + `method_title` - string admin method title, ie 'Intuit QBMS', defaults to 'Settings'
* + `method_description` - string admin method description, defaults to ''
* + `supports` - array list of supported gateway features, possible values include:
* 'products', 'card_types', 'tokenziation', 'charge', 'authorization', 'subscriptions',
* 'subscription_suspension', 'subscription_cancellation', 'subscription_reactivation',
* 'subscription_amount_changes', 'subscription_date_changes', 'subscription_payment_method_change',
* 'customer_decline_messages'
* Defaults to 'products', 'charge' (credit-card gateways only)
* + `payment_type` - string one of 'credit-card' or 'echeck', defaults to 'credit-card'
* + `card_types` - array associative array of card type to display name, used if the payment_type is 'credit-card' and the 'card_types' feature is supported. Defaults to:
* 'VISA' => 'Visa', 'MC' => 'MasterCard', 'AMEX' => 'American Express', 'DISC' => 'Discover', 'DINERS' => 'Diners', 'JCB' => 'JCB'
* + `echeck_fields` - array of supported echeck fields, including 'check_number', 'account_type'
* + `environments` - associative array of environment id to display name, merged with default of 'production' => 'Production'
* + `currencies` - array of currency codes this gateway is allowed for, defaults to plugin accepted currencies
* + `countries` - array of two-letter country codes this gateway is allowed for, defaults to all
* + `shared_settings` - array of shared setting names, if any. This can be used for instance when a single plugin supports both credit card and echeck payments, and the same credentials can be used for both gateways
*
* @since 1.0.0
* @param string $id the gateway id
* @param SV_WC_Payment_Gateway_Plugin $plugin the parent plugin class
* @param array $args gateway arguments
*/
public function __construct( $id, $plugin, $args ) {
// first setup the gateway and payment type for this gateway
$this->payment_type = isset( $args['payment_type'] ) ? $args['payment_type'] : self::PAYMENT_TYPE_CREDIT_CARD;
// default credit card gateways to supporting 'charge' transaction type, this could be overridden by the 'supports' constructor parameter to include (or only support) authorization
if ( $this->is_credit_card_gateway() ) {
$this->add_support( self::FEATURE_CREDIT_CARD_CHARGE );
}
// required fields
$this->id = $id; // @see WC_Payment_Gateway::$id
$this->plugin = $plugin;
// kind of sucks, but we need to register back to the plugin because
// there's no other way of grabbing existing gateways so as to avoid
// double-instantiation errors (esp for shared settings)
$this->get_plugin()->set_gateway( $id, $this );
// optional parameters
if ( isset( $args['method_title'] ) ) {
$this->method_title = $args['method_title']; // @see WC_Settings_API::$method_title
}
if ( isset( $args['method_description'] ) ) {
$this->method_description = $args['method_description']; // @see WC_Settings_API::$method_description
}
if ( isset( $args['supports'] ) ) {
$this->set_supports( $args['supports'] );
}
if ( isset( $args['card_types'] ) ) {
$this->available_card_types = $args['card_types'];
}
if ( isset( $args['echeck_fields'] ) ) {
$this->supported_check_fields = $args['echeck_fields'];
}
if ( isset( $args['environments'] ) ) {
$this->environments = array_merge( $this->get_environments(), $args['environments'] );
}
if ( isset( $args['countries'] ) ) {
$this->countries = $args['countries']; // @see WC_Payment_Gateway::$countries
}
if ( isset( $args['shared_settings'] ) ) {
$this->shared_settings = $args['shared_settings'];
}
if ( isset( $args['currencies'] ) ) {
$this->currencies = $args['currencies'];
} else {
$this->currencies = $this->get_plugin()->get_accepted_currencies();
}
if ( isset( $args['order_button_text'] ) ) {
$this->order_button_text = $args['order_button_text'];
} else {
$this->order_button_text = $this->get_order_button_text();
}
// always want to render the field area, even for gateways with no fields, so we can display messages @see WC_Payment_Gateway::$has_fields
$this->has_fields = true;
// default icon filter @see WC_Payment_Gateway::$icon
$this->icon = apply_filters( 'wc_' . $this->get_id() . '_icon', '' );
// Load the form fields
$this->init_form_fields();
// initialize and load the settings
$this->init_settings();
$this->load_settings();
$this->init_payment_tokens_handler();
$this->init_integrations();
// initialize the capture handler
$this->init_capture_handler();
// initialize the payment form
$this->maybe_init_payment_form();
// pay page fallback
$this->add_pay_page_handler();
// filter order received text for held orders
add_filter( 'woocommerce_thankyou_order_received_text', array( $this, 'maybe_render_held_order_received_text' ), 10, 2 );
// admin only
if ( is_admin() ) {
// save settings
add_action( 'woocommerce_update_options_payment_gateways_' . $this->get_id(), array( $this, 'process_admin_options' ) );
}
// Enqueue the necessary scripts & styles
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
// add API request logging
$this->add_api_request_logging();
// add milestone action hooks
$this->add_milestone_hooks();
}
/**
* Adds the various milestone hooks like "payment processed".
*
* @since 5.1.0
*/
protected function add_milestone_hooks() {
$plugin = $this->get_plugin();
// first successful payment
add_action( 'wc_payment_gateway_' . $this->get_id() . '_payment_processed', function( $order ) use ( &$plugin ) {
$plugin->get_lifecycle_handler()->trigger_milestone( 'payment-processed', lcfirst( _x( 'You successfully processed a payment!', 'Congratulatory message displayed to the merchant when they process their first payment', 'woocommerce-plugin-framework' ) ) );
} );
// first successful refund
add_action( 'wc_payment_gateway_' . $this->get_id() . '_refund_processed', function( $order ) use ( &$plugin ) {
$plugin->get_lifecycle_handler()->trigger_milestone( 'refund-processed', lcfirst( _x( 'You successfully processed a refund!', 'Congratulatory message displayed to the merchant when they process their first refund','woocommerce-plugin-framework' ) ) );
} );
}
/**
* Loads the plugin configuration settings
*
* @since 1.0.0
*/
public function load_settings() {
// define user set variables
foreach ( $this->settings as $setting_key => $setting ) {
$this->$setting_key = $setting;
}
// inherit settings from sibling gateway(s)
if ( $this->inherit_settings() ) {
$this->load_shared_settings();
}
}
/**
* Loads any shared settings from sibling gateways.
*
* @since 4.5.0
*/
protected function load_shared_settings() {
// get any other sibling gateways
$other_gateway_ids = $this->get_ids_of_gateways_to_inherit_settings_from();
// determine if any sibling gateways have any configured shared settings
foreach ( $other_gateway_ids as $other_gateway_id ) {
$other_gateway_settings = $this->get_plugin()->get_gateway_settings( $other_gateway_id );
// if the other gateway isn't also trying to inherit settings...
if ( ! isset( $other_gateway_settings['inherit_settings'] ) || 'no' === $other_gateway_settings['inherit_settings'] ) {
// load the other gateway so we can access the shared settings properly
$other_gateway = $this->get_plugin()->get_gateway( $other_gateway_id );
// skip this gateway if it isn't meant to share its settings
if ( ! $other_gateway->share_settings() ) {
continue;
}
foreach ( $this->shared_settings as $setting_key ) {
$this->$setting_key = $other_gateway->$setting_key;
}
}
}
}
/**
* Gets the IDs of sibling gateways that this gateway can inherit settings from.
*
* @since 5.10.0
*
* @return array
*/
protected function get_ids_of_gateways_to_inherit_settings_from() {
return array_diff( $this->get_plugin()->get_gateway_ids(), [ $this->get_id() ] );
}
/**
* Enqueue the necessary scripts & styles for the gateway, including the
* payment form assets (if supported) and any gateway-specific assets.
*
* @since 1.0.0
*/
public function enqueue_scripts() {
if ( ! $this->is_available() ) {
return;
}
// payment form assets
if ( $this->supports_payment_form() || $this->supports_add_payment_method() ) {
$this->enqueue_payment_form_assets();
}
// gateway-specific assets
$this->enqueue_gateway_assets();
}
/**
* Enqueue the payment form JS, CSS, and localized
* JS params
*
* @since 4.3.0
*/
protected function enqueue_payment_form_assets() {
// bail if on my account page and *not* on add payment method page
if ( is_account_page() && ! is_add_payment_method_page() ) {
return;
}
$handle = 'sv-wc-payment-gateway-payment-form';
$versioned_handle = $handle . '-v5_15_1';
// Frontend JS
wp_enqueue_script( $versioned_handle, $this->get_plugin()->get_payment_gateway_framework_assets_url() . '/dist/frontend/' . $handle . '.js', array( 'jquery-payment' ), SV_WC_Plugin::VERSION, true );
// Frontend CSS
wp_enqueue_style( $versioned_handle, $this->get_plugin()->get_payment_gateway_framework_assets_url() . '/css/frontend/' . $handle . '.min.css', array(), SV_WC_Plugin::VERSION );
// localized JS params
$this->localize_script( $versioned_handle, $this->get_payment_form_js_localized_script_params(), 'sv_wc_payment_gateway_payment_form_params' );
}
/**
* Returns an array of JS script params to localize for the payment form JS. Generally used for i18n purposes.
*
* @note This method exists for backwards compatibility with the shortcode-based checkout.
*
* @since 4.3.0
*
* @return array<string, string> associative array of param name to value
*/
protected function get_payment_form_js_localized_script_params() : array {
return $this->get_gateway_payment_form_localized_params();
}
/**
* Gets a list of messages that could be displayed in the front end payment form or checkout block.
*
* @since 5.12.0
*
* @return array<string, string>
*/
public function get_gateway_payment_form_localized_params() : array {
/**
* Filters localized params that may be used at checkout.
*
* Typically, these include a list of customer-facing messages that may be displayed at checkout when there's a gateway error.
* These messages shouldn't disclose any sensitive information, or include excessive details about the error, to avoid helping bad actors.
*
* @since 5.12.0
*
* @param array<string, string> $params
* @param SV_WC_Payment_Gateway $gateway
*/
$params = (array) apply_filters( 'sv_wc_payment_gateway_payment_form_localized_script_params', [
'order_button_text' => $this->get_order_button_text(),
'card_number_missing' => esc_html_x( 'Card number is missing', 'Credit or debit card','woocommerce-plugin-framework' ),
'card_number_invalid' => esc_html_x( 'Card number is invalid', 'Credit or debit card', 'woocommerce-plugin-framework' ),
'card_number_digits_invalid' => esc_html_x( 'Card number is invalid (only digits allowed)', 'Credit or debit card', 'woocommerce-plugin-framework' ),
'card_number_length_invalid' => esc_html_x( 'Card number is invalid (wrong length)', 'Credit or debit card', 'woocommerce-plugin-framework' ),
'card_type_invalid' => esc_html_x( 'Card is invalid', 'Credit or debit card', 'woocommerce-plugin-framework' ),
/* translators: {card_type} will be replaced by a corresponding card type name, e.g. American Express */
'card_type_invalid_specific_type' => esc_html__( '{card_type} card is invalid', 'woocommerce-plugin-framework' ),
'cvv_missing' => esc_html_x( 'Card security code is missing', 'Credit or debit card', 'woocommerce-plugin-framework' ),
'cvv_digits_invalid' => esc_html_x( 'Card security code is invalid (only digits are allowed)', 'Credit or debit card','woocommerce-plugin-framework' ),
'cvv_length_invalid' => esc_html_x( 'Card security code is invalid (must be 3 or 4 digits)', 'Credit or debit card', 'woocommerce-plugin-framework' ),
'card_exp_date_invalid' => esc_html_x( 'Card expiration date is invalid', 'Credit or debit card', 'woocommerce-plugin-framework' ),
'check_number_digits_invalid' => esc_html_x( 'Check Number is invalid (only digits are allowed)', 'Bank check (noun)', 'woocommerce-plugin-framework' ),
'check_number_missing' => esc_html_x( 'Check Number is missing', 'Bank check (noun)', 'woocommerce-plugin-framework' ),
'drivers_license_state_missing' => esc_html_x( "Driver's license state is missing", 'For countries without states, "state" can be replaced with "place of issue".', 'woocommerce-plugin-framework' ),
'drivers_license_number_missing' => esc_html__( "Driver's license number is missing", 'woocommerce-plugin-framework' ),
'drivers_license_number_invalid' => esc_html__( "Driver's license number is invalid", 'woocommerce-plugin-framework' ),
'account_number_missing' => esc_html_x( 'Account Number is missing', 'Bank account', 'woocommerce-plugin-framework' ),
'account_number_invalid' => esc_html_x( 'Account Number is invalid (only digits are allowed)', 'Bank account', 'woocommerce-plugin-framework' ),
'account_number_length_invalid' => esc_html_x( 'Account Number is invalid (must be between 5 and 17 digits)', 'Bank account', 'woocommerce-plugin-framework' ),
'routing_number_missing' => esc_html_x( 'Routing Number is missing', 'Bank account', 'woocommerce-plugin-framework' ),
'routing_number_digits_invalid' => esc_html_x( 'Routing Number is invalid (only digits are allowed)', 'Bank account', 'woocommerce-plugin-framework' ),
'routing_number_length_invalid' => esc_html_x( 'Routing Number is invalid (must be 9 digits)', 'Bank account', 'woocommerce-plugin-framework' ),
], $this );
/**
* Payment Form JS Localized Script Params filter.
*
* Allow actors to modify the JS localized script params for the payment form.
*
* @note This is a legacy filter that is kept here for backwards compatibility.
*
* @since 4.3.0
*
* @param array<string, string> $params
*/
return (array) apply_filters( 'sv_wc_payment_gateway_payment_form_js_localized_script_params', $params );
}
/**
* Enqueue the gateway-specific assets if present, including JS, CSS, and localized script params.
*
* @since 4.3.0
*
* @return void
*/
protected function enqueue_gateway_assets() {
if ( ! $this->should_enqueue_gateway_assets() ) {
return;
}
$handle = $this->get_gateway_js_handle();
$js_path = $this->get_plugin()->get_plugin_path() . '/assets/js/frontend/' . $handle . '.min.js';
$css_path = $this->get_plugin()->get_plugin_path() . '/assets/css/frontend/' . $handle . '.min.css';
$version = $this->get_plugin()->get_assets_version( $this->get_id() );
// JS
if ( is_readable( $js_path ) ) {
$js_url = $this->get_plugin()->get_plugin_url() . '/assets/js/frontend/' . $handle . '.min.js';
/**
* Concrete Payment Gateway JS URL
*
* Allow actors to modify the URL used when loading a concrete
* payment gateway's javascript.
*
* @since 2.0.0
* @param string $js_url JS asset URL
* @return string
*/
$js_url = apply_filters( 'wc_payment_gateway_' . $this->get_plugin()->get_id() . '_javascript_url', $js_url );
wp_enqueue_script( $handle, $js_url, [], $version, [ 'in_footer' => true ] );
}
// CSS
if ( is_readable( $css_path ) ) {
$css_url = $this->get_plugin()->get_plugin_url() . '/assets/css/frontend/' . $handle . '.min.css';
/**
* Concrete Payment Gateway CSS URL
*
* Allow actors to modify the URL used when loading a concrete payment
* gateway's CSS.
*
* @since 4.3.0
* @param string $css_url CSS asset URL
* @return string
*/
$css_url = apply_filters( 'wc_payment_gateway_' . $this->get_plugin()->get_id() . '_css_url', $css_url );
wp_enqueue_style( $handle, $css_url, [], $version );
}
// localized JS script params
if ( $params = $this->get_gateway_js_localized_script_params() ) {
/**
* Payment Gateway Localized JS Script Params Filter.
*
* Allow actors to modify the localized script params passed to the
* frontend for the concrete payment gateway's JS.
*
* @since 2.2.0
* @param $params array
* @return array
*/
$params = apply_filters( 'wc_gateway_' . $this->get_plugin()->get_id() . '_js_localize_script_params', $params );
$this->localize_script( $handle, $params );
}
}
/**
* Determines whether the front end gateway assets should load.
*
* By default, we don't load the legacy frontend when the checkout block is in use.
*
* @since 5.12.0
*
* @return bool
*/
protected function should_enqueue_gateway_assets() : bool {
global $post;
if ( is_checkout() && ! is_checkout_pay_page() && Blocks_Handler::is_checkout_block_in_use() && ( $post && ! Blocks_Handler::page_contains_checkout_shortcode( $post ) ) ) {
return false;
}
return true;
}
/**
* Return the gateway-specifics JS script handle. This is used for:
*
* + enqueuing the script
* + the localized JS script param object name
*
* Defaults to 'wc-<plugin ID dasherized>'.
*
* @since 4.3.0
* @return string
*/
protected function get_gateway_js_handle() {
return 'wc-' . $this->get_plugin()->get_id_dasherized();
}
/**
* Returns an array of JS script params to localize for the gateway-specific
* JS. Concrete classes must override this as needed.
*
* @since 4.3.0
* @return array
*/
protected function get_gateway_js_localized_script_params() {
// stub method
}
/**
* Localize a script once. Gateway plugins that have multiple gateways should
* only have their params localized once.
*
* @since 4.3.0
* @param string $handle script handle to localize
* @param array $params script params to localize
* @param string $object_name the localized object name. Defaults to a snake-cased version of $handle
*/
protected function localize_script( $handle, $params, $object_name = '' ) {
// If the script isn't loaded, bail
if ( ! wp_script_is( $handle, 'enqueued' ) ) {
return;
}
global $wp_scripts;
// generate the object name from the handle if none is specified
if ( ! $object_name ) {
$object_name = str_replace( '-', '_', $handle ) . '_params';
}
// If the plugin's JS params already exists in the localized data, bail
if ( $wp_scripts instanceof \WP_Scripts && strpos( $wp_scripts->get_data( $handle, 'data' ), $object_name ) ) {
return;
}
wp_localize_script( $handle, $object_name, $params );
}
/**
* Initializes the payment form handler.
*
* @since 5.7.0
*/
protected function maybe_init_payment_form() {
// only if the gateway supports it
if ( ! $this->supports_payment_form() ) {
return;
}
// only load on the frontend and AJAX
if ( is_admin() && ! wp_doing_ajax() ) {
return;
}
$this->payment_form = $this->init_payment_form_instance();
}
/**
* Initializes the payment form instance.
*
* @since 5.7.0
*
* @return SV_WC_Payment_Gateway_Payment_Form
*/
protected function init_payment_form_instance() {
return new SV_WC_Payment_Gateway_Payment_Form( $this );
}
/**
* Returns true if on the pay page and this is the currently selected gateway
*
* @since 1.0.0
*
* @return null|bool true if on pay page and is currently selected gateways, false if on pay page and not the selected gateway, null otherwise
*/
public function is_pay_page_gateway() {
if ( is_checkout_pay_page() ) {
$order_id = $this->get_checkout_pay_page_order_id();
if ( $order_id && ( $order = wc_get_order( $order_id ) ) ) {
return $order->get_payment_method( 'edit' ) === $this->get_id();
}
}
return null;
}
/**
* Gets the order button text:
*
* Direct gateway: "Place order"
* Redirect/Hosted gateway: "Continue to Payment"
*
* If a gateway has a separate payment page, then we expect to show "Continue to Payment" on checkout, and
* "Place order" on the separate pay page.
*
* @since 4.0.0
*/
protected function get_order_button_text() {
$text = $this->is_hosted_gateway() ? esc_html__( 'Continue to Payment', 'woocommerce-plugin-framework' ) : esc_html__( 'Place order', 'woocommerce-plugin-framework' );
if ($this->hasSeparatePaymentPage()) {
$text = SV_WC_Helper::isCheckoutPayPage() ? esc_html__( 'Place order', 'woocommerce-plugin-framework' ) : esc_html__( 'Continue to Payment', 'woocommerce-plugin-framework' );
}
/**
* Payment Gateway Place Order Button Text Filter.
*
* Allow actors to modify the "place order" button text.
*
* @since 4.0.0
*
* @param string $text button text
* @param SV_WC_Payment_Gateway $gateway the current gateway instance
*/
return apply_filters( 'wc_payment_gateway_' . $this->get_id() . '_order_button_text', $text, $this );
}
/**
* Adds a default simple pay page handler
*
* @since 1.0.0
*/
protected function add_pay_page_handler() {
add_action( 'woocommerce_receipt_' . $this->get_id(), array( $this, 'payment_page' ) );
}
/**
* Render a simple payment page
*
* @since 2.1.0
* @param int $order_id identifies the order
*/
public function payment_page( $order_id ) {
echo '<p>' . esc_html__( 'Thank you for your order.', 'woocommerce-plugin-framework' ) . '</p>';
}
/**
* Gets the WooCommerce Checkout Block integration.
*
* @since 5.11.11
*
* @return Gateway_Checkout_Block_Integration|null
*/
public function get_checkout_block_integration_instance() : ?Gateway_Checkout_Block_Integration {
// individual gateways may override this method to provide their own integration
return null;
}
/**
* Gets the WooCommerce Cart Block integration.
*
* @since 5.11.11
*
* @return IntegrationInterface|null
*/
public function get_cart_block_integration_instance() : ?IntegrationInterface {
// individual gateways may override this method to provide their own integration
return null;
}
/** Payment Form Feature **************************************************/
/**
* Returns true if the gateway supports the payment form feature
*
* @since 4.0.0
* @return bool
*/
public function supports_payment_form() {
return $this->supports( self::FEATURE_PAYMENT_FORM );
}
/**
* Render the payment fields
*
* @since 4.0.0
* @see WC_Payment_Gateway::payment_fields()
* @see SV_WC_Payment_Gateway_Payment_Form class
*/
public function payment_fields() {
if ( $this->get_payment_form_instance() && is_callable( [ $this->get_payment_form_instance(), 'render' ] ) && $this->supports_payment_form() ) {
$this->get_payment_form_instance()->render();
} else {
parent::payment_fields();
}
$this->payment_method_selector_styles();
}
/**
* Renders the payment method selector styles.
*
* Uses flexbox for any Framework-powered payment method selector, to ensure credit card icons are aligned properly.
*
* Note that we cannot apply the styles in the CSS file because there is no generic selector that would only affect FW gateways.
* Instead, each FW gateway prints these styles with the label selector for its own gateway.
*
* @since 5.11.7
*
* @TODO: this method can be removed if/when WooCommerce updates their frontend to use flexbox {@itambek 2023-07-26}
*
* @return void
*/
protected function payment_method_selector_styles(): void
{
?><style>
#payment ul.payment_methods li label[for='payment_method_<?php echo $this->get_id(); ?>'] { display: flex; flex-wrap: wrap; row-gap: 10px; }
#payment ul.payment_methods li label[for='payment_method_<?php echo $this->get_id(); ?>'] > img { margin-left: auto; }
</style><?php
}
/**
* Gets the payment form class instance.
*
* @since 4.1.2
*
* @return SV_WC_Payment_Gateway_Payment_Form|null
*/
public function get_payment_form_instance() {
return $this->payment_form;
}
/**
* Get the payment form field defaults, primarily for gateways to override
* and set dummy credit card/eCheck info when in the test environment
*
* @since 4.0.0
* @return array
*/
public function get_payment_method_defaults() {
$this->get_plugin()->assert( $this->supports_payment_form() );
$defaults = array(
'account-number' => '',
'routing-number' => '',
'expiry' => '',
'csc' => '',
);
if ( $this->is_test_environment() ) {
$defaults['expiry'] = '01/' . ( date( 'y' ) + 1 );
$defaults['csc'] = '123';
}
return $defaults;
}
/** Tokenization **************************************************/
/**
* Initialize payment tokens handler.
*
* @since 5.0.0
*/
protected function init_payment_tokens_handler() {
$this->payment_tokens_handler = $this->build_payment_tokens_handler();
}
/**
* Gets the payment tokens handler instance.
*
* Concrete classes can override this method to return a custom implementation.
*
* @since 5.0.0
*
* @return SV_WC_Payment_Gateway_Payment_Tokens_Handler
*/
protected function build_payment_tokens_handler() {
return new SV_WC_Payment_Gateway_Payment_Tokens_Handler( $this );
}
/**
* Gets the payment tokens handler instance.
*
* @since 5.0.0
*
* @return SV_WC_Payment_Gateway_Payment_Tokens_Handler
*/
public function get_payment_tokens_handler() {