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