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

FrmTransLiteActionsController.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.7, at stripe/controllers/FrmTransLiteActionsController.php

441 lines 12.6 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 FrmTransLiteActionsController {
7
8 /**
9 * Track the entry IDs we're destroying so we don't attempt to delete an entry more than once.
10 * Set in self::destroy_entry_later.
11 *
12 * @var array
13 */
14 private static $entry_ids_to_destroy_later = array();
15
16 /**
17 * Register payment action type.
18 *
19 * @param array $actions
20 * @return array
21 */
22 public static function register_actions( $actions ) {
23 $actions['payment'] = 'FrmTransLiteAction';
24 return $actions;
25 }
26
27 /**
28 * Include scripts for handling payments at an administrative level.
29 * This includes handling the after payment settings for Stripe actions.
30 * It also handles refunds and canceling subscriptions.
31 *
32 * @return void
33 */
34 public static function actions_js() {
35 wp_enqueue_script(
36 'frmtrans_admin',
37 FrmTransLiteAppHelper::plugin_url() . '/js/frmtrans_admin.js',
38 array( 'jquery' ),
39 FrmAppHelper::plugin_version()
40 );
41 wp_localize_script(
42 'frmtrans_admin',
43 'frm_trans_vars',
44 array(
45 'nonce' => wp_create_nonce( 'frm_trans_ajax' ),
46 )
47 );
48 }
49
50 /**
51 * Add event types for actions so an email can trigger on a successful payment.
52 *
53 * @param array $triggers
54 * @return array
55 */
56 public static function add_payment_trigger( $triggers ) {
57 $triggers['payment-success'] = __( 'Successful Payment', 'formidable' );
58 $triggers['payment-failed'] = __( 'Failed Payment', 'formidable' );
59 $triggers['payment-refunded'] = __( 'Refunded Payment', 'formidable' );
60 $triggers['payment-processing'] = __( 'Processing Payment', 'formidable' );
61 $triggers['payment-future-cancel'] = __( 'Canceled Subscription', 'formidable' );
62 $triggers['payment-canceled'] = __( 'Subscription Canceled and Expired', 'formidable' );
63 return $triggers;
64 }
65
66 /**
67 * @param array $options
68 * @return array
69 */
70 public static function add_trigger_to_action( $options ) {
71 $options['event'][] = 'payment-success';
72 $options['event'][] = 'payment-failed';
73 $options['event'][] = 'payment-processing';
74 $options['event'][] = 'payment-future-cancel';
75 $options['event'][] = 'payment-canceled';
76 $options['event'][] = 'payment-refunded';
77 return $options;
78 }
79
80 /**
81 * @param WP_Post $action
82 * @param stdClass $entry
83 * @param mixed $form
84 * @return void
85 */
86 public static function trigger_action( $action, $entry, $form ) {
87 self::prepare_description( $action, compact( 'entry', 'form' ) );
88 FrmStrpLiteActionsController::trigger_gateway( $action, $entry, $form );
89 }
90
91 /**
92 * @param WP_Post $action
93 * @param stdClass $entry
94 * @param mixed $form
95 * @return array
96 */
97 public static function trigger_gateway( $action, $entry, $form ) {
98 // This function must be overridden in a subclass.
99 return array(
100 'success' => false,
101 'run_triggers' => false,
102 'show_errors' => true,
103 );
104 }
105
106 /**
107 * @return string
108 */
109 public static function force_message_after_create() {
110 return 'message';
111 }
112
113 /**
114 * @since 6.5, introduced in v1.12 of the Payments submodule.
115 *
116 * @param object $sub
117 * @return void
118 */
119 public static function trigger_subscription_status_change( $sub ) {
120 $frm_payment = new FrmTransLitePayment();
121 $payment = $frm_payment->get_one_by( $sub->id, 'sub_id' );
122
123 if ( $payment && $payment->action_id ) {
124 self::trigger_payment_status_change(
125 array(
126 'status' => $sub->status,
127 'payment' => $payment,
128 )
129 );
130 }
131 }
132
133 /**
134 * @param array $atts
135 * @return void
136 */
137 public static function trigger_payment_status_change( $atts ) {
138 $action = isset( $atts['action'] ) ? $atts['action'] : $atts['payment']->action_id;
139 $entry_id = isset( $atts['entry'] ) ? $atts['entry']->id : $atts['payment']->item_id;
140 $atts = array(
141 'trigger' => $atts['status'],
142 'entry_id' => $entry_id,
143 );
144
145 if ( ! isset( $atts['payment'] ) ) {
146 $frm_payment = new FrmTransLitePayment();
147 $atts['payment'] = $frm_payment->get_one_by( $entry_id, 'item_id' );
148 }
149
150 if ( ! isset( $atts['trigger'] ) ) {
151 $atts['trigger'] = $atts['status'];
152 }
153
154 // Set future-cancel as trigger when applicable.
155 $atts['trigger'] = str_replace( '_', '-', $atts['trigger'] );
156
157 if ( $atts['payment'] ) {
158 self::trigger_actions_after_payment( $atts['payment'], $atts );
159 }
160 }
161
162 /**
163 * Maybe trigger payment-success or payment-failed event after payment so actions (like emails) can run.
164 *
165 * @param object $payment
166 * @param array $atts
167 * @return void
168 */
169 public static function trigger_actions_after_payment( $payment, $atts = array() ) {
170 if ( 'pending' === $payment->status ) {
171 // 3D Secure has a delayed payment status, so avoid sending a payment failed email for a pending payment.
172 return;
173 }
174
175 $entry = FrmEntry::getOne( $payment->item_id );
176
177 if ( isset( $atts['trigger'] ) ) {
178 $trigger_event = 'payment-' . $atts['trigger'];
179 } else {
180 $trigger_event = 'payment-' . $payment->status;
181 }
182
183 $allowed_triggers = array_keys( self::add_payment_trigger( array() ) );
184 if ( ! in_array( $trigger_event, $allowed_triggers, true ) ) {
185 $trigger_event = ( $payment->status === 'complete' ) ? 'payment-success' : 'payment-failed';
186 }
187 FrmFormActionsController::trigger_actions( $trigger_event, $entry->form_id, $entry->id );
188 }
189
190 /**
191 * Filter fields in description.
192 *
193 * @param WP_Post $action
194 * @param array $atts
195 * @return void
196 */
197 public static function prepare_description( &$action, $atts ) {
198 $description = $action->post_content['description'];
199 if ( ! empty( $description ) ) {
200 $atts['value'] = $description;
201 $description = FrmTransLiteAppHelper::process_shortcodes( $atts );
202 $action->post_content['description'] = $description;
203 }
204 }
205
206 /**
207 * Convert the amount into 10.00.
208 *
209 * @param mixed $amount
210 * @param array $atts
211 * @return string
212 */
213 public static function prepare_amount( $amount, $atts = array() ) {
214 if ( isset( $atts['form'] ) ) {
215 $atts['value'] = $amount;
216 $amount = FrmTransLiteAppHelper::process_shortcodes( $atts );
217 }
218
219 if ( is_string( $amount ) && strlen( $amount ) >= 2 && $amount[0] == '[' && substr( $amount, -1 ) == ']' ) {
220 // make sure we don't use a field id as the amount
221 $amount = 0;
222 }
223
224 $currency = self::get_currency_for_action( $atts );
225
226 $total = 0;
227 foreach ( (array) $amount as $a ) {
228 $this_amount = self::get_amount_from_string( $a );
229 self::maybe_use_decimal( $this_amount, $currency );
230 self::normalize_number( $this_amount, $currency );
231
232 $total += $this_amount;
233 unset( $a, $this_amount );
234 }
235
236 return number_format( $total, $currency['decimals'], '.', '' );
237 }
238
239 /**
240 * Get currency to use when preparing amount.
241 *
242 * @param array $atts
243 * @return array
244 */
245 public static function get_currency_for_action( $atts ) {
246 $currency = 'usd';
247 if ( isset( $atts['form'] ) ) {
248 $currency = $atts['action']->post_content['currency'];
249 } elseif ( isset( $atts['currency'] ) ) {
250 $currency = $atts['currency'];
251 }
252
253 return FrmCurrencyHelper::get_currency( $currency );
254 }
255
256 /**
257 * @param string $amount
258 *
259 * @return string
260 */
261 private static function get_amount_from_string( $amount ) {
262 $amount = html_entity_decode( $amount );
263 $amount = trim( $amount );
264 preg_match_all( '/[0-9,.]*\.?\,?[0-9]+/', $amount, $matches );
265 $amount = $matches ? end( $matches[0] ) : 0;
266 return $amount;
267 }
268
269 /**
270 * @param string $amount
271 * @param array $currency
272 * @return void
273 */
274 private static function maybe_use_decimal( &$amount, $currency ) {
275 if ( $currency['thousand_separator'] !== '.' ) {
276 return;
277 }
278
279 $amount_parts = explode( '.', $amount );
280 $used_for_decimal = count( $amount_parts ) === 2 && strlen( $amount_parts[1] ) === 2;
281 if ( $used_for_decimal ) {
282 $amount = str_replace( '.', $currency['decimal_separator'], $amount );
283 }
284 }
285
286 /**
287 * @param string $amount
288 * @param array $currency
289 * @return void
290 */
291 private static function normalize_number( &$amount, $currency ) {
292 $amount = str_replace( $currency['thousand_separator'], '', $amount );
293 $amount = str_replace( $currency['decimal_separator'], '.', $amount );
294 $amount = number_format( (float) $amount, $currency['decimals'], '.', '' );
295 }
296
297 /**
298 * These settings are included in frm_stripe_vars.settings global JavaScript object on Stripe forms.
299 *
300 * @param int $form_id
301 * @return array
302 */
303 public static function prepare_settings_for_js( $form_id ) {
304 $payment_actions = self::get_actions_for_form( $form_id );
305 $action_settings = array();
306 foreach ( $payment_actions as $payment_action ) {
307 $settings_for_action = array(
308 'id' => $payment_action->ID,
309 'first_name' => $payment_action->post_content['billing_first_name'],
310 'last_name' => $payment_action->post_content['billing_last_name'],
311 'gateways' => $payment_action->post_content['gateway'],
312 'fields' => self::get_fields_for_price( $payment_action ),
313 'one' => $payment_action->post_content['type'],
314 'email' => $payment_action->post_content['email'],
315 );
316
317 /**
318 * @param array $settings_for_action
319 * @param WP_Post $payment_action
320 */
321 $settings_for_action = apply_filters( 'frm_trans_settings_for_js', $settings_for_action, $payment_action );
322 $action_settings[] = $settings_for_action;
323 }
324
325 return $action_settings;
326 }
327
328 /**
329 * Include the price field ids to pass to the javascript.
330 *
331 * @since 6.5, introduced in v2.0 of the Payments submodule.
332 *
333 * @param WP_Post $action
334 * @return array|int
335 */
336 private static function get_fields_for_price( $action ) {
337 $amount = $action->post_content['amount'];
338 $shortcodes = FrmFieldsHelper::get_shortcodes( $amount, $action->menu_order );
339 return isset( $shortcodes[2] ) ? $shortcodes[2] : -1;
340 }
341
342 /**
343 * Get all published payment actions.
344 *
345 * @param int|string $form_id
346 * @return array
347 */
348 public static function get_actions_for_form( $form_id ) {
349 $action_status = array(
350 'post_status' => 'publish',
351 );
352 $payment_actions = FrmFormAction::get_action_for_form( $form_id, 'payment', $action_status );
353 if ( ! $payment_actions ) {
354 $payment_actions = array();
355 }
356 return $payment_actions;
357 }
358
359 /**
360 * Make sure a gateway field is hidden on the front end.
361 *
362 * @param array $values
363 * @param stdClass $field
364 * @return array
365 */
366 public static function hide_gateway_field_on_front_end( $values, $field ) {
367 if ( $field->type !== 'gateway' ) {
368 return $values;
369 }
370
371 // This is also called from the frm_enqueue_form_scripts hook.
372 // With this here, the value of frm_stripe_vars.settings[0].fields is -1
373 // This is because the amount value is processed and a shortcode is not found in '000'.
374 FrmStrpLiteActionsController::load_scripts( (int) $field->form_id );
375
376 $values['type'] = 'hidden';
377 return $values;
378 }
379
380 /**
381 * Entries are deleted on payment failure so set the form values after an error from the entry data that gets deleted.
382 *
383 * @since 6.5.1
384 *
385 * @param array $values
386 * @param stdClass $field
387 * @return array
388 */
389 public static function fill_entry_from_previous( $values, $field ) {
390 global $frm_vars;
391 $previous_entry = isset( $frm_vars['frm_trans']['pay_entry'] ) ? $frm_vars['frm_trans']['pay_entry'] : false;
392 if ( empty( $previous_entry ) || $previous_entry->form_id != $field->form_id ) {
393 return $values;
394 }
395
396 if ( is_array( $previous_entry->metas ) && isset( $previous_entry->metas[ $field->id ] ) ) {
397 $values['value'] = $previous_entry->metas[ $field->id ];
398 }
399
400 $frm_vars['trans_filled'] = true;
401
402 $previous_entry_id = $previous_entry->id;
403 self::destroy_entry_later( $previous_entry_id );
404
405 return $values;
406 }
407
408 /**
409 * Destroy an entry, but delay it to happen when the form is displayed.
410 * It needs to happen late enough that FrmProNestedFormsController::display_single_iteration_of_nested_form is able to fill in data for repeater fields.
411 * See Formidable Stripe issue #136 for more information.
412 *
413 * @since 6.5.1
414 *
415 * @param string|int $entry_id
416 * @return void
417 */
418 private static function destroy_entry_later( $entry_id ) {
419 if ( in_array( (int) $entry_id, self::$entry_ids_to_destroy_later, true ) ) {
420 // Avoid trying to delete this multiple times as fill_entry_from_previous is called more than once.
421 return;
422 }
423
424 $destroy_callback =
425 /**
426 * Destroy an entry and remove this action so it only tries to destroy the entry once.
427 *
428 * @param string|int $entry_id
429 * @param Closure $destroy_callback
430 * @return void
431 */
432 function() use ( $entry_id, &$destroy_callback ) {
433 FrmEntry::destroy( $entry_id );
434 remove_action( 'frm_entry_form', $destroy_callback ); // Only call this once.
435 };
436 add_action( 'frm_entry_form', $destroy_callback );
437
438 self::$entry_ids_to_destroy_later[] = (int) $entry_id;
439 }
440 }
441