PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.7.0.5
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.7.0.5
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.7.0.5, at includes/donations/class-charitable-donation-processor.php

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