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

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