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

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