PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / trunk
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More vtrunk
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 / FrmSquareLiteAppController.php

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

344 lines 9.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 FrmSquareLiteAppController {
7
8 /**
9 * Add the gateway for compatibility with the Payments submodule.
10 * This adds the Stripe checkbox option to the list of gateways.
11 *
12 * @param array $gateways
13 *
14 * @return array
15 */
16 public static function add_gateway( $gateways ) {
17 $gateways['square'] = array(
18 'label' => 'Square',
19 'user_label' => __( 'Payment', 'formidable' ),
20 'class' => 'SquareLite',
21 'recurring' => true,
22 'include' => array(
23 'billing_first_name',
24 'billing_last_name',
25 'credit_card',
26 'billing_address',
27 ),
28 );
29 return $gateways;
30 }
31
32 /**
33 * Handle the request to initialize with Square Api
34 *
35 * @return void
36 */
37 public static function handle_oauth() {
38 FrmAppHelper::permission_check( 'frm_change_settings' );
39
40 if ( ! check_admin_referer( 'frm_ajax', 'nonce' ) ) {
41 wp_send_json_error();
42 }
43
44 $redirect_url = FrmSquareLiteConnectHelper::get_oauth_redirect_url();
45
46 if ( false === $redirect_url ) {
47 wp_send_json_error( 'Unable to connect to Square successfully' );
48 }
49
50 $response_data = array(
51 'redirect_url' => $redirect_url,
52 );
53 wp_send_json_success( $response_data );
54 }
55
56 public static function handle_disconnect() {
57 FrmAppHelper::permission_check( 'frm_change_settings' );
58
59 if ( ! check_admin_referer( 'frm_ajax', 'nonce' ) ) {
60 wp_send_json_error();
61 }
62
63 FrmSquareLiteConnectHelper::handle_disconnect();
64 wp_send_json_success();
65 }
66
67 /**
68 * Handle the verify buyer action.
69 *
70 * @return void
71 */
72 public static function verify_buyer() {
73 check_ajax_referer( 'frm_square_ajax', 'nonce' );
74
75 $form_id = FrmAppHelper::get_post_param( 'form_id', 0, 'absint' );
76
77 if ( ! $form_id ) {
78 wp_send_json_error( __( 'Invalid form ID', 'formidable' ) );
79 }
80
81 $actions = FrmSquareLiteActionsController::get_actions_before_submit( $form_id );
82
83 if ( ! $actions ) {
84 wp_send_json_error( __( 'No Square actions found for this form', 'formidable' ) );
85 }
86
87 $action = self::get_action_for_verification( $actions );
88 $verification_details = array(
89 'amount' => self::get_amount_value_for_verification( $action ),
90 'billingContact' => self::get_billing_contact( $action ),
91 'currencyCode' => strtoupper( $action->post_content['currency'] ),
92 'intent' => 'CHARGE',
93 );
94
95 wp_send_json_success(
96 array(
97 'verificationDetails' => $verification_details,
98 'hash' => md5( serialize( $verification_details ) ),
99 )
100 );
101 }
102
103 /**
104 * Get the action that the submission will actually trigger.
105 *
106 * Square verifies a single amount, so when a form has more than one Square action,
107 * the conditional logic of each action is checked against the posted values. Without
108 * this, the first action always wins and the buyer gets verified for an amount that
109 * a different action is going to charge.
110 *
111 * @since 6.35
112 *
113 * @param array $actions Payment actions from FrmSquareLiteActionsController::get_actions_before_submit. Never empty.
114 *
115 * @return WP_Post
116 */
117 private static function get_action_for_verification( $actions ) {
118 if ( count( $actions ) > 1 ) {
119 $entry = self::generate_false_entry();
120
121 foreach ( $actions as $action ) {
122 if ( ! FrmFormAction::action_conditions_met( $action, $entry ) ) {
123 // Conditions were met, so this is the action that will charge the buyer.
124 return $action;
125 }
126 }
127 }
128
129 // Either there is a single action, or no action passed its conditional logic.
130 return reset( $actions );
131 }
132
133 /**
134 * Get the amount value for verification.
135 *
136 * Square's verifyBuyer expects the amount as a decimal string in the currency's
137 * major units ("20.00" for twenty pounds), not the smallest denomination that the
138 * Payments API uses. FrmSquareLiteActionsController::prepare_amount returns the
139 * smallest denomination, so the parent is called here instead.
140 *
141 * @param WP_Post $action
142 *
143 * @return string
144 */
145 private static function get_amount_value_for_verification( $action ) {
146 $amount = $action->post_content['amount'];
147
148 if ( ! str_contains( $amount, '[' ) ) {
149 $currency = $action->post_content['currency'];
150 return FrmTransLiteActionsController::prepare_amount( $amount, compact( 'currency' ) );
151 }
152
153 $form = FrmForm::getOne( $action->menu_order );
154
155 if ( ! $form ) {
156 return $amount;
157 }
158
159 // Update amount based on field shortcodes.
160 $entry = self::generate_false_entry();
161
162 return FrmTransLiteActionsController::prepare_amount( $amount, compact( 'form', 'entry', 'action' ) );
163 }
164
165 /**
166 * Show a warning in the payment action settings when the selected address field
167 * uses an address type without a country, as Square requires a country code.
168 *
169 * @since 6.34
170 *
171 * @param object $action
172 *
173 * @return void
174 */
175 public static function maybe_show_address_type_warning( $action ) {
176 if ( is_callable( 'FrmProSquareLiteController::maybe_show_address_type_warning' ) ) {
177 // Pro renders this warning with the same hook.
178 return;
179 }
180
181 if ( empty( $action->post_content['gateway'] ) ) {
182 return;
183 }
184
185 $gateways = (array) $action->post_content['gateway'];
186
187 if ( ! in_array( 'square', $gateways, true ) ) {
188 return;
189 }
190
191 if ( empty( $action->post_content['billing_address'] ) ) {
192 return;
193 }
194
195 $address_field = FrmField::getOne( $action->post_content['billing_address'] );
196
197 if ( ! $address_field || self::address_field_is_compatible_with_square( $address_field ) ) {
198 return;
199 }
200 ?>
201 <div class="frm_warning_style">
202 <?php
203 esc_html_e( 'The address field selected is not compatible with Square, because it does not include the country code. Select another address type to prevent checkout errors.', 'formidable' ); // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
204 ?>
205 </div>
206 <?php
207 }
208
209 /**
210 * Square requires a country code, which the generic address type does not collect.
211 *
212 * @since 6.34
213 *
214 * @param stdClass $field
215 *
216 * @return bool
217 */
218 private static function address_field_is_compatible_with_square( $field ) {
219 return ! isset( $field->field_options['address_type'] ) || 'generic' !== $field->field_options['address_type'];
220 }
221
222 /**
223 * @param WP_Post $action
224 *
225 * @return array
226 */
227 public static function get_billing_contact( $action ) {
228 $email_setting = $action->post_content['email'];
229 $first_name_setting = $action->post_content['billing_first_name'];
230 $last_name_setting = $action->post_content['billing_last_name'];
231
232 // @phpstan-ignore-next-line
233 $address_setting = $action->post_content['billing_address'] ?? '';
234
235 $entry = self::generate_false_entry();
236 $first_name = $first_name_setting && isset( $entry->metas[ $first_name_setting ] ) ? $entry->metas[ $first_name_setting ] : '';
237 $last_name = $last_name_setting && isset( $entry->metas[ $last_name_setting ] ) ? $entry->metas[ $last_name_setting ] : '';
238 $address = $address_setting && isset( $entry->metas[ $address_setting ] ) ? $entry->metas[ $address_setting ] : '';
239
240 if ( is_array( $first_name ) && isset( $first_name['first'] ) ) {
241 $first_name = $first_name['first'];
242 }
243
244 if ( is_array( $last_name ) && isset( $last_name['last'] ) ) {
245 $last_name = $last_name['last'];
246 }
247
248 $details = array(
249 'givenName' => $first_name,
250 'familyName' => $last_name,
251 );
252
253 if ( $email_setting ) {
254 $shortcode_atts = array(
255 'entry' => $entry,
256 'form' => $action->menu_order,
257 'value' => $email_setting,
258 );
259 $details['email'] = FrmTransLiteAppHelper::process_shortcodes( $shortcode_atts );
260 }
261
262 self::maybe_add_address_data( $details, $address, (int) $address_setting );
263
264 return $details;
265 }
266
267 /**
268 * @since 6.25
269 *
270 * @param array $details
271 * @param array $address
272 * @param int $address_field_id
273 *
274 * @return void
275 */
276 private static function maybe_add_address_data( &$details, $address, $address_field_id ) {
277 if ( ! is_array( $address ) || ! isset( $address['line1'] ) || ! isset( $address['line2'] ) ) {
278 return;
279 }
280
281 $address_field = FrmField::getOne( $address_field_id );
282
283 if ( ! $address_field ) {
284 return;
285 }
286
287 if ( 'us' === $address_field->field_options['address_type'] ) {
288 $country_code = 'US';
289 } else {
290 $country_code = FrmAddressesController::get_country_code( $address['country'] );
291 }
292
293 if ( ! $address['line1'] && ! $address['line2'] && ! $address['city'] && ! $address['state'] && ! $address['zip'] && ! $country_code ) {
294 return;
295 }
296
297 $details['addressLines'] = array( $address['line1'], $address['line2'] );
298 $details['city'] = $address['city'];
299 $details['state'] = $address['state'];
300 $details['postalCode'] = $address['zip'];
301 $details['countryCode'] = $country_code;
302 }
303
304 /**
305 * Create an entry object with posted values.
306 *
307 * @since 6.22
308 *
309 * @return stdClass
310 */
311 private static function generate_false_entry() {
312 $entry = new stdClass();
313 $entry->post_id = 0;
314 $entry->id = 0;
315 $entry->item_key = '';
316 // Shortcode replacement reads ip off of the entry, so it cannot be left unset.
317 $entry->ip = '';
318 $entry->metas = array();
319
320 // phpcs:ignore WordPress.Security.NonceVerification.Missing
321 foreach ( $_POST as $k => $v ) {
322 $k = sanitize_text_field( stripslashes( $k ) );
323 $v = wp_unslash( $v );
324
325 if ( $k !== 'item_meta' ) {
326 FrmAppHelper::sanitize_value( 'wp_kses_post', $v );
327 $entry->{$k} = $v;
328 continue;
329 }
330
331 if ( ! is_array( $v ) ) {
332 continue;
333 }
334
335 foreach ( $v as $f => $value ) {
336 FrmAppHelper::sanitize_value( 'wp_kses_post', $value );
337 $entry->metas[ absint( $f ) ] = $value;
338 }
339 }
340
341 return $entry;
342 }
343 }
344