PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.6.60
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.6.60
1.8.13.1 1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 All 212 releases
charitable / includes / donations / class-charitable-donation-processor.php

class-charitable-donation-processor.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.6.60, at includes/donations/class-charitable-donation-processor.php

963 lines 27.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The class that is responsible for responding to donation events.
4 *
5 * @package Charitable/Classes/Charitable_Donation_Processor
6 * @author Eric Daams
7 * @copyright Copyright (c) 2022, Studio 164a
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 * @since 1.0.0
10 * @version 1.6.55
11 */
12
13 // Exit if accessed directly.
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 if ( ! class_exists( 'Charitable_Donation_Processor' ) ) :
19
20 /**
21 * Charitable Donation Processor.
22 *
23 * @since 1.0.0
24 */
25 class Charitable_Donation_Processor {
26
27 /**
28 * The single instance of this class.
29 *
30 * @var Charitable_Donation_Processor|null
31 */
32 private static $instance = null;
33
34 /**
35 * The campaign receiving a donation.
36 *
37 * @var Charitable_Campaign|false
38 */
39 protected $campaign;
40
41 /**
42 * The donation data.
43 *
44 * @var mixed[]
45 */
46 protected $donation_data;
47
48 /**
49 * The campaign donations array.
50 *
51 * @var array
52 */
53 protected $campaign_donations_data;
54
55 /**
56 * The donor ID for the current donation.
57 *
58 * @var int
59 */
60 protected $donor_id;
61
62 /**
63 * The donoration ID for the current donation.
64 *
65 * @var int
66 */
67 protected $donation_id;
68
69 /**
70 * Create class object. A protected constructor, so this is used in a singleton context.
71 *
72 * @since 1.0.0
73 */
74 protected function __construct() {
75 $this->campaign = charitable_get_current_campaign();
76 }
77
78 /**
79 * Returns and/or create the single instance of this class.
80 *
81 * @since 1.0.0
82 *
83 * @return Charitable_Donation_Processor
84 */
85 public static function get_instance() {
86 if ( is_null( self::$instance ) ) {
87 self::$instance = new self();
88 }
89
90 return self::$instance;
91 }
92
93 /**
94 * Destroy the Charitable_Donation_Processor instance.
95 *
96 * This is primarily useful for testing purposes, as it allows you to
97 * create multiple donations in a single request.
98 *
99 * @since 1.3.0
100 *
101 * @return void
102 */
103 public static function destroy() {
104 self::$instance = null;
105 }
106
107 /**
108 * Return the current campaign.
109 *
110 * @since 1.0.0
111 *
112 * @return Charitable_Campaign|false False if no campaign is set. Campaign object otherwise.
113 */
114 public function get_campaign() {
115 return $this->campaign;
116 }
117
118 /**
119 * Return the donation ID.
120 *
121 * @since 1.4.0
122 *
123 * @return int
124 */
125 public function get_donation_id() {
126 return $this->donation_id;
127 }
128
129 /**
130 * Executed when a user first clicks the Donate button on a campaign.
131 *
132 * @since 1.0.0
133 *
134 * @return void
135 */
136 public static function add_donation_to_session() {
137 $processor = self::get_instance();
138
139 if ( ! $processor->get_campaign() ) {
140 return;
141 }
142
143 $nonce = $_POST['charitable-donate-now'];
144
145 if ( ! wp_verify_nonce( $nonce, 'charitable-donate' ) ) {
146
147 /* For backwards compatibility, we also check this nonce. */
148 if ( ! wp_verify_nonce( $nonce, 'charitable-donate-' ) ) {
149 return;
150 }
151 }
152
153 /* Save the donation in the session */
154 charitable_get_session()->add_donation( $processor->get_campaign()->ID, 0 );
155
156 $donations_url = charitable_get_permalink( 'campaign_donation_page', array( 'campaign_id' => $processor->get_campaign()->ID ) );
157
158 wp_redirect( $donations_url );
159
160 die();
161 }
162
163 /**
164 * Process the donation.
165 *
166 * This is used by all donation form submissions, AJAX or not.
167 *
168 * @since 1.3.0
169 *
170 * @return mixed
171 */
172 public function process_donation() {
173 $processor = self::get_instance();
174 $campaign = $processor->get_campaign();
175
176 if ( ! $campaign ) {
177 return;
178 }
179
180 /* Validate the form submission and retrieve the values. */
181 $form = $campaign->get_donation_form();
182
183 /**
184 * Do something before any donation form processing takes place.
185 *
186 * @since 1.0.0
187 *
188 * @param Charitable_Donation_Processor $processor The Donation Processor object.
189 * @param Charitable_Donation_Form $form The Donation Form object.
190 */
191 do_action( 'charitable_before_process_donation_form', $processor, $form );
192
193 if ( ! $form->validate_submission() ) {
194 return false;
195 }
196
197 $values = $form->get_donation_values();
198 $gateway = $values['gateway'];
199
200 /**
201 * Validate the gateway values.
202 *
203 * @since 1.0.0
204 *
205 * @param boolean $valid Whether the submission passes validation.
206 * @param string $gateway The gateway ID.
207 * @param array $values The values submitted by the user.
208 */
209 if ( ! apply_filters( 'charitable_validate_donation_form_submission_gateway', true, $gateway, $values ) ) {
210 return false;
211 }
212
213 $this->donation_id = $processor->save_donation( $values );
214
215 /**
216 * Set a transient to allow plugins to act on this donation on the next page load.
217 */
218 set_transient( 'charitable_donation_' . charitable_get_session()->get_session_id(), $this );
219
220 /**
221 * We check whether the gateway is compatible with version 1.3, since Charitable 1.3
222 * change the hook into a filter (instead of an action).
223 */
224 if ( $this->gateway_is_130_compatible( $gateway ) ) {
225
226 /**
227 * Pass donation processing to the payment gateway and return the results.
228 *
229 * Clients should return one of three results:
230 *
231 * - TRUE : If the donation was processed successfully and the user should
232 * be redirected to the donation receipt.
233 * - FALSE : If the donation could not be processed successfully and the user
234 * should be redirected back to the donation form.
235 * - ARRAY : If the user needs to be redirected somewhere other than the donation
236 * receipt or donation form. Array should contain two values:
237 * `redirect` : The url to be redirected to.
238 * `safe` : Whether to use wp_safe_redirect. If unset, will default to true.
239 *
240 * @since 1.3.0
241 *
242 * @param mixed $result The result of gateway processing.
243 * @param int $donation_id The donation ID.
244 * @param Charitable_Donation_Processor $processor The Donation Processor object.
245 * @return mixed
246 */
247 return apply_filters( 'charitable_process_donation_' . $gateway, true, $this->donation_id, $processor );
248
249 } else {
250 /**
251 * Send donation processing to the payment gateway.
252 *
253 * This is a fallback for payment gateways that have not been updated to the filter method above.
254 *
255 * @since 1.0.0
256 *
257 * @param int $donation_id The donation ID.
258 * @param Charitable_Donation_Processor $processor The Donation Processor object.
259 */
260 do_action( 'charitable_process_donation_' . $gateway, $this->donation_id, $processor );
261
262 /**
263 * If we get this far, forward the user through to the receipt page.
264 */
265 wp_safe_redirect( charitable_get_permalink( 'donation_receipt_page', array( 'donation_id' => $this->donation_id ) ) );
266
267 die();
268
269 }//end if
270 }
271
272 /**
273 * Save a donation submitted through a page reload (i.e. not AJAX).
274 *
275 * @since 1.0.0
276 *
277 * @return void
278 */
279 public static function process_donation_form_submission() {
280 $processor = self::get_instance();
281 $result = $processor->process_donation();
282
283 $processor->redirect_after_gateway_processing( $result );
284 }
285
286 /**
287 * Add a donation with AJAX.
288 *
289 * @since 1.3.0
290 *
291 * @return void
292 */
293 public static function ajax_process_donation_form_submission() {
294 /* Prevent any displayed errors from messing with the AJAX response. */
295 ini_set( 'display_errors', false );
296
297 if ( ! isset( $_POST['campaign_id'] ) ) {
298 wp_send_json_error( new WP_Error( 'missing_campaign_id', __( 'Campaign ID was not found. Unable to create donation.', 'charitable' ) ) );
299 }
300
301 $processor = self::get_instance();
302
303 $result = $processor->process_donation();
304 $response = is_array( $result ) ? $result : array();
305
306 if ( $processor->donation_processed_successfully( $result ) ) {
307 $response['success'] = true;
308 $response['redirect_to'] = $processor->get_redirection_after_gateway_processing( $result );
309 } else {
310 $errors = charitable_get_notices()->get_errors();
311
312 if ( empty( $errors ) ) {
313 $errors = array( __( 'Unable to process donation.', 'charitable' ) );
314 }
315
316 $response['success'] = false;
317 $response['errors'] = $errors;
318 $response['donation_id'] = (int) $processor->get_donation_id();
319 }
320
321 wp_send_json( $response );
322
323 exit();
324 }
325
326 /**
327 * Save a donation.
328 *
329 * @since 1.0.0
330 *
331 * @return void
332 */
333 public static function make_donation_streamlined() {
334 $processor = self::get_instance();
335 $campaign = $processor->get_campaign();
336
337 if ( ! $campaign ) {
338 return;
339 }
340
341 /* Validate the form submission and retrieve the values. */
342 $form = new Charitable_Donation_Amount_Form( $campaign );
343
344 /**
345 * @hook charitable_before_process_donation_amount_form
346 */
347 do_action( 'charitable_before_process_donation_amount_form', $processor, $form );
348
349 /**
350 * Form validated, so add the donation to session.
351 */
352 if ( $form->validate_submission() ) {
353 $submitted = $form->get_donation_values();
354 $period = array_key_exists( 'donation_period', $submitted ) ? $submitted['donation_period'] : 'once';
355
356 charitable_get_session()->add_donation( $submitted['campaign_id'], $submitted['amount'], $period );
357
358 /**
359 * @hook charitable_after_process_donation_amount_form
360 */
361 do_action( 'charitable_after_process_donation_amount_form', $processor, $submitted );
362 }
363
364 $redirect_url = charitable_get_permalink( 'campaign_donation_page', array( 'campaign_id' => $campaign->ID ) );
365
366 if ( 'same_page' == charitable_get_option( 'donation_form_display', 'separate_page' ) ) {
367 $redirect_url .= '#charitable-donation-form';
368 }
369
370 wp_safe_redirect( $redirect_url );
371 die();
372 }
373
374 /**
375 * Checks whether a donation has been processed successfully.
376 *
377 * @since 1.6.44
378 *
379 * @param boolean|array $result The result returned from process_donation.
380 * @return boolean
381 */
382 public function donation_processed_successfully( $result ) {
383 if ( is_array( $result ) ) {
384 return array_key_exists( 'success', $result ) ? $result['success'] : true;
385 }
386
387 return $result;
388 }
389
390 /**
391 * Inserts a new donation.
392 *
393 * This method is designed to be completely form agnostic.
394 *
395 * We use this when integrating third-party systems like Easy Digital Downloads and
396 * WooCommerce.
397 *
398 * @since 1.0.0
399 *
400 * @param mixed[] $values Submitted donation values.
401 * @return int $donation_id Returns 0 in case of failure. Positive donation ID otherwise.
402 */
403 public function save_donation( array $values ) {
404 /**
405 * Filter the donation values to be saved.
406 *
407 * @since 1.0.0
408 *
409 * @param array $values The donation values to be saved.
410 */
411 $this->donation_data = apply_filters( 'charitable_donation_values', $values );
412
413 if ( ! $this->get_campaign_donations_data() ) {
414 charitable_get_deprecated()->doing_it_wrong(
415 __METHOD__,
416 __( 'A donation cannot be inserted without an array of campaigns being donated to.', 'charitable' ),
417 '1.0.0'
418 );
419 return 0;
420 }
421
422 if ( ! $this->is_valid_user_data() ) {
423 charitable_get_deprecated()->doing_it_wrong(
424 __METHOD__,
425 __( 'A donation cannot be inserted without valid user data.', 'charitable' ),
426 '1.0.0'
427 );
428 return 0;
429 }
430
431 /**
432 * Do something right before the donation is inserted into the database.
433 *
434 * @since 1.0.0
435 *
436 * @param Charitable_Donation_Processor $this The Processor instance.
437 */
438 do_action( 'charitable_before_save_donation', $this );
439
440 $donation_id = wp_insert_post( $this->parse_donation_data() );
441
442 $this->set_donation_key();
443
444 if ( is_wp_error( $donation_id ) ) {
445 charitable_get_notices()->add_errors_from_wp_error( $donation_id );
446 return 0;
447 }
448
449 if ( 0 == $donation_id ) {
450 charitable_get_notices()->add_error( __( 'We were unable to save the donation. Please try again.', 'charitable' ) );
451 return 0;
452 }
453
454 $this->save_campaign_donations( $donation_id );
455
456 $this->save_donation_meta( $donation_id );
457
458 $this->save_donor_contact_consent();
459
460 $log_note = array_key_exists( 'log_note', $values ) ? $values['log_note'] : '';
461
462 if ( empty( $log_note ) ) {
463 $log_note = isset( $values['ID'] ) && 0 !== $values['ID'] ? __( 'Payment attempted.', 'charitable' ) : __( 'Donation created.', 'charitable' );
464 }
465
466 $this->update_donation_log( $donation_id, $log_note );
467
468 /**
469 * Update the user session if we're on the public site or in an AJAX request.
470 *
471 * 1. Write the donation key to the session.
472 * 2. Write the campaign donation amounts to the session. This is required in
473 * case the donor gets sent back to the donation form (cancellation, failure).
474 */
475 if ( ! is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
476
477 $session = charitable_get_session();
478
479 /* Required for the donation receipt to load for logged out donors. */
480 $session->add_donation_key( $this->get_donation_data_value( 'donation_key' ) );
481
482 /* Required in case the donor is redirected back to the donation form. */
483 foreach ( $this->get_campaign_donations_data() as $campaign ) {
484 $session->add_donation( $campaign['campaign_id'], $campaign['amount'], $this->get_donation_data_value( 'donation_key', 'once' ) );
485 }
486 }
487
488 /**
489 * Do something after the donation has been saved in the database.
490 *
491 * @since 1.0.0
492 *
493 * @param int $donation_id The donation ID.
494 * @param Charitable_Donation_Processor $this The Processor instance.
495 */
496 do_action( 'charitable_after_save_donation', $donation_id, $this );
497
498 return $donation_id;
499 }
500
501 /**
502 * Inserts the campaign donations into the campaign_donations table.
503 *
504 * @since 1.0.0
505 *
506 * @param int $donation_id The donation ID.
507 * @return int The number of donations inserted.
508 */
509 public function save_campaign_donations( $donation_id ) {
510 $donor_id = $this->get_donor_id();
511 $campaigns = $this->get_campaign_donations_data();
512
513 if ( false !== $this->get_donation_data_value( 'ID', false ) ) {
514 $records = charitable_get_table( 'campaign_donations' )->get_donation_records( $donation_id );
515 $campaign_donations = wp_list_pluck( $records, 'campaign_id', 'campaign_donation_id' );
516 } else {
517 $campaign_donations = array();
518 }
519
520 foreach ( $campaigns as $campaign ) {
521
522 if ( array_key_exists( 'campaign_donation_id', $campaign ) ) {
523 $campaign_donation_id = $campaign['campaign_donation_id'];
524 } else {
525 $campaign_donation_id = array_search( $campaign['campaign_id'], $campaign_donations );
526 }
527
528 /* Avoid adding duplicate campaign donations when re-submitting a campaign. */
529 if ( false !== $campaign_donation_id ) {
530
531 /* Cast the existing campaign_donation record to an array and sanitize the amount. */
532 $campaign_donation = (array) $records[ $campaign_donation_id ];
533 $campaign_donation['amount'] = charitable_sanitize_amount( $campaign_donation['amount'], true );
534
535 /* Get our updated campaign donation values. */
536 $args = charitable_array_subset( $campaign, array( 'campaign_id', 'campaign_name', 'amount' ) );
537 $args['donor_id'] = $donor_id;
538
539 /* If any values have changed, update the record. */
540 if ( count( array_diff( $args, $campaign_donation ) ) ) {
541 charitable_get_table( 'campaign_donations' )->update( $campaign_donation_id, $args, 'campaign_donation_id' );
542 }
543 } else {
544
545 /* Add a new donation */
546 $campaign['donor_id'] = $donor_id;
547 $campaign['donation_id'] = $donation_id;
548 $campaign_donation_id = charitable_get_table( 'campaign_donations' )->insert( $campaign );
549
550 if ( 0 == $campaign_donation_id ) {
551 return 0;
552 }
553 }
554 }//end foreach
555
556 return count( $campaigns );
557 }
558
559 /**
560 * Save the meta for the donation.
561 *
562 * @since 1.0.0
563 *
564 * @param int $donation_id The donation ID.
565 * @return void
566 */
567 public function save_donation_meta( $donation_id ) {
568 $meta = array(
569 'donation_gateway' => $this->get_donation_data_value( 'gateway' ),
570 'donor' => $this->get_donation_data_value( 'user' ),
571 'test_mode' => charitable_get_option( 'test_mode', 0 ),
572 'donation_key' => $this->get_donation_data_value( 'donation_key' ),
573 'currency' => charitable_get_currency(),
574 );
575
576 if ( $this->get_donation_data_value( 'meta' ) ) {
577 $meta = array_merge( $meta, $this->get_donation_data_value( 'meta' ) );
578 }
579
580 /**
581 * Filter the donation meta to be saved.
582 *
583 * @since 1.0.0
584 *
585 * @param array $meta The meta to be saved, in a key=>value array.
586 * @param int $donation_id The donation ID.
587 * @param Charitable_Donation_Processor $processor This instance of `Charitable_Donation_Processor`.
588 */
589 $meta = apply_filters( 'charitable_donation_meta', $meta, $donation_id, $this );
590
591 foreach ( $meta as $meta_key => $value ) {
592 /**
593 * Sanitize a particular meta value.
594 *
595 * @since 1.0.0
596 *
597 * @param mixed $value The value.
598 * @param string $meta_key The meta key.
599 */
600 $value = apply_filters( 'charitable_sanitize_donation_meta', $value, $meta_key );
601 update_post_meta( $donation_id, $meta_key, $value );
602 }
603 }
604
605 /**
606 * Save whether the donor gave consent to be contacted.
607 *
608 * @since 1.6.0
609 *
610 * @return void
611 */
612 public function save_donor_contact_consent() {
613 /* Contact consent requires he database upgrade to have been completed. */
614 if ( ! charitable_is_contact_consent_activated() ) {
615 return;
616 }
617
618 $donor_id = $this->get_donor_id();
619 $meta = $this->get_donation_data_value( 'meta', array() );
620 $contact_consent = array_key_exists( 'contact_consent', $meta ) ? (bool) $meta['contact_consent'] : false;
621
622 charitable_get_table( 'donors' )->update(
623 $donor_id,
624 array(
625 'contact_consent' => $contact_consent,
626 )
627 );
628 }
629
630 /**
631 * Add a message to the donation log.
632 *
633 * @since 1.0.0
634 *
635 * @param int $donation_id The donation ID.
636 * @param string $message The message to add to the log.
637 * @return void
638 */
639 public function update_donation_log( $donation_id, $message ) {
640 $donation = charitable_get_donation( $donation_id );
641 if ( $donation ) {
642 $donation->log()->add( $message );
643 }
644 }
645
646 /**
647 * Returns the submitted donation data.
648 *
649 * @since 1.0.0
650 *
651 * @return mixed[]
652 */
653 public function get_donation_data() {
654 return $this->donation_data;
655 }
656
657 /**
658 * Return the submitted value for a particular key.
659 *
660 * @since 1.0.0
661 *
662 * @param string $key The key to search for.
663 * @param mixed $default Fallback value to return if the data is not set.
664 * @return mixed
665 */
666 public function get_donation_data_value( $key, $default = false ) {
667 $data = $this->get_donation_data();
668 return isset( $data[ $key ] ) ? $data[ $key ] : $default;
669 }
670
671 /**
672 * Set a value for a particular key.
673 *
674 * @since 1.4.0
675 *
676 * @param string $key The key to set.
677 * @param mixed $value The value to be set.
678 * @return void
679 */
680 public function set_donation_data_value( $key, $value ) {
681 $this->donation_data[ $key ] = $value;
682 }
683
684 /**
685 * Returns the campaign donations array, or false if the data is invalid.
686 *
687 * @since 1.0.0
688 *
689 * @return array[]|false
690 */
691 public function get_campaign_donations_data() {
692 if ( ! isset( $this->campaign_donations_data ) ) {
693
694 if ( ! is_array( $this->get_donation_data_value( 'campaigns' ) ) ) {
695 return false;
696 }
697
698 $this->campaign_donations_data = array();
699
700 foreach ( $this->get_donation_data_value( 'campaigns' ) as $campaign ) {
701
702 /* If the amount or campaign_id are missing, this donation won't work. */
703 if ( ! isset( $campaign['campaign_id'] ) || ! isset( $campaign['amount'] ) ) {
704 return false;
705 }
706
707 if ( ! isset( $campaign['campaign_name'] ) ) {
708 $campaign['campaign_name'] = get_post_field( 'post_title', $campaign['campaign_id'], 'raw' );
709 }
710
711 $this->campaign_donations_data[] = $campaign;
712 }
713 }//end if
714
715 return $this->campaign_donations_data;
716 }
717
718 /**
719 * Returns the donor_id for the current donation.
720 *
721 * @since 1.0.0
722 *
723 * @return int
724 */
725 public function get_donor_id() {
726 if ( ! isset( $this->donor_id ) ) {
727 $this->donor_id = $this->get_donation_data_value( 'donor_id', 0 );
728
729 if ( $this->donor_id ) {
730 return $this->donor_id;
731 }
732
733 $user_data = $this->get_donation_data_value( 'user', array() );
734
735 if ( array_key_exists( 'email', $user_data ) ) {
736 $this->donor_id = charitable_get_table( 'donors' )->get_donor_id_by_email( $user_data['email'] );
737 }
738
739 /* If we still do not have a donor ID, it means that this is a first-time donor */
740 if ( 0 == $this->donor_id ) {
741 $user = new Charitable_User( $this->get_donation_data_value( 'user_id', get_current_user_id() ) );
742 $this->donor_id = $user->add_donor( $user_data );
743 }
744 }//end if
745
746 return $this->donor_id;
747 }
748
749 /**
750 * Returns the name of the donor.
751 *
752 * @since 1.0.0
753 * @since 1.6.14 Changed access to public. Previously protected.
754 *
755 * @return string
756 */
757 public function get_donor_name() {
758 $user = new WP_User( $this->get_donation_data_value( 'user_id', 0 ) );
759 $user_data = $this->get_donation_data_value( 'user' );
760 $first_name = isset( $user_data['first_name'] ) ? $user_data['first_name'] : $user->get( 'first_name' );
761 $last_name = isset( $user_data['last_name'] ) ? $user_data['last_name'] : $user->get( 'last_name' );
762 return trim( sprintf( '%s %s', $first_name, $last_name ) );
763 }
764
765 /**
766 * Returns a comma separated list of the campaigns that are being donated to.
767 *
768 * @since 1.0.0
769 * @since 1.6.14 Changed access to public. Previously protected.
770 *
771 * @return string
772 */
773 public function get_campaign_names() {
774 $campaigns = wp_list_pluck( $this->get_campaign_donations_data(), 'campaign_name' );
775 return implode( ', ', $campaigns );
776 }
777
778 /**
779 * Returns the donation status. Defaults to charitable-pending.
780 *
781 * @since 1.0.0
782 *
783 * @return string
784 */
785 protected function get_donation_status() {
786 $status = $this->get_donation_data_value( 'status', 'charitable-pending' );
787
788 if ( ! charitable_is_valid_donation_status( $status ) ) {
789 $status = 'charitable-pending';
790 }
791
792 return $status;
793 }
794
795 /**
796 * Redirect the user after the gateway has processed the donation.
797 *
798 * @uses Charitable_Donation_Processor::get_redirection_after_gateway_processing()
799 *
800 * @since 1.3.0
801 *
802 * @param mixed $gateway_processing The result of the gateway processing.
803 * @return void
804 */
805 private function redirect_after_gateway_processing( $gateway_processing ) {
806 $redirect_url = $this->get_redirection_after_gateway_processing( $gateway_processing );
807
808 /* If the gateway processing failed, add the error notices to the session. */
809 if ( false == $gateway_processing ) {
810
811 /* Log the failed payment. */
812 $this->update_donation_log(
813 $this->donation_id,
814 sprintf(
815 /* translators: %s: error message */
816 __( 'Payment failed with errors: %s', 'charitable' ),
817 PHP_EOL . implode( PHP_EOL, charitable_get_notices()->get_errors() )
818 )
819 );
820
821 charitable_get_session()->add_notices();
822 }
823
824 /* Set the redirect status to use. */
825 $status = isset( $gateway_processing['status'] ) ? $gateway_processing['status'] : 302;
826
827 /**
828 * If the gateway processing returned an array with a directive to NOT
829 * use wp_safe_redirect, use wp_redirect instead.
830 */
831 if ( isset( $gateway_processing['safe'] ) && false == $gateway_processing['safe'] ) {
832 wp_redirect( $redirect_url, $status );
833 die();
834 }
835
836 wp_safe_redirect( $redirect_url, $status );
837
838 die();
839 }
840
841 /**
842 * Return the URL that the donor should be redirected to.
843 *
844 * @since 1.3.0
845 *
846 * @param mixed $gateway_processing The result of the gateway processing.
847 * @return string
848 */
849 private function get_redirection_after_gateway_processing( $gateway_processing ) {
850 if ( false == $gateway_processing ) {
851 $redirect_url = esc_url( add_query_arg( array( 'donation_id' => $this->donation_id ), wp_get_referer() ) );
852 } elseif ( is_array( $gateway_processing ) && isset( $gateway_processing['redirect'] ) ) {
853 $redirect_url = $gateway_processing['redirect'];
854 } else {
855 /* Fall back to returning the donation receipt URL. */
856 $redirect_url = charitable_get_permalink( 'donation_receipt_page', array( 'donation_id' => $this->donation_id ) );
857 }
858
859 return $redirect_url;
860 }
861
862 /**
863 * Validate user data passed to insert.
864 *
865 * @since 1.0.0
866 *
867 * @return boolean
868 */
869 protected function is_valid_user_data() {
870 if ( charitable_permit_donor_without_email() ) {
871 return true;
872 }
873
874 $ret = $this->get_donation_data_value( 'user_id' ) || $this->get_donation_data_value( 'donor_id' );
875
876 if ( ! $ret ) {
877 $user = $this->get_donation_data_value( 'user', array() );
878 $ret = array_key_exists( 'email', $user ) && is_email( $user['email'] );
879 }
880
881 return $ret;
882 }
883
884 /**
885 * Parse the donation data, based on the passed $values array.
886 *
887 * @since 1.0.0
888 *
889 * @return array
890 */
891 protected function parse_donation_data() {
892 $core_values = array(
893 'ID' => $this->get_donation_data_value( 'ID', 0 ),
894 'post_type' => Charitable::DONATION_POST_TYPE,
895 'post_author' => $this->get_donation_data_value( 'user_id', get_current_user_id() ),
896 'post_status' => $this->get_donation_status(),
897 'post_content' => $this->get_donation_data_value( 'note', '' ),
898 'post_parent' => $this->get_donation_data_value( 'donation_plan', 0 ),
899 'post_date_gmt' => $this->get_donation_data_value( 'date_gmt', current_time( 'mysql', true ) ),
900 'post_title' => sprintf( '%s &ndash; %s', $this->get_donor_name(), $this->get_campaign_names() ),
901 );
902
903 $core_values['post_date'] = get_date_from_gmt( $core_values['post_date_gmt'] );
904
905 /**
906 * Filter the core Donation post values to store.
907 *
908 * @since 1.0.0
909 *
910 * @param array $core_values The values to save to the posts table.
911 * @param Charitable_Donation_Processor $processor The donation processer object.
912 */
913 return apply_filters( 'charitable_donation_values_core', $core_values, $this );
914 }
915
916 /**
917 * Set a unique key for the donation.
918 *
919 * @since 1.0.0
920 *
921 * @return void
922 */
923 protected function set_donation_key() {
924 if ( array_key_exists( 'donation_key', $this->donation_data ) ) {
925 return;
926 }
927
928 $this->donation_data['donation_key'] = strtolower( md5( uniqid() ) );
929 }
930
931 /**
932 * Checks whether the given gateway has been updated for compatibility with 1.3.
933 *
934 * @since 1.3.0
935 *
936 * @param string $gateway The gateway for the donation.
937 * @return boolean
938 */
939 private function gateway_is_130_compatible( $gateway ) {
940 return Charitable_Gateways::get_instance()->get_gateway_object( $gateway )->supports( '1.3.0' );
941 }
942
943 /**
944 * Return the IPN url.
945 *
946 * IPNs in Charitable are structured in this way: charitable-listener=gateway
947 *
948 * @deprecated
949 *
950 * @since 1.0.0
951 *
952 * @param string $gateway The gateway for the donation.
953 * @return string
954 */
955 public function get_ipn_url( $gateway ) {
956 charitable_get_deprecated()->deprecated_function( __METHOD__, '1.4.0', 'charitable_get_ipn_url()' );
957
958 return charitable_get_ipn_url( $gateway );
959 }
960 }
961
962 endif;
963