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/abilities/config-ability.php +167 -1153 1.6.0 → 1.1.0 View file →
@@ -9,9 +9,8 @@
9 9 */
10 10
11 11 namespace SureDonation\Inc\Abilities;
12 12
13 -use SureDonation\Inc\Database\Tables\Donations;
14 13 use SureDonation\Inc\Helper;
15 14
16 15 // Exit if accessed directly.
17 16 if ( ! defined( 'ABSPATH' ) ) {
@@ -24,22 +23,8 @@
24 23 * @since 0.0.1
25 24 */
26 25 class Config_Ability {
27 26 /**
28 - * Setting key gating abilities that create or modify records.
29 - *
30 - * @since 1.5.0
31 - */
32 - public const GATE_UPDATE = 'allow_updates';
33 -
34 - /**
35 - * Setting key gating abilities that destroy records.
36 - *
37 - * @since 1.5.0
38 - */
39 - public const GATE_DELETE = 'allow_delete';
40 -
41 - /**
42 27 * Cached abilities.
43 28 *
44 29 * @var array<string, array<string, mixed>>|null
45 30 */
@@ -45,52 +30,8 @@
45 30 */
46 31 private static $abilities = null;
47 32
48 33 /**
49 - * Whether a write/delete gate is open.
50 - *
51 - * These gates are opt-IN: an absent key is closed, exactly as an explicit
52 - * `false` is. That has to match two other places or the product lies to the
53 - * admin — `Settings_API::get_ai_settings()` reports an absent key as `false`
54 - * via its defaults, and the settings screen renders the switch off. An
55 - * earlier revision treated absent as OPEN for parity with SureForms, which
56 - * meant a site whose `ai_settings` was written programmatically with only
57 - * `enable_abilities` (WP-CLI, a migration, another plugin) enforced deletes
58 - * and gateway refunds as enabled while the UI stated they were disabled.
59 - *
60 - * SureForms can afford absent-means-open because its toggles are flat
61 - * options that always exist once its settings page has been saved. Ours live
62 - * inside a serialized array that other code paths write, so the absent state
63 - * is reachable and must fail closed.
64 - *
65 - * Agents are not shown tools they cannot call: Runtime::register() skips a
66 - * gated-off ability entirely rather than registering one whose permission
67 - * callback always fails.
68 - *
69 - * @param string $gate One of the GATE_* constants.
70 - * @return bool True when abilities behind this gate may register and run.
71 - * @since 1.5.0
72 - */
73 - public static function is_gate_open( $gate ) {
74 - if ( '' === $gate ) {
75 - return true;
76 - }
77 -
78 - $ai_option = Helper::get_suredonation_option( 'ai_settings', [] );
79 - $ai_settings = is_array( $ai_option ) ? $ai_option : [];
80 -
81 - // Absent means closed. Settings_API::get_ai_settings() reports both gates
82 - // as false by default and the settings screen renders them off, so
83 - // treating an absent key as open would let the plugin enforce writes and
84 - // permanent deletes it is telling the admin are disabled. Reachable on a
85 - // partial PATCH of just {enable_abilities:true}, or a site that toggled
86 - // abilities before these sub-keys shipped. Deny is the only safe
87 - // fallthrough for a gate whose whole purpose is a deliberate second
88 - // decision about destructive or money-moving operations.
89 - return ! empty( $ai_settings[ $gate ] );
90 - }
91 -
92 - /**
93 34 * Get all ability configurations.
94 35 *
95 36 * @return array<string, array<string, mixed>> Ability definitions.
96 37 */
@@ -98,27 +39,29 @@
98 39 if ( null !== self::$abilities ) {
99 40 return self::$abilities;
100 41 }
101 42
102 - $runtime = new Runtime();
43 + $runtime = new Runtime();
44 + $ai_option = Helper::get_suredonation_option( 'ai_settings', [] );
45 + $ai_settings = is_array( $ai_option ) ? $ai_option : [];
103 46
104 47 $perm_read = static function () use ( $runtime ) {
105 48 return $runtime->permission_callback( 'manage_options' );
106 49 };
107 50
108 - $perm_edit = static function () use ( $runtime ) {
109 - return self::is_gate_open( self::GATE_UPDATE ) && $runtime->permission_callback( 'manage_options' );
51 + $perm_edit = static function () use ( $runtime, $ai_settings ) {
52 + return ! empty( $ai_settings['allow_updates'] ) && $runtime->permission_callback( 'manage_options' );
110 53 };
111 54
112 - $perm_delete = static function () use ( $runtime ) {
113 - return self::is_gate_open( self::GATE_DELETE ) && $runtime->permission_callback( 'manage_options' );
55 + $perm_delete = static function () use ( $runtime, $ai_settings ) {
56 + return ! empty( $ai_settings['allow_delete'] ) && $runtime->permission_callback( 'manage_options' );
114 57 };
115 58
116 59 $abilities = array_merge(
117 60 self::get_campaign_abilities( $runtime, $perm_read, $perm_edit, $perm_delete ),
118 - self::get_donation_abilities( $runtime, $perm_read, $perm_edit, $perm_delete ),
119 - self::get_donor_abilities( $runtime, $perm_read, $perm_edit ),
120 - self::get_form_abilities( $runtime, $perm_read, $perm_edit, $perm_delete ),
61 + self::get_donation_abilities( $runtime, $perm_read, $perm_edit ),
62 + self::get_donor_abilities( $runtime, $perm_read ),
63 + self::get_form_abilities( $runtime, $perm_read ),
121 64 self::get_analytics_abilities( $runtime, $perm_read )
122 65 );
123 66
124 67 /**
@@ -166,55 +109,25 @@
166 109
167 110 /**
168 111 * Build meta block for an ability.
169 112 *
170 - * Three consumers read this block and each needs a different key:
171 - *
172 - * - `show_in_rest` gates the core `wp-abilities/v1` REST controllers. It
173 - * defaults to false in WP_Ability, so omitting it makes an ability
174 - * invisible to `GET /abilities` and unrunnable via `/run`.
175 - * - `annotations` must use core's key names (`readonly`, `destructive`,
176 - * `idempotent`). The MCP-spec spellings (`readOnlyHint` and friends) are
177 - * not recognised by core, which leaves its own keys null.
178 - * - `tool_type` is a TOP-LEVEL key read by MCP clients to classify the
179 - * operation. Without it a client has to guess from the tool name, and a
180 - * mutating ability whose name starts with a read-ish verb can slip past
181 - * an approval gate.
182 - *
183 - * Public rather than private: this is the single place that encodes the meta
184 - * contract, and SureDonation Pro registers its own abilities through the
185 - * `suredonation_config_abilities` filter. Pro hand-rolling this block would
186 - * guarantee drift the moment any consumer's expectations change.
187 - *
188 - * @param string $tool_type One of read|write|list|search|action|delete.
189 - * @param float $priority Priority level (1.0 read, 2.0 write, 3.0 destructive).
190 - * @param bool $read_only Whether the ability only reads data.
191 - * @param bool $destructive Whether the ability destroys data.
192 - * @param bool $idempotent Whether repeated calls produce the same result.
193 - * @param string $instructions Optional guidance for the calling model.
113 + * @param float $priority Priority level (1.0 read, 2.0 write, 3.0 destructive).
114 + * @param bool $read_only Whether the ability only reads data.
115 + * @param bool $destructive Whether the ability destroys data.
116 + * @param bool $idempotent Whether repeated calls produce the same result.
194 117 * @return array<string, mixed> Meta configuration.
195 118 */
196 - public static function build_meta( $tool_type = 'read', $priority = 1.0, $read_only = true, $destructive = false, $idempotent = true, $instructions = '' ) {
197 - $annotations = [
198 - 'readonly' => $read_only,
199 - 'destructive' => $destructive,
200 - 'idempotent' => $idempotent,
201 - 'priority' => $priority,
202 - // Deliberate MCP-spec spelling among core's snake_case annotation keys:
203 - // core does not define this one, and clients read the camelCase name.
204 - 'openWorldHint' => false,
205 - ];
206 -
207 - if ( '' !== $instructions ) {
208 - $annotations['instructions'] = $instructions;
209 - }
210 -
119 + private static function build_meta( $priority = 1.0, $read_only = true, $destructive = false, $idempotent = true ) {
211 120 return [
212 - 'show_in_rest' => true,
213 - 'tool_type' => $tool_type,
214 - 'annotations' => $annotations,
215 - 'mcp' => [
216 - 'public' => false,
121 + 'annotations' => [
122 + 'priority' => $priority,
123 + 'readOnlyHint' => $read_only,
124 + 'destructiveHint' => $destructive,
125 + 'idempotentHint' => $idempotent,
126 + 'openWorldHint' => false,
127 + ],
128 + 'mcp' => [
129 + 'public' => true,
217 130 'type' => 'tool',
218 131 ],
219 132 ];
220 133 }
@@ -246,11 +159,11 @@
246 159 'default' => '',
247 160 ],
248 161 'status' => [
249 162 'type' => 'string',
250 - 'enum' => [ 'all', 'publish', 'draft', 'trash', 'paused' ],
163 + 'enum' => [ 'all', 'publish', 'draft' ],
251 164 'default' => 'all',
252 - 'description' => __( 'Filter by status. "publish", "draft" and "trash" are WordPress post statuses; "paused" matches published campaigns whose campaign status is paused.', 'suredonation' ),
165 + 'description' => __( 'Filter by post status.', 'suredonation' ),
253 166 ],
254 167 'sort_by' => [
255 168 'type' => 'string',
256 169 'enum' => [ 'date', 'title', 'status' ],
@@ -282,29 +195,18 @@
282 195 'type' => 'array',
283 196 'items' => [
284 197 'type' => 'object',
285 198 'properties' => [
286 - 'id' => [ 'type' => 'integer' ],
287 - 'title' => [ 'type' => 'string' ],
288 - 'status' => [ 'type' => 'string' ],
289 - 'goal_type' => [ 'type' => 'string' ],
290 - 'goal' => [ 'type' => 'number' ],
291 - 'raised' => [ 'type' => 'number' ],
292 - 'donors' => [ 'type' => 'integer' ],
293 - 'progress' => [ 'type' => 'number' ],
294 - 'created_at' => [ 'type' => 'string' ],
295 - 'modified_at' => [ 'type' => 'string' ],
296 - 'post_status' => [ 'type' => 'string' ],
297 - 'currency' => [ 'type' => 'string' ],
298 - 'terms_text' => [ 'type' => 'string' ],
299 - 'thank_you_message' => [ 'type' => 'string' ],
300 - 'featured_image' => [ 'type' => 'integer' ],
301 - 'featured_image_url' => [ 'type' => 'string' ],
302 - 'has_page' => [ 'type' => 'boolean' ],
303 - 'permalink' => [ 'type' => 'string' ],
304 - 'author' => [ 'type' => 'string' ],
305 - 'edit_url' => [ 'type' => 'string' ],
306 - 'default_form_id' => [ 'type' => 'integer' ],
199 + 'id' => [ 'type' => 'integer' ],
200 + 'title' => [ 'type' => 'string' ],
201 + 'status' => [ 'type' => 'string' ],
202 + 'goal_type' => [ 'type' => 'string' ],
203 + 'goal' => [ 'type' => 'number' ],
204 + 'raised' => [ 'type' => 'number' ],
205 + 'donors' => [ 'type' => 'integer' ],
206 + 'progress' => [ 'type' => 'number' ],
207 + 'created_at' => [ 'type' => 'string' ],
208 + 'modified_at' => [ 'type' => 'string' ],
307 209 ],
308 210 ],
309 211 ],
310 212 'total' => [
@@ -319,9 +221,9 @@
319 221 ],
320 222 'execute_callback' => static function ( $input ) use ( $runtime ) {
321 223 return $runtime->list_campaigns( $input );
322 224 },
323 - 'meta' => self::build_meta( 'list', 1.0, true, false, true ),
225 + 'meta' => self::build_meta( 1.0, true, false, true ),
324 226 ],
325 227
326 228 $ns . 'get-campaign' => [
327 229 'label' => __( 'Get campaign', 'suredonation' ),
@@ -340,41 +242,30 @@
340 242 ],
341 243 'output_schema' => [
342 244 'type' => 'object',
343 245 'properties' => [
344 - 'id' => [ 'type' => 'integer' ],
345 - 'title' => [ 'type' => 'string' ],
346 - 'description' => [ 'type' => 'string' ],
347 - 'status' => [ 'type' => 'string' ],
348 - 'goal_type' => [ 'type' => 'string' ],
349 - 'goal' => [ 'type' => 'number' ],
350 - 'raised' => [ 'type' => 'number' ],
351 - 'donors' => [ 'type' => 'integer' ],
352 - 'progress' => [ 'type' => 'number' ],
353 - 'donation_count' => [ 'type' => 'integer' ],
354 - 'average_donation' => [ 'type' => 'number' ],
355 - 'largest_donation' => [ 'type' => 'number' ],
356 - 'is_goal_reached' => [ 'type' => 'boolean' ],
357 - 'require_terms' => [ 'type' => 'boolean' ],
358 - 'created_at' => [ 'type' => 'string' ],
359 - 'modified_at' => [ 'type' => 'string' ],
360 - 'post_status' => [ 'type' => 'string' ],
361 - 'currency' => [ 'type' => 'string' ],
362 - 'terms_text' => [ 'type' => 'string' ],
363 - 'thank_you_message' => [ 'type' => 'string' ],
364 - 'featured_image' => [ 'type' => 'integer' ],
365 - 'featured_image_url' => [ 'type' => 'string' ],
366 - 'has_page' => [ 'type' => 'boolean' ],
367 - 'permalink' => [ 'type' => 'string' ],
368 - 'author' => [ 'type' => 'string' ],
369 - 'edit_url' => [ 'type' => 'string' ],
370 - 'default_form_id' => [ 'type' => 'integer' ],
246 + 'id' => [ 'type' => 'integer' ],
247 + 'title' => [ 'type' => 'string' ],
248 + 'description' => [ 'type' => 'string' ],
249 + 'status' => [ 'type' => 'string' ],
250 + 'goal_type' => [ 'type' => 'string' ],
251 + 'goal' => [ 'type' => 'number' ],
252 + 'raised' => [ 'type' => 'number' ],
253 + 'donors' => [ 'type' => 'integer' ],
254 + 'progress' => [ 'type' => 'number' ],
255 + 'donation_count' => [ 'type' => 'integer' ],
256 + 'average_donation' => [ 'type' => 'number' ],
257 + 'largest_donation' => [ 'type' => 'number' ],
258 + 'is_goal_reached' => [ 'type' => 'boolean' ],
259 + 'require_terms' => [ 'type' => 'boolean' ],
260 + 'created_at' => [ 'type' => 'string' ],
261 + 'modified_at' => [ 'type' => 'string' ],
371 262 ],
372 263 ],
373 264 'execute_callback' => static function ( $input ) use ( $runtime ) {
374 265 return $runtime->get_campaign( $input );
375 266 },
376 - 'meta' => self::build_meta( 'read', 1.0, true, false, true ),
267 + 'meta' => self::build_meta( 1.0, true, false, true ),
377 268 ],
378 269
379 270 $ns . 'create-campaign' => [
380 271 'label' => __( 'Create campaign', 'suredonation' ),
@@ -384,55 +275,45 @@
384 275 'input_schema' => [
385 276 'type' => 'object',
386 277 'required' => [ 'title' ],
387 278 'properties' => [
388 - 'title' => [
279 + 'title' => [
389 280 'type' => 'string',
390 281 'description' => __( 'Campaign title.', 'suredonation' ),
391 282 ],
392 - 'description' => [
283 + 'description' => [
393 284 'type' => 'string',
394 285 'format' => 'html',
395 286 'description' => __( 'Campaign description (HTML allowed).', 'suredonation' ),
396 287 'default' => '',
397 288 ],
398 - 'goal_type' => [
289 + 'goal_type' => [
399 290 'type' => 'string',
400 291 'enum' => [ 'raised_amount', 'donation_count' ],
401 292 'default' => 'raised_amount',
402 293 'description' => __( 'Goal type: track by amount raised or donation count.', 'suredonation' ),
403 294 ],
404 - 'goal_amount' => [
295 + 'goal_amount' => [
405 296 'type' => 'number',
406 297 'description' => __( 'Goal amount (0 for no goal).', 'suredonation' ),
407 298 'default' => 0,
408 299 ],
409 - 'campaign_status' => [
300 + 'campaign_status' => [
410 301 'type' => 'string',
411 302 'enum' => [ 'active', 'paused', 'completed' ],
412 303 'default' => 'active',
413 304 'description' => __( 'Campaign status.', 'suredonation' ),
414 305 ],
415 - 'require_terms' => [
306 + 'require_terms' => [
416 307 'type' => 'boolean',
417 308 'default' => false,
418 309 'description' => __( 'Require terms acceptance before donating.', 'suredonation' ),
419 310 ],
420 - 'terms_text' => [
311 + 'terms_text' => [
421 312 'type' => 'string',
422 313 'default' => '',
423 314 'description' => __( 'Terms and conditions text.', 'suredonation' ),
424 315 ],
425 - 'thank_you_message' => [
426 - 'type' => 'string',
427 - 'default' => '',
428 - 'description' => __( 'Message shown to the donor after a successful donation.', 'suredonation' ),
429 - ],
430 - 'featured_image' => [
431 - 'type' => 'integer',
432 - 'default' => 0,
433 - 'description' => __( 'Attachment ID to use as the campaign featured image (0 for none).', 'suredonation' ),
434 - ],
435 316 ],
436 317 ],
437 318 'output_schema' => [
438 319 'type' => 'object',
@@ -448,10 +329,9 @@
448 329 ],
449 330 'execute_callback' => static function ( $input ) use ( $runtime ) {
450 331 return $runtime->create_campaign( $input );
451 332 },
452 - 'meta' => self::build_meta( 'write', 2.0, false, false, false ),
453 - 'gate' => self::GATE_UPDATE,
333 + 'meta' => self::build_meta( 2.0, false, false, false ),
454 334 ],
455 335
456 336 $ns . 'update-campaign' => [
457 337 'label' => __( 'Update campaign', 'suredonation' ),
@@ -461,51 +341,43 @@
461 341 'input_schema' => [
462 342 'type' => 'object',
463 343 'required' => [ 'id' ],
464 344 'properties' => [
465 - 'id' => [
345 + 'id' => [
466 346 'type' => 'integer',
467 347 'description' => __( 'Campaign ID to update.', 'suredonation' ),
468 348 ],
469 - 'title' => [
349 + 'title' => [
470 350 'type' => 'string',
471 351 'description' => __( 'Campaign title.', 'suredonation' ),
472 352 ],
473 - 'description' => [
353 + 'description' => [
474 354 'type' => 'string',
475 355 'format' => 'html',
476 356 'description' => __( 'Campaign description (HTML allowed).', 'suredonation' ),
477 357 ],
478 - 'goal_type' => [
358 + 'goal_type' => [
479 359 'type' => 'string',
480 360 'enum' => [ 'raised_amount', 'donation_count' ],
481 361 'description' => __( 'Goal type.', 'suredonation' ),
482 362 ],
483 - 'goal_amount' => [
363 + 'goal_amount' => [
484 364 'type' => 'number',
485 365 'description' => __( 'Goal amount.', 'suredonation' ),
486 366 ],
487 - 'campaign_status' => [
367 + 'campaign_status' => [
488 368 'type' => 'string',
489 369 'enum' => [ 'active', 'paused', 'completed' ],
490 370 'description' => __( 'Campaign status.', 'suredonation' ),
491 371 ],
492 - 'require_terms' => [
372 + 'require_terms' => [
493 373 'type' => 'boolean',
494 374 'description' => __( 'Require terms acceptance.', 'suredonation' ),
495 375 ],
496 - 'terms_text' => [
376 + 'terms_text' => [
497 377 'type' => 'string',
498 378 'description' => __( 'Terms and conditions text.', 'suredonation' ),
499 379 ],
500 - 'thank_you_message' => [
501 - 'type' => 'string',
502 - 'description' => __( 'Message shown to the donor after a successful donation.', 'suredonation' ),
503 - ],
504 - 'featured_image' => [
505 - 'type' => 'integer',
506 - 'description' => __( 'Attachment ID to use as the campaign featured image (0 clears it).', 'suredonation' ),
507 - ],
508 380 ],
509 381 ],
510 382 'output_schema' => [
511 383 'type' => 'object',
@@ -518,15 +390,14 @@
518 390 ],
519 391 'execute_callback' => static function ( $input ) use ( $runtime ) {
520 392 return $runtime->update_campaign( $input );
521 393 },
522 - 'meta' => self::build_meta( 'write', 2.0, false, false, false ),
523 - 'gate' => self::GATE_UPDATE,
394 + 'meta' => self::build_meta( 2.0, false, false, false ),
524 395 ],
525 396
526 397 $ns . 'delete-campaign' => [
527 398 'label' => __( 'Delete campaign', 'suredonation' ),
528 - 'description' => __( 'Permanently deletes a campaign by ID, along with its donation forms. Refused when the campaign has donations recorded against it, since those are financial records. This action cannot be undone.', 'suredonation' ),
399 + 'description' => __( 'Permanently deletes a campaign by ID. This action cannot be undone.', 'suredonation' ),
529 400 'category' => 'suredonation',
530 401 'permission_callback' => $perm_delete,
531 402 'input_schema' => [
532 403 'type' => 'object',
@@ -540,27 +411,16 @@
540 411 ],
541 412 'output_schema' => [
542 413 'type' => 'object',
543 414 'properties' => [
544 - 'id' => [ 'type' => 'integer' ],
545 - 'deleted_forms' => [
546 - 'type' => 'array',
547 - 'items' => [ 'type' => 'integer' ],
548 - 'description' => __( 'IDs of the campaign donation forms deleted alongside it.', 'suredonation' ),
549 - ],
550 - 'kept_forms' => [
551 - 'type' => 'array',
552 - 'items' => [ 'type' => 'integer' ],
553 - 'description' => __( 'Forms left in place because they still have donations recorded against them.', 'suredonation' ),
554 - ],
555 - 'message' => [ 'type' => 'string' ],
415 + 'id' => [ 'type' => 'integer' ],
416 + 'message' => [ 'type' => 'string' ],
556 417 ],
557 418 ],
558 419 'execute_callback' => static function ( $input ) use ( $runtime ) {
559 420 return $runtime->delete_campaign( $input );
560 421 },
561 - 'meta' => self::build_meta( 'delete', 3.0, false, true, false, __( 'Permanent and not undoable. It also deletes the campaign\'s donation forms. Confirm with the user before executing.', 'suredonation' ) ),
562 - 'gate' => self::GATE_DELETE,
422 + 'meta' => self::build_meta( 3.0, false, true, false ),
563 423 ],
564 424
565 425 $ns . 'duplicate-campaign' => [
566 426 'label' => __( 'Duplicate campaign', 'suredonation' ),
@@ -590,10 +450,9 @@
590 450 ],
591 451 'execute_callback' => static function ( $input ) use ( $runtime ) {
592 452 return $runtime->duplicate_campaign( $input );
593 453 },
594 - 'meta' => self::build_meta( 'write', 2.0, false, false, false ),
595 - 'gate' => self::GATE_UPDATE,
454 + 'meta' => self::build_meta( 2.0, false, false, false ),
596 455 ],
597 456
598 457 $ns . 'get-campaign-form-locations' => [
599 458 'label' => __( 'Get campaign form locations', 'suredonation' ),
@@ -617,15 +476,14 @@
617 476 'type' => 'array',
618 477 'items' => [
619 478 'type' => 'object',
620 479 'properties' => [
621 - 'id' => [ 'type' => 'integer' ],
622 - 'title' => [ 'type' => 'string' ],
623 - 'type' => [ 'type' => 'string' ],
624 - 'status' => [ 'type' => 'string' ],
625 - 'modified_at' => [ 'type' => 'string' ],
626 - 'edit_url' => [ 'type' => 'string' ],
627 - 'view_url' => [ 'type' => 'string' ],
480 + 'id' => [ 'type' => 'integer' ],
481 + 'title' => [ 'type' => 'string' ],
482 + 'type' => [ 'type' => 'string' ],
483 + 'status' => [ 'type' => 'string' ],
484 + 'edit_url' => [ 'type' => 'string' ],
485 + 'view_url' => [ 'type' => 'string' ],
628 486 ],
629 487 ],
630 488 ],
631 489 ],
@@ -632,47 +490,10 @@
632 490 ],
633 491 'execute_callback' => static function ( $input ) use ( $runtime ) {
634 492 return $runtime->get_campaign_form_locations( $input );
635 493 },
636 - 'meta' => self::build_meta( 'read', 1.0, true, false, true ),
494 + 'meta' => self::build_meta( 1.0, true, false, true ),
637 495 ],
638 -
639 - $ns . 'update-campaign-status' => [
640 - 'label' => __( 'Publish, draft or trash a campaign', 'suredonation' ),
641 - 'description' => __( 'Changes a campaign\'s WordPress post status: publish makes it live, draft hides it, trash removes it from listings without deleting it. This is different from the campaign business status (active/paused/completed), which update-campaign sets via campaign_status.', 'suredonation' ),
642 - 'category' => 'suredonation',
643 - 'permission_callback' => $perm_edit,
644 - 'gate' => self::GATE_UPDATE,
645 - 'input_schema' => [
646 - 'type' => 'object',
647 - 'required' => [ 'id', 'status' ],
648 - 'properties' => [
649 - 'id' => [
650 - 'type' => 'integer',
651 - 'description' => __( 'Campaign ID.', 'suredonation' ),
652 - ],
653 - 'status' => [
654 - 'type' => 'string',
655 - 'enum' => [ 'publish', 'draft', 'trash' ],
656 - 'description' => __( 'The new post status.', 'suredonation' ),
657 - ],
658 - ],
659 - ],
660 - 'output_schema' => [
661 - 'type' => 'object',
662 - 'properties' => [
663 - 'id' => [ 'type' => 'integer' ],
664 - 'post_status' => [ 'type' => 'string' ],
665 - 'previous_status' => [ 'type' => 'string' ],
666 - 'changed' => [ 'type' => 'boolean' ],
667 - 'message' => [ 'type' => 'string' ],
668 - ],
669 - ],
670 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
671 - return $runtime->update_campaign_status( $input );
672 - },
673 - 'meta' => self::build_meta( 'write', 2.0, false, false, true ),
674 - ],
675 496 ];
676 497 }
677 498
678 499 /**
@@ -677,19 +498,18 @@
677 498
678 499 /**
679 500 * Get donation ability configurations.
680 501 *
681 - * @param Runtime $runtime Runtime instance.
682 - * @param callable $perm_read Read permission closure.
683 - * @param callable $perm_edit Edit permission closure.
684 - * @param callable $perm_delete Delete permission closure.
502 + * @param Runtime $runtime Runtime instance.
503 + * @param callable $perm_read Read permission closure.
504 + * @param callable $perm_edit Edit permission closure.
685 505 * @return array<string, array<string, mixed>> Donation abilities.
686 506 */
687 - private static function get_donation_abilities( $runtime, $perm_read, $perm_edit, $perm_delete ) {
507 + private static function get_donation_abilities( $runtime, $perm_read, $perm_edit ) {
688 508 $ns = SUREDONATION_ABILITY_API_NAMESPACE;
689 509
690 510 return [
691 - $ns . 'list-donations' => [
511 + $ns . 'list-donations' => [
692 512 'label' => __( 'List donations', 'suredonation' ),
693 513 'description' => __( 'Returns a paginated list of donations with optional search, status filter, campaign filter, and sorting.', 'suredonation' ),
694 514 'category' => 'suredonation',
695 515 'permission_callback' => $perm_read,
@@ -697,17 +517,14 @@
697 517 'type' => 'object',
698 518 'properties' => [
699 519 'search' => [
700 520 'type' => 'string',
701 - 'description' => __( 'Search donations by donor name, donor email, or transaction ID.', 'suredonation' ),
521 + 'description' => __( 'Search donations by donor name or email.', 'suredonation' ),
702 522 'default' => '',
703 523 ],
704 524 'status' => [
705 525 'type' => 'string',
706 - // Sourced from the table's whitelist so this cannot drift
707 - // from what the codebase actually writes, as the
708 - // write schemas below had.
709 - 'enum' => array_merge( [ 'all' ], Donations::get_valid_statuses() ),
526 + 'enum' => [ 'all', 'pending', 'processing', 'completed', 'failed', 'refunded', 'partially_refunded', 'cancelled' ],
710 527 'default' => 'all',
711 528 'description' => __( 'Filter by payment status.', 'suredonation' ),
712 529 ],
713 530 'campaign_id' => [
@@ -716,9 +533,9 @@
716 533 'description' => __( 'Filter by campaign ID (0 for all campaigns).', 'suredonation' ),
717 534 ],
718 535 'sort_by' => [
719 536 'type' => 'string',
720 - 'enum' => [ 'id', 'created_at', 'updated_at', 'amount', 'donor_name', 'donor_email', 'payment_status', 'campaign_id', 'subscription_status' ],
537 + 'enum' => [ 'created_at', 'amount', 'donor_name', 'payment_status' ],
721 538 'default' => 'created_at',
722 539 'description' => __( 'Column to sort by.', 'suredonation' ),
723 540 ],
724 541 'order' => [
@@ -746,23 +563,19 @@
746 563 'type' => 'array',
747 564 'items' => [
748 565 'type' => 'object',
749 566 'properties' => [
750 - 'id' => [ 'type' => 'integer' ],
751 - 'campaign_id' => [ 'type' => 'integer' ],
752 - 'campaign_title' => [ 'type' => 'string' ],
753 - 'donor_name' => [ 'type' => 'string' ],
754 - 'donor_email' => [ 'type' => 'string' ],
755 - 'amount' => [ 'type' => 'number' ],
756 - 'currency' => [ 'type' => 'string' ],
757 - 'payment_status' => [ 'type' => 'string' ],
758 - 'donation_type' => [ 'type' => 'string' ],
759 - 'gateway' => [ 'type' => 'string' ],
760 - 'form_id' => [ 'type' => 'integer' ],
761 - 'form_title' => [ 'type' => 'string' ],
762 - 'subscription_id' => [ 'type' => 'string' ],
763 - 'subscription_status' => [ 'type' => 'string' ],
764 - 'created_at' => [ 'type' => 'string' ],
567 + 'id' => [ 'type' => 'integer' ],
568 + 'campaign_id' => [ 'type' => 'integer' ],
569 + 'campaign_title' => [ 'type' => 'string' ],
570 + 'donor_name' => [ 'type' => 'string' ],
571 + 'donor_email' => [ 'type' => 'string' ],
572 + 'amount' => [ 'type' => 'number' ],
573 + 'currency' => [ 'type' => 'string' ],
574 + 'payment_status' => [ 'type' => 'string' ],
575 + 'donation_type' => [ 'type' => 'string' ],
576 + 'gateway' => [ 'type' => 'string' ],
577 + 'created_at' => [ 'type' => 'string' ],
765 578 ],
766 579 ],
767 580 ],
768 581 'total' => [
@@ -777,12 +590,12 @@
777 590 ],
778 591 'execute_callback' => static function ( $input ) use ( $runtime ) {
779 592 return $runtime->list_donations( $input );
780 593 },
781 - 'meta' => self::build_meta( 'list', 1.0, true, false, true ),
594 + 'meta' => self::build_meta( 1.0, true, false, true ),
782 595 ],
783 596
784 - $ns . 'get-donation' => [
597 + $ns . 'get-donation' => [
785 598 'label' => __( 'Get donation', 'suredonation' ),
786 599 'description' => __( 'Returns a single donation by ID with full details including donor info, payment data, transaction ID, and activity logs.', 'suredonation' ),
787 600 'category' => 'suredonation',
788 601 'permission_callback' => $perm_read,
@@ -798,69 +611,38 @@
798 611 ],
799 612 'output_schema' => [
800 613 'type' => 'object',
801 614 'properties' => [
802 - 'id' => [ 'type' => 'integer' ],
803 - 'campaign_id' => [ 'type' => 'integer' ],
804 - 'campaign_title' => [ 'type' => 'string' ],
805 - 'donor_id' => [ 'type' => 'integer' ],
806 - 'donor_name' => [ 'type' => 'string' ],
807 - 'donor_email' => [ 'type' => 'string' ],
808 - 'donor_phone' => [ 'type' => 'string' ],
809 - 'amount' => [ 'type' => 'number' ],
810 - 'fees_covered' => [ 'type' => 'number' ],
811 - 'refunded_amount' => [ 'type' => 'number' ],
812 - 'currency' => [ 'type' => 'string' ],
813 - 'donation_type' => [ 'type' => 'string' ],
814 - 'is_anonymous' => [ 'type' => 'boolean' ],
815 - 'donor_comment' => [ 'type' => 'string' ],
816 - 'donor_comment_status' => [
817 - 'type' => 'string',
818 - 'enum' => [ 'approved', 'pending', 'rejected' ],
819 - ],
820 - 'payment_status' => [ 'type' => 'string' ],
821 - 'payment_mode' => [ 'type' => 'string' ],
822 - 'gateway' => [ 'type' => 'string' ],
823 - 'transaction_id' => [ 'type' => 'string' ],
824 - 'form_id' => [ 'type' => 'integer' ],
825 - 'form_title' => [ 'type' => 'string' ],
826 - 'stripe_customer_id' => [ 'type' => 'string' ],
827 - 'stripe_account_id' => [
828 - 'type' => 'string',
829 - 'description' => __( 'Connected Stripe account that processed this donation.', 'suredonation' ),
830 - ],
831 - 'subscription_id' => [ 'type' => 'string' ],
832 - 'subscription_status' => [ 'type' => 'string' ],
833 - 'parent_subscription_id' => [ 'type' => 'integer' ],
834 - 'subscription_interval' => [ 'type' => 'string' ],
835 - 'billing_cycles' => [ 'type' => 'string' ],
836 - 'receipt_sent' => [ 'type' => 'boolean' ],
837 - 'receipt_pdf_url' => [ 'type' => 'string' ],
838 - 'import_source' => [ 'type' => 'string' ],
839 - 'fields' => [
840 - 'type' => 'array',
841 - 'description' => __( 'Field values the donor submitted, as label/value/group triples.', 'suredonation' ),
842 - 'items' => [
843 - 'type' => 'object',
844 - 'properties' => [
845 - 'label' => [ 'type' => 'string' ],
846 - 'value' => [ 'type' => 'string' ],
847 - 'group' => [ 'type' => 'string' ],
848 - ],
849 - ],
850 - ],
851 - 'created_at' => [ 'type' => 'string' ],
852 - 'updated_at' => [ 'type' => 'string' ],
853 - 'logs' => [ 'type' => 'array' ],
615 + 'id' => [ 'type' => 'integer' ],
616 + 'campaign_id' => [ 'type' => 'integer' ],
617 + 'campaign_title' => [ 'type' => 'string' ],
618 + 'donor_id' => [ 'type' => 'integer' ],
619 + 'donor_name' => [ 'type' => 'string' ],
620 + 'donor_email' => [ 'type' => 'string' ],
621 + 'donor_phone' => [ 'type' => 'string' ],
622 + 'amount' => [ 'type' => 'number' ],
623 + 'fees_covered' => [ 'type' => 'number' ],
624 + 'refunded_amount' => [ 'type' => 'number' ],
625 + 'currency' => [ 'type' => 'string' ],
626 + 'donation_type' => [ 'type' => 'string' ],
627 + 'is_anonymous' => [ 'type' => 'boolean' ],
628 + 'donor_comment' => [ 'type' => 'string' ],
629 + 'payment_status' => [ 'type' => 'string' ],
630 + 'payment_mode' => [ 'type' => 'string' ],
631 + 'gateway' => [ 'type' => 'string' ],
632 + 'transaction_id' => [ 'type' => 'string' ],
633 + 'created_at' => [ 'type' => 'string' ],
634 + 'updated_at' => [ 'type' => 'string' ],
635 + 'logs' => [ 'type' => 'array' ],
854 636 ],
855 637 ],
856 638 'execute_callback' => static function ( $input ) use ( $runtime ) {
857 639 return $runtime->get_donation( $input );
858 640 },
859 - 'meta' => self::build_meta( 'read', 1.0, true, false, true ),
641 + 'meta' => self::build_meta( 1.0, true, false, true ),
860 642 ],
861 643
862 - $ns . 'get-donation-notes' => [
644 + $ns . 'get-donation-notes' => [
863 645 'label' => __( 'Get donation notes', 'suredonation' ),
864 646 'description' => __( 'Returns paginated notes for a donation. Notes are admin-added comments for internal tracking.', 'suredonation' ),
865 647 'category' => 'suredonation',
866 648 'permission_callback' => $perm_read,
@@ -891,13 +673,12 @@
891 673 'type' => 'array',
892 674 'items' => [
893 675 'type' => 'object',
894 676 'properties' => [
895 - 'id' => [ 'type' => 'string' ],
896 - 'content' => [ 'type' => 'string' ],
897 - 'author_id' => [ 'type' => 'integer' ],
898 - 'author_name' => [ 'type' => 'string' ],
899 - 'created_at' => [ 'type' => 'string' ],
677 + 'id' => [ 'type' => 'string' ],
678 + 'content' => [ 'type' => 'string' ],
679 + 'author_id' => [ 'type' => 'integer' ],
680 + 'created_at' => [ 'type' => 'string' ],
900 681 ],
901 682 ],
902 683 ],
903 684 'total' => [
@@ -912,12 +693,12 @@
912 693 ],
913 694 'execute_callback' => static function ( $input ) use ( $runtime ) {
914 695 return $runtime->get_donation_notes( $input );
915 696 },
916 - 'meta' => self::build_meta( 'read', 1.0, true, false, true ),
697 + 'meta' => self::build_meta( 1.0, true, false, true ),
917 698 ],
918 699
919 - $ns . 'add-donation-note' => [
700 + $ns . 'add-donation-note' => [
920 701 'label' => __( 'Add donation note', 'suredonation' ),
921 702 'description' => __( 'Adds an internal note to a donation for admin tracking purposes.', 'suredonation' ),
922 703 'category' => 'suredonation',
923 704 'permission_callback' => $perm_edit,
@@ -947,227 +728,10 @@
947 728 ],
948 729 'execute_callback' => static function ( $input ) use ( $runtime ) {
949 730 return $runtime->add_donation_note( $input );
950 731 },
951 - 'meta' => self::build_meta( 'write', 2.0, false, false, false ),
952 - 'gate' => self::GATE_UPDATE,
732 + 'meta' => self::build_meta( 2.0, false, false, false ),
953 733 ],
954 -
955 - $ns . 'update-donation-status' => [
956 - 'label' => __( 'Update donation status', 'suredonation' ),
957 - 'description' => __( 'Changes a donation\'s payment status. Use refund-donation instead when money should actually move: this only changes the record.', 'suredonation' ),
958 - 'category' => 'suredonation',
959 - 'permission_callback' => $perm_edit,
960 - 'gate' => self::GATE_UPDATE,
961 - 'input_schema' => [
962 - 'type' => 'object',
963 - 'required' => [ 'id', 'status' ],
964 - 'properties' => [
965 - 'id' => [
966 - 'type' => 'integer',
967 - 'description' => __( 'The donation ID.', 'suredonation' ),
968 - ],
969 - 'status' => [
970 - 'type' => 'string',
971 - 'enum' => Donations::get_valid_statuses(),
972 - 'description' => __( 'The new payment status.', 'suredonation' ),
973 - ],
974 - ],
975 - ],
976 - 'output_schema' => [
977 - 'type' => 'object',
978 - 'properties' => [
979 - 'id' => [ 'type' => 'integer' ],
980 - 'payment_status' => [ 'type' => 'string' ],
981 - 'previous_status' => [ 'type' => 'string' ],
982 - 'changed' => [
983 - 'type' => 'boolean',
984 - 'description' => __( 'False when the donation already had that status.', 'suredonation' ),
985 - ],
986 - 'message' => [ 'type' => 'string' ],
987 - ],
988 - ],
989 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
990 - return $runtime->update_donation_status( $input );
991 - },
992 - 'meta' => self::build_meta( 'write', 2.0, false, false, true ),
993 - ],
994 -
995 - $ns . 'refund-donation' => [
996 - 'label' => __( 'Refund donation', 'suredonation' ),
997 - 'description' => __( 'Refunds a donation through the gateway that processed it (Stripe or PayPal), fully or partially. Moves real money and cannot be undone. Amounts are in the donation currency (for example 25.50), not cents. Omit the amount to refund everything still refundable. This also emails the donor a refund notification and fires any connected refund automations. For a recurring donation it refunds the one charge only and does not cancel the subscription.', 'suredonation' ),
998 - 'category' => 'suredonation',
999 - 'permission_callback' => $perm_edit,
1000 - 'gate' => self::GATE_UPDATE,
1001 - 'input_schema' => [
1002 - 'type' => 'object',
1003 - 'required' => [ 'id' ],
1004 - 'properties' => [
1005 - 'id' => [
1006 - 'type' => 'integer',
1007 - 'description' => __( 'The donation ID.', 'suredonation' ),
1008 - ],
1009 - 'amount' => [
1010 - 'type' => 'number',
1011 - 'default' => 0,
1012 - 'description' => __( 'Amount to refund in the donation currency. 0 or omitted refunds the full remaining balance.', 'suredonation' ),
1013 - ],
1014 - 'transaction_id' => [
1015 - 'type' => 'string',
1016 - 'default' => '',
1017 - 'description' => __( 'Optional safety check. When supplied it must match the donation\'s gateway transaction ID.', 'suredonation' ),
1018 - ],
1019 - 'notes' => [
1020 - 'type' => 'string',
1021 - 'default' => '',
1022 - 'description' => __( 'Internal note recorded against the refund.', 'suredonation' ),
1023 - ],
1024 - ],
1025 - ],
1026 - 'output_schema' => [
1027 - 'type' => 'object',
1028 - 'properties' => [
1029 - 'id' => [ 'type' => 'integer' ],
1030 - 'refunded' => [
1031 - 'type' => 'number',
1032 - 'description' => __( 'Amount refunded by this call, in the donation currency.', 'suredonation' ),
1033 - ],
1034 - 'currency' => [ 'type' => 'string' ],
1035 - 'refunded_total' => [
1036 - 'type' => 'number',
1037 - 'description' => __( 'Total refunded against this donation so far.', 'suredonation' ),
1038 - ],
1039 - 'payment_status' => [ 'type' => 'string' ],
1040 - 'message' => [ 'type' => 'string' ],
1041 - ],
1042 - ],
1043 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
1044 - return $runtime->refund_donation( $input );
1045 - },
1046 - 'meta' => self::build_meta( 'action', 3.0, false, true, false, __( 'Moves real money through the payment gateway and cannot be undone, emails the donor a refund notification, and does not cancel a subscription. Always confirm the donation and the amount with the user before executing.', 'suredonation' ) ),
1047 - ],
1048 -
1049 - $ns . 'create-donation' => [
1050 - 'label' => __( 'Record a donation', 'suredonation' ),
1051 - 'description' => __( 'Records a donation taken outside the online checkout — a cheque, cash, or bank transfer. This does NOT charge anyone: it only creates the record. Never use it to take a card payment.', 'suredonation' ),
1052 - 'category' => 'suredonation',
1053 - 'permission_callback' => $perm_edit,
1054 - 'gate' => self::GATE_UPDATE,
1055 - 'input_schema' => [
1056 - 'type' => 'object',
1057 - 'required' => [ 'campaign_id', 'amount' ],
1058 - 'properties' => [
1059 - 'campaign_id' => [
1060 - 'type' => 'integer',
1061 - 'description' => __( 'Campaign the donation belongs to.', 'suredonation' ),
1062 - ],
1063 - 'amount' => [
1064 - 'type' => 'number',
1065 - 'description' => __( 'Donation amount in the store currency.', 'suredonation' ),
1066 - ],
1067 - 'donor_name' => [
1068 - 'type' => 'string',
1069 - 'default' => '',
1070 - 'description' => __( 'Donor name.', 'suredonation' ),
1071 - ],
1072 - 'donor_email' => [
1073 - 'type' => 'string',
1074 - 'default' => '',
1075 - 'description' => __( 'Donor email address.', 'suredonation' ),
1076 - ],
1077 - 'donor_phone' => [
1078 - 'type' => 'string',
1079 - 'default' => '',
1080 - 'description' => __( 'Donor phone number.', 'suredonation' ),
1081 - ],
1082 - 'donor_comment' => [
1083 - 'type' => 'string',
1084 - 'default' => '',
1085 - 'description' => __( 'Comment left by the donor.', 'suredonation' ),
1086 - ],
1087 - 'payment_status' => [
1088 - 'type' => 'string',
1089 - 'enum' => Donations::get_valid_statuses(),
1090 - 'default' => 'pending',
1091 - 'description' => __( 'Status to record. Defaults to "pending" so recording a donation does not send donor receipts or fire completion automations; pass "completed" explicitly for a gift that has already cleared.', 'suredonation' ),
1092 - ],
1093 - 'donation_type' => [
1094 - 'type' => 'string',
1095 - 'enum' => [ 'one-time', 'recurring', 'renewal' ],
1096 - 'default' => 'one-time',
1097 - 'description' => __( 'Donation type.', 'suredonation' ),
1098 - ],
1099 - 'gateway' => [
1100 - 'type' => 'string',
1101 - 'default' => 'offline',
1102 - 'description' => __( 'How the donation was taken (for example "offline").', 'suredonation' ),
1103 - ],
1104 - 'transaction_id' => [
1105 - 'type' => 'string',
1106 - 'default' => '',
1107 - 'description' => __( 'External reference, such as a cheque number.', 'suredonation' ),
1108 - ],
1109 - 'fees_covered' => [
1110 - 'type' => 'number',
1111 - 'default' => 0,
1112 - 'description' => __( 'Amount the donor added to cover processing fees.', 'suredonation' ),
1113 - ],
1114 - 'is_anonymous' => [
1115 - 'type' => 'boolean',
1116 - 'default' => false,
1117 - 'description' => __( 'Whether the donation should be shown anonymously.', 'suredonation' ),
1118 - ],
1119 - ],
1120 - ],
1121 - 'output_schema' => [
1122 - 'type' => 'object',
1123 - 'properties' => [
1124 - 'id' => [ 'type' => 'integer' ],
1125 - 'campaign_id' => [ 'type' => 'integer' ],
1126 - 'amount' => [ 'type' => 'number' ],
1127 - 'payment_status' => [ 'type' => 'string' ],
1128 - 'message' => [ 'type' => 'string' ],
1129 - ],
1130 - ],
1131 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
1132 - return $runtime->create_donation( $input );
1133 - },
1134 - 'meta' => self::build_meta( 'write', 2.0, false, false, false ),
1135 - ],
1136 -
1137 - $ns . 'delete-donation-note' => [
1138 - 'label' => __( 'Delete donation note', 'suredonation' ),
1139 - 'description' => __( 'Permanently removes an internal note from a donation. Get the note ID from get-donation-notes first.', 'suredonation' ),
1140 - 'category' => 'suredonation',
1141 - 'permission_callback' => $perm_delete,
1142 - 'gate' => self::GATE_DELETE,
1143 - 'input_schema' => [
1144 - 'type' => 'object',
1145 - 'required' => [ 'id', 'note_id' ],
1146 - 'properties' => [
1147 - 'id' => [
1148 - 'type' => 'integer',
1149 - 'description' => __( 'The donation ID.', 'suredonation' ),
1150 - ],
1151 - 'note_id' => [
1152 - 'type' => 'string',
1153 - 'description' => __( 'The note ID, as returned by get-donation-notes.', 'suredonation' ),
1154 - ],
1155 - ],
1156 - ],
1157 - 'output_schema' => [
1158 - 'type' => 'object',
1159 - 'properties' => [
1160 - 'id' => [ 'type' => 'integer' ],
1161 - 'note_id' => [ 'type' => 'string' ],
1162 - 'message' => [ 'type' => 'string' ],
1163 - ],
1164 - ],
1165 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
1166 - return $runtime->delete_donation_note( $input );
1167 - },
1168 - 'meta' => self::build_meta( 'delete', 3.0, false, true, false, __( 'Permanently removes the note. Confirm with the user before executing.', 'suredonation' ) ),
1169 - ],
1170 734 ];
1171 735 }
1172 736
1173 737 /**
@@ -1174,12 +738,11 @@
1174 738 * Get donor ability configurations.
1175 739 *
1176 740 * @param Runtime $runtime Runtime instance.
1177 741 * @param callable $perm_read Read permission closure.
1178 - * @param callable $perm_edit Edit permission closure.
1179 742 * @return array<string, array<string, mixed>> Donor abilities.
1180 743 */
1181 - private static function get_donor_abilities( $runtime, $perm_read, $perm_edit ) {
744 + private static function get_donor_abilities( $runtime, $perm_read ) {
1182 745 $ns = SUREDONATION_ABILITY_API_NAMESPACE;
1183 746
1184 747 $donor_detail_schema = [
1185 748 'type' => 'object',
@@ -1187,11 +750,8 @@
1187 750 'id' => [ 'type' => 'integer' ],
1188 751 'name' => [ 'type' => 'string' ],
1189 752 'email' => [ 'type' => 'string' ],
1190 753 'phone' => [ 'type' => 'string' ],
1191 - 'company' => [ 'type' => 'string' ],
1192 - 'address' => [ 'type' => 'string' ],
1193 - 'stripe_customer_id' => [ 'type' => 'string' ],
1194 754 'user_id' => [ 'type' => 'integer' ],
1195 755 'donor_status' => [ 'type' => 'string' ],
1196 756 'total_donated' => [ 'type' => 'number' ],
1197 757 'donation_count' => [ 'type' => 'integer' ],
@@ -1204,60 +764,40 @@
1204 764 ],
1205 765 ];
1206 766
1207 767 return [
1208 - $ns . 'list-donors' => [
768 + $ns . 'list-donors' => [
1209 769 'label' => __( 'List donors', 'suredonation' ),
1210 - 'description' => __( 'Returns a paginated list of donors with optional search, status filter, campaign filter, date range, and sorting.', 'suredonation' ),
770 + 'description' => __( 'Returns a paginated list of donors with optional status filter and sorting by name, total donated, donation count, or date.', 'suredonation' ),
1211 771 'category' => 'suredonation',
1212 772 'permission_callback' => $perm_read,
1213 773 'input_schema' => [
1214 774 'type' => 'object',
1215 775 'properties' => [
1216 - 'search' => [
776 + 'status' => [
1217 777 'type' => 'string',
1218 - 'default' => '',
1219 - 'description' => __( 'Search donors by name or email.', 'suredonation' ),
1220 - ],
1221 - 'campaign_id' => [
1222 - 'type' => 'integer',
1223 - 'default' => 0,
1224 - 'description' => __( 'Only donors who gave to this campaign (0 for all campaigns).', 'suredonation' ),
1225 - ],
1226 - 'after' => [
1227 - 'type' => 'string',
1228 - 'default' => '',
1229 - 'description' => __( 'Only donors whose last donation was on or after this date (YYYY-MM-DD).', 'suredonation' ),
1230 - ],
1231 - 'before' => [
1232 - 'type' => 'string',
1233 - 'default' => '',
1234 - 'description' => __( 'Only donors whose last donation was on or before this date (YYYY-MM-DD).', 'suredonation' ),
1235 - ],
1236 - 'status' => [
1237 - 'type' => 'string',
1238 778 'enum' => [ 'all', 'active', 'inactive', 'blocked' ],
1239 779 'default' => 'all',
1240 780 'description' => __( 'Filter by donor status.', 'suredonation' ),
1241 781 ],
1242 - 'sort_by' => [
782 + 'sort_by' => [
1243 783 'type' => 'string',
1244 - 'enum' => [ 'id', 'created_at', 'updated_at', 'name', 'email', 'total_donated', 'donation_count', 'last_donation_date' ],
784 + 'enum' => [ 'created_at', 'name', 'email', 'total_donated', 'donation_count', 'last_donation_date' ],
1245 785 'default' => 'created_at',
1246 786 'description' => __( 'Column to sort by.', 'suredonation' ),
1247 787 ],
1248 - 'order' => [
788 + 'order' => [
1249 789 'type' => 'string',
1250 790 'enum' => [ 'ASC', 'DESC' ],
1251 791 'default' => 'DESC',
1252 792 'description' => __( 'Sort direction.', 'suredonation' ),
1253 793 ],
1254 - 'page' => [
794 + 'page' => [
1255 795 'type' => 'integer',
1256 796 'default' => 1,
1257 797 'description' => __( 'Page number (1-based).', 'suredonation' ),
1258 798 ],
1259 - 'per_page' => [
799 + 'per_page' => [
1260 800 'type' => 'integer',
1261 801 'default' => 20,
1262 802 'description' => __( 'Results per page (max 100).', 'suredonation' ),
1263 803 ],
@@ -1274,11 +814,8 @@
1274 814 'id' => [ 'type' => 'integer' ],
1275 815 'name' => [ 'type' => 'string' ],
1276 816 'email' => [ 'type' => 'string' ],
1277 817 'phone' => [ 'type' => 'string' ],
1278 - 'company' => [ 'type' => 'string' ],
1279 - 'address' => [ 'type' => 'string' ],
1280 - 'stripe_customer_id' => [ 'type' => 'string' ],
1281 818 'donor_status' => [ 'type' => 'string' ],
1282 819 'total_donated' => [ 'type' => 'number' ],
1283 820 'donation_count' => [ 'type' => 'integer' ],
1284 821 'largest_donation' => [ 'type' => 'number' ],
@@ -1300,12 +837,12 @@
1300 837 ],
1301 838 'execute_callback' => static function ( $input ) use ( $runtime ) {
1302 839 return $runtime->list_donors( $input );
1303 840 },
1304 - 'meta' => self::build_meta( 'list', 1.0, true, false, true ),
841 + 'meta' => self::build_meta( 1.0, true, false, true ),
1305 842 ],
1306 843
1307 - $ns . 'get-donor' => [
844 + $ns . 'get-donor' => [
1308 845 'label' => __( 'Get donor', 'suredonation' ),
1309 846 'description' => __( 'Returns a single donor by ID with full stats including total donated, donation count, largest donation, and donation dates.', 'suredonation' ),
1310 847 'category' => 'suredonation',
1311 848 'permission_callback' => $perm_read,
@@ -1322,12 +859,12 @@
1322 859 'output_schema' => $donor_detail_schema,
1323 860 'execute_callback' => static function ( $input ) use ( $runtime ) {
1324 861 return $runtime->get_donor( $input );
1325 862 },
1326 - 'meta' => self::build_meta( 'read', 1.0, true, false, true ),
863 + 'meta' => self::build_meta( 1.0, true, false, true ),
1327 864 ],
1328 865
1329 - $ns . 'get-donor-by-email' => [
866 + $ns . 'get-donor-by-email' => [
1330 867 'label' => __( 'Get donor by email', 'suredonation' ),
1331 868 'description' => __( 'Looks up a donor by email address. Returns full donor details if found, or an error if no donor exists with that email.', 'suredonation' ),
1332 869 'category' => 'suredonation',
1333 870 'permission_callback' => $perm_read,
@@ -1344,14 +881,14 @@
1344 881 'output_schema' => $donor_detail_schema,
1345 882 'execute_callback' => static function ( $input ) use ( $runtime ) {
1346 883 return $runtime->get_donor_by_email( $input );
1347 884 },
1348 - 'meta' => self::build_meta( 'read', 1.0, true, false, true ),
885 + 'meta' => self::build_meta( 1.0, true, false, true ),
1349 886 ],
1350 887
1351 - $ns . 'get-top-donors' => [
888 + $ns . 'get-top-donors' => [
1352 889 'label' => __( 'Get top donors', 'suredonation' ),
1353 - 'description' => __( 'Returns the top active donors ranked by total donated amount. Blocked and inactive donors are excluded. Useful for identifying major supporters and generating donor reports.', 'suredonation' ),
890 + 'description' => __( 'Returns top donors ranked by total donated amount. Useful for identifying major supporters and generating donor reports.', 'suredonation' ),
1354 891 'category' => 'suredonation',
1355 892 'permission_callback' => $perm_read,
1356 893 'input_schema' => [
1357 894 'type' => 'object',
@@ -1383,132 +920,10 @@
1383 920 ],
1384 921 'execute_callback' => static function ( $input ) use ( $runtime ) {
1385 922 return $runtime->get_top_donors( $input );
1386 923 },
1387 - 'meta' => self::build_meta( 'read', 1.0, true, false, true ),
924 + 'meta' => self::build_meta( 1.0, true, false, true ),
1388 925 ],
1389 -
1390 - $ns . 'get-donor-donations' => [
1391 - 'label' => __( 'Get donor donation history', 'suredonation' ),
1392 - 'description' => __( 'Returns a paginated donation history for one donor, newest first.', 'suredonation' ),
1393 - 'category' => 'suredonation',
1394 - 'permission_callback' => $perm_read,
1395 - 'input_schema' => [
1396 - 'type' => 'object',
1397 - 'required' => [ 'id' ],
1398 - 'properties' => [
1399 - 'id' => [
1400 - 'type' => 'integer',
1401 - 'description' => __( 'The donor ID.', 'suredonation' ),
1402 - ],
1403 - 'page' => [
1404 - 'type' => 'integer',
1405 - 'default' => 1,
1406 - 'description' => __( 'Page number (1-based).', 'suredonation' ),
1407 - ],
1408 - 'per_page' => [
1409 - 'type' => 'integer',
1410 - 'default' => 10,
1411 - 'description' => __( 'Results per page (max 100).', 'suredonation' ),
1412 - ],
1413 - ],
1414 - ],
1415 - 'output_schema' => [
1416 - 'type' => 'object',
1417 - 'properties' => [
1418 - 'donor_id' => [ 'type' => 'integer' ],
1419 - 'donations' => [
1420 - 'type' => 'array',
1421 - 'items' => [
1422 - 'type' => 'object',
1423 - 'properties' => [
1424 - 'id' => [ 'type' => 'integer' ],
1425 - 'campaign_id' => [ 'type' => 'integer' ],
1426 - 'campaign_title' => [ 'type' => 'string' ],
1427 - 'donor_name' => [ 'type' => 'string' ],
1428 - 'donor_email' => [ 'type' => 'string' ],
1429 - 'amount' => [ 'type' => 'number' ],
1430 - 'currency' => [ 'type' => 'string' ],
1431 - 'payment_status' => [ 'type' => 'string' ],
1432 - 'created_at' => [ 'type' => 'string' ],
1433 - ],
1434 - 'additionalProperties' => false,
1435 - ],
1436 - ],
1437 - 'total' => [ 'type' => 'integer' ],
1438 - 'total_pages' => [ 'type' => 'integer' ],
1439 - ],
1440 - ],
1441 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
1442 - return $runtime->get_donor_donations( $input );
1443 - },
1444 - 'meta' => self::build_meta( 'list', 1.0, true, false, true ),
1445 - ],
1446 -
1447 - $ns . 'update-donor' => [
1448 - 'label' => __( 'Update donor', 'suredonation' ),
1449 - 'description' => __( 'Updates a donor\'s contact details, status or tags. Only the fields you send are changed; everything else is left as-is. Donation totals are derived from donations and cannot be set here.', 'suredonation' ),
1450 - 'category' => 'suredonation',
1451 - 'permission_callback' => $perm_edit,
1452 - 'gate' => self::GATE_UPDATE,
1453 - 'input_schema' => [
1454 - 'type' => 'object',
1455 - 'required' => [ 'id' ],
1456 - 'properties' => [
1457 - 'id' => [
1458 - 'type' => 'integer',
1459 - 'description' => __( 'The donor ID.', 'suredonation' ),
1460 - ],
1461 - 'name' => [
1462 - 'type' => 'string',
1463 - 'description' => __( 'Donor name.', 'suredonation' ),
1464 - ],
1465 - 'email' => [
1466 - 'type' => 'string',
1467 - 'description' => __( 'Donor email address.', 'suredonation' ),
1468 - ],
1469 - 'phone' => [
1470 - 'type' => 'string',
1471 - 'description' => __( 'Donor phone number.', 'suredonation' ),
1472 - ],
1473 - 'company' => [
1474 - 'type' => 'string',
1475 - 'description' => __( 'Donor company.', 'suredonation' ),
1476 - ],
1477 - 'address' => [
1478 - 'type' => 'string',
1479 - 'description' => __( 'Donor address.', 'suredonation' ),
1480 - ],
1481 - 'donor_status' => [
1482 - 'type' => 'string',
1483 - 'enum' => [ 'active', 'inactive', 'blocked' ],
1484 - 'description' => __( 'Donor status. Blocked donors are excluded from top-donor reports.', 'suredonation' ),
1485 - ],
1486 - 'donor_tags' => [
1487 - 'type' => 'array',
1488 - 'items' => [ 'type' => 'string' ],
1489 - 'description' => __( 'Replaces the donor\'s tags with this list.', 'suredonation' ),
1490 - ],
1491 - ],
1492 - ],
1493 - 'output_schema' => [
1494 - 'type' => 'object',
1495 - 'properties' => [
1496 - 'id' => [ 'type' => 'integer' ],
1497 - 'updated' => [
1498 - 'type' => 'array',
1499 - 'items' => [ 'type' => 'string' ],
1500 - 'description' => __( 'Names of the fields this call changed.', 'suredonation' ),
1501 - ],
1502 - 'donor' => [ 'type' => 'object' ],
1503 - 'message' => [ 'type' => 'string' ],
1504 - ],
1505 - ],
1506 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
1507 - return $runtime->update_donor( $input );
1508 - },
1509 - 'meta' => self::build_meta( 'write', 2.0, false, false, true ),
1510 - ],
1511 926 ];
1512 927 }
1513 928
1514 929 /**
@@ -1513,19 +928,17 @@
1513 928
1514 929 /**
1515 930 * Get form ability configurations.
1516 931 *
1517 - * @param Runtime $runtime Runtime instance.
1518 - * @param callable $perm_read Read permission closure.
1519 - * @param callable $perm_edit Edit permission closure.
1520 - * @param callable $perm_delete Delete permission closure.
932 + * @param Runtime $runtime Runtime instance.
933 + * @param callable $perm_read Read permission closure.
1521 934 * @return array<string, array<string, mixed>> Form abilities.
1522 935 */
1523 - private static function get_form_abilities( $runtime, $perm_read, $perm_edit, $perm_delete ) {
936 + private static function get_form_abilities( $runtime, $perm_read ) {
1524 937 $ns = SUREDONATION_ABILITY_API_NAMESPACE;
1525 938
1526 939 return [
1527 - $ns . 'list-forms' => [
940 + $ns . 'list-forms' => [
1528 941 'label' => __( 'List donation forms', 'suredonation' ),
1529 942 'description' => __( 'Returns donation forms with optional campaign filter and status filter. Forms are the front-end donation widgets linked to campaigns.', 'suredonation' ),
1530 943 'category' => 'suredonation',
1531 944 'permission_callback' => $perm_read,
@@ -1547,27 +960,14 @@
1547 960 'type' => 'integer',
1548 961 'default' => 20,
1549 962 'description' => __( 'Results per page (max 100).', 'suredonation' ),
1550 963 ],
1551 - 'page' => [
1552 - 'type' => 'integer',
1553 - 'default' => 1,
1554 - 'description' => __( 'Page number (1-based).', 'suredonation' ),
1555 - ],
1556 964 ],
1557 965 ],
1558 966 'output_schema' => [
1559 967 'type' => 'object',
1560 968 'properties' => [
1561 - 'total' => [
1562 - 'type' => 'integer',
1563 - 'description' => __( 'Total matching forms.', 'suredonation' ),
1564 - ],
1565 - 'total_pages' => [
1566 - 'type' => 'integer',
1567 - 'description' => __( 'Total pages.', 'suredonation' ),
1568 - ],
1569 - 'forms' => [
969 + 'forms' => [
1570 970 'type' => 'array',
1571 971 'items' => [
1572 972 'type' => 'object',
1573 973 'properties' => [
@@ -1575,11 +975,8 @@
1575 975 'title' => [ 'type' => 'string' ],
1576 976 'status' => [ 'type' => 'string' ],
1577 977 'campaign_id' => [ 'type' => 'integer' ],
1578 978 'campaign_name' => [ 'type' => 'string' ],
1579 - 'entries' => [ 'type' => 'integer' ],
1580 - 'revenue' => [ 'type' => 'number' ],
1581 - 'is_default' => [ 'type' => 'boolean' ],
1582 979 'created_at' => [ 'type' => 'string' ],
1583 980 'modified_at' => [ 'type' => 'string' ],
1584 981 'edit_url' => [ 'type' => 'string' ],
1585 982 ],
@@ -1589,12 +986,12 @@
1589 986 ],
1590 987 'execute_callback' => static function ( $input ) use ( $runtime ) {
1591 988 return $runtime->list_forms( $input );
1592 989 },
1593 - 'meta' => self::build_meta( 'list', 1.0, true, false, true ),
990 + 'meta' => self::build_meta( 1.0, true, false, true ),
1594 991 ],
1595 992
1596 - $ns . 'get-form' => [
993 + $ns . 'get-form' => [
1597 994 'label' => __( 'Get donation form', 'suredonation' ),
1598 995 'description' => __( 'Returns a single donation form by ID with campaign association, status, and edit URL.', 'suredonation' ),
1599 996 'category' => 'suredonation',
1600 997 'permission_callback' => $perm_read,
@@ -1615,11 +1012,8 @@
1615 1012 'title' => [ 'type' => 'string' ],
1616 1013 'status' => [ 'type' => 'string' ],
1617 1014 'campaign_id' => [ 'type' => 'integer' ],
1618 1015 'campaign_name' => [ 'type' => 'string' ],
1619 - 'entries' => [ 'type' => 'integer' ],
1620 - 'revenue' => [ 'type' => 'number' ],
1621 - 'is_default' => [ 'type' => 'boolean' ],
1622 1016 'created_at' => [ 'type' => 'string' ],
1623 1017 'modified_at' => [ 'type' => 'string' ],
1624 1018 'edit_url' => [ 'type' => 'string' ],
1625 1019 ],
@@ -1626,157 +1020,10 @@
1626 1020 ],
1627 1021 'execute_callback' => static function ( $input ) use ( $runtime ) {
1628 1022 return $runtime->get_form( $input );
1629 1023 },
1630 - 'meta' => self::build_meta( 'read', 1.0, true, false, true ),
1024 + 'meta' => self::build_meta( 1.0, true, false, true ),
1631 1025 ],
1632 -
1633 - $ns . 'update-form' => [
1634 - 'label' => __( 'Move a form to another campaign', 'suredonation' ),
1635 - 'description' => __( 'Reassigns a donation form to a different campaign. Donations already recorded through the form keep their original campaign.', 'suredonation' ),
1636 - 'category' => 'suredonation',
1637 - 'permission_callback' => $perm_edit,
1638 - 'gate' => self::GATE_UPDATE,
1639 - 'input_schema' => [
1640 - 'type' => 'object',
1641 - 'required' => [ 'id', 'campaign_id' ],
1642 - 'properties' => [
1643 - 'id' => [
1644 - 'type' => 'integer',
1645 - 'description' => __( 'The donation form ID.', 'suredonation' ),
1646 - ],
1647 - 'campaign_id' => [
1648 - 'type' => 'integer',
1649 - 'description' => __( 'Campaign to attach the form to.', 'suredonation' ),
1650 - ],
1651 - ],
1652 - ],
1653 - 'output_schema' => [
1654 - 'type' => 'object',
1655 - 'properties' => [
1656 - 'id' => [ 'type' => 'integer' ],
1657 - 'title' => [ 'type' => 'string' ],
1658 - 'status' => [ 'type' => 'string' ],
1659 - 'campaign_id' => [ 'type' => 'integer' ],
1660 - 'campaign_name' => [ 'type' => 'string' ],
1661 - 'entries' => [ 'type' => 'integer' ],
1662 - 'revenue' => [ 'type' => 'number' ],
1663 - 'is_default' => [ 'type' => 'boolean' ],
1664 - 'created_at' => [ 'type' => 'string' ],
1665 - 'modified_at' => [ 'type' => 'string' ],
1666 - 'edit_url' => [ 'type' => 'string' ],
1667 - 'message' => [ 'type' => 'string' ],
1668 - ],
1669 - ],
1670 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
1671 - return $runtime->update_form( $input );
1672 - },
1673 - 'meta' => self::build_meta( 'write', 2.0, false, false, true ),
1674 - ],
1675 -
1676 - $ns . 'duplicate-form' => [
1677 - 'label' => __( 'Duplicate donation form', 'suredonation' ),
1678 - 'description' => __( 'Creates a copy of a donation form, including its fields and settings.', 'suredonation' ),
1679 - 'category' => 'suredonation',
1680 - 'permission_callback' => $perm_edit,
1681 - 'gate' => self::GATE_UPDATE,
1682 - 'input_schema' => [
1683 - 'type' => 'object',
1684 - 'required' => [ 'id' ],
1685 - 'properties' => [
1686 - 'id' => [
1687 - 'type' => 'integer',
1688 - 'description' => __( 'The donation form ID to copy.', 'suredonation' ),
1689 - ],
1690 - ],
1691 - ],
1692 - 'output_schema' => [
1693 - 'type' => 'object',
1694 - 'properties' => [
1695 - 'id' => [
1696 - 'type' => 'integer',
1697 - 'description' => __( 'The new form ID.', 'suredonation' ),
1698 - ],
1699 - 'source_id' => [ 'type' => 'integer' ],
1700 - 'title' => [ 'type' => 'string' ],
1701 - 'message' => [ 'type' => 'string' ],
1702 - ],
1703 - ],
1704 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
1705 - return $runtime->duplicate_form( $input );
1706 - },
1707 - 'meta' => self::build_meta( 'write', 2.0, false, false, false ),
1708 - ],
1709 -
1710 - $ns . 'set-default-form' => [
1711 - 'label' => __( 'Set a campaign\'s default form', 'suredonation' ),
1712 - 'description' => __( 'Chooses which donation form a campaign renders. A campaign can have several forms attached but renders only its default.', 'suredonation' ),
1713 - 'category' => 'suredonation',
1714 - 'permission_callback' => $perm_edit,
1715 - 'gate' => self::GATE_UPDATE,
1716 - 'input_schema' => [
1717 - 'type' => 'object',
1718 - 'required' => [ 'form_id', 'campaign_id' ],
1719 - 'properties' => [
1720 - 'form_id' => [
1721 - 'type' => 'integer',
1722 - 'description' => __( 'Form to make the default.', 'suredonation' ),
1723 - ],
1724 - 'campaign_id' => [
1725 - 'type' => 'integer',
1726 - 'description' => __( 'Campaign to set it on.', 'suredonation' ),
1727 - ],
1728 - ],
1729 - ],
1730 - 'output_schema' => [
1731 - 'type' => 'object',
1732 - 'properties' => [
1733 - 'campaign_id' => [ 'type' => 'integer' ],
1734 - 'default_form_id' => [ 'type' => 'integer' ],
1735 - 'message' => [ 'type' => 'string' ],
1736 - ],
1737 - ],
1738 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
1739 - return $runtime->set_default_form( $input );
1740 - },
1741 - 'meta' => self::build_meta( 'write', 2.0, false, false, true ),
1742 - ],
1743 -
1744 - $ns . 'manage-form' => [
1745 - 'label' => __( 'Trash, restore or delete a form', 'suredonation' ),
1746 - 'description' => __( 'Moves a donation form to the trash, restores it, or deletes it permanently. Trashing is reversible; deleting is not. Donations already recorded through the form are never removed.', 'suredonation' ),
1747 - 'category' => 'suredonation',
1748 - 'permission_callback' => $perm_delete,
1749 - 'gate' => self::GATE_DELETE,
1750 - 'input_schema' => [
1751 - 'type' => 'object',
1752 - 'required' => [ 'id', 'action' ],
1753 - 'properties' => [
1754 - 'id' => [
1755 - 'type' => 'integer',
1756 - 'description' => __( 'The donation form ID.', 'suredonation' ),
1757 - ],
1758 - 'action' => [
1759 - 'type' => 'string',
1760 - 'enum' => [ 'trash', 'restore', 'delete' ],
1761 - 'description' => __( 'What to do with the form. "delete" is permanent.', 'suredonation' ),
1762 - ],
1763 - ],
1764 - ],
1765 - 'output_schema' => [
1766 - 'type' => 'object',
1767 - 'properties' => [
1768 - 'id' => [ 'type' => 'integer' ],
1769 - 'action' => [ 'type' => 'string' ],
1770 - 'post_status' => [ 'type' => 'string' ],
1771 - 'message' => [ 'type' => 'string' ],
1772 - ],
1773 - ],
1774 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
1775 - return $runtime->manage_form( $input );
1776 - },
1777 - 'meta' => self::build_meta( 'delete', 3.0, false, true, false, 'The "delete" action is permanent and not undoable. Prefer "trash", and confirm with the user before deleting.' ),
1778 - ],
1779 1026 ];
1780 1027 }
1781 1028
1782 1029 /**
@@ -1789,48 +1036,32 @@
1789 1036 private static function get_analytics_abilities( $runtime, $perm_read ) {
1790 1037 $ns = SUREDONATION_ABILITY_API_NAMESPACE;
1791 1038
1792 1039 return [
1793 - $ns . 'get-donation-trends' => [
1040 + $ns . 'get-donation-trends' => [
1794 1041 'label' => __( 'Get donation trends', 'suredonation' ),
1795 - 'description' => __( 'Returns donation trend data grouped by day, week, or month for a single currency. Supports date-range and campaign filtering; defaults to the last 30 days in the store currency. Useful for charts and analytics.', 'suredonation' ),
1042 + 'description' => __( 'Returns donation trend data grouped by day, week, or month. Supports date range filtering. Useful for charts and analytics.', 'suredonation' ),
1796 1043 'category' => 'suredonation',
1797 1044 'permission_callback' => $perm_read,
1798 1045 'input_schema' => [
1799 1046 'type' => 'object',
1800 1047 'properties' => [
1801 - 'after' => [
1048 + 'after' => [
1802 1049 'type' => 'string',
1803 1050 'default' => '',
1804 - 'description' => __( 'Start date (YYYY-MM-DD). Empty defaults to 30 days ago.', 'suredonation' ),
1051 + 'description' => __( 'Start date (YYYY-MM-DD). Empty for no lower bound.', 'suredonation' ),
1805 1052 ],
1806 - 'before' => [
1053 + 'before' => [
1807 1054 'type' => 'string',
1808 1055 'default' => '',
1809 - 'description' => __( 'End date (YYYY-MM-DD). Empty defaults to today.', 'suredonation' ),
1056 + 'description' => __( 'End date (YYYY-MM-DD). Empty for no upper bound.', 'suredonation' ),
1810 1057 ],
1811 - 'group' => [
1058 + 'group' => [
1812 1059 'type' => 'string',
1813 1060 'enum' => [ 'day', 'week', 'month' ],
1814 1061 'default' => 'day',
1815 1062 'description' => __( 'Group results by time period.', 'suredonation' ),
1816 1063 ],
1817 - 'currency' => [
1818 - 'type' => 'string',
1819 - 'default' => '',
1820 - 'description' => __( 'Three-letter currency code to report on. Defaults to the store currency. Amounts across currencies are never summed together.', 'suredonation' ),
1821 - ],
1822 - 'campaign_id' => [
1823 - 'type' => 'integer',
1824 - 'default' => 0,
1825 - 'description' => __( 'Limit to one campaign (0 for all campaigns).', 'suredonation' ),
1826 - ],
1827 - 'payment_mode' => [
1828 - 'type' => 'string',
1829 - 'enum' => [ 'test', 'live' ],
1830 - 'default' => '',
1831 - 'description' => __( 'Report on test or live donations. Defaults to the store\'s current mode.', 'suredonation' ),
1832 - ],
1833 1064 ],
1834 1065 ],
1835 1066 'output_schema' => [
1836 1067 'type' => 'object',
@@ -1846,231 +1077,14 @@
1846 1077 ],
1847 1078 ],
1848 1079 ],
1849 1080 'currency' => [ 'type' => 'string' ],
1850 - 'after' => [
1851 - 'type' => 'string',
1852 - 'description' => __( 'Start of the window actually queried.', 'suredonation' ),
1853 - ],
1854 - 'before' => [
1855 - 'type' => 'string',
1856 - 'description' => __( 'End of the window actually queried.', 'suredonation' ),
1857 - ],
1858 - 'payment_mode' => [ 'type' => 'string' ],
1859 1081 ],
1860 1082 ],
1861 1083 'execute_callback' => static function ( $input ) use ( $runtime ) {
1862 1084 return $runtime->get_donation_trends( $input );
1863 1085 },
1864 - 'meta' => self::build_meta( 'read', 1.0, true, false, true ),
1865 - ],
1866 -
1867 - $ns . 'get-dashboard-stats' => [
1868 - 'label' => __( 'Get donation dashboard stats', 'suredonation' ),
1869 - 'description' => __( 'Returns the site-wide donation totals: number of donations, amount raised, unique donors, average and largest donation, and how many campaigns are published. The single best call for answering "how are we doing?".', 'suredonation' ),
1870 - 'category' => 'suredonation',
1871 - 'permission_callback' => $perm_read,
1872 - 'input_schema' => [
1873 - 'type' => 'object',
1874 - 'properties' => [
1875 - 'currency' => [
1876 - 'type' => 'string',
1877 - 'default' => '',
1878 - 'description' => __( 'Three-letter currency code to report on. Defaults to the store currency. Amounts across currencies are never summed together.', 'suredonation' ),
1879 - ],
1880 - 'payment_mode' => [
1881 - 'type' => 'string',
1882 - 'enum' => [ 'test', 'live' ],
1883 - 'default' => '',
1884 - 'description' => __( 'Report on test or live donations. Defaults to the store\'s current mode. Test and live figures are never summed together.', 'suredonation' ),
1885 - ],
1886 - ],
1887 - ],
1888 - 'output_schema' => [
1889 - 'type' => 'object',
1890 - 'properties' => [
1891 - 'total_donations' => [ 'type' => 'integer' ],
1892 - 'total_raised' => [ 'type' => 'number' ],
1893 - 'unique_donors' => [ 'type' => 'integer' ],
1894 - 'average_donation' => [ 'type' => 'number' ],
1895 - 'largest_donation' => [ 'type' => 'number' ],
1896 - 'published_campaigns' => [ 'type' => 'integer' ],
1897 - 'currency' => [ 'type' => 'string' ],
1898 - 'payment_mode' => [ 'type' => 'string' ],
1899 - ],
1900 - ],
1901 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
1902 - return $runtime->get_dashboard_stats( $input );
1903 - },
1904 - 'meta' => self::build_meta( 'read', 1.0, true, false, true ),
1905 - ],
1906 -
1907 - $ns . 'get-recent-donations' => [
1908 - 'label' => __( 'Get recent donations', 'suredonation' ),
1909 - 'description' => __( 'Returns the most recent donations across all campaigns, newest first.', 'suredonation' ),
1910 - 'category' => 'suredonation',
1911 - 'permission_callback' => $perm_read,
1912 - 'input_schema' => [
1913 - 'type' => 'object',
1914 - 'properties' => [
1915 - 'limit' => [
1916 - 'type' => 'integer',
1917 - 'default' => 5,
1918 - 'description' => __( 'How many donations to return (max 100).', 'suredonation' ),
1919 - ],
1920 - 'currency' => [
1921 - 'type' => 'string',
1922 - 'default' => '',
1923 - 'description' => __( 'Three-letter currency code to report on. Defaults to the store currency. Amounts across currencies are never summed together.', 'suredonation' ),
1924 - ],
1925 - 'payment_mode' => [
1926 - 'type' => 'string',
1927 - 'enum' => [ 'test', 'live' ],
1928 - 'default' => '',
1929 - 'description' => __( 'Report on test or live donations. Defaults to the store\'s current mode. Test and live figures are never summed together.', 'suredonation' ),
1930 - ],
1931 - ],
1932 - ],
1933 - 'output_schema' => [
1934 - 'type' => 'object',
1935 - 'properties' => [
1936 - 'donations' => [
1937 - 'type' => 'array',
1938 - 'items' => [
1939 - 'type' => 'object',
1940 - 'properties' => [
1941 - 'id' => [ 'type' => 'integer' ],
1942 - 'campaign_id' => [ 'type' => 'integer' ],
1943 - 'campaign_title' => [ 'type' => 'string' ],
1944 - 'donor_name' => [ 'type' => 'string' ],
1945 - 'donor_email' => [ 'type' => 'string' ],
1946 - 'amount' => [ 'type' => 'number' ],
1947 - 'currency' => [ 'type' => 'string' ],
1948 - 'payment_status' => [ 'type' => 'string' ],
1949 - 'created_at' => [ 'type' => 'string' ],
1950 - ],
1951 - 'additionalProperties' => false,
1952 - ],
1953 - ],
1954 - 'currency' => [ 'type' => 'string' ],
1955 - 'payment_mode' => [ 'type' => 'string' ],
1956 - ],
1957 - ],
1958 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
1959 - return $runtime->get_recent_donations( $input );
1960 - },
1961 - 'meta' => self::build_meta( 'list', 1.0, true, false, true ),
1962 - ],
1963 -
1964 - $ns . 'get-top-campaigns' => [
1965 - 'label' => __( 'Get top campaigns', 'suredonation' ),
1966 - 'description' => __( 'Returns the campaigns that have raised the most, ranked by amount raised.', 'suredonation' ),
1967 - 'category' => 'suredonation',
1968 - 'permission_callback' => $perm_read,
1969 - 'input_schema' => [
1970 - 'type' => 'object',
1971 - 'properties' => [
1972 - 'limit' => [
1973 - 'type' => 'integer',
1974 - 'default' => 5,
1975 - 'description' => __( 'How many campaigns to return (max 100).', 'suredonation' ),
1976 - ],
1977 - 'currency' => [
1978 - 'type' => 'string',
1979 - 'default' => '',
1980 - 'description' => __( 'Three-letter currency code to report on. Defaults to the store currency. Amounts across currencies are never summed together.', 'suredonation' ),
1981 - ],
1982 - 'payment_mode' => [
1983 - 'type' => 'string',
1984 - 'enum' => [ 'test', 'live' ],
1985 - 'default' => '',
1986 - 'description' => __( 'Report on test or live donations. Defaults to the store\'s current mode. Test and live figures are never summed together.', 'suredonation' ),
1987 - ],
1988 - ],
1989 - ],
1990 - 'output_schema' => [
1991 - 'type' => 'object',
1992 - 'properties' => [
1993 - 'campaigns' => [
1994 - 'type' => 'array',
1995 - 'items' => [
1996 - 'type' => 'object',
1997 - 'properties' => [
1998 - 'id' => [ 'type' => 'integer' ],
1999 - 'title' => [ 'type' => 'string' ],
2000 - 'total_raised' => [ 'type' => 'number' ],
2001 - 'donation_count' => [ 'type' => 'integer' ],
2002 - ],
2003 - ],
2004 - ],
2005 - 'currency' => [ 'type' => 'string' ],
2006 - 'payment_mode' => [ 'type' => 'string' ],
2007 - ],
2008 - ],
2009 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
2010 - return $runtime->get_top_campaigns( $input );
2011 - },
2012 - 'meta' => self::build_meta( 'list', 1.0, true, false, true ),
2013 - ],
2014 -
2015 - $ns . 'get-settings' => [
2016 - 'label' => __( 'Get donation settings', 'suredonation' ),
2017 - 'description' => __( 'Returns the non-sensitive store settings: currency and how its symbol is positioned, whether payments are in test or live mode, and the donor/spam options. Payment credentials and the AI settings that gate these abilities are never returned.', 'suredonation' ),
2018 - 'category' => 'suredonation',
2019 - 'permission_callback' => $perm_read,
2020 - 'input_schema' => [
2021 - 'type' => 'object',
2022 - 'properties' => [],
2023 - ],
2024 - 'output_schema' => [
2025 - 'type' => 'object',
2026 - 'properties' => [
2027 - 'currency' => [ 'type' => 'string' ],
2028 - 'currency_symbol' => [ 'type' => 'string' ],
2029 - 'currency_sign_position' => [ 'type' => 'string' ],
2030 - 'payment_mode' => [
2031 - 'type' => 'string',
2032 - 'description' => __( 'test or live. Global, not per gateway.', 'suredonation' ),
2033 - ],
2034 - 'honeypot_enabled' => [ 'type' => 'boolean' ],
2035 - 'create_wp_user' => [ 'type' => 'boolean' ],
2036 - ],
2037 - ],
2038 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
2039 - return $runtime->get_settings( $input );
2040 - },
2041 - 'meta' => self::build_meta( 'read', 1.0, true, false, true ),
2042 - ],
2043 -
2044 - $ns . 'get-payment-gateways' => [
2045 - 'label' => __( 'Get payment gateway status', 'suredonation' ),
2046 - 'description' => __( 'Returns which payment gateways are connected and whether the store is in test or live mode. Connection state only, never credentials. Useful for diagnosing why a donation form offers no payment options.', 'suredonation' ),
2047 - 'category' => 'suredonation',
2048 - 'permission_callback' => $perm_read,
2049 - 'input_schema' => [
2050 - 'type' => 'object',
2051 - 'properties' => [],
2052 - ],
2053 - 'output_schema' => [
2054 - 'type' => 'object',
2055 - 'properties' => [
2056 - 'payment_mode' => [ 'type' => 'string' ],
2057 - 'gateways' => [
2058 - 'type' => 'array',
2059 - 'items' => [
2060 - 'type' => 'object',
2061 - 'properties' => [
2062 - 'id' => [ 'type' => 'string' ],
2063 - 'connected' => [ 'type' => 'boolean' ],
2064 - ],
2065 - ],
2066 - ],
2067 - ],
2068 - ],
2069 - 'execute_callback' => static function ( $input ) use ( $runtime ) {
2070 - return $runtime->get_payment_gateways( $input );
2071 - },
2072 - 'meta' => self::build_meta( 'read', 1.0, true, false, true ),
1086 + 'meta' => self::build_meta( 1.0, true, false, true ),
2073 1087 ],
2074 1088 ];
2075 1089 }
2076 1090 }