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

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