PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.26
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.26
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.26, at stripe/controllers/FrmStrpLiteActionsController.php

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