| @@ -310,8 +310,26 @@ | ||
| 310 | 310 | $result = $instance->use_insert( $data ); |
| 311 | 311 | |
| 312 | 312 | if ( $result ) { |
| 313 | 313 | Campaign_Stats::clear_cache( absint( Helper::get_string_value( $data['campaign_id'] ) ) ); |
| 314 | + | |
| 315 | + // Notify integration hooks (e.g. OttoKit) about the new donation. | |
| 316 | + // Imported rows carry an import_source and are skipped: migrating | |
| 317 | + // historical donations must not replay automations. | |
| 318 | + if ( empty( $data['import_source'] ) ) { | |
| 319 | + $donation_id = absint( $result ); | |
| 320 | + $donation = self::get( $donation_id ); | |
| 321 | + $donation = is_array( $donation ) ? $donation : []; | |
| 322 | + | |
| 323 | + /** | |
| 324 | + * Fires when a new donation record is created. | |
| 325 | + * | |
| 326 | + * @param int $donation_id Newly created donation ID. | |
| 327 | + * @param array<mixed> $donation Complete donation record. | |
| 328 | + * @since 1.1.0 | |
| 329 | + */ | |
| 330 | + do_action( 'suredonation_donation_created', $donation_id, $donation ); | |
| 331 | + } | |
| 314 | 332 | } |
| 315 | 333 | |
| 316 | 334 | return $result; |
| 317 | 335 | } |
| @@ -328,8 +346,17 @@ | ||
| 328 | 346 | if ( empty( $donation_id ) ) { |
| 329 | 347 | return false; |
| 330 | 348 | } |
| 331 | 349 | |
| 350 | + // Capture the current status before the write so integration hooks | |
| 351 | + // (e.g. OttoKit) can react to the actual status transition, not just | |
| 352 | + // the resulting value. | |
| 353 | + $old_status = ''; | |
| 354 | + if ( isset( $data['payment_status'] ) ) { | |
| 355 | + $existing = self::get( absint( $donation_id ) ); | |
| 356 | + $old_status = is_array( $existing ) ? Helper::get_string_value( $existing['payment_status'] ?? '' ) : ''; | |
| 357 | + } | |
| 358 | + | |
| 332 | 359 | // Set updated_at. |
| 333 | 360 | $data['updated_at'] = current_time( 'mysql' ); |
| 334 | 361 | |
| 335 | 362 | $updated = self::get_instance()->use_update( $data, [ 'id' => absint( $donation_id ) ] ); |
| @@ -339,8 +366,31 @@ | ||
| 339 | 366 | if ( $updated ) { |
| 340 | 367 | $donation = self::get( absint( $donation_id ) ); |
| 341 | 368 | if ( ! empty( $donation['campaign_id'] ) ) { |
| 342 | 369 | Campaign_Stats::clear_cache( absint( Helper::get_string_value( $donation['campaign_id'] ) ) ); |
| 370 | + } | |
| 371 | + | |
| 372 | + // Notify integration hooks about a genuine status transition. | |
| 373 | + // Fired from update() — the single choke point every status write | |
| 374 | + // passes through (update_status() delegates here, as do the payment | |
| 375 | + // frontends and webhooks) — so all transitions are caught. | |
| 376 | + if ( isset( $data['payment_status'] ) ) { | |
| 377 | + $new_status = Helper::get_string_value( $data['payment_status'] ); | |
| 378 | + | |
| 379 | + if ( $new_status !== $old_status ) { | |
| 380 | + $donation = is_array( $donation ) ? $donation : []; | |
| 381 | + | |
| 382 | + /** | |
| 383 | + * Fires when a donation's payment status changes. | |
| 384 | + * | |
| 385 | + * @param int $donation_id Donation ID. | |
| 386 | + * @param string $new_status New payment status. | |
| 387 | + * @param string $old_status Previous payment status (empty string if unknown). | |
| 388 | + * @param array<mixed> $donation Complete donation record after the update. | |
| 389 | + * @since 1.1.0 | |
| 390 | + */ | |
| 391 | + do_action( 'suredonation_donation_status_changed', absint( $donation_id ), $new_status, $old_status, $donation ); | |
| 392 | + } | |
| 343 | 393 | } |
| 344 | 394 | } |
| 345 | 395 | |
| 346 | 396 | return $updated; |