← All changes
|
includes/donations/class-charitable-donation-processor.php
+150
-23
1.6.60
→
1.8.13
View file →
| @@ -2,10 +2,10 @@ | ||
| 2 | 2 | /** |
| 3 | 3 | * The class that is responsible for responding to donation events. |
| 4 | 4 | * |
| 5 | 5 | * @package Charitable/Classes/Charitable_Donation_Processor |
| 6 | - * @author Eric Daams | |
| 7 | - * @copyright Copyright (c) 2022, Studio 164a | |
| 6 | + * @author David Bisset | |
| 7 | + * @copyright Copyright (c) 2023, WP Charitable LLC | |
| 8 | 8 | * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License |
| 9 | 9 | * @since 1.0.0 |
| 10 | 10 | * @version 1.6.55 |
| 11 | 11 | */ |
| @@ -129,8 +129,9 @@ | ||
| 129 | 129 | /** |
| 130 | 130 | * Executed when a user first clicks the Donate button on a campaign. |
| 131 | 131 | * |
| 132 | 132 | * @since 1.0.0 |
| 133 | + * @version 1.8.3.2 | |
| 133 | 134 | * |
| 134 | 135 | * @return void |
| 135 | 136 | */ |
| 136 | 137 | public static function add_donation_to_session() { |
| @@ -139,10 +140,14 @@ | ||
| 139 | 140 | if ( ! $processor->get_campaign() ) { |
| 140 | 141 | return; |
| 141 | 142 | } |
| 142 | 143 | |
| 143 | - $nonce = $_POST['charitable-donate-now']; | |
| 144 | + if ( ! isset( $_POST['charitable-donate-now'] ) ) { | |
| 145 | + return; | |
| 146 | + } | |
| 144 | 147 | |
| 148 | + $nonce = sanitize_text_field( wp_unslash( $_POST['charitable-donate-now'] ) ); | |
| 149 | + | |
| 145 | 150 | if ( ! wp_verify_nonce( $nonce, 'charitable-donate' ) ) { |
| 146 | 151 | |
| 147 | 152 | /* For backwards compatibility, we also check this nonce. */ |
| 148 | 153 | if ( ! wp_verify_nonce( $nonce, 'charitable-donate-' ) ) { |
| @@ -149,16 +154,21 @@ | ||
| 149 | 154 | return; |
| 150 | 155 | } |
| 151 | 156 | } |
| 152 | 157 | |
| 158 | + /* Possibly add a donation amount if user clicked on a campaign builder donate button */ | |
| 159 | + $donation_amount = ! empty( $_POST['charitable_donation_amount'] ) && ! empty( $_POST['charitable_builder'] ) ? esc_html( $_POST['charitable_donation_amount'] ) : 0; // phpcs:ignore | |
| 160 | + | |
| 161 | + /* Strip any currency symbols and make sure $donation_amount is a float. */ | |
| 162 | + $donation_amount = charitable_sanitize_amount( (string) $donation_amount ); | |
| 163 | + | |
| 153 | 164 | /* Save the donation in the session */ |
| 154 | - charitable_get_session()->add_donation( $processor->get_campaign()->ID, 0 ); | |
| 165 | + charitable_get_session()->add_donation( $processor->get_campaign()->ID, $donation_amount ); | |
| 155 | 166 | |
| 156 | 167 | $donations_url = charitable_get_permalink( 'campaign_donation_page', array( 'campaign_id' => $processor->get_campaign()->ID ) ); |
| 157 | 168 | |
| 158 | - wp_redirect( $donations_url ); | |
| 159 | - | |
| 160 | - die(); | |
| 169 | + wp_safe_redirect( $donations_url ); | |
| 170 | + exit(); | |
| 161 | 171 | } |
| 162 | 172 | |
| 163 | 173 | /** |
| 164 | 174 | * Process the donation. |
| @@ -172,15 +182,41 @@ | ||
| 172 | 182 | public function process_donation() { |
| 173 | 183 | $processor = self::get_instance(); |
| 174 | 184 | $campaign = $processor->get_campaign(); |
| 175 | 185 | |
| 186 | + if ( charitable_is_debug() ) { | |
| 187 | + error_log( 'process_donation start' ); // phpcs:ignore | |
| 188 | + } | |
| 189 | + | |
| 176 | 190 | if ( ! $campaign ) { |
| 177 | 191 | return; |
| 178 | 192 | } |
| 179 | 193 | |
| 194 | + /* Check to ensure the donation is not going to an inactive campaign post. */ | |
| 195 | + if ( ! $campaign->can_receive_donations() ) { | |
| 196 | + if ( charitable_is_debug() ) { | |
| 197 | + error_log( 'Campaign cannot receive donations' ); // phpcs:ignore | |
| 198 | + } | |
| 199 | + return; | |
| 200 | + } | |
| 201 | + | |
| 180 | 202 | /* Validate the form submission and retrieve the values. */ |
| 181 | 203 | $form = $campaign->get_donation_form(); |
| 204 | + if ( charitable_is_debug() ) { | |
| 205 | + // phpcs:disable | |
| 206 | + error_log( 'process_donation - form' ); | |
| 207 | + error_log( print_r( $form, true ) ); | |
| 208 | + // phpcs:enable | |
| 209 | + } | |
| 182 | 210 | |
| 211 | + if ( charitable_is_debug() ) { | |
| 212 | + // phpcs:disable | |
| 213 | + error_log( '=== FORM DATA DEBUG ===' ); | |
| 214 | + error_log( 'POST data available to form: ' . print_r( $_POST, true ) ); | |
| 215 | + error_log( 'REQUEST data available to form: ' . print_r( $_REQUEST, true ) ); | |
| 216 | + // phpcs:enable | |
| 217 | + } | |
| 218 | + | |
| 183 | 219 | /** |
| 184 | 220 | * Do something before any donation form processing takes place. |
| 185 | 221 | * |
| 186 | 222 | * @since 1.0.0 |
| @@ -190,8 +226,13 @@ | ||
| 190 | 226 | */ |
| 191 | 227 | do_action( 'charitable_before_process_donation_form', $processor, $form ); |
| 192 | 228 | |
| 193 | 229 | if ( ! $form->validate_submission() ) { |
| 230 | + if ( charitable_is_debug() ) { | |
| 231 | + // phpcs:disable | |
| 232 | + error_log( 'Validation form not valid' ); | |
| 233 | + // phpcs:enable | |
| 234 | + } | |
| 194 | 235 | return false; |
| 195 | 236 | } |
| 196 | 237 | |
| 197 | 238 | $values = $form->get_donation_values(); |
| @@ -196,8 +237,17 @@ | ||
| 196 | 237 | |
| 197 | 238 | $values = $form->get_donation_values(); |
| 198 | 239 | $gateway = $values['gateway']; |
| 199 | 240 | |
| 241 | + if ( charitable_is_debug() ) { | |
| 242 | + // phpcs:disable | |
| 243 | + error_log( '=== DONATION PROCESSOR DEBUG ===' ); | |
| 244 | + error_log( 'Gateway from form values: ' . $gateway ); | |
| 245 | + error_log( 'All form values: ' . print_r( $values, true ) ); | |
| 246 | + error_log( 'POST data: ' . print_r( $_POST, true ) ); | |
| 247 | + // phpcs:enable | |
| 248 | + } | |
| 249 | + | |
| 200 | 250 | /** |
| 201 | 251 | * Validate the gateway values. |
| 202 | 252 | * |
| 203 | 253 | * @since 1.0.0 |
| @@ -206,8 +256,15 @@ | ||
| 206 | 256 | * @param string $gateway The gateway ID. |
| 207 | 257 | * @param array $values The values submitted by the user. |
| 208 | 258 | */ |
| 209 | 259 | if ( ! apply_filters( 'charitable_validate_donation_form_submission_gateway', true, $gateway, $values ) ) { |
| 260 | + if ( charitable_is_debug() ) { | |
| 261 | + // phpcs:disable | |
| 262 | + error_log( 'process_donation not valid' ); | |
| 263 | + error_log( print_r( $gateway, true ) ); | |
| 264 | + error_log( print_r( $values, true ) ); | |
| 265 | + // phpcs:enable | |
| 266 | + } | |
| 210 | 267 | return false; |
| 211 | 268 | } |
| 212 | 269 | |
| 213 | 270 | $this->donation_id = $processor->save_donation( $values ); |
| @@ -243,10 +300,20 @@ | ||
| 243 | 300 | * @param int $donation_id The donation ID. |
| 244 | 301 | * @param Charitable_Donation_Processor $processor The Donation Processor object. |
| 245 | 302 | * @return mixed |
| 246 | 303 | */ |
| 247 | - return apply_filters( 'charitable_process_donation_' . $gateway, true, $this->donation_id, $processor ); | |
| 304 | + if ( charitable_is_debug() ) { | |
| 305 | + // phpcs:disable | |
| 306 | + error_log( 'This Gateway is Charitable version 1.3.0 compatible... The gateway is ' . $gateway ); | |
| 307 | + error_log( 'The donation id is ' . $this->donation_id ); | |
| 308 | + error_log( 'Charitable_Donation_Processor Object is: [commented out] ' ); | |
| 309 | + // phpcs:enable | |
| 310 | + } | |
| 248 | 311 | |
| 312 | + $hook_name = 'charitable_process_donation_' . $gateway; | |
| 313 | + | |
| 314 | + return apply_filters( $hook_name, true, $this->donation_id, $processor ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Hook name is dynamically constructed with the plugin prefix 'charitable_process_donation_'. | |
| 315 | + | |
| 249 | 316 | } else { |
| 250 | 317 | /** |
| 251 | 318 | * Send donation processing to the payment gateway. |
| 252 | 319 | * |
| @@ -285,23 +352,28 @@ | ||
| 285 | 352 | |
| 286 | 353 | /** |
| 287 | 354 | * Add a donation with AJAX. |
| 288 | 355 | * |
| 289 | - * @since 1.3.0 | |
| 356 | + * @since 1.3.0 | |
| 357 | + * @version 1.8.7 | |
| 290 | 358 | * |
| 291 | 359 | * @return void |
| 292 | 360 | */ |
| 293 | 361 | public static function ajax_process_donation_form_submission() { |
| 294 | 362 | /* Prevent any displayed errors from messing with the AJAX response. */ |
| 295 | - ini_set( 'display_errors', false ); | |
| 363 | + if ( charitable_is_debug() ) { // phpcs:ignore | |
| 364 | + // do nothing, allow php/server settings to determine php logging. | |
| 365 | + } else { | |
| 366 | + ini_set( 'display_errors', 0 ); // phpcs:ignore | |
| 367 | + } | |
| 296 | 368 | |
| 297 | - if ( ! isset( $_POST['campaign_id'] ) ) { | |
| 369 | + if ( ! isset( $_POST['campaign_id'] ) ) { // @codingStandardsIgnoreLine | |
| 298 | 370 | wp_send_json_error( new WP_Error( 'missing_campaign_id', __( 'Campaign ID was not found. Unable to create donation.', 'charitable' ) ) ); |
| 299 | 371 | } |
| 300 | 372 | |
| 301 | 373 | $processor = self::get_instance(); |
| 374 | + $result = $processor->process_donation(); | |
| 302 | 375 | |
| 303 | - $result = $processor->process_donation(); | |
| 304 | 376 | $response = is_array( $result ) ? $result : array(); |
| 305 | 377 | |
| 306 | 378 | if ( $processor->donation_processed_successfully( $result ) ) { |
| 307 | 379 | $response['success'] = true; |
| @@ -306,19 +378,70 @@ | ||
| 306 | 378 | if ( $processor->donation_processed_successfully( $result ) ) { |
| 307 | 379 | $response['success'] = true; |
| 308 | 380 | $response['redirect_to'] = $processor->get_redirection_after_gateway_processing( $result ); |
| 309 | 381 | } else { |
| 382 | + | |
| 383 | + // Start with getting errors from the notices class. | |
| 310 | 384 | $errors = charitable_get_notices()->get_errors(); |
| 385 | + // If there is no errors, set the errors to an empty array. | |
| 386 | + if ( empty( $errors ) || ! is_array( $errors ) ) { | |
| 387 | + $errors = array(); | |
| 388 | + } | |
| 311 | 389 | |
| 390 | + // Check and see if there are errors in the response too. | |
| 391 | + if ( ! empty( $response['errors'] ) ) { | |
| 392 | + $errors = array_merge( $errors, $response['errors'] ); | |
| 393 | + } | |
| 394 | + | |
| 312 | 395 | if ( empty( $errors ) ) { |
| 313 | 396 | $errors = array( __( 'Unable to process donation.', 'charitable' ) ); |
| 314 | 397 | } |
| 315 | 398 | |
| 399 | + // Log the AJAX processing failure | |
| 400 | + $context = array( | |
| 401 | + 'campaign_id' => $processor->get_donation_data_value( 'campaign_id' ), | |
| 402 | + 'amount' => $processor->get_donation_data_value( 'donation_amount' ), | |
| 403 | + 'gateway' => $processor->get_donation_data_value( 'gateway' ), | |
| 404 | + 'donation_id' => (int) $processor->get_donation_id(), | |
| 405 | + ); | |
| 406 | + charitable_log_form_error( 'ajax_failure', 'donation_processing_failed', $context ); | |
| 407 | + | |
| 316 | 408 | $response['success'] = false; |
| 317 | 409 | $response['errors'] = $errors; |
| 318 | 410 | $response['donation_id'] = (int) $processor->get_donation_id(); |
| 411 | + | |
| 412 | + // get the gateway from the processor. | |
| 413 | + $gateway = $processor->get_donation_data_value( 'gateway' ); | |
| 414 | + | |
| 415 | + // If the gateway is square, we need to add to add errors to the donation log. | |
| 416 | + if ( 'square' === $gateway ) { | |
| 417 | + // If we are in test mode, remove the first error because that reads "Test mode is enabled". | |
| 418 | + if ( charitable_get_option( 'test_mode' ) ) { | |
| 419 | + // If there is at least one element, check the first one for a value that contains "Test mode is enabled". | |
| 420 | + if ( count( $errors ) > 0 ) { | |
| 421 | + if ( strpos( $errors[0], 'Test mode is enabled' ) !== false ) { | |
| 422 | + $errors = array_slice( $errors, 1 ); | |
| 423 | + } | |
| 424 | + } | |
| 425 | + } | |
| 426 | + $errors = implode( ', ', $errors ); | |
| 427 | + $processor->update_donation_log( $processor->get_donation_id(), 'Payment attempt failed.' ); | |
| 428 | + } | |
| 319 | 429 | } |
| 320 | 430 | |
| 431 | + if ( charitable_is_debug() ) { | |
| 432 | + // phpcs:disable | |
| 433 | + error_log( 'ajax_process_donation_form_submission - response' ); | |
| 434 | + error_log( print_r( $response, true ) ); | |
| 435 | + // phpcs:enable | |
| 436 | + } | |
| 437 | + | |
| 438 | + // Experimenting with a different response structure. | |
| 439 | + // These are errors displayed to the user (and hopefully not loggeD). | |
| 440 | + if ( ! empty( $response['errors'] ) ) { | |
| 441 | + // Insert an element into the first position of the array. | |
| 442 | + $response['errors'] = array_merge( array( 'An error occurred while processing your donation.' ), $response['errors'] ); | |
| 443 | + } | |
| 321 | 444 | wp_send_json( $response ); |
| 322 | 445 | |
| 323 | 446 | exit(); |
| 324 | 447 | } |
| @@ -341,8 +464,10 @@ | ||
| 341 | 464 | /* Validate the form submission and retrieve the values. */ |
| 342 | 465 | $form = new Charitable_Donation_Amount_Form( $campaign ); |
| 343 | 466 | |
| 344 | 467 | /** |
| 468 | + * Do something before the donation amount form is processed. | |
| 469 | + * | |
| 345 | 470 | * @hook charitable_before_process_donation_amount_form |
| 346 | 471 | */ |
| 347 | 472 | do_action( 'charitable_before_process_donation_amount_form', $processor, $form ); |
| 348 | 473 | |
| @@ -355,8 +480,10 @@ | ||
| 355 | 480 | |
| 356 | 481 | charitable_get_session()->add_donation( $submitted['campaign_id'], $submitted['amount'], $period ); |
| 357 | 482 | |
| 358 | 483 | /** |
| 484 | + * Do something after the donation amount form is processed. | |
| 485 | + * | |
| 359 | 486 | * @hook charitable_after_process_donation_amount_form |
| 360 | 487 | */ |
| 361 | 488 | do_action( 'charitable_after_process_donation_amount_form', $processor, $submitted ); |
| 362 | 489 | } |
| @@ -362,9 +489,9 @@ | ||
| 362 | 489 | } |
| 363 | 490 | |
| 364 | 491 | $redirect_url = charitable_get_permalink( 'campaign_donation_page', array( 'campaign_id' => $campaign->ID ) ); |
| 365 | 492 | |
| 366 | - if ( 'same_page' == charitable_get_option( 'donation_form_display', 'separate_page' ) ) { | |
| 493 | + if ( 'same_page' == charitable_get_option( 'donation_form_display', 'separate_page' ) ) { // phpcs:ignore | |
| 367 | 494 | $redirect_url .= '#charitable-donation-form'; |
| 368 | 495 | } |
| 369 | 496 | |
| 370 | 497 | wp_safe_redirect( $redirect_url ); |
| @@ -445,9 +572,9 @@ | ||
| 445 | 572 | charitable_get_notices()->add_errors_from_wp_error( $donation_id ); |
| 446 | 573 | return 0; |
| 447 | 574 | } |
| 448 | 575 | |
| 449 | - if ( 0 == $donation_id ) { | |
| 576 | + if ( 0 == $donation_id ) { // phpcs:ignore | |
| 450 | 577 | charitable_get_notices()->add_error( __( 'We were unable to save the donation. Please try again.', 'charitable' ) ); |
| 451 | 578 | return 0; |
| 452 | 579 | } |
| 453 | 580 | |
| @@ -521,9 +648,9 @@ | ||
| 521 | 648 | |
| 522 | 649 | if ( array_key_exists( 'campaign_donation_id', $campaign ) ) { |
| 523 | 650 | $campaign_donation_id = $campaign['campaign_donation_id']; |
| 524 | 651 | } else { |
| 525 | - $campaign_donation_id = array_search( $campaign['campaign_id'], $campaign_donations ); | |
| 652 | + $campaign_donation_id = array_search( $campaign['campaign_id'], $campaign_donations ); // phpcs:ignore | |
| 526 | 653 | } |
| 527 | 654 | |
| 528 | 655 | /* Avoid adding duplicate campaign donations when re-submitting a campaign. */ |
| 529 | 656 | if ( false !== $campaign_donation_id ) { |
| @@ -546,9 +673,9 @@ | ||
| 546 | 673 | $campaign['donor_id'] = $donor_id; |
| 547 | 674 | $campaign['donation_id'] = $donation_id; |
| 548 | 675 | $campaign_donation_id = charitable_get_table( 'campaign_donations' )->insert( $campaign ); |
| 549 | 676 | |
| 550 | - if ( 0 == $campaign_donation_id ) { | |
| 677 | + if ( 0 == $campaign_donation_id ) { // phpcs:ignore | |
| 551 | 678 | return 0; |
| 552 | 679 | } |
| 553 | 680 | } |
| 554 | 681 | }//end foreach |
| @@ -662,9 +789,9 @@ | ||
| 662 | 789 | * @param string $key The key to search for. |
| 663 | 790 | * @param mixed $default Fallback value to return if the data is not set. |
| 664 | 791 | * @return mixed |
| 665 | 792 | */ |
| 666 | - public function get_donation_data_value( $key, $default = false ) { | |
| 793 | + public function get_donation_data_value( $key, $default = false ) { // phpcs:ignore | |
| 667 | 794 | $data = $this->get_donation_data(); |
| 668 | 795 | return isset( $data[ $key ] ) ? $data[ $key ] : $default; |
| 669 | 796 | } |
| 670 | 797 | |
| @@ -736,9 +863,9 @@ | ||
| 736 | 863 | $this->donor_id = charitable_get_table( 'donors' )->get_donor_id_by_email( $user_data['email'] ); |
| 737 | 864 | } |
| 738 | 865 | |
| 739 | 866 | /* If we still do not have a donor ID, it means that this is a first-time donor */ |
| 740 | - if ( 0 == $this->donor_id ) { | |
| 867 | + if ( 0 == $this->donor_id ) { // phpcs:ignore | |
| 741 | 868 | $user = new Charitable_User( $this->get_donation_data_value( 'user_id', get_current_user_id() ) ); |
| 742 | 869 | $this->donor_id = $user->add_donor( $user_data ); |
| 743 | 870 | } |
| 744 | 871 | }//end if |
| @@ -805,9 +932,9 @@ | ||
| 805 | 932 | private function redirect_after_gateway_processing( $gateway_processing ) { |
| 806 | 933 | $redirect_url = $this->get_redirection_after_gateway_processing( $gateway_processing ); |
| 807 | 934 | |
| 808 | 935 | /* If the gateway processing failed, add the error notices to the session. */ |
| 809 | - if ( false == $gateway_processing ) { | |
| 936 | + if ( false == $gateway_processing ) { // phpcs:ignore | |
| 810 | 937 | |
| 811 | 938 | /* Log the failed payment. */ |
| 812 | 939 | $this->update_donation_log( |
| 813 | 940 | $this->donation_id, |
| @@ -827,10 +954,10 @@ | ||
| 827 | 954 | /** |
| 828 | 955 | * If the gateway processing returned an array with a directive to NOT |
| 829 | 956 | * use wp_safe_redirect, use wp_redirect instead. |
| 830 | 957 | */ |
| 831 | - if ( isset( $gateway_processing['safe'] ) && false == $gateway_processing['safe'] ) { | |
| 832 | - wp_redirect( $redirect_url, $status ); | |
| 958 | + if ( isset( $gateway_processing['safe'] ) && false == $gateway_processing['safe'] ) { // phpcs:ignore | |
| 959 | + wp_safe_redirect( $redirect_url, $status ); | |
| 833 | 960 | die(); |
| 834 | 961 | } |
| 835 | 962 | |
| 836 | 963 | wp_safe_redirect( $redirect_url, $status ); |
| @@ -846,9 +973,9 @@ | ||
| 846 | 973 | * @param mixed $gateway_processing The result of the gateway processing. |
| 847 | 974 | * @return string |
| 848 | 975 | */ |
| 849 | 976 | private function get_redirection_after_gateway_processing( $gateway_processing ) { |
| 850 | - if ( false == $gateway_processing ) { | |
| 977 | + if ( false == $gateway_processing ) { // phpcs:ignore | |
| 851 | 978 | $redirect_url = esc_url( add_query_arg( array( 'donation_id' => $this->donation_id ), wp_get_referer() ) ); |
| 852 | 979 | } elseif ( is_array( $gateway_processing ) && isset( $gateway_processing['redirect'] ) ) { |
| 853 | 980 | $redirect_url = $gateway_processing['redirect']; |
| 854 | 981 | } else { |
| @@ -855,9 +982,9 @@ | ||
| 855 | 982 | /* Fall back to returning the donation receipt URL. */ |
| 856 | 983 | $redirect_url = charitable_get_permalink( 'donation_receipt_page', array( 'donation_id' => $this->donation_id ) ); |
| 857 | 984 | } |
| 858 | 985 | |
| 859 | - return $redirect_url; | |
| 986 | + return apply_filters( 'charitable_redirection_after_gateway_processing', $redirect_url, $gateway_processing, $this ); | |
| 860 | 987 | } |
| 861 | 988 | |
| 862 | 989 | /** |
| 863 | 990 | * Validate user data passed to insert. |