PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.28
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.28
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / stripe / controllers / FrmStrpLiteActionsController.php

FrmStrpLiteActionsController.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.28, at stripe/controllers/FrmStrpLiteActionsController.php

670 lines 18.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmStrpLiteActionsController extends FrmTransLiteActionsController {
7
8 /**
9 * @var object|string|null A memoized value for the current Stripe customer object.
10 */
11 private static $customer;
12
13 /**
14 * @since 6.22
15 *
16 * @param string $callback
17 * @param array|false|object $field
18 *
19 * @return string
20 */
21 public static function maybe_show_card( $callback, $field = false ) {
22 if ( false === $field ) {
23 // Pro isn't up to date.
24 // Fallback to Stripe Lite if we do not know the form.
25 // This way we do not display the default credit card field
26 // when Pro is not up to version 6.21.
27 return self::class . '::show_card';
28 }
29
30 $form_id = is_object( $field ) ? $field->form_id : $field['form_id'];
31 $actions = self::get_actions_before_submit( $form_id );
32
33 return $actions ? self::class . '::show_card' : $callback;
34 }
35
36 /**
37 * Override the credit card field HTML if there is a Stripe action.
38 *
39 * @since 6.5, introduced in v2.0 of the Stripe add on.
40 *
41 * @param array $field
42 * @param string $field_name
43 * @param array $atts
44 *
45 * @return void
46 */
47 public static function show_card( $field, $field_name, $atts ) {
48 $actions = self::get_actions_before_submit( $field['form_id'] );
49
50 // Use the Pro function when there are no Stripe actions.
51 // This is required for other gateways like Authorize.Net.
52 if ( ! $actions && is_callable( 'FrmProCreditCardsController::show_in_form' ) ) {
53 FrmProCreditCardsController::show_in_form( $field, $field_name, $atts );
54 }
55
56 $html_id = $atts['html_id'];
57 include FrmStrpLiteAppHelper::plugin_path() . '/views/payments/card-field.php';
58 }
59
60 /**
61 * Get all published payment actions with the Stripe gateway that have an amount set.
62 *
63 * @since 6.5, introduced in v2.0 of the Stripe add on.
64 *
65 * @param int|string $form_id
66 *
67 * @return array
68 */
69 public static function get_actions_before_submit( $form_id ) {
70 $payment_actions = self::get_actions_for_form( $form_id );
71
72 foreach ( $payment_actions as $k => $payment_action ) {
73 $gateway = $payment_action->post_content['gateway'];
74 $is_stripe = $gateway === 'stripe' || ( is_array( $gateway ) && in_array( 'stripe', $gateway, true ) );
75
76 if ( ! $is_stripe || empty( $payment_action->post_content['amount'] ) ) {
77 unset( $payment_actions[ $k ] );
78 }
79 }
80
81 return $payment_actions;
82 }
83
84 /**
85 * Check the Stripe link action for a form. There should only be one.
86 * We want to ignore $action->post_content['stripe_link'].
87 * This way any Stripe action will fall back to Stripe link when the Stripe add on is unavailable.
88 *
89 * @since 6.5, introduced in v3.0 of the Stripe add on.
90 *
91 * @param int|string $form_id
92 *
93 * @return false|WP_Post
94 */
95 public static function get_stripe_link_action( $form_id ) {
96 $actions = self::get_actions_before_submit( $form_id );
97 return reset( $actions );
98 }
99
100 /**
101 * Trigger a Stripe payment after a form is submitted.
102 * This is called for both one time and recurring payments.
103 * It is also called when Stripe link is active but the Stripe payment is triggered instead with JavaScript.
104 *
105 * @param WP_Post $action
106 * @param stdClass $entry
107 * @param mixed $form
108 *
109 * @return array
110 */
111 public static function trigger_gateway( $action, $entry, $form ) {
112 $response = array(
113 'success' => false,
114 'run_triggers' => false,
115 'show_errors' => true,
116 );
117 $atts = compact( 'action', 'entry', 'form' );
118 $amount = self::prepare_amount( $action->post_content['amount'], $atts );
119
120 // phpcs:ignore Universal.Operators.StrictComparisons
121 if ( ! $amount || $amount == 000 ) {
122 $response['error'] = __( 'Please specify an amount for the payment', 'formidable' );
123 return $response;
124 }
125
126 if ( ! self::stripe_is_configured() ) {
127 $response['error'] = __( 'There was a problem communicating with Stripe. Please try again.', 'formidable' );
128 return $response;
129 }
130
131 $customer = self::set_customer_with_token( $atts );
132
133 if ( ! is_object( $customer ) ) {
134 $response['error'] = $customer;
135 return $response;
136 }
137
138 $one_time_payment_args = compact( 'customer', 'form', 'entry', 'action', 'amount' );
139
140 FrmStrpLiteLinkController::create_pending_stripe_link_payment( $one_time_payment_args );
141 $response['show_errors'] = false;
142 return $response;
143 }
144
145 /**
146 * Check if either Stripe integration is enabled.
147 *
148 * @return bool true if Stripe Connect is set up.
149 */
150 private static function stripe_is_configured() {
151 return FrmStrpLiteAppHelper::call_stripe_helper_class( 'initialize_api' );
152 }
153
154 /**
155 * Set a customer object to $_POST['customer'] to use later.
156 *
157 * @param array $atts
158 *
159 * @return object|string
160 */
161 private static function set_customer_with_token( $atts ) {
162 if ( isset( self::$customer ) ) {
163 // It's an object if this isn't the first Stripe action running.
164 return self::$customer;
165 }
166
167 $payment_info = array(
168 'user_id' => FrmTransLiteAppHelper::get_user_id_for_current_payment(),
169 );
170
171 if ( ! empty( $atts['action']->post_content['email'] ) ) {
172 $payment_info['email'] = apply_filters( 'frm_content', $atts['action']->post_content['email'], $atts['form'], $atts['entry'] );
173 $payment_info['email'] = self::replace_email_shortcode( $payment_info['email'] );
174 }
175
176 self::add_customer_name( $atts, $payment_info );
177
178 $customer = FrmStrpLiteAppHelper::call_stripe_helper_class( 'get_customer', $payment_info );
179 // Set for later use.
180 self::$customer = $customer;
181
182 return $customer;
183 }
184
185 /**
186 * Replace an [email] shortcode with the current user email.
187 *
188 * @param string $email
189 *
190 * @return string
191 */
192 private static function replace_email_shortcode( $email ) {
193 if ( ! str_contains( $email, '[email]' ) ) {
194 return $email;
195 }
196
197 global $current_user;
198 return str_replace(
199 '[email]',
200 ! empty( $current_user->user_email ) ? $current_user->user_email : '',
201 $email
202 );
203 }
204
205 /**
206 * Set the customer name based on the mapped first and last name fields in the Stripe action.
207 *
208 * @since 6.5, introduced in v2.02 of the Stripe add on.
209 *
210 * @param array $atts
211 * @param array $payment_info
212 *
213 * @return void
214 */
215 private static function add_customer_name( $atts, &$payment_info ) {
216 if ( empty( $atts['action']->post_content['billing_first_name'] ) ) {
217 return;
218 }
219
220 $name = '[' . $atts['action']->post_content['billing_first_name'] . ' show="first"]';
221
222 if ( ! empty( $atts['action']->post_content['billing_last_name'] ) ) {
223 $name .= ' [' . $atts['action']->post_content['billing_last_name'] . ' show="last"]';
224 }
225
226 $payment_info['name'] = apply_filters( 'frm_content', $name, $atts['form'], $atts['entry'] );
227 }
228
229 /**
230 * Get the trial period from the settings or from the connected entry.
231 *
232 * @since 6.5, introduced in v1.16 of the Stripe add on.
233 *
234 * @param array $atts Includes 'customer', 'entry', 'action', 'amount'.
235 *
236 * @return int The timestamp when the trial should end. 0 for no trial
237 */
238 public static function get_trial_end_time( $atts ) {
239 $settings = $atts['action']->post_content;
240
241 if ( empty( $settings['trial_interval_count'] ) ) {
242 return 0;
243 }
244
245 $trial = $settings['trial_interval_count'];
246
247 if ( ! is_numeric( $trial ) ) {
248 $trial = FrmTransLiteAppHelper::process_shortcodes(
249 array(
250 'value' => $trial,
251 'entry' => $atts['entry'],
252 )
253 );
254 }
255
256 if ( ! $trial ) {
257 return 0;
258 }
259
260 return strtotime( '+' . absint( $trial ) . ' days' );
261 }
262
263 /**
264 * Convert the amount from 10.00 to 1000.
265 *
266 * @param mixed $amount
267 * @param array $atts
268 *
269 * @return string
270 */
271 public static function prepare_amount( $amount, $atts = array() ) {
272 $amount = parent::prepare_amount( $amount, $atts );
273 $currency = self::get_currency_for_action( $atts );
274 return number_format( $amount, $currency['decimals'], '', '' );
275 }
276
277 /**
278 * Add defaults for additional Stripe action options.
279 *
280 * @param array $defaults
281 *
282 * @return array
283 */
284 public static function add_action_defaults( $defaults ) {
285 $defaults['plan_id'] = '';
286 $defaults['capture'] = '';
287 $defaults['stripe_link'] = '';
288 return $defaults;
289 }
290
291 /**
292 * Print additional options for Stripe action settings.
293 *
294 * @param array $atts
295 *
296 * @return void
297 */
298 public static function add_action_options( $atts ) {
299 $form_action = $atts['form_action'];
300 $action_control = $atts['action_control'];
301 include FrmStrpLiteAppHelper::plugin_path() . '/views/action-settings/options.php';
302 }
303
304 /**
305 * Filter Stripe action on save.
306 *
307 * @param array $settings
308 * @param array $action
309 *
310 * @return array
311 */
312 public static function before_save_settings( $settings, $action ) {
313 $settings['currency'] = strtolower( $settings['currency'] );
314
315 // Gateway is a radio button but it should always be an array in the database for
316 // compatibility with the payments submodule where it is a checkbox.
317 $settings['gateway'] = ! empty( $settings['gateway'] ) ? (array) $settings['gateway'] : array( 'stripe' );
318
319 $is_stripe = in_array( 'stripe', $settings['gateway'], true );
320
321 if ( ! $is_stripe ) {
322 return $settings;
323 }
324
325 // In Lite Stripe link is always used.
326 $settings['stripe_link'] = 1;
327
328 return self::create_plans( $settings );
329 }
330
331 /**
332 * Create any required Stripe plans, used for subscriptions.
333 *
334 * @param array $settings
335 *
336 * @return array
337 */
338 public static function create_plans( $settings ) {
339 if ( $settings['type'] !== 'recurring' || strpos( $settings['amount'], ']' ) ) {
340 // Don't create a plan for one time payments, or for actions with shortcodes in the value.
341 $settings['plan_id'] = '';
342 return $settings;
343 }
344
345 $plan_opts = FrmStrpLiteSubscriptionHelper::prepare_plan_options( $settings );
346
347 // phpcs:ignore Universal.Operators.StrictComparisons
348 if ( $plan_opts['id'] != $settings['plan_id'] ) {
349 $settings['plan_id'] = FrmStrpLiteSubscriptionHelper::maybe_create_plan( $plan_opts );
350 }
351
352 return $settings;
353 }
354
355 /**
356 * Include settings in the plan description in order to make sure the correct plan is used.
357 *
358 * @param array $settings
359 *
360 * @return string
361 */
362 public static function create_plan_id( $settings ) {
363 $amount = self::prepare_amount( $settings['amount'], $settings );
364 return sanitize_title_with_dashes( $settings['description'] ) . '_' . $amount . '_' . $settings['interval_count'] . $settings['interval'] . '_' . $settings['currency'];
365 }
366
367 /**
368 * If this form submits with ajax, load the scripts on the first page.
369 *
370 * @param array $params
371 *
372 * @return void
373 */
374 public static function maybe_load_scripts( $params ) {
375 // phpcs:ignore Universal.Operators.StrictComparisons
376 if ( $params['form_id'] == $params['posted_form_id'] ) {
377 // This form has already been posted, so we aren't on the first page.
378 return;
379 }
380
381 $form = FrmForm::getOne( $params['form_id'] );
382
383 if ( ! $form ) {
384 return;
385 }
386
387 $credit_card_field = FrmField::getAll(
388 array(
389 'fi.form_id' => $form->id,
390 'type' => 'credit_card',
391 )
392 );
393
394 if ( ! $credit_card_field ) {
395 return;
396 }
397
398 self::load_scripts( (int) $form->id );
399 }
400
401 /**
402 * Load front end JavaScript for a Stripe form.
403 *
404 * @param int $form_id
405 *
406 * @return void
407 */
408 public static function load_scripts( $form_id ) {
409 if ( FrmAppHelper::is_admin_page( 'formidable-entries' ) ) {
410 return;
411 }
412
413 if ( wp_script_is( 'formidable-stripe', 'enqueued' ) ) {
414 return;
415 }
416
417 $stripe_connect_is_setup = FrmStrpLiteConnectHelper::stripe_connect_is_setup();
418
419 if ( ! $stripe_connect_is_setup ) {
420 return;
421 }
422
423 if ( ! $form_id || ! is_int( $form_id ) ) {
424 _doing_it_wrong( __METHOD__, '$form_id parameter must be a non-zero integer', '6.5' );
425 return;
426 }
427
428 $settings = FrmStrpLiteAppHelper::get_settings();
429 $publishable = $settings->get_active_publishable_key();
430
431 wp_register_script(
432 'stripe',
433 'https://js.stripe.com/v3/',
434 array(),
435 '3.0',
436 false
437 );
438
439 $suffix = FrmAppHelper::js_suffix();
440 $dependencies = array( 'stripe', 'formidable' );
441
442 if ( '.min' === $suffix && is_readable( FrmAppHelper::plugin_path() . '/js/frmstrp.min.js' ) ) {
443 // Use the combined file if it is available.
444 $script_url = FrmAppHelper::plugin_url() . '/js/frmstrp.min.js';
445 } else {
446 if ( ! $suffix && ! is_readable( FrmStrpLiteAppHelper::plugin_path() . 'js/frmstrp.js' ) ) {
447 // The unminified file is not included in releases so force the minified script.
448 $suffix = '.min';
449 }
450
451 $script_url = FrmStrpLiteAppHelper::plugin_url() . 'js/frmstrp' . $suffix . '.js';
452 }
453
454 if ( class_exists( 'FrmProStrpLiteController' ) && ( ! $suffix || ! FrmProAppController::has_combo_js_file() ) ) {
455 $dependencies[] = 'formidablepro';
456 }
457
458 $action_settings = self::prepare_settings_for_js( $form_id );
459 $found_gateway = false;
460
461 foreach ( $action_settings as $action ) {
462 $gateways = $action['gateways'];
463
464 if ( ! $gateways || in_array( 'stripe', (array) $gateways, true ) ) {
465 $found_gateway = true;
466 break;
467 }
468 }
469
470 if ( ! $found_gateway ) {
471 return;
472 }
473
474 wp_enqueue_script(
475 'formidable-stripe',
476 $script_url,
477 $dependencies,
478 FrmAppHelper::plugin_version(),
479 false
480 );
481
482 $style_settings = self::get_style_settings_for_form( $form_id );
483 $stripe_vars = array(
484 'publishable_key' => $publishable,
485 'form_id' => $form_id,
486 'nonce' => wp_create_nonce( 'frm_strp_ajax' ),
487 'ajax' => esc_url_raw( FrmAppHelper::get_ajax_url() ),
488 'settings' => $action_settings,
489 'locale' => self::get_locale(),
490 'baseFontSize' => $style_settings['field_font_size'],
491 'appearanceRules' => self::get_appearance_rules( $style_settings ),
492 'account_id' => FrmStrpLiteConnectHelper::get_account_id(),
493 );
494
495 wp_localize_script( 'formidable-stripe', 'frm_stripe_vars', $stripe_vars );
496 }
497
498 /**
499 * Get and format the style settings for JavaScript to use with the get_appearance_rules function.
500 *
501 * @since 6.5, introduced in v3.0 of the Stripe add on.
502 *
503 * @param mixed $form_id
504 *
505 * @return array
506 */
507 private static function get_style_settings_for_form( $form_id ) {
508 if ( ! $form_id ) {
509 return array();
510 }
511
512 $style = FrmStylesController::get_form_style( $form_id );
513
514 if ( ! $style ) {
515 return array();
516 }
517
518 $settings = FrmStylesHelper::get_settings_for_output( $style );
519 $disallowed = array( ';', ':', '!important' );
520
521 foreach ( $settings as $k => $s ) {
522 if ( is_string( $s ) ) {
523 $settings[ $k ] = str_replace( $disallowed, '', $s );
524 }
525 }
526
527 return $settings;
528 }
529
530 /**
531 * Get the style rules for Stripe link Authentication and Payment elements.
532 * These settings get set to frm_stripe_vars.appearanceRules.
533 * They're in the format that Stripe accepts.
534 * Documentation for Appearance rules can be found at https://stripe.com/docs/elements/appearance-api?platform=web#rules
535 *
536 * @since 6.5, introduced in v3.0 of the Stripe add on.
537 *
538 * @param array $settings
539 *
540 * @return array
541 */
542 private static function get_appearance_rules( $settings ) {
543 $rules = array(
544 '.Input' => array(
545 'fontFamily' => $settings['font'],
546 'color' => $settings['text_color'],
547 'backgroundColor' => $settings['bg_color'],
548 'padding' => $settings['field_pad'],
549 'lineHeight' => 1.3,
550 'borderColor' => $settings['border_color'],
551 'borderWidth' => self::get_border_width( $settings ),
552 'borderStyle' => $settings['field_border_style'],
553 'borderRadius' => self::get_border_radius( $settings ),
554 'fontWeight' => $settings['field_weight'],
555 ),
556 '.Input::placeholder' => array(
557 'color' => $settings['text_color_disabled'],
558 ),
559 '.Input:focus' => array(
560 'backgroundColor' => $settings['bg_color_active'],
561 ),
562 '.Label' => array(
563 'color' => $settings['label_color'],
564 'fontSize' => $settings['font_size'],
565 'fontWeight' => $settings['weight'],
566 'padding' => $settings['label_padding'],
567 'marginBottom' => 0,
568 ),
569 '.Error' => array(
570 'color' => $settings['border_color_error'],
571 ),
572 );
573
574 /*
575 * Filters the appearance rules for Stripe elements.
576 *
577 * @since 6.21
578 *
579 * @param array $rules
580 * @param array $settings
581 */
582 return apply_filters( 'frm_stripe_appearance_rules', $rules, $settings );
583 }
584
585 /**
586 * Get the border width for Stripe elements.
587 *
588 * @since 6.21
589 *
590 * @param array $settings
591 *
592 * @return string
593 */
594 private static function get_border_width( $settings ) {
595 if ( ! empty( $settings['field_shape_type'] ) && 'underline' === $settings['field_shape_type'] ) {
596 return '0 0 ' . $settings['field_border_width'] . ' 0';
597 }
598 return $settings['field_border_width'];
599 }
600
601 /**
602 * Get the border radius for Stripe elements.
603 *
604 * @since 6.21
605 *
606 * @param array $settings
607 *
608 * @return string
609 */
610 private static function get_border_radius( $settings ) {
611 if ( ! empty( $settings['field_shape_type'] ) ) {
612 switch ( $settings['field_shape_type'] ) {
613 case 'underline':
614 case 'regular':
615 return '0px';
616 case 'circle':
617 return '30px';
618 }
619 }
620 return $settings['border_radius'];
621 }
622
623 /**
624 * Get the language to use for Stripe elements.
625 *
626 * @since 6.5, introduced in v2.0 of the Stripe add on.
627 *
628 * @return string
629 */
630 private static function get_locale() {
631 $allowed = array( 'ar', 'da', 'de', 'en', 'es', 'fi', 'fr', 'he', 'it', 'ja', 'nl', 'no', 'pl', 'ru', 'sv', 'zh' );
632 $current = get_locale();
633 $parts = explode( '_', $current );
634 $part = strtolower( $parts[0] );
635 return in_array( $part, $allowed, true ) ? $part : 'auto';
636 }
637
638 /**
639 * If the names are being used on the CC fields,
640 * make sure it doesn't prevent the submission if Stripe has approved.
641 *
642 * @since 6.7.1
643 *
644 * @param array $errors
645 * @param stdClass $field
646 * @param array $values
647 *
648 * @return array
649 */
650 public static function remove_cc_validation( $errors, $field, $values ) {
651 $has_processed = isset( $_POST[ 'frmintent' . $field->form_id ] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
652
653 if ( ! $has_processed ) {
654 return $errors;
655 }
656
657 $field_id = $field->temp_id ?? $field->id;
658
659 if ( isset( $errors[ 'field' . $field_id . '-cc' ] ) ) {
660 unset( $errors[ 'field' . $field_id . '-cc' ] );
661 }
662
663 if ( isset( $errors[ 'field' . $field_id ] ) ) {
664 unset( $errors[ 'field' . $field_id ] );
665 }
666
667 return $errors;
668 }
669 }
670