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

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