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

FrmSquareLiteActionsController.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.26.1, at square/controllers/FrmSquareLiteActionsController.php

741 lines 19.5 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 FrmSquareLiteActionsController extends FrmTransLiteActionsController {
7
8 /**
9 * @since 6.22
10 *
11 * @param string $callback
12 * @param array|false|object $field
13 *
14 * @return string
15 */
16 public static function maybe_show_card( $callback, $field = false ) {
17 if ( false === $field ) {
18 // Pro isn't up to date.
19 return $callback;
20 }
21
22 $form_id = is_object( $field ) ? $field->form_id : $field['form_id'];
23 $actions = self::get_actions_before_submit( $form_id );
24
25 if ( empty( $actions ) ) {
26 return $callback;
27 }
28
29 $field_id = is_object( $field ) ? $field->id : $field['id'];
30
31 foreach ( $actions as $action ) {
32 if ( (int) $action->post_content['credit_card'] === (int) $field_id ) {
33 return self::class . '::show_card';
34 }
35 }
36
37 return $callback;
38 }
39
40 /**
41 * Override the credit card field HTML if there is a Square action.
42 *
43 * @since 6.22
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 if ( $actions ) {
55 self::load_scripts( (int) $field['form_id'] );
56
57 $html_id = $atts['html_id'];
58 include FrmStrpLiteAppHelper::plugin_path() . '/views/payments/card-field.php';
59 return;
60 }
61
62 // Use the Pro function when there are no Stripe actions.
63 // This is required for other gateways like Authorize.Net.
64 if ( is_callable( 'FrmProCreditCardsController::show_in_form' ) ) {
65 FrmProCreditCardsController::show_in_form( $field, $field_name, $atts );
66 }
67 }
68
69 /**
70 * Get all published payment actions with the Square gateway that have an amount set.
71 *
72 * @since 6.22
73 *
74 * @param int|string $form_id
75 *
76 * @return array
77 */
78 public static function get_actions_before_submit( $form_id ) {
79 $payment_actions = self::get_actions_for_form( $form_id );
80
81 foreach ( $payment_actions as $k => $payment_action ) {
82 $gateway = $payment_action->post_content['gateway'];
83 $is_square = $gateway === 'square' || ( is_array( $gateway ) && in_array( 'square', $gateway, true ) );
84
85 if ( ! $is_square || empty( $payment_action->post_content['amount'] ) ) {
86 unset( $payment_actions[ $k ] );
87 }
88 }
89 return $payment_actions;
90 }
91
92 /**
93 * Trigger a Square payment after a form is submitted.
94 * This is called for both one time and recurring payments.
95 *
96 * @param WP_Post $action
97 * @param stdClass $entry
98 * @param mixed $form
99 *
100 * @return array
101 */
102 public static function trigger_gateway( $action, $entry, $form ) {
103 $response = array(
104 'success' => false,
105 'run_triggers' => false,
106 'show_errors' => true,
107 );
108 $atts = compact( 'action', 'entry', 'form' );
109
110 $amount = self::prepare_amount( $action->post_content['amount'], $atts );
111
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::square_is_configured() ) {
118 $response['error'] = __( 'There was a problem communicating with Square. Please try again.', 'formidable' );
119 return $response;
120 }
121
122 $payment_args = compact( 'form', 'entry', 'action', 'amount' );
123
124 // Attempt to charge the customer's card.
125 if ( 'recurring' === $action->post_content['type'] ) {
126 $charge = self::trigger_recurring_payment( $payment_args );
127 } else {
128 $charge = self::trigger_one_time_payment( $payment_args );
129 }
130
131 if ( $charge === true ) {
132 $response['success'] = true;
133 } else {
134 $response['error'] = $charge;
135 }
136
137 return $response;
138 }
139
140 /**
141 * Trigger a one time payment.
142 *
143 * @param array $atts The arguments for the payment.
144 *
145 * @return string|true string on error, true on success.
146 */
147 private static function trigger_one_time_payment( $atts ) {
148 if ( empty( $_POST['square-token'] ) || empty( $_POST['square-verification-token'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
149 return __( 'Please enter a valid credit card', 'formidable' );
150 }
151
152 $currency = strtoupper( $atts['action']->post_content['currency'] );
153 $square_token = sanitize_text_field( $_POST['square-token'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
154 $verification_token = sanitize_text_field( $_POST['square-verification-token'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
155 $description = FrmTransLiteAppHelper::process_shortcodes(
156 array(
157 'entry' => $atts['entry'],
158 'form' => $atts['entry']->form_id,
159 'value' => $atts['action']->post_content['description'],
160 )
161 );
162 $result = FrmSquareLiteConnectHelper::create_payment( $atts['amount'], $currency, $square_token, $verification_token, $description );
163
164 if ( false === $result ) {
165 return FrmSquareLiteConnectHelper::get_latest_error_from_square_api();
166 }
167
168 $atts['status'] = $result->status === 'COMPLETED' ? 'complete' : 'failed';
169 $atts['charge'] = new stdClass();
170 $atts['charge']->id = $result->id;
171 $atts['charge']->amount = $atts['amount'];
172
173 $payment_id = self::create_new_payment( $atts );
174 $frm_payment = new FrmTransLitePayment();
175 $payment = $frm_payment->get_one( $payment_id );
176 $status = $atts['status'];
177
178 FrmTransLiteActionsController::trigger_payment_status_change( compact( 'status', 'payment' ) );
179
180 return true;
181 }
182
183 /**
184 * Add a payment row for the payments table.
185 *
186 * @param array $atts The arguments for the payment.
187 *
188 * @return int
189 */
190 private static function create_new_payment( $atts ) {
191 $atts['charge'] = (object) $atts['charge'];
192
193 $new_values = array(
194 'amount' => FrmTransLiteAppHelper::get_formatted_amount_for_currency( $atts['charge']->amount, $atts['action'] ),
195 'status' => $atts['status'],
196 'paysys' => 'square',
197 'item_id' => $atts['entry']->id,
198 'action_id' => $atts['action']->ID,
199 'receipt_id' => $atts['charge']->id,
200 'sub_id' => $atts['charge']->sub_id ?? '',
201 'test' => 'test' === FrmSquareLiteAppHelper::active_mode() ? 1 : 0,
202 );
203
204 $frm_payment = new FrmTransLitePayment();
205 $payment_id = $frm_payment->create( $new_values );
206 return $payment_id;
207 }
208
209 /**
210 * Create a new Square subscription and a subscription and payment for the payments tables.
211 *
212 * @param array $atts Includes 'customer', 'entry', 'action', 'amount'.
213 *
214 * @return bool|string True on success, error message on failure
215 */
216 private static function trigger_recurring_payment( $atts ) {
217 if ( empty( $_POST['square-token'] ) || empty( $_POST['square-verification-token'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
218 return __( 'Please enter a valid credit card', 'formidable' );
219 }
220
221 $currency = strtoupper( $atts['action']->post_content['currency'] );
222 $square_token = sanitize_text_field( $_POST['square-token'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
223 $verification_token = sanitize_text_field( $_POST['square-verification-token'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
224
225 // We can put this all behind our API.
226 // It will require that we pass the customer info and the catalog info.
227 // 1. Call the API with the customer and catalog info.
228 // 2. Add the database rows.
229
230 $action = $atts['action'];
231 $billing_contact = FrmSquareLiteAppController::get_billing_contact( $action );
232
233 $info = array(
234 'payment' => array(
235 'token' => $square_token,
236 'verificationToken' => $verification_token,
237 ),
238 'customer' => array(
239 'givenName' => $billing_contact['givenName'],
240 'familyName' => $billing_contact['familyName'],
241 'emailAddress' => $billing_contact['email'],
242 ),
243 'catalog' => array(
244 'name' => self::prepare_subscription_description( $action->post_content['description'], $atts ),
245 'trialDays' => $action->post_content['trial_interval_count'],
246 'limit' => $action->post_content['payment_limit'],
247 'amount' => $atts['amount'],
248 'currency' => $currency,
249 'cadence' => $action->post_content['repeat_cadence'] ?? 'DAILY',
250 ),
251 );
252
253 if ( isset( $billing_contact['addressLines'] ) ) {
254 $info['customer']['address'] = array(
255 'addressLine1' => $billing_contact['addressLines'][0],
256 'addressLine2' => $billing_contact['addressLines'][1],
257 'locality' => $billing_contact['city'],
258 'administrativeDistrictLevel1' => $billing_contact['state'],
259 'postalCode' => $billing_contact['postalCode'],
260 'country' => $billing_contact['countryCode'],
261 );
262 }
263
264 $response = FrmSquareLiteConnectHelper::create_subscription( $info );
265
266 if ( false === $response ) {
267 return FrmSquareLiteConnectHelper::get_latest_error_from_square_api();
268 }
269
270 if ( is_string( $response ) ) {
271 return $response;
272 }
273
274 if ( ! is_object( $response ) || ! isset( $response->id ) ) {
275 return __( 'There was a problem creating the subscription', 'formidable' );
276 }
277
278 // Add subscription database row.
279 // We do not add a payment row at this time. This is handled with our webhook handling.
280 self::create_new_subscription( $response->id, $atts );
281
282 return true;
283 }
284
285 /**
286 * Prepare the description for a subscription.
287 *
288 * @param string $description
289 * @param array $atts
290 *
291 * @return string
292 */
293 private static function prepare_subscription_description( $description, $atts ) {
294 $shortcode_atts = array(
295 'entry' => $atts['entry'],
296 'form' => $atts['entry']->form_id,
297 'value' => $description,
298 );
299 return FrmTransLiteAppHelper::process_shortcodes( $shortcode_atts );
300 }
301
302 /**
303 * Create a new subscription and payment for the payments tables.
304 *
305 * @param string $subscription_id
306 * @param array $atts
307 *
308 * @return int
309 */
310 private static function create_new_subscription( $subscription_id, $atts ) {
311 $repeat_cadence = $atts['action']->post_content['repeat_cadence'] ?? 'DAILY';
312 $interval_count = self::get_interval_count_from_repeat_cadence( $repeat_cadence );
313 $interval = self::get_interval_from_repeat_cadence( $repeat_cadence );
314
315 $new_values = array(
316 'amount' => FrmTransLiteAppHelper::get_formatted_amount_for_currency( $atts['amount'], $atts['action'] ),
317 'paysys' => 'square',
318 'item_id' => $atts['entry']->id,
319 'action_id' => $atts['action']->ID,
320 'sub_id' => $subscription_id,
321 'interval_count' => $interval_count,
322 'time_interval' => $interval,
323 'status' => 'active',
324 'next_bill_date' => gmdate( 'Y-m-d' ),
325 'test' => 'test' === FrmSquareLiteAppHelper::active_mode() ? 1 : 0,
326 );
327
328 $frm_payment = new FrmTransLiteSubscription();
329 $payment_id = $frm_payment->create( $new_values );
330
331 return $payment_id;
332 }
333
334 /**
335 * @param string $repeat_cadence
336 *
337 * @return int
338 */
339 private static function get_interval_count_from_repeat_cadence( $repeat_cadence ) {
340 switch ( $repeat_cadence ) {
341 case 'NINETY_DAYS':
342 return 90;
343 case 'SIXTY_DAYS':
344 return 60;
345 case 'THIRTY_DAYS':
346 return 30;
347 case 'EVERY_SIX_MONTHS':
348 return 6;
349 case 'EVERY_FOUR_MONTHS':
350 return 4;
351 case 'QUARTERLY':
352 return 3;
353 case 'EVERY_TWO_WEEKS':
354 case 'EVERY_TWO_MONTHS':
355 case 'EVERY_TWO_YEARS':
356 return 2;
357 case 'DAILY':
358 case 'ANNUAL':
359 case 'MONTHLY':
360 case 'WEEKLY':
361 default:
362 return 1;
363 }//end switch
364 }
365
366 /**
367 * @param string $repeat_cadence
368 *
369 * @return string
370 */
371 private static function get_interval_from_repeat_cadence( $repeat_cadence ) {
372 switch ( $repeat_cadence ) {
373 case 'ANNUAL':
374 case 'EVERY_TWO_YEARS':
375 return 'year';
376
377 case 'MONTHLY':
378 case 'EVERY_TWO_MONTHS':
379 case 'QUARTERLY':
380 case 'EVERY_FOUR_MONTHS':
381 case 'EVERY_SIX_MONTHS':
382 return 'month';
383
384 case 'WEEKLY':
385 case 'EVERY_TWO_WEEKS':
386 return 'week';
387
388 case 'DAILY':
389 case 'THIRTY_DAYS':
390 case 'SIXTY_DAYS':
391 case 'NINETY_DAYS':
392 default:
393 return 'day';
394 }//end switch
395 }
396
397 /**
398 * Check if Square integration is enabled.
399 *
400 * @return bool true if Square is set up.
401 */
402 private static function square_is_configured() {
403 return (bool) FrmSquareLiteConnectHelper::get_merchant_id();
404 }
405
406 /**
407 * Replace an [email] shortcode with the current user email.
408 *
409 * @param string $email
410 *
411 * @return string
412 */
413 private static function replace_email_shortcode( $email ) {
414 if ( false === strpos( $email, '[email]' ) ) {
415 return $email;
416 }
417
418 global $current_user;
419 return str_replace(
420 '[email]',
421 ! empty( $current_user->user_email ) ? $current_user->user_email : '',
422 $email
423 );
424 }
425
426 /**
427 * Convert the amount from 10.00 to 1000.
428 *
429 * @param mixed $amount
430 * @param array $atts
431 *
432 * @return string
433 */
434 public static function prepare_amount( $amount, $atts = array() ) {
435 $amount = parent::prepare_amount( $amount, $atts );
436 $currency = self::get_currency_for_action( $atts );
437 return number_format( $amount, $currency['decimals'], '', '' );
438 }
439
440 /**
441 * If this form submits with ajax, load the scripts on the first page.
442 *
443 * @param array $params
444 *
445 * @return void
446 */
447 public static function maybe_load_scripts( $params ) {
448 if ( $params['form_id'] == $params['posted_form_id'] ) {
449 // This form has already been posted, so we aren't on the first page.
450 return;
451 }
452
453 $form = FrmForm::getOne( $params['form_id'] );
454
455 if ( ! $form ) {
456 return;
457 }
458
459 $credit_card_field = FrmField::getAll(
460 array(
461 'fi.form_id' => $form->id,
462 'type' => 'credit_card',
463 )
464 );
465
466 if ( ! $credit_card_field ) {
467 return;
468 }
469
470 $payment_actions = self::get_actions_before_submit( $form->id );
471
472 if ( ! $payment_actions ) {
473 return;
474 }
475
476 $found_gateway = false;
477
478 foreach ( $payment_actions as $action ) {
479 $gateways = $action->post_content['gateway'];
480
481 if ( in_array( 'square', (array) $gateways, true ) ) {
482 $found_gateway = true;
483 break;
484 }
485 }
486
487 if ( ! $found_gateway ) {
488 return;
489 }
490
491 self::load_scripts( (int) $form->id );
492 }
493
494 /**
495 * Load front end JavaScript for a Stripe form.
496 *
497 * @param int $form_id
498 *
499 * @return void
500 */
501 public static function load_scripts( $form_id ) {
502 if ( FrmAppHelper::is_admin_page( 'formidable-entries' ) ) {
503 return;
504 }
505
506 if ( wp_script_is( 'formidable-square', 'enqueued' ) ) {
507 return;
508 }
509
510 if ( ! $form_id || ! is_int( $form_id ) ) {
511 _doing_it_wrong( __METHOD__, '$form_id parameter must be a non-zero integer', '6.22' );
512 return;
513 }
514
515 $action_settings = self::prepare_settings_for_js( $form_id );
516 $found_gateway = false;
517
518 foreach ( $action_settings as $action ) {
519 $gateways = $action['gateways'];
520
521 if ( ! $gateways || in_array( 'square', (array) $gateways, true ) ) {
522 $found_gateway = true;
523 break;
524 }
525 }
526
527 if ( ! $found_gateway ) {
528 return;
529 }
530
531 wp_register_script(
532 'square',
533 FrmSquareLiteAppHelper::active_mode() === 'live' ? 'https://web.squarecdn.com/v1/square.js' : 'https://sandbox.web.squarecdn.com/v1/square.js',
534 array(),
535 '1.0',
536 false
537 );
538
539 $dependencies = array( 'square', 'formidable' );
540 $script_url = FrmSquareLiteAppHelper::plugin_url() . 'js/frontend.js';
541
542 wp_enqueue_script(
543 'formidable-square',
544 $script_url,
545 $dependencies,
546 FrmAppHelper::plugin_version(),
547 false
548 );
549
550 $square_vars = array(
551 'formId' => $form_id,
552 'nonce' => wp_create_nonce( 'frm_square_ajax' ),
553 'ajax' => esc_url_raw( FrmAppHelper::get_ajax_url() ),
554 'settings' => $action_settings,
555 'appId' => self::get_app_id(),
556 'locationId' => self::get_location_id(),
557 'style' => self::get_style( $form_id ),
558 );
559
560 wp_localize_script( 'formidable-square', 'frmSquareVars', $square_vars );
561 }
562
563 /**
564 * Get the app ID for the Square app.
565 *
566 * @return string
567 */
568 private static function get_app_id() {
569 $mode = FrmSquareLiteAppHelper::active_mode();
570
571 if ( 'live' === $mode ) {
572 return 'sq0idp-eR4XI1xgNduJAXcBvjemTg';
573 }
574 return 'sandbox-sq0idb-MXl8ilzmhAgsHWKV9c6ycQ';
575 }
576
577 /**
578 * Get the location ID for the Square app.
579 *
580 * @return string
581 */
582 private static function get_location_id() {
583 return FrmSquareLiteConnectHelper::get_location_id();
584 }
585
586 /**
587 * @param int $form_id
588 *
589 * @return array
590 */
591 private static function get_style( $form_id ) {
592 $settings = self::get_style_settings_for_form( $form_id );
593 $style = array(
594 'input' => array(
595 'fontSize' => $settings['field_font_size'],
596 'color' => $settings['text_color'],
597 'backgroundColor' => $settings['bg_color'],
598 'fontWeight' => $settings['field_weight'],
599 ),
600 'input::placeholder' => array(
601 'color' => $settings['text_color_disabled'],
602 ),
603 '.input-container' => array(
604 'borderRadius' => self::get_border_radius( $settings ),
605 ),
606 '.input-container.is-focus' => array(
607 'borderColor' => $settings['border_color_active'],
608 ),
609 );
610
611 if ( ! empty( $settings['font'] ) ) {
612 $style['input']['fontFamily'] = self::prepare_font_family_setting( $settings['font'] );
613 }
614
615 /**
616 * @since 6.22
617 *
618 * @param array $style
619 * @param array $settings
620 * @param int $form_id
621 */
622 return apply_filters( 'frm_square_style', $style, $settings, $form_id );
623 }
624
625 /**
626 * Prepare the font family setting for the Stripe element.
627 *
628 * @since 6.26
629 *
630 * @param string $font
631 *
632 * @return string
633 */
634 private static function prepare_font_family_setting( $font ) {
635 if ( false === strpos( $font, ',' ) ) {
636 return $font;
637 }
638
639 $fonts = explode( ',', $font );
640 return trim( reset( $fonts ) );
641 }
642
643 /**
644 * Get the border radius for Stripe elements.
645 *
646 * @since 6.22
647 *
648 * @param array $settings
649 *
650 * @return string
651 */
652 private static function get_border_radius( $settings ) {
653 if ( ! empty( $settings['field_shape_type'] ) ) {
654 switch ( $settings['field_shape_type'] ) {
655 case 'underline':
656 case 'regular':
657 return '0px';
658 case 'circle':
659 return '30px';
660 }
661 }
662 return $settings['border_radius'];
663 }
664
665 /**
666 * Get and format the style settings for JavaScript to use with the get_style function.
667 *
668 * @since 6.22
669 *
670 * @param int $form_id
671 *
672 * @return array
673 */
674 private static function get_style_settings_for_form( $form_id ) {
675 if ( ! $form_id ) {
676 return array();
677 }
678
679 $style = FrmStylesController::get_form_style( $form_id );
680
681 if ( ! $style ) {
682 return array();
683 }
684
685 $settings = FrmStylesHelper::get_settings_for_output( $style );
686 $disallowed = array( ';', ':', '!important' );
687
688 foreach ( $settings as $k => $s ) {
689 if ( is_string( $s ) ) {
690 $settings[ $k ] = str_replace( $disallowed, '', $s );
691 }
692 }
693
694 return $settings;
695 }
696
697 /**
698 * If the names are being used on the CC fields,
699 * make sure it doesn't prevent the submission if Stripe has approved.
700 *
701 * @since 6.22
702 *
703 * @param array $errors
704 * @param stdClass $field
705 * @param array $values
706 *
707 * @return array
708 */
709 public static function remove_cc_validation( $errors, $field, $values ) {
710 $has_processed = ! empty( $_POST['square-token'] ) && ! empty( $_POST['square-verification-token'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
711
712 if ( ! $has_processed ) {
713 return $errors;
714 }
715
716 $field_id = $field->temp_id ?? $field->id;
717
718 if ( isset( $errors[ 'field' . $field_id . '-cc' ] ) ) {
719 unset( $errors[ 'field' . $field_id . '-cc' ] );
720 }
721
722 if ( isset( $errors[ 'field' . $field_id ] ) ) {
723 unset( $errors[ 'field' . $field_id ] );
724 }
725
726 return $errors;
727 }
728
729 /**
730 * @return void
731 */
732 public static function actions_js() {
733 wp_enqueue_script(
734 'frm_square_admin',
735 FrmSquareLiteAppHelper::plugin_url() . 'js/action.js',
736 array( 'wp-hooks', 'wp-i18n' ),
737 FrmAppHelper::plugin_version()
738 );
739 }
740 }
741