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

712 lines 18.9 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 ( ! $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
90 return $payment_actions;
91 }
92
93 /**
94 * Trigger a Square payment after a form is submitted.
95 * This is called for both one time and recurring payments.
96 *
97 * @param WP_Post $action
98 * @param stdClass $entry
99 * @param mixed $form
100 *
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 $amount = self::prepare_amount( $action->post_content['amount'], $atts );
111
112 // phpcs:ignore Universal.Operators.StrictComparisons
113 if ( ! $amount || $amount == 000 ) {
114 $response['error'] = __( 'Please specify an amount for the payment', 'formidable' );
115 return $response;
116 }
117
118 if ( ! self::square_is_configured() ) {
119 $response['error'] = __( 'Square still needs to be configured.', 'formidable' );
120 return $response;
121 }
122
123 $payment_args = compact( 'form', 'entry', 'action', 'amount' );
124
125 // Attempt to charge the customer's card.
126 if ( 'recurring' === $action->post_content['type'] ) {
127 $charge = self::trigger_recurring_payment( $payment_args );
128 } else {
129 $charge = self::trigger_one_time_payment( $payment_args );
130 }
131
132 if ( $charge === true ) {
133 $response['success'] = true;
134 } else {
135 $response['error'] = $charge;
136 }
137
138 return $response;
139 }
140
141 /**
142 * Trigger a one time payment.
143 *
144 * @param array $atts The arguments for the payment.
145 *
146 * @return string|true string on error, true on success.
147 */
148 private static function trigger_one_time_payment( $atts ) {
149 if ( empty( $_POST['square-token'] ) || empty( $_POST['square-verification-token'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
150 return __( 'Please enter a valid credit card', 'formidable' );
151 }
152
153 $currency = strtoupper( $atts['action']->post_content['currency'] );
154 $square_token = sanitize_text_field( $_POST['square-token'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
155 $verification_token = sanitize_text_field( $_POST['square-verification-token'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
156 $description = FrmTransLiteAppHelper::process_shortcodes(
157 array(
158 'entry' => $atts['entry'],
159 'form' => $atts['entry']->form_id,
160 'value' => $atts['action']->post_content['description'],
161 )
162 );
163 $result = FrmSquareLiteConnectHelper::create_payment( $atts['amount'], $currency, $square_token, $verification_token, $description );
164
165 if ( false === $result ) {
166 return FrmSquareLiteConnectHelper::get_latest_error_from_square_api();
167 }
168
169 $atts['status'] = $result->status === 'COMPLETED' ? 'complete' : 'failed';
170 $atts['charge'] = new stdClass();
171 $atts['charge']->id = $result->id;
172 $atts['charge']->amount = $atts['amount'];
173
174 $payment_id = self::create_new_payment( $atts );
175 $frm_payment = new FrmTransLitePayment();
176 $payment = $frm_payment->get_one( $payment_id );
177 $status = $atts['status'];
178
179 FrmTransLiteActionsController::trigger_payment_status_change( compact( 'status', 'payment' ) );
180
181 return true;
182 }
183
184 /**
185 * Add a payment row for the payments table.
186 *
187 * @param array $atts The arguments for the payment.
188 *
189 * @return int
190 */
191 private static function create_new_payment( $atts ) {
192 $atts['charge'] = (object) $atts['charge'];
193
194 $new_values = array(
195 'amount' => FrmTransLiteAppHelper::get_formatted_amount_for_currency( $atts['charge']->amount, $atts['action'] ),
196 'status' => $atts['status'],
197 'paysys' => 'square',
198 'item_id' => $atts['entry']->id,
199 'action_id' => $atts['action']->ID,
200 'receipt_id' => $atts['charge']->id,
201 'sub_id' => $atts['charge']->sub_id ?? '',
202 'test' => 'test' === FrmSquareLiteAppHelper::active_mode() ? 1 : 0,
203 );
204
205 $frm_payment = new FrmTransLitePayment();
206 return $frm_payment->create( $new_values );
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
330 return $frm_payment->create( $new_values );
331 }
332
333 /**
334 * @param string $repeat_cadence
335 *
336 * @return int
337 */
338 private static function get_interval_count_from_repeat_cadence( $repeat_cadence ) {
339 switch ( $repeat_cadence ) {
340 case 'NINETY_DAYS':
341 return 90;
342 case 'SIXTY_DAYS':
343 return 60;
344 case 'THIRTY_DAYS':
345 return 30;
346 case 'EVERY_SIX_MONTHS':
347 return 6;
348 case 'EVERY_FOUR_MONTHS':
349 return 4;
350 case 'QUARTERLY':
351 return 3;
352 case 'EVERY_TWO_WEEKS':
353 case 'EVERY_TWO_MONTHS':
354 case 'EVERY_TWO_YEARS':
355 return 2;
356 case 'DAILY':
357 case 'ANNUAL':
358 case 'MONTHLY':
359 case 'WEEKLY':
360 default:
361 return 1;
362 }//end switch
363 }
364
365 /**
366 * @param string $repeat_cadence
367 *
368 * @return string
369 */
370 private static function get_interval_from_repeat_cadence( $repeat_cadence ) {
371 switch ( $repeat_cadence ) {
372 case 'ANNUAL':
373 case 'EVERY_TWO_YEARS':
374 return 'year';
375
376 case 'MONTHLY':
377 case 'EVERY_TWO_MONTHS':
378 case 'QUARTERLY':
379 case 'EVERY_FOUR_MONTHS':
380 case 'EVERY_SIX_MONTHS':
381 return 'month';
382
383 case 'WEEKLY':
384 case 'EVERY_TWO_WEEKS':
385 return 'week';
386
387 case 'DAILY':
388 case 'THIRTY_DAYS':
389 case 'SIXTY_DAYS':
390 case 'NINETY_DAYS':
391 default:
392 return 'day';
393 }//end switch
394 }
395
396 /**
397 * Check if Square integration is enabled.
398 *
399 * @return bool true if Square is set up.
400 */
401 private static function square_is_configured() {
402 return (bool) FrmSquareLiteConnectHelper::get_merchant_id();
403 }
404
405 /**
406 * Convert the amount from 10.00 to 1000.
407 *
408 * @param mixed $amount
409 * @param array $atts
410 *
411 * @return string
412 */
413 public static function prepare_amount( $amount, $atts = array() ) {
414 $amount = parent::prepare_amount( $amount, $atts );
415 $currency = self::get_currency_for_action( $atts );
416 return number_format( $amount, $currency['decimals'], '', '' );
417 }
418
419 /**
420 * If this form submits with ajax, load the scripts on the first page.
421 *
422 * @param array $params
423 *
424 * @return void
425 */
426 public static function maybe_load_scripts( $params ) {
427 // phpcs:ignore Universal.Operators.StrictComparisons
428 if ( $params['form_id'] == $params['posted_form_id'] ) {
429 // This form has already been posted, so we aren't on the first page.
430 return;
431 }
432
433 $form = FrmForm::getOne( $params['form_id'] );
434
435 if ( ! $form ) {
436 return;
437 }
438
439 $credit_card_field = FrmField::getAll(
440 array(
441 'fi.form_id' => $form->id,
442 'type' => 'credit_card',
443 )
444 );
445
446 if ( ! $credit_card_field ) {
447 return;
448 }
449
450 $payment_actions = self::get_actions_before_submit( $form->id );
451
452 if ( ! $payment_actions ) {
453 return;
454 }
455
456 $found_gateway = false;
457
458 foreach ( $payment_actions as $action ) {
459 $gateways = $action->post_content['gateway'];
460
461 if ( in_array( 'square', (array) $gateways, true ) ) {
462 $found_gateway = true;
463 break;
464 }
465 }
466
467 if ( ! $found_gateway ) {
468 return;
469 }
470
471 self::load_scripts( (int) $form->id );
472 }
473
474 /**
475 * Load front end JavaScript for a Stripe form.
476 *
477 * @param int $form_id
478 *
479 * @return void
480 */
481 public static function load_scripts( $form_id ) {
482 if ( FrmAppHelper::is_admin_page( 'formidable-entries' ) ) {
483 return;
484 }
485
486 if ( wp_script_is( 'formidable-square', 'enqueued' ) ) {
487 return;
488 }
489
490 if ( ! $form_id || ! is_int( $form_id ) ) {
491 _doing_it_wrong( __METHOD__, '$form_id parameter must be a non-zero integer', '6.22' );
492 return;
493 }
494
495 $action_settings = self::prepare_settings_for_js( $form_id );
496 $found_gateway = false;
497
498 foreach ( $action_settings as $action ) {
499 $gateways = $action['gateways'];
500
501 if ( ! $gateways || in_array( 'square', (array) $gateways, true ) ) {
502 $found_gateway = true;
503 break;
504 }
505 }
506
507 if ( ! $found_gateway ) {
508 return;
509 }
510
511 wp_register_script(
512 'square',
513 FrmSquareLiteAppHelper::active_mode() === 'live' ? 'https://web.squarecdn.com/v1/square.js' : 'https://sandbox.web.squarecdn.com/v1/square.js',
514 array(),
515 '1.0',
516 false
517 );
518
519 $dependencies = array( 'square', 'formidable' );
520 $script_url = FrmSquareLiteAppHelper::plugin_url() . 'js/frontend.js';
521
522 wp_enqueue_script(
523 'formidable-square',
524 $script_url,
525 $dependencies,
526 FrmAppHelper::plugin_version(),
527 false
528 );
529
530 $square_vars = array(
531 'formId' => $form_id,
532 'nonce' => wp_create_nonce( 'frm_square_ajax' ),
533 'ajax' => esc_url_raw( FrmAppHelper::get_ajax_url() ),
534 'settings' => $action_settings,
535 'appId' => self::get_app_id(),
536 'locationId' => self::get_location_id(),
537 'style' => self::get_style( $form_id ),
538 );
539
540 wp_localize_script( 'formidable-square', 'frmSquareVars', $square_vars );
541 }
542
543 /**
544 * Get the app ID for the Square app.
545 *
546 * @return string
547 */
548 private static function get_app_id() {
549 $mode = FrmSquareLiteAppHelper::active_mode();
550
551 if ( 'live' === $mode ) {
552 return 'sq0idp-eR4XI1xgNduJAXcBvjemTg';
553 }
554
555 return 'sandbox-sq0idb-MXl8ilzmhAgsHWKV9c6ycQ';
556 }
557
558 /**
559 * Get the location ID for the Square app.
560 *
561 * @return string
562 */
563 private static function get_location_id() {
564 return FrmSquareLiteConnectHelper::get_location_id();
565 }
566
567 /**
568 * @param int $form_id
569 *
570 * @return array
571 */
572 private static function get_style( $form_id ) {
573 $settings = self::get_style_settings_for_form( $form_id );
574 $style = array(
575 'input' => array(
576 'fontSize' => $settings['field_font_size'],
577 'color' => $settings['text_color'],
578 'backgroundColor' => $settings['bg_color'],
579 'fontWeight' => $settings['field_weight'],
580 ),
581 'input::placeholder' => array(
582 'color' => $settings['text_color_disabled'],
583 ),
584 '.input-container' => array(
585 'borderRadius' => self::get_border_radius( $settings ),
586 ),
587 '.input-container.is-focus' => array(
588 'borderColor' => $settings['border_color_active'],
589 ),
590 );
591
592 if ( ! empty( $settings['font'] ) ) {
593 $style['input']['fontFamily'] = self::prepare_font_family_setting( $settings['font'] );
594 }
595
596 /**
597 * @since 6.22
598 *
599 * @param array $style
600 * @param array $settings
601 * @param int $form_id
602 */
603 return apply_filters( 'frm_square_style', $style, $settings, $form_id );
604 }
605
606 /**
607 * Prepare the font family setting for the Stripe element.
608 *
609 * @since 6.26
610 *
611 * @param string $font
612 *
613 * @return string
614 */
615 private static function prepare_font_family_setting( $font ) {
616 if ( ! str_contains( $font, ',' ) ) {
617 return $font;
618 }
619
620 $fonts = explode( ',', $font );
621 return trim( reset( $fonts ) );
622 }
623
624 /**
625 * Get the border radius for Stripe elements.
626 *
627 * @since 6.22
628 *
629 * @param array $settings
630 *
631 * @return string
632 */
633 private static function get_border_radius( $settings ) {
634 if ( ! empty( $settings['field_shape_type'] ) ) {
635 switch ( $settings['field_shape_type'] ) {
636 case 'underline':
637 case 'regular':
638 return '0px';
639 case 'circle':
640 return '30px';
641 }
642 }
643 return $settings['border_radius'];
644 }
645
646 /**
647 * Get and format the style settings for JavaScript to use with the get_style function.
648 *
649 * @since 6.22
650 *
651 * @param int $form_id
652 *
653 * @return array
654 */
655 private static function get_style_settings_for_form( $form_id ) {
656 if ( ! $form_id ) {
657 return array();
658 }
659
660 $style = FrmStylesController::get_form_style( $form_id );
661
662 if ( ! $style ) {
663 return array();
664 }
665
666 $settings = FrmStylesHelper::get_settings_for_output( $style );
667 $disallowed = array( ';', ':', '!important' );
668
669 foreach ( $settings as $k => $s ) {
670 if ( is_string( $s ) ) {
671 $settings[ $k ] = str_replace( $disallowed, '', $s );
672 }
673 }
674
675 return $settings;
676 }
677
678 /**
679 * If the names are being used on the CC fields,
680 * make sure it doesn't prevent the submission if Stripe has approved.
681 *
682 * @since 6.22
683 *
684 * @param array $errors
685 * @param stdClass $field
686 * @param array $values
687 *
688 * @return array
689 */
690 public static function remove_cc_validation( $errors, $field, $values ) {
691 $has_processed = ! empty( $_POST['square-token'] ) && ! empty( $_POST['square-verification-token'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
692
693 if ( ! $has_processed ) {
694 return $errors;
695 }
696
697 return FrmTransLiteActionsController::remove_cc_errors( $errors, $field );
698 }
699
700 /**
701 * @return void
702 */
703 public static function actions_js() {
704 wp_enqueue_script(
705 'frm_square_admin',
706 FrmSquareLiteAppHelper::plugin_url() . 'js/action.js',
707 array( 'wp-hooks', 'wp-i18n' ),
708 FrmAppHelper::plugin_version()
709 );
710 }
711 }
712