| @@ -29,8 +29,24 @@ | ||
| 29 | 29 | * |
| 30 | 30 | * @since 1.2.0 |
| 31 | 31 | */ |
| 32 | 32 | class Privacy_Data { |
| 33 | + /** | |
| 34 | + * The donation_data keys that hold the donor's own personal data and are | |
| 35 | + * removed on erasure. Everything else in the column is an operational | |
| 36 | + * record (refunds, notes, subscription metadata) and is written back. | |
| 37 | + * | |
| 38 | + * `gift_aid` is written by SureDonation Pro: a UK Gift Aid declaration | |
| 39 | + * carries the donor's legal name and home address. It is named here, in | |
| 40 | + * free, because an eraser that ran only while Pro was active would report | |
| 41 | + * a completed erasure with that address still on the row the moment Pro | |
| 42 | + * was deactivated, and the anonymised email would then hide the row from | |
| 43 | + * every later request. | |
| 44 | + * | |
| 45 | + * @since 1.6.1 | |
| 46 | + */ | |
| 47 | + public const ERASED_DONATION_DATA_KEYS = [ 'fields', 'gift_aid' ]; | |
| 48 | + | |
| 33 | 49 | use Get_Instance; |
| 34 | 50 | |
| 35 | 51 | /** |
| 36 | 52 | * Constructor — register the exporter + eraser with WordPress. |
| @@ -155,8 +171,27 @@ | ||
| 155 | 171 | } |
| 156 | 172 | $rows[ $unique ] = $field['value'] ?? ''; |
| 157 | 173 | } |
| 158 | 174 | |
| 175 | + // The Gift Aid declaration (SureDonation Pro) is the record actually | |
| 176 | + // filed with HMRC and holds the corrected name and address; the | |
| 177 | + // submitted fields above keep whatever the donor first typed. | |
| 178 | + $gift_aid = isset( $donation['donation_data']['gift_aid'] ) && is_array( $donation['donation_data']['gift_aid'] ) ? $donation['donation_data']['gift_aid'] : []; | |
| 179 | + if ( [] !== $gift_aid ) { | |
| 180 | + $rows[ __( 'Gift Aid Declared', 'suredonation' ) ] = true === ( $gift_aid['declared'] ?? false ) ? __( 'Yes', 'suredonation' ) : __( 'No', 'suredonation' ); | |
| 181 | + foreach ( [ | |
| 182 | + 'first_name' => __( 'Gift Aid First Name', 'suredonation' ), | |
| 183 | + 'last_name' => __( 'Gift Aid Last Name', 'suredonation' ), | |
| 184 | + 'house' => __( 'Gift Aid House Name or Number', 'suredonation' ), | |
| 185 | + 'postcode' => __( 'Gift Aid Postcode', 'suredonation' ), | |
| 186 | + 'country' => __( 'Gift Aid Country', 'suredonation' ), | |
| 187 | + ] as $key => $label ) { | |
| 188 | + if ( isset( $gift_aid[ $key ] ) && is_scalar( $gift_aid[ $key ] ) ) { | |
| 189 | + $rows[ $label ] = (string) $gift_aid[ $key ]; | |
| 190 | + } | |
| 191 | + } | |
| 192 | + } | |
| 193 | + | |
| 159 | 194 | $export[] = [ |
| 160 | 195 | 'group_id' => 'suredonation-donations', |
| 161 | 196 | 'group_label' => __( 'SureDonation Donations', 'suredonation' ), |
| 162 | 197 | 'item_id' => 'suredonation-donation-' . absint( Helper::get_string_value( $donation['id'] ?? 0 ) ), |
| @@ -219,9 +254,11 @@ | ||
| 219 | 254 | |
| 220 | 255 | // Strip the personal-data keys from donation_data, keep the rest |
| 221 | 256 | // (e.g. refunds/notes are operational records, not donor PII). |
| 222 | 257 | $donation_data = isset( $donation['donation_data'] ) && is_array( $donation['donation_data'] ) ? $donation['donation_data'] : []; |
| 223 | - unset( $donation_data['fields'] ); | |
| 258 | + foreach ( self::ERASED_DONATION_DATA_KEYS as $key ) { | |
| 259 | + unset( $donation_data[ $key ] ); | |
| 260 | + } | |
| 224 | 261 | |
| 225 | 262 | // The receipt PDF is generated from the donor's name/email/address — |
| 226 | 263 | // erasure must remove the file from disk, not just the DB columns. |
| 227 | 264 | $receipt_deleted = Receipt_Generator::delete_receipt( Helper::get_string_value( $donation['receipt_pdf_url'] ?? '' ) ); |
| @@ -247,10 +284,13 @@ | ||
| 247 | 284 | 'parent_subscription_id' => 0, |
| 248 | 285 | 'log' => '', |
| 249 | 286 | ]; |
| 250 | 287 | |
| 251 | - // Clear the receipt pointer only when the file is actually gone — | |
| 252 | - // otherwise keep it so a retried erasure can still find the file. | |
| 288 | + // Clear the receipt pointer once nothing further will be done with | |
| 289 | + // it — otherwise keep it so a retried erasure can still find the | |
| 290 | + // file. A pointer refused by containment also reports true and is | |
| 291 | + // dropped deliberately: no caller can act on it, so retaining it | |
| 292 | + // would fail the erasure forever with no remedy for the admin. | |
| 253 | 293 | if ( $receipt_deleted ) { |
| 254 | 294 | $anonymized['receipt_pdf_url'] = ''; |
| 255 | 295 | } |
| 256 | 296 | |