PluginProbe
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management / 1.1.0
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management v1.1.0
1.6.1 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
← All changes | inc/api/onboarding-api.php +16 -122 1.4.0 → 1.1.0 View file →
@@ -4,9 +4,9 @@
4 4 *
5 5 * Routes (under `suredonation/v1`):
6 6 * - GET /onboarding/get-status — return { completed: 'yes'|'no' }
7 7 * - POST /onboarding/set-status — write completion + optional analytics
8 - * - POST /onboarding/create-campaign — create a published suredonation_cmpgn
8 + * - POST /onboarding/create-campaign — create a draft suredonation_cmpgn
9 9 * - POST /onboarding/user-details — persist lead capture (free-only step)
10 10 *
11 11 * @package SureDonation
12 12 */
@@ -70,40 +70,15 @@
70 70 ];
71 71 }
72 72
73 73 /**
74 - * Permission gate. Write requests (POST/PUT/PATCH/DELETE) additionally
75 - * require a valid wp_rest nonce, matching Donors_API — onboarding forwards a
76 - * lead to the BSF CRM, so the write boundary is pinned explicitly.
74 + * Permission gate.
77 75 *
78 - * @param \WP_REST_Request<array<string,mixed>>|null $request Current request.
79 - * @return bool|\WP_Error
76 + * @return bool
80 77 * @since 1.0.0
81 78 */
82 - public function check_permissions( $request = null ) {
83 - if ( ! current_user_can( 'manage_options' ) ) {
84 - return false;
85 - }
86 -
87 - if ( $request instanceof \WP_REST_Request ) {
88 - $method = strtoupper( $request->get_method() );
89 - if ( in_array( $method, [ 'POST', 'PUT', 'PATCH', 'DELETE' ], true ) ) {
90 - $nonce = $request->get_header( 'X-WP-Nonce' );
91 - if ( empty( $nonce ) ) {
92 - $nonce_param = $request->get_param( '_wpnonce' );
93 - $nonce = is_string( $nonce_param ) ? $nonce_param : '';
94 - }
95 - if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
96 - return new \WP_Error(
97 - 'rest_forbidden',
98 - __( 'Invalid or missing nonce.', 'suredonation' ),
99 - [ 'status' => 403 ]
100 - );
101 - }
102 - }
103 - }
104 -
105 - return true;
79 + public function check_permissions() {
80 + return current_user_can( 'manage_options' );
106 81 }
107 82
108 83 /**
109 84 * GET /onboarding/get-status.
@@ -135,9 +110,9 @@
135 110
136 111 /**
137 112 * POST /onboarding/create-campaign.
138 113 *
139 - * Creates a published campaign post + writes its meta. Returns the new
114 + * Creates a draft campaign post + writes its meta. Returns the new
140 115 * campaign id + edit URL so the JS can persist it in onboarding state.
141 116 *
142 117 * @param WP_REST_Request $request Request.
143 118 * @return WP_REST_Response|WP_Error
@@ -169,16 +144,12 @@
169 144 if ( ! in_array( $goal_type, self::GOAL_TYPES, true ) ) {
170 145 $goal_type = 'raised_amount';
171 146 }
172 147
173 - // Publish the campaign so it behaves like one created via the normal
174 - // flow: the save_post_suredonation_cmpgn hook auto-creates its default
175 - // donation form, and the campaign becomes selectable in the Donation
176 - // Form block (whose query is limited to published campaigns).
177 148 $result = wp_insert_post(
178 149 [
179 150 'post_type' => Campaign_Cpt::POST_TYPE,
180 - 'post_status' => 'publish',
151 + 'post_status' => 'draft',
181 152 'post_title' => $name,
182 153 'post_excerpt' => $description,
183 154 'post_author' => get_current_user_id(),
184 155 ],
@@ -230,10 +201,8 @@
230 201 * @return WP_REST_Response
231 202 * @since 1.0.0
232 203 */
233 204 public function save_user_details( $request ) {
234 - $onboarding = Onboarding::get_instance();
235 -
236 205 $payload = [
237 206 'first_name' => sanitize_text_field( (string) $request->get_param( 'first_name' ) ),
238 207 'last_name' => sanitize_text_field( (string) $request->get_param( 'last_name' ) ),
239 208 'email' => sanitize_email( (string) $request->get_param( 'email' ) ),
@@ -239,22 +208,26 @@
239 208 'email' => sanitize_email( (string) $request->get_param( 'email' ) ),
240 209 'opted_in' => (bool) $request->get_param( 'opted_in' ),
241 210 ];
242 211
243 - $onboarding->set_user_details( $payload );
212 + Onboarding::get_instance()->set_user_details( $payload );
244 213
214 + // Persist the usage-tracking opt-in as its own option so other
215 + // plugin code (analytics, telemetry pings) can check it without
216 + // loading the consolidated onboarding details. Site option — the
217 + // BSF Analytics library reads it via get_site_option(), so the
218 + // write must use the same scope to stay in sync on multisite.
245 219 update_site_option(
246 220 'suredonation_usage_optin',
247 221 $payload['opted_in'] ? 'yes' : 'no'
248 222 );
249 223
250 - if ( ! $onboarding->is_lead_sent() && $this->forward_lead_to_crm( $payload ) ) {
251 - $onboarding->mark_lead_sent();
252 - }
253 -
254 224 /**
255 225 * Fires after onboarding lead-capture details are persisted.
256 226 *
227 + * Listeners (e.g. Pro analytics) can forward the payload to a
228 + * metrics endpoint when `opted_in` is true.
229 + *
257 230 * @since 1.0.0
258 231 *
259 232 * @param array<string,mixed> $payload Sanitised payload.
260 233 */
@@ -260,85 +233,6 @@
260 233 */
261 234 do_action( 'suredonation_onboarding_user_details_saved', $payload );
262 235
263 236 return new WP_REST_Response( [ 'success' => true ] );
264 - }
265 -
266 - /**
267 - * Generate lead.
268 - *
269 - * @param array<string,mixed> $payload Sanitised lead-capture payload.
270 - * @return bool True when the CRM accepted the lead, false otherwise.
271 - * @since 1.1.2
272 - */
273 - private function forward_lead_to_crm( array $payload ) {
274 - $email_raw = $payload['email'] ?? '';
275 - $email = is_string( $email_raw ) ? sanitize_email( $email_raw ) : '';
276 - if ( empty( $email ) || ! is_email( $email ) ) {
277 - return false;
278 - }
279 -
280 - $url = 'https://metrics.brainstormforce.com/wp-json/bsf-metrics-server/v1/subscribe';
281 -
282 - if ( defined( 'SUREDONATION_METRICS_ENDPOINT' ) && is_string( SUREDONATION_METRICS_ENDPOINT ) ) {
283 - $url = SUREDONATION_METRICS_ENDPOINT;
284 - }
285 -
286 - /**
287 - * Filters the endpoint.
288 - *
289 - * @since 1.1.2
290 - *
291 - * @param string $url Endpoint URL.
292 - * @param array<string,mixed> $payload Lead payload being sent.
293 - */
294 - $filtered = apply_filters( 'suredonation_metrics_subscribe_url', $url, $payload );
295 - $url = is_string( $filtered ) ? $filtered : $url;
296 -
297 - if ( '' === $url ) {
298 - return false;
299 - }
300 -
301 - $first_name = isset( $payload['first_name'] ) && is_string( $payload['first_name'] ) ? $payload['first_name'] : '';
302 - $last_name = isset( $payload['last_name'] ) && is_string( $payload['last_name'] ) ? $payload['last_name'] : '';
303 - $domain = wp_parse_url( home_url(), PHP_URL_HOST );
304 - $domain = is_string( $domain ) ? $domain : '';
305 -
306 - $body = wp_json_encode(
307 - [
308 - // Lowercase keys satisfy the current BSF Metrics REST args.
309 - 'email' => $email,
310 - 'first_name' => $first_name,
311 - 'last_name' => $last_name,
312 - 'domain' => $domain,
313 - 'source' => 'suredonation',
314 - // Legacy uppercase keys kept for backward compatibility.
315 - 'EMAIL' => $email,
316 - 'FIRSTNAME' => $first_name,
317 - 'LASTNAME' => $last_name,
318 - 'DOMAIN' => $domain,
319 - ]
320 - );
321 -
322 - if ( false === $body ) {
323 - return false;
324 - }
325 -
326 - // `source` identifies the originating plugin on the shared CRM server.
327 - // wp_safe_remote_post with WP's default 5s timeout keeps a slow or
328 - // hung endpoint from stalling onboarding completion.
329 - $response = wp_safe_remote_post(
330 - $url,
331 - [
332 - 'headers' => [ 'Content-Type' => 'application/json' ],
333 - 'body' => $body,
334 - ]
335 - );
336 -
337 - if ( is_wp_error( $response ) ) {
338 - return false;
339 - }
340 -
341 - $code = (int) wp_remote_retrieve_response_code( $response );
342 - return in_array( $code, [ 200, 201, 204 ], true );
343 237 }
344 238 }