| @@ -1,8 +1,9 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | namespace Better_Payment\Lite\Campaign; |
| 4 | 4 | |
| 5 | +use Better_Payment\Lite\Classes\Helper; | |
| 5 | 6 | use Better_Payment\Lite\Controller; |
| 6 | 7 | |
| 7 | 8 | if ( ! defined( 'ABSPATH' ) ) { |
| 8 | 9 | exit; |
| @@ -9,8 +10,25 @@ | ||
| 9 | 10 | } |
| 10 | 11 | |
| 11 | 12 | /** |
| 12 | 13 | * Computes and caches campaign stats (total raised, donor count, progress %). |
| 14 | + * | |
| 15 | + * `donor_count` is the count of approved donation *transactions*, not of unique | |
| 16 | + * donors — it must agree with the per-transaction list the Donors Wall renders | |
| 17 | + * (one card per approved row). Counting distinct emails made the summary read | |
| 18 | + * "3 Donors" above a 5-card list; a repeat donor (or a row with an empty email, | |
| 19 | + * which `COUNT(DISTINCT email)` drops entirely) is one transaction here. | |
| 20 | + * | |
| 21 | + * Which rows count, and what they add, is Helper::approved_donation_sql() and | |
| 22 | + * Helper::COUNTABLE_AMOUNT_SQL — the one definition every aggregation shares. | |
| 23 | + * | |
| 24 | + * A confirmed donation that charged nothing — a 100%-off promotion code, a subscription trial — | |
| 25 | + * is a donor too: Handler::confirm_stripe_session() stores it `paid` with its real amount, 0, so | |
| 26 | + * it adds one to `donor_count` and nothing to `total_raised`. Nothing here filters on the amount. | |
| 27 | + * Rows confirmed before 2.4.0 stored such a session as `no_payment_required` at the form's | |
| 28 | + * amount: one that was confirmed counts as a donor and adds 0 (its stored amount was never | |
| 29 | + * charged); one that never was (Stripe reports the same status for a free checkout at creation) | |
| 30 | + * is not counted at all. See Helper::LEGACY_CONFIRMATION_MARKER. | |
| 13 | 31 | */ |
| 14 | 32 | class CampaignStats extends Controller { |
| 15 | 33 | |
| 16 | 34 | private static int $cache_ttl = 2 * MINUTE_IN_SECONDS; |
| @@ -15,9 +33,9 @@ | ||
| 15 | 33 | |
| 16 | 34 | private static int $cache_ttl = 2 * MINUTE_IN_SECONDS; |
| 17 | 35 | |
| 18 | 36 | /** |
| 19 | - * Get stats for a campaign, using a 1-hour transient cache. | |
| 37 | + * Get stats for a campaign, using a 2-minute transient cache. | |
| 20 | 38 | */ |
| 21 | 39 | public static function get_stats( int $campaign_id, bool $use_cache = true ): array { |
| 22 | 40 | $transient_key = 'bpc_stats_' . $campaign_id; |
| 23 | 41 | |
| @@ -91,20 +109,22 @@ | ||
| 91 | 109 | |
| 92 | 110 | private static function query_stats( int $campaign_id ): array { |
| 93 | 111 | global $wpdb; |
| 94 | 112 | |
| 95 | - $payment_table = $wpdb->prefix . 'better_payment'; | |
| 96 | - $approved_statuses = "'" . implode( "','", array_map( 'esc_sql', self::approved_statuses() ) ) . "'"; | |
| 113 | + $payment_table = $wpdb->prefix . 'better_payment'; | |
| 114 | + $approved = Helper::approved_donation_sql(); | |
| 115 | + $amount = Helper::COUNTABLE_AMOUNT_SQL; | |
| 97 | 116 | |
| 117 | + // Both fragments are Helper constants (no request data, no `%`). | |
| 98 | 118 | $row = $wpdb->get_row( |
| 99 | 119 | $wpdb->prepare( |
| 100 | 120 | "SELECT |
| 101 | - COALESCE(SUM(amount), 0) AS total_raised, | |
| 102 | - COUNT(DISTINCT email) AS donor_count, | |
| 121 | + COALESCE(SUM({$amount}), 0) AS total_raised, | |
| 122 | + COUNT(*) AS donor_count, | |
| 103 | 123 | MAX(payment_date) AS last_donation_date |
| 104 | 124 | FROM `{$payment_table}` |
| 105 | 125 | WHERE campaign_id = %s |
| 106 | - AND status IN ({$approved_statuses})", | |
| 126 | + AND {$approved}", | |
| 107 | 127 | (string) $campaign_id |
| 108 | 128 | ), |
| 109 | 129 | ARRAY_A |
| 110 | 130 | ); |
| @@ -113,10 +133,6 @@ | ||
| 113 | 133 | 'total_raised' => (float) ( $row['total_raised'] ?? 0 ), |
| 114 | 134 | 'donor_count' => (int) ( $row['donor_count'] ?? 0 ), |
| 115 | 135 | 'last_donation_date' => $row['last_donation_date'] ?? null, |
| 116 | 136 | ]; |
| 117 | - } | |
| 118 | - | |
| 119 | - private static function approved_statuses(): array { | |
| 120 | - return [ 'Completed', 'paid', 'complete', 'succeeded' ]; | |
| 121 | 137 | } |
| 122 | 138 | } |