PluginProbe
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management / 1.1.0
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management v1.1.0
1.6.0 1.5.1 1.5.0 1.4.0 1.3.0 trunk 0.0.1 1.0.0 1.1.0 1.1.1 1.1.2 1.2.0
suredonation / inc / payments / offline / offline-frontend.php

offline-frontend.php in SureDonation – Donation Forms, Fundraising Campaigns & Donor Management 1.1.0, at inc/payments/offline/offline-frontend.php

190 lines 6.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Offline Frontend - AJAX handler for offline donations
4 *
5 * @package SureDonation
6 */
7
8 namespace SureDonation\Inc\Payments\Offline;
9
10 use SureDonation\Inc\Database\Tables\Donations;
11 use SureDonation\Inc\Database\Tables\Donors;
12 use SureDonation\Inc\Emails\Email_Handler;
13 use SureDonation\Inc\Helper;
14 use SureDonation\Inc\Payments\Payment_Helper;
15 use SureDonation\Inc\Traits\Get_Instance;
16
17 // Exit if accessed directly.
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit;
20 }
21
22 /**
23 * Offline_Frontend class
24 * Handles frontend offline donation processing
25 *
26 * @since 1.0.0
27 */
28 class Offline_Frontend {
29 use Get_Instance;
30
31 /**
32 * Constructor
33 *
34 * @since 1.0.0
35 */
36 public function __construct() {
37 // AJAX handlers for both logged in and non-logged in users.
38 add_action( 'wp_ajax_suredonation_create_offline_donation', [ $this, 'create_offline_donation' ] );
39 add_action( 'wp_ajax_nopriv_suredonation_create_offline_donation', [ $this, 'create_offline_donation' ] );
40 }
41
42 /**
43 * Create offline donation via AJAX.
44 *
45 * @return void
46 * @since 1.0.0
47 */
48 public function create_offline_donation() {
49 // Throttle abuse on this public endpoint before doing any work.
50 if ( ! Helper::check_rate_limit( 'offline_create_donation' ) ) {
51 wp_send_json_error( [ 'message' => __( 'Too many requests. Please wait a moment and try again.', 'suredonation' ) ], 429 );
52 }
53
54 check_ajax_referer( 'suredonation_donation_form', 'nonce' );
55
56 // Reject bot submissions caught by the honeypot before processing.
57 if ( Helper::is_honeypot_spam() ) {
58 wp_send_json_error( [ 'message' => __( 'Your submission was flagged as spam. Please try again.', 'suredonation' ) ] );
59 }
60
61 // Verify offline donations are enabled.
62 if ( ! Offline_Helper::is_offline_enabled() ) {
63 wp_send_json_error( [ 'message' => __( 'Offline donations are not enabled.', 'suredonation' ) ] );
64 }
65
66 // Extract and sanitize form data.
67 // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified above.
68 $is_standalone = isset( $_POST['is_standalone'] ) && '1' === $_POST['is_standalone'];
69 $campaign_id = isset( $_POST['campaign_id'] ) ? absint( $_POST['campaign_id'] ) : 0;
70 $amount = isset( $_POST['amount'] ) ? floatval( wp_unslash( $_POST['amount'] ) ) : 0;
71 $donor_email = isset( $_POST['donor_email'] ) ? sanitize_email( wp_unslash( $_POST['donor_email'] ) ) : '';
72 $donor_name = isset( $_POST['donor_name'] ) ? sanitize_text_field( wp_unslash( $_POST['donor_name'] ) ) : '';
73 $form_id = isset( $_POST['form_id'] ) ? absint( $_POST['form_id'] ) : 0;
74 $block_id = isset( $_POST['block_id'] ) ? sanitize_text_field( wp_unslash( $_POST['block_id'] ) ) : '';
75 // phpcs:enable WordPress.Security.NonceVerification.Missing
76
77 // Standalone forms must not have a campaign.
78 if ( $is_standalone ) {
79 $campaign_id = 0;
80 }
81
82 // Validate required fields — campaign only required for non-standalone forms.
83 if ( ! $is_standalone && empty( $campaign_id ) ) {
84 wp_send_json_error( [ 'message' => __( 'Invalid campaign.', 'suredonation' ) ] );
85 }
86
87 if ( $amount <= 0 ) {
88 wp_send_json_error( [ 'message' => __( 'Invalid donation amount.', 'suredonation' ) ] );
89 }
90
91 if ( empty( $donor_email ) ) {
92 wp_send_json_error( [ 'message' => __( 'Email address is required.', 'suredonation' ) ] );
93 }
94
95 // Validate campaign only if not standalone.
96 if ( ! $is_standalone ) {
97 $campaign = get_post( $campaign_id );
98 if ( ! $campaign || SUREDONATION_POST_TYPE !== $campaign->post_type ) {
99 wp_send_json_error( [ 'message' => __( 'Invalid campaign.', 'suredonation' ) ] );
100 }
101
102 if ( 'publish' !== $campaign->post_status ) {
103 wp_send_json_error( [ 'message' => __( 'This campaign is not available for donations.', 'suredonation' ) ] );
104 }
105
106 $campaign_status = Helper::get_campaign_meta_value( $campaign_id, 'campaign_status', 'active' );
107 if ( 'paused' === $campaign_status || 'completed' === $campaign_status ) {
108 wp_send_json_error( [ 'message' => __( 'This campaign is not currently accepting donations.', 'suredonation' ) ] );
109 }
110 }
111
112 // Validate form_id and block_id are present for amount validation.
113 if ( empty( $form_id ) || empty( $block_id ) ) {
114 wp_send_json_error( [ 'message' => __( 'Invalid form configuration.', 'suredonation' ) ] );
115 }
116
117 // Validate field values + amount against block config (skip Stripe minimum for offline).
118 $currency = Payment_Helper::get_currency();
119 $validation_result = Payment_Helper::validate_submission( Payment_Helper::get_submitted_fields(), $amount, $currency, $form_id, $block_id, 'offline' );
120 if ( ! $validation_result['valid'] ) {
121 wp_send_json_error(
122 [
123 'message' => esc_html( $validation_result['message'] ),
124 'fieldErrors' => $validation_result['field_errors'],
125 ]
126 );
127 }
128
129 // Get or create donor.
130 $donor_id = Donors::get_or_create( $donor_email, $donor_name );
131
132 // Create donation record.
133 $donation_id = Donations::add(
134 [
135 'campaign_id' => $campaign_id,
136 'donor_id' => $donor_id ? $donor_id : 0,
137 'amount' => $amount,
138 'fees_covered' => 0,
139 'currency' => $currency,
140 'gateway' => 'offline',
141 'payment_status' => 'pending',
142 'payment_mode' => Payment_Helper::get_payment_mode(),
143 'donor_name' => $donor_name,
144 'donor_email' => $donor_email,
145 'donation_type' => 'one-time',
146 'form_id' => $form_id,
147 'ip_address' => Helper::get_client_ip(),
148 'user_agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '',
149 'referer_url' => isset( $_SERVER['HTTP_REFERER'] ) ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : '',
150 ]
151 );
152
153 if ( ! $donation_id ) {
154 wp_send_json_error( [ 'message' => __( 'Failed to create donation record.', 'suredonation' ) ] );
155 }
156
157 // Add log entry.
158 Donations::add_log(
159 $donation_id,
160 'created',
161 __( 'Offline donation created — pending payment', 'suredonation' ),
162 [
163 'gateway' => 'offline',
164 ]
165 );
166
167 // Send donation processing email (offline donations are pending, not completed).
168 Email_Handler::send_donation_processing(
169 $donation_id,
170 $campaign_id,
171 [
172 'donor_name' => $donor_name,
173 'donor_email' => $donor_email,
174 'amount' => $amount,
175 'currency' => $currency,
176 'gateway' => 'offline',
177 'donation_type' => 'one-time',
178 ],
179 $form_id
180 );
181
182 wp_send_json_success(
183 [
184 'donationId' => $donation_id,
185 'message' => Helper::render_confirmation_message( $donation_id ),
186 ]
187 );
188 }
189 }
190