| 1 |
<?php |
| 2 |
/** |
| 3 |
* Abilities API Configuration |
| 4 |
* |
| 5 |
* Defines all ability configurations for the SureDonation plugin. |
| 6 |
* |
| 7 |
* @package SureDonation |
| 8 |
* @since 0.0.1 |
| 9 |
*/ |
| 10 |
|
| 11 |
namespace SureDonation\Inc\Abilities; |
| 12 |
|
| 13 |
use SureDonation\Inc\Database\Tables\Donations; |
| 14 |
use SureDonation\Inc\Helper; |
| 15 |
|
| 16 |
// Exit if accessed directly. |
| 17 |
if ( ! defined( 'ABSPATH' ) ) { |
| 18 |
exit; |
| 19 |
} |
| 20 |
|
| 21 |
/** |
| 22 |
* Config_Ability class. |
| 23 |
* |
| 24 |
* @since 0.0.1 |
| 25 |
*/ |
| 26 |
class Config_Ability { |
| 27 |
/** |
| 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 |
* Cached abilities. |
| 43 |
* |
| 44 |
* @var array<string, array<string, mixed>>|null |
| 45 |
*/ |
| 46 |
private static $abilities = null; |
| 47 |
|
| 48 |
/** |
| 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 |
* Get all ability configurations. |
| 94 |
* |
| 95 |
* @return array<string, array<string, mixed>> Ability definitions. |
| 96 |
*/ |
| 97 |
public static function get_abilities() { |
| 98 |
if ( null !== self::$abilities ) { |
| 99 |
return self::$abilities; |
| 100 |
} |
| 101 |
|
| 102 |
$runtime = new Runtime(); |
| 103 |
|
| 104 |
$perm_read = static function () use ( $runtime ) { |
| 105 |
return $runtime->permission_callback( 'manage_options' ); |
| 106 |
}; |
| 107 |
|
| 108 |
$perm_edit = static function () use ( $runtime ) { |
| 109 |
return self::is_gate_open( self::GATE_UPDATE ) && $runtime->permission_callback( 'manage_options' ); |
| 110 |
}; |
| 111 |
|
| 112 |
$perm_delete = static function () use ( $runtime ) { |
| 113 |
return self::is_gate_open( self::GATE_DELETE ) && $runtime->permission_callback( 'manage_options' ); |
| 114 |
}; |
| 115 |
|
| 116 |
$abilities = array_merge( |
| 117 |
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 ), |
| 121 |
self::get_analytics_abilities( $runtime, $perm_read ) |
| 122 |
); |
| 123 |
|
| 124 |
/** |
| 125 |
* Filter SureDonation ability configurations. |
| 126 |
* |
| 127 |
* @param array $abilities Ability definitions. |
| 128 |
*/ |
| 129 |
$abilities = apply_filters( 'suredonation_config_abilities', $abilities ); |
| 130 |
if ( ! is_array( $abilities ) ) { |
| 131 |
$abilities = []; |
| 132 |
} |
| 133 |
|
| 134 |
self::$abilities = $abilities; |
| 135 |
|
| 136 |
return $abilities; |
| 137 |
} |
| 138 |
|
| 139 |
/** |
| 140 |
* Get a single ability config by name. |
| 141 |
* |
| 142 |
* @param string $ability_name Ability identifier. |
| 143 |
* @return array<string, mixed>|false Ability config or false. |
| 144 |
*/ |
| 145 |
public static function get_ability( $ability_name ) { |
| 146 |
if ( null === self::$abilities ) { |
| 147 |
self::$abilities = self::get_abilities(); |
| 148 |
} |
| 149 |
return self::$abilities[ $ability_name ] ?? false; |
| 150 |
} |
| 151 |
|
| 152 |
/** |
| 153 |
* Get ability input schema. |
| 154 |
* |
| 155 |
* @param string $ability_name Ability identifier. |
| 156 |
* @return array<string, mixed>|false Input schema or false. |
| 157 |
*/ |
| 158 |
public static function get_ability_input_schema( $ability_name ) { |
| 159 |
$ability = self::get_ability( $ability_name ); |
| 160 |
if ( false === $ability ) { |
| 161 |
return false; |
| 162 |
} |
| 163 |
$schema = $ability['input_schema'] ?? false; |
| 164 |
return is_array( $schema ) ? $schema : false; |
| 165 |
} |
| 166 |
|
| 167 |
/** |
| 168 |
* Build meta block for an ability. |
| 169 |
* |
| 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. |
| 194 |
* @return array<string, mixed> Meta configuration. |
| 195 |
*/ |
| 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 |
|
| 211 |
return [ |
| 212 |
'show_in_rest' => true, |
| 213 |
'tool_type' => $tool_type, |
| 214 |
'annotations' => $annotations, |
| 215 |
'mcp' => [ |
| 216 |
'public' => false, |
| 217 |
'type' => 'tool', |
| 218 |
], |
| 219 |
]; |
| 220 |
} |
| 221 |
|
| 222 |
/** |
| 223 |
* Get campaign ability configurations. |
| 224 |
* |
| 225 |
* @param Runtime $runtime Runtime instance. |
| 226 |
* @param callable $perm_read Read permission closure. |
| 227 |
* @param callable $perm_edit Edit permission closure. |
| 228 |
* @param callable $perm_delete Delete permission closure. |
| 229 |
* @return array<string, array<string, mixed>> Campaign abilities. |
| 230 |
*/ |
| 231 |
private static function get_campaign_abilities( $runtime, $perm_read, $perm_edit, $perm_delete ) { |
| 232 |
$ns = SUREDONATION_ABILITY_API_NAMESPACE; |
| 233 |
|
| 234 |
return [ |
| 235 |
$ns . 'list-campaigns' => [ |
| 236 |
'label' => __( 'List campaigns', 'suredonation' ), |
| 237 |
'description' => __( 'Returns a paginated list of fundraising campaigns with optional search, status filter, and sorting.', 'suredonation' ), |
| 238 |
'category' => 'suredonation', |
| 239 |
'permission_callback' => $perm_read, |
| 240 |
'input_schema' => [ |
| 241 |
'type' => 'object', |
| 242 |
'properties' => [ |
| 243 |
'search' => [ |
| 244 |
'type' => 'string', |
| 245 |
'description' => __( 'Search campaigns by title.', 'suredonation' ), |
| 246 |
'default' => '', |
| 247 |
], |
| 248 |
'status' => [ |
| 249 |
'type' => 'string', |
| 250 |
'enum' => [ 'all', 'publish', 'draft', 'trash', 'paused' ], |
| 251 |
'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' ), |
| 253 |
], |
| 254 |
'sort_by' => [ |
| 255 |
'type' => 'string', |
| 256 |
'enum' => [ 'date', 'title', 'status' ], |
| 257 |
'default' => 'date', |
| 258 |
'description' => __( 'Column to sort by.', 'suredonation' ), |
| 259 |
], |
| 260 |
'order' => [ |
| 261 |
'type' => 'string', |
| 262 |
'enum' => [ 'ASC', 'DESC' ], |
| 263 |
'default' => 'DESC', |
| 264 |
'description' => __( 'Sort direction.', 'suredonation' ), |
| 265 |
], |
| 266 |
'page' => [ |
| 267 |
'type' => 'integer', |
| 268 |
'default' => 1, |
| 269 |
'description' => __( 'Page number (1-based).', 'suredonation' ), |
| 270 |
], |
| 271 |
'per_page' => [ |
| 272 |
'type' => 'integer', |
| 273 |
'default' => 20, |
| 274 |
'description' => __( 'Results per page (max 100).', 'suredonation' ), |
| 275 |
], |
| 276 |
], |
| 277 |
], |
| 278 |
'output_schema' => [ |
| 279 |
'type' => 'object', |
| 280 |
'properties' => [ |
| 281 |
'campaigns' => [ |
| 282 |
'type' => 'array', |
| 283 |
'items' => [ |
| 284 |
'type' => 'object', |
| 285 |
'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' ], |
| 307 |
], |
| 308 |
], |
| 309 |
], |
| 310 |
'total' => [ |
| 311 |
'type' => 'integer', |
| 312 |
'description' => __( 'Total matching campaigns.', 'suredonation' ), |
| 313 |
], |
| 314 |
'total_pages' => [ |
| 315 |
'type' => 'integer', |
| 316 |
'description' => __( 'Total pages.', 'suredonation' ), |
| 317 |
], |
| 318 |
], |
| 319 |
], |
| 320 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 321 |
return $runtime->list_campaigns( $input ); |
| 322 |
}, |
| 323 |
'meta' => self::build_meta( 'list', 1.0, true, false, true ), |
| 324 |
], |
| 325 |
|
| 326 |
$ns . 'get-campaign' => [ |
| 327 |
'label' => __( 'Get campaign', 'suredonation' ), |
| 328 |
'description' => __( 'Returns a single fundraising campaign by ID with real-time stats including total raised, donor count, and progress.', 'suredonation' ), |
| 329 |
'category' => 'suredonation', |
| 330 |
'permission_callback' => $perm_read, |
| 331 |
'input_schema' => [ |
| 332 |
'type' => 'object', |
| 333 |
'required' => [ 'id' ], |
| 334 |
'properties' => [ |
| 335 |
'id' => [ |
| 336 |
'type' => 'integer', |
| 337 |
'description' => __( 'The campaign ID.', 'suredonation' ), |
| 338 |
], |
| 339 |
], |
| 340 |
], |
| 341 |
'output_schema' => [ |
| 342 |
'type' => 'object', |
| 343 |
'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' ], |
| 371 |
], |
| 372 |
], |
| 373 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 374 |
return $runtime->get_campaign( $input ); |
| 375 |
}, |
| 376 |
'meta' => self::build_meta( 'read', 1.0, true, false, true ), |
| 377 |
], |
| 378 |
|
| 379 |
$ns . 'create-campaign' => [ |
| 380 |
'label' => __( 'Create campaign', 'suredonation' ), |
| 381 |
'description' => __( 'Creates a new fundraising campaign with title, description, goal settings, and optional fee coverage/terms configuration.', 'suredonation' ), |
| 382 |
'category' => 'suredonation', |
| 383 |
'permission_callback' => $perm_edit, |
| 384 |
'input_schema' => [ |
| 385 |
'type' => 'object', |
| 386 |
'required' => [ 'title' ], |
| 387 |
'properties' => [ |
| 388 |
'title' => [ |
| 389 |
'type' => 'string', |
| 390 |
'description' => __( 'Campaign title.', 'suredonation' ), |
| 391 |
], |
| 392 |
'description' => [ |
| 393 |
'type' => 'string', |
| 394 |
'format' => 'html', |
| 395 |
'description' => __( 'Campaign description (HTML allowed).', 'suredonation' ), |
| 396 |
'default' => '', |
| 397 |
], |
| 398 |
'goal_type' => [ |
| 399 |
'type' => 'string', |
| 400 |
'enum' => [ 'raised_amount', 'donation_count' ], |
| 401 |
'default' => 'raised_amount', |
| 402 |
'description' => __( 'Goal type: track by amount raised or donation count.', 'suredonation' ), |
| 403 |
], |
| 404 |
'goal_amount' => [ |
| 405 |
'type' => 'number', |
| 406 |
'description' => __( 'Goal amount (0 for no goal).', 'suredonation' ), |
| 407 |
'default' => 0, |
| 408 |
], |
| 409 |
'campaign_status' => [ |
| 410 |
'type' => 'string', |
| 411 |
'enum' => [ 'active', 'paused', 'completed' ], |
| 412 |
'default' => 'active', |
| 413 |
'description' => __( 'Campaign status.', 'suredonation' ), |
| 414 |
], |
| 415 |
'require_terms' => [ |
| 416 |
'type' => 'boolean', |
| 417 |
'default' => false, |
| 418 |
'description' => __( 'Require terms acceptance before donating.', 'suredonation' ), |
| 419 |
], |
| 420 |
'terms_text' => [ |
| 421 |
'type' => 'string', |
| 422 |
'default' => '', |
| 423 |
'description' => __( 'Terms and conditions text.', 'suredonation' ), |
| 424 |
], |
| 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 |
], |
| 436 |
], |
| 437 |
'output_schema' => [ |
| 438 |
'type' => 'object', |
| 439 |
'properties' => [ |
| 440 |
'id' => [ |
| 441 |
'type' => 'integer', |
| 442 |
'description' => __( 'New campaign ID.', 'suredonation' ), |
| 443 |
], |
| 444 |
'title' => [ 'type' => 'string' ], |
| 445 |
'status' => [ 'type' => 'string' ], |
| 446 |
'message' => [ 'type' => 'string' ], |
| 447 |
], |
| 448 |
], |
| 449 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 450 |
return $runtime->create_campaign( $input ); |
| 451 |
}, |
| 452 |
'meta' => self::build_meta( 'write', 2.0, false, false, false ), |
| 453 |
'gate' => self::GATE_UPDATE, |
| 454 |
], |
| 455 |
|
| 456 |
$ns . 'update-campaign' => [ |
| 457 |
'label' => __( 'Update campaign', 'suredonation' ), |
| 458 |
'description' => __( 'Updates an existing campaign. All fields except ID are optional — only provided fields are updated.', 'suredonation' ), |
| 459 |
'category' => 'suredonation', |
| 460 |
'permission_callback' => $perm_edit, |
| 461 |
'input_schema' => [ |
| 462 |
'type' => 'object', |
| 463 |
'required' => [ 'id' ], |
| 464 |
'properties' => [ |
| 465 |
'id' => [ |
| 466 |
'type' => 'integer', |
| 467 |
'description' => __( 'Campaign ID to update.', 'suredonation' ), |
| 468 |
], |
| 469 |
'title' => [ |
| 470 |
'type' => 'string', |
| 471 |
'description' => __( 'Campaign title.', 'suredonation' ), |
| 472 |
], |
| 473 |
'description' => [ |
| 474 |
'type' => 'string', |
| 475 |
'format' => 'html', |
| 476 |
'description' => __( 'Campaign description (HTML allowed).', 'suredonation' ), |
| 477 |
], |
| 478 |
'goal_type' => [ |
| 479 |
'type' => 'string', |
| 480 |
'enum' => [ 'raised_amount', 'donation_count' ], |
| 481 |
'description' => __( 'Goal type.', 'suredonation' ), |
| 482 |
], |
| 483 |
'goal_amount' => [ |
| 484 |
'type' => 'number', |
| 485 |
'description' => __( 'Goal amount.', 'suredonation' ), |
| 486 |
], |
| 487 |
'campaign_status' => [ |
| 488 |
'type' => 'string', |
| 489 |
'enum' => [ 'active', 'paused', 'completed' ], |
| 490 |
'description' => __( 'Campaign status.', 'suredonation' ), |
| 491 |
], |
| 492 |
'require_terms' => [ |
| 493 |
'type' => 'boolean', |
| 494 |
'description' => __( 'Require terms acceptance.', 'suredonation' ), |
| 495 |
], |
| 496 |
'terms_text' => [ |
| 497 |
'type' => 'string', |
| 498 |
'description' => __( 'Terms and conditions text.', 'suredonation' ), |
| 499 |
], |
| 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 |
], |
| 509 |
], |
| 510 |
'output_schema' => [ |
| 511 |
'type' => 'object', |
| 512 |
'properties' => [ |
| 513 |
'id' => [ 'type' => 'integer' ], |
| 514 |
'title' => [ 'type' => 'string' ], |
| 515 |
'status' => [ 'type' => 'string' ], |
| 516 |
'message' => [ 'type' => 'string' ], |
| 517 |
], |
| 518 |
], |
| 519 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 520 |
return $runtime->update_campaign( $input ); |
| 521 |
}, |
| 522 |
'meta' => self::build_meta( 'write', 2.0, false, false, false ), |
| 523 |
'gate' => self::GATE_UPDATE, |
| 524 |
], |
| 525 |
|
| 526 |
$ns . 'delete-campaign' => [ |
| 527 |
'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' ), |
| 529 |
'category' => 'suredonation', |
| 530 |
'permission_callback' => $perm_delete, |
| 531 |
'input_schema' => [ |
| 532 |
'type' => 'object', |
| 533 |
'required' => [ 'id' ], |
| 534 |
'properties' => [ |
| 535 |
'id' => [ |
| 536 |
'type' => 'integer', |
| 537 |
'description' => __( 'Campaign ID to delete.', 'suredonation' ), |
| 538 |
], |
| 539 |
], |
| 540 |
], |
| 541 |
'output_schema' => [ |
| 542 |
'type' => 'object', |
| 543 |
'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' ], |
| 556 |
], |
| 557 |
], |
| 558 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 559 |
return $runtime->delete_campaign( $input ); |
| 560 |
}, |
| 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, |
| 563 |
], |
| 564 |
|
| 565 |
$ns . 'duplicate-campaign' => [ |
| 566 |
'label' => __( 'Duplicate campaign', 'suredonation' ), |
| 567 |
'description' => __( 'Creates a copy of an existing campaign as a draft. Copies title (with " (Copy)" suffix), description, and settings.', 'suredonation' ), |
| 568 |
'category' => 'suredonation', |
| 569 |
'permission_callback' => $perm_edit, |
| 570 |
'input_schema' => [ |
| 571 |
'type' => 'object', |
| 572 |
'required' => [ 'id' ], |
| 573 |
'properties' => [ |
| 574 |
'id' => [ |
| 575 |
'type' => 'integer', |
| 576 |
'description' => __( 'Campaign ID to duplicate.', 'suredonation' ), |
| 577 |
], |
| 578 |
], |
| 579 |
], |
| 580 |
'output_schema' => [ |
| 581 |
'type' => 'object', |
| 582 |
'properties' => [ |
| 583 |
'id' => [ |
| 584 |
'type' => 'integer', |
| 585 |
'description' => __( 'New campaign ID.', 'suredonation' ), |
| 586 |
], |
| 587 |
'title' => [ 'type' => 'string' ], |
| 588 |
'message' => [ 'type' => 'string' ], |
| 589 |
], |
| 590 |
], |
| 591 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 592 |
return $runtime->duplicate_campaign( $input ); |
| 593 |
}, |
| 594 |
'meta' => self::build_meta( 'write', 2.0, false, false, false ), |
| 595 |
'gate' => self::GATE_UPDATE, |
| 596 |
], |
| 597 |
|
| 598 |
$ns . 'get-campaign-form-locations' => [ |
| 599 |
'label' => __( 'Get campaign form locations', 'suredonation' ), |
| 600 |
'description' => __( 'Finds all pages and posts where a campaign donation form block is embedded. Returns page IDs, titles, and edit/view URLs.', 'suredonation' ), |
| 601 |
'category' => 'suredonation', |
| 602 |
'permission_callback' => $perm_read, |
| 603 |
'input_schema' => [ |
| 604 |
'type' => 'object', |
| 605 |
'required' => [ 'id' ], |
| 606 |
'properties' => [ |
| 607 |
'id' => [ |
| 608 |
'type' => 'integer', |
| 609 |
'description' => __( 'Campaign ID.', 'suredonation' ), |
| 610 |
], |
| 611 |
], |
| 612 |
], |
| 613 |
'output_schema' => [ |
| 614 |
'type' => 'object', |
| 615 |
'properties' => [ |
| 616 |
'locations' => [ |
| 617 |
'type' => 'array', |
| 618 |
'items' => [ |
| 619 |
'type' => 'object', |
| 620 |
'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' ], |
| 628 |
], |
| 629 |
], |
| 630 |
], |
| 631 |
], |
| 632 |
], |
| 633 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 634 |
return $runtime->get_campaign_form_locations( $input ); |
| 635 |
}, |
| 636 |
'meta' => self::build_meta( 'read', 1.0, true, false, true ), |
| 637 |
], |
| 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 |
]; |
| 676 |
} |
| 677 |
|
| 678 |
/** |
| 679 |
* Get donation ability configurations. |
| 680 |
* |
| 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. |
| 685 |
* @return array<string, array<string, mixed>> Donation abilities. |
| 686 |
*/ |
| 687 |
private static function get_donation_abilities( $runtime, $perm_read, $perm_edit, $perm_delete ) { |
| 688 |
$ns = SUREDONATION_ABILITY_API_NAMESPACE; |
| 689 |
|
| 690 |
return [ |
| 691 |
$ns . 'list-donations' => [ |
| 692 |
'label' => __( 'List donations', 'suredonation' ), |
| 693 |
'description' => __( 'Returns a paginated list of donations with optional search, status filter, campaign filter, and sorting.', 'suredonation' ), |
| 694 |
'category' => 'suredonation', |
| 695 |
'permission_callback' => $perm_read, |
| 696 |
'input_schema' => [ |
| 697 |
'type' => 'object', |
| 698 |
'properties' => [ |
| 699 |
'search' => [ |
| 700 |
'type' => 'string', |
| 701 |
'description' => __( 'Search donations by donor name, donor email, or transaction ID.', 'suredonation' ), |
| 702 |
'default' => '', |
| 703 |
], |
| 704 |
'status' => [ |
| 705 |
'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() ), |
| 710 |
'default' => 'all', |
| 711 |
'description' => __( 'Filter by payment status.', 'suredonation' ), |
| 712 |
], |
| 713 |
'campaign_id' => [ |
| 714 |
'type' => 'integer', |
| 715 |
'default' => 0, |
| 716 |
'description' => __( 'Filter by campaign ID (0 for all campaigns).', 'suredonation' ), |
| 717 |
], |
| 718 |
'sort_by' => [ |
| 719 |
'type' => 'string', |
| 720 |
'enum' => [ 'id', 'created_at', 'updated_at', 'amount', 'donor_name', 'donor_email', 'payment_status', 'campaign_id', 'subscription_status' ], |
| 721 |
'default' => 'created_at', |
| 722 |
'description' => __( 'Column to sort by.', 'suredonation' ), |
| 723 |
], |
| 724 |
'order' => [ |
| 725 |
'type' => 'string', |
| 726 |
'enum' => [ 'ASC', 'DESC' ], |
| 727 |
'default' => 'DESC', |
| 728 |
'description' => __( 'Sort direction.', 'suredonation' ), |
| 729 |
], |
| 730 |
'page' => [ |
| 731 |
'type' => 'integer', |
| 732 |
'default' => 1, |
| 733 |
'description' => __( 'Page number (1-based).', 'suredonation' ), |
| 734 |
], |
| 735 |
'per_page' => [ |
| 736 |
'type' => 'integer', |
| 737 |
'default' => 20, |
| 738 |
'description' => __( 'Results per page (max 100).', 'suredonation' ), |
| 739 |
], |
| 740 |
], |
| 741 |
], |
| 742 |
'output_schema' => [ |
| 743 |
'type' => 'object', |
| 744 |
'properties' => [ |
| 745 |
'donations' => [ |
| 746 |
'type' => 'array', |
| 747 |
'items' => [ |
| 748 |
'type' => 'object', |
| 749 |
'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' ], |
| 765 |
], |
| 766 |
], |
| 767 |
], |
| 768 |
'total' => [ |
| 769 |
'type' => 'integer', |
| 770 |
'description' => __( 'Total matching donations.', 'suredonation' ), |
| 771 |
], |
| 772 |
'total_pages' => [ |
| 773 |
'type' => 'integer', |
| 774 |
'description' => __( 'Total pages.', 'suredonation' ), |
| 775 |
], |
| 776 |
], |
| 777 |
], |
| 778 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 779 |
return $runtime->list_donations( $input ); |
| 780 |
}, |
| 781 |
'meta' => self::build_meta( 'list', 1.0, true, false, true ), |
| 782 |
], |
| 783 |
|
| 784 |
$ns . 'get-donation' => [ |
| 785 |
'label' => __( 'Get donation', 'suredonation' ), |
| 786 |
'description' => __( 'Returns a single donation by ID with full details including donor info, payment data, transaction ID, and activity logs.', 'suredonation' ), |
| 787 |
'category' => 'suredonation', |
| 788 |
'permission_callback' => $perm_read, |
| 789 |
'input_schema' => [ |
| 790 |
'type' => 'object', |
| 791 |
'required' => [ 'id' ], |
| 792 |
'properties' => [ |
| 793 |
'id' => [ |
| 794 |
'type' => 'integer', |
| 795 |
'description' => __( 'The donation ID.', 'suredonation' ), |
| 796 |
], |
| 797 |
], |
| 798 |
], |
| 799 |
'output_schema' => [ |
| 800 |
'type' => 'object', |
| 801 |
'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' ], |
| 854 |
], |
| 855 |
], |
| 856 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 857 |
return $runtime->get_donation( $input ); |
| 858 |
}, |
| 859 |
'meta' => self::build_meta( 'read', 1.0, true, false, true ), |
| 860 |
], |
| 861 |
|
| 862 |
$ns . 'get-donation-notes' => [ |
| 863 |
'label' => __( 'Get donation notes', 'suredonation' ), |
| 864 |
'description' => __( 'Returns paginated notes for a donation. Notes are admin-added comments for internal tracking.', 'suredonation' ), |
| 865 |
'category' => 'suredonation', |
| 866 |
'permission_callback' => $perm_read, |
| 867 |
'input_schema' => [ |
| 868 |
'type' => 'object', |
| 869 |
'required' => [ 'id' ], |
| 870 |
'properties' => [ |
| 871 |
'id' => [ |
| 872 |
'type' => 'integer', |
| 873 |
'description' => __( 'The donation ID.', 'suredonation' ), |
| 874 |
], |
| 875 |
'page' => [ |
| 876 |
'type' => 'integer', |
| 877 |
'default' => 1, |
| 878 |
'description' => __( 'Page number.', 'suredonation' ), |
| 879 |
], |
| 880 |
'per_page' => [ |
| 881 |
'type' => 'integer', |
| 882 |
'default' => 10, |
| 883 |
'description' => __( 'Notes per page (max 100).', 'suredonation' ), |
| 884 |
], |
| 885 |
], |
| 886 |
], |
| 887 |
'output_schema' => [ |
| 888 |
'type' => 'object', |
| 889 |
'properties' => [ |
| 890 |
'notes' => [ |
| 891 |
'type' => 'array', |
| 892 |
'items' => [ |
| 893 |
'type' => 'object', |
| 894 |
'properties' => [ |
| 895 |
'id' => [ 'type' => 'string' ], |
| 896 |
'content' => [ 'type' => 'string' ], |
| 897 |
'author_id' => [ 'type' => 'integer' ], |
| 898 |
'author_name' => [ 'type' => 'string' ], |
| 899 |
'created_at' => [ 'type' => 'string' ], |
| 900 |
], |
| 901 |
], |
| 902 |
], |
| 903 |
'total' => [ |
| 904 |
'type' => 'integer', |
| 905 |
'description' => __( 'Total notes.', 'suredonation' ), |
| 906 |
], |
| 907 |
'total_pages' => [ |
| 908 |
'type' => 'integer', |
| 909 |
'description' => __( 'Total pages.', 'suredonation' ), |
| 910 |
], |
| 911 |
], |
| 912 |
], |
| 913 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 914 |
return $runtime->get_donation_notes( $input ); |
| 915 |
}, |
| 916 |
'meta' => self::build_meta( 'read', 1.0, true, false, true ), |
| 917 |
], |
| 918 |
|
| 919 |
$ns . 'add-donation-note' => [ |
| 920 |
'label' => __( 'Add donation note', 'suredonation' ), |
| 921 |
'description' => __( 'Adds an internal note to a donation for admin tracking purposes.', 'suredonation' ), |
| 922 |
'category' => 'suredonation', |
| 923 |
'permission_callback' => $perm_edit, |
| 924 |
'input_schema' => [ |
| 925 |
'type' => 'object', |
| 926 |
'required' => [ 'id', 'note' ], |
| 927 |
'properties' => [ |
| 928 |
'id' => [ |
| 929 |
'type' => 'integer', |
| 930 |
'description' => __( 'The donation ID.', 'suredonation' ), |
| 931 |
], |
| 932 |
'note' => [ |
| 933 |
'type' => 'string', |
| 934 |
'description' => __( 'The note content.', 'suredonation' ), |
| 935 |
], |
| 936 |
], |
| 937 |
], |
| 938 |
'output_schema' => [ |
| 939 |
'type' => 'object', |
| 940 |
'properties' => [ |
| 941 |
'note_id' => [ |
| 942 |
'type' => 'string', |
| 943 |
'description' => __( 'The new note ID.', 'suredonation' ), |
| 944 |
], |
| 945 |
'message' => [ 'type' => 'string' ], |
| 946 |
], |
| 947 |
], |
| 948 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 949 |
return $runtime->add_donation_note( $input ); |
| 950 |
}, |
| 951 |
'meta' => self::build_meta( 'write', 2.0, false, false, false ), |
| 952 |
'gate' => self::GATE_UPDATE, |
| 953 |
], |
| 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 |
]; |
| 1171 |
} |
| 1172 |
|
| 1173 |
/** |
| 1174 |
* Get donor ability configurations. |
| 1175 |
* |
| 1176 |
* @param Runtime $runtime Runtime instance. |
| 1177 |
* @param callable $perm_read Read permission closure. |
| 1178 |
* @param callable $perm_edit Edit permission closure. |
| 1179 |
* @return array<string, array<string, mixed>> Donor abilities. |
| 1180 |
*/ |
| 1181 |
private static function get_donor_abilities( $runtime, $perm_read, $perm_edit ) { |
| 1182 |
$ns = SUREDONATION_ABILITY_API_NAMESPACE; |
| 1183 |
|
| 1184 |
$donor_detail_schema = [ |
| 1185 |
'type' => 'object', |
| 1186 |
'properties' => [ |
| 1187 |
'id' => [ 'type' => 'integer' ], |
| 1188 |
'name' => [ 'type' => 'string' ], |
| 1189 |
'email' => [ 'type' => 'string' ], |
| 1190 |
'phone' => [ 'type' => 'string' ], |
| 1191 |
'company' => [ 'type' => 'string' ], |
| 1192 |
'address' => [ 'type' => 'string' ], |
| 1193 |
'stripe_customer_id' => [ 'type' => 'string' ], |
| 1194 |
'user_id' => [ 'type' => 'integer' ], |
| 1195 |
'donor_status' => [ 'type' => 'string' ], |
| 1196 |
'total_donated' => [ 'type' => 'number' ], |
| 1197 |
'donation_count' => [ 'type' => 'integer' ], |
| 1198 |
'largest_donation' => [ 'type' => 'number' ], |
| 1199 |
'first_donation_date' => [ 'type' => 'string' ], |
| 1200 |
'last_donation_date' => [ 'type' => 'string' ], |
| 1201 |
'donor_tags' => [ 'type' => 'array' ], |
| 1202 |
'created_at' => [ 'type' => 'string' ], |
| 1203 |
'updated_at' => [ 'type' => 'string' ], |
| 1204 |
], |
| 1205 |
]; |
| 1206 |
|
| 1207 |
return [ |
| 1208 |
$ns . 'list-donors' => [ |
| 1209 |
'label' => __( 'List donors', 'suredonation' ), |
| 1210 |
'description' => __( 'Returns a paginated list of donors with optional search, status filter, campaign filter, date range, and sorting.', 'suredonation' ), |
| 1211 |
'category' => 'suredonation', |
| 1212 |
'permission_callback' => $perm_read, |
| 1213 |
'input_schema' => [ |
| 1214 |
'type' => 'object', |
| 1215 |
'properties' => [ |
| 1216 |
'search' => [ |
| 1217 |
'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 |
'enum' => [ 'all', 'active', 'inactive', 'blocked' ], |
| 1239 |
'default' => 'all', |
| 1240 |
'description' => __( 'Filter by donor status.', 'suredonation' ), |
| 1241 |
], |
| 1242 |
'sort_by' => [ |
| 1243 |
'type' => 'string', |
| 1244 |
'enum' => [ 'id', 'created_at', 'updated_at', 'name', 'email', 'total_donated', 'donation_count', 'last_donation_date' ], |
| 1245 |
'default' => 'created_at', |
| 1246 |
'description' => __( 'Column to sort by.', 'suredonation' ), |
| 1247 |
], |
| 1248 |
'order' => [ |
| 1249 |
'type' => 'string', |
| 1250 |
'enum' => [ 'ASC', 'DESC' ], |
| 1251 |
'default' => 'DESC', |
| 1252 |
'description' => __( 'Sort direction.', 'suredonation' ), |
| 1253 |
], |
| 1254 |
'page' => [ |
| 1255 |
'type' => 'integer', |
| 1256 |
'default' => 1, |
| 1257 |
'description' => __( 'Page number (1-based).', 'suredonation' ), |
| 1258 |
], |
| 1259 |
'per_page' => [ |
| 1260 |
'type' => 'integer', |
| 1261 |
'default' => 20, |
| 1262 |
'description' => __( 'Results per page (max 100).', 'suredonation' ), |
| 1263 |
], |
| 1264 |
], |
| 1265 |
], |
| 1266 |
'output_schema' => [ |
| 1267 |
'type' => 'object', |
| 1268 |
'properties' => [ |
| 1269 |
'donors' => [ |
| 1270 |
'type' => 'array', |
| 1271 |
'items' => [ |
| 1272 |
'type' => 'object', |
| 1273 |
'properties' => [ |
| 1274 |
'id' => [ 'type' => 'integer' ], |
| 1275 |
'name' => [ 'type' => 'string' ], |
| 1276 |
'email' => [ 'type' => 'string' ], |
| 1277 |
'phone' => [ 'type' => 'string' ], |
| 1278 |
'company' => [ 'type' => 'string' ], |
| 1279 |
'address' => [ 'type' => 'string' ], |
| 1280 |
'stripe_customer_id' => [ 'type' => 'string' ], |
| 1281 |
'donor_status' => [ 'type' => 'string' ], |
| 1282 |
'total_donated' => [ 'type' => 'number' ], |
| 1283 |
'donation_count' => [ 'type' => 'integer' ], |
| 1284 |
'largest_donation' => [ 'type' => 'number' ], |
| 1285 |
'first_donation_date' => [ 'type' => 'string' ], |
| 1286 |
'last_donation_date' => [ 'type' => 'string' ], |
| 1287 |
'created_at' => [ 'type' => 'string' ], |
| 1288 |
], |
| 1289 |
], |
| 1290 |
], |
| 1291 |
'total' => [ |
| 1292 |
'type' => 'integer', |
| 1293 |
'description' => __( 'Total matching donors.', 'suredonation' ), |
| 1294 |
], |
| 1295 |
'total_pages' => [ |
| 1296 |
'type' => 'integer', |
| 1297 |
'description' => __( 'Total pages.', 'suredonation' ), |
| 1298 |
], |
| 1299 |
], |
| 1300 |
], |
| 1301 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 1302 |
return $runtime->list_donors( $input ); |
| 1303 |
}, |
| 1304 |
'meta' => self::build_meta( 'list', 1.0, true, false, true ), |
| 1305 |
], |
| 1306 |
|
| 1307 |
$ns . 'get-donor' => [ |
| 1308 |
'label' => __( 'Get donor', 'suredonation' ), |
| 1309 |
'description' => __( 'Returns a single donor by ID with full stats including total donated, donation count, largest donation, and donation dates.', 'suredonation' ), |
| 1310 |
'category' => 'suredonation', |
| 1311 |
'permission_callback' => $perm_read, |
| 1312 |
'input_schema' => [ |
| 1313 |
'type' => 'object', |
| 1314 |
'required' => [ 'id' ], |
| 1315 |
'properties' => [ |
| 1316 |
'id' => [ |
| 1317 |
'type' => 'integer', |
| 1318 |
'description' => __( 'The donor ID.', 'suredonation' ), |
| 1319 |
], |
| 1320 |
], |
| 1321 |
], |
| 1322 |
'output_schema' => $donor_detail_schema, |
| 1323 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 1324 |
return $runtime->get_donor( $input ); |
| 1325 |
}, |
| 1326 |
'meta' => self::build_meta( 'read', 1.0, true, false, true ), |
| 1327 |
], |
| 1328 |
|
| 1329 |
$ns . 'get-donor-by-email' => [ |
| 1330 |
'label' => __( 'Get donor by email', 'suredonation' ), |
| 1331 |
'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 |
'category' => 'suredonation', |
| 1333 |
'permission_callback' => $perm_read, |
| 1334 |
'input_schema' => [ |
| 1335 |
'type' => 'object', |
| 1336 |
'required' => [ 'email' ], |
| 1337 |
'properties' => [ |
| 1338 |
'email' => [ |
| 1339 |
'type' => 'string', |
| 1340 |
'description' => __( 'The donor email address.', 'suredonation' ), |
| 1341 |
], |
| 1342 |
], |
| 1343 |
], |
| 1344 |
'output_schema' => $donor_detail_schema, |
| 1345 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 1346 |
return $runtime->get_donor_by_email( $input ); |
| 1347 |
}, |
| 1348 |
'meta' => self::build_meta( 'read', 1.0, true, false, true ), |
| 1349 |
], |
| 1350 |
|
| 1351 |
$ns . 'get-top-donors' => [ |
| 1352 |
'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' ), |
| 1354 |
'category' => 'suredonation', |
| 1355 |
'permission_callback' => $perm_read, |
| 1356 |
'input_schema' => [ |
| 1357 |
'type' => 'object', |
| 1358 |
'properties' => [ |
| 1359 |
'limit' => [ |
| 1360 |
'type' => 'integer', |
| 1361 |
'default' => 10, |
| 1362 |
'description' => __( 'Number of top donors to return (max 100).', 'suredonation' ), |
| 1363 |
], |
| 1364 |
], |
| 1365 |
], |
| 1366 |
'output_schema' => [ |
| 1367 |
'type' => 'object', |
| 1368 |
'properties' => [ |
| 1369 |
'donors' => [ |
| 1370 |
'type' => 'array', |
| 1371 |
'items' => [ |
| 1372 |
'type' => 'object', |
| 1373 |
'properties' => [ |
| 1374 |
'id' => [ 'type' => 'integer' ], |
| 1375 |
'name' => [ 'type' => 'string' ], |
| 1376 |
'email' => [ 'type' => 'string' ], |
| 1377 |
'total_donated' => [ 'type' => 'number' ], |
| 1378 |
'donation_count' => [ 'type' => 'integer' ], |
| 1379 |
], |
| 1380 |
], |
| 1381 |
], |
| 1382 |
], |
| 1383 |
], |
| 1384 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 1385 |
return $runtime->get_top_donors( $input ); |
| 1386 |
}, |
| 1387 |
'meta' => self::build_meta( 'read', 1.0, true, false, true ), |
| 1388 |
], |
| 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 |
]; |
| 1512 |
} |
| 1513 |
|
| 1514 |
/** |
| 1515 |
* Get form ability configurations. |
| 1516 |
* |
| 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. |
| 1521 |
* @return array<string, array<string, mixed>> Form abilities. |
| 1522 |
*/ |
| 1523 |
private static function get_form_abilities( $runtime, $perm_read, $perm_edit, $perm_delete ) { |
| 1524 |
$ns = SUREDONATION_ABILITY_API_NAMESPACE; |
| 1525 |
|
| 1526 |
return [ |
| 1527 |
$ns . 'list-forms' => [ |
| 1528 |
'label' => __( 'List donation forms', 'suredonation' ), |
| 1529 |
'description' => __( 'Returns donation forms with optional campaign filter and status filter. Forms are the front-end donation widgets linked to campaigns.', 'suredonation' ), |
| 1530 |
'category' => 'suredonation', |
| 1531 |
'permission_callback' => $perm_read, |
| 1532 |
'input_schema' => [ |
| 1533 |
'type' => 'object', |
| 1534 |
'properties' => [ |
| 1535 |
'campaign_id' => [ |
| 1536 |
'type' => 'integer', |
| 1537 |
'default' => 0, |
| 1538 |
'description' => __( 'Filter by campaign ID (0 for all campaigns).', 'suredonation' ), |
| 1539 |
], |
| 1540 |
'status' => [ |
| 1541 |
'type' => 'string', |
| 1542 |
'enum' => [ 'any', 'publish', 'draft', 'trash' ], |
| 1543 |
'default' => 'any', |
| 1544 |
'description' => __( 'Filter by form status.', 'suredonation' ), |
| 1545 |
], |
| 1546 |
'per_page' => [ |
| 1547 |
'type' => 'integer', |
| 1548 |
'default' => 20, |
| 1549 |
'description' => __( 'Results per page (max 100).', 'suredonation' ), |
| 1550 |
], |
| 1551 |
'page' => [ |
| 1552 |
'type' => 'integer', |
| 1553 |
'default' => 1, |
| 1554 |
'description' => __( 'Page number (1-based).', 'suredonation' ), |
| 1555 |
], |
| 1556 |
], |
| 1557 |
], |
| 1558 |
'output_schema' => [ |
| 1559 |
'type' => 'object', |
| 1560 |
'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' => [ |
| 1570 |
'type' => 'array', |
| 1571 |
'items' => [ |
| 1572 |
'type' => 'object', |
| 1573 |
'properties' => [ |
| 1574 |
'id' => [ 'type' => 'integer' ], |
| 1575 |
'title' => [ 'type' => 'string' ], |
| 1576 |
'status' => [ 'type' => 'string' ], |
| 1577 |
'campaign_id' => [ 'type' => 'integer' ], |
| 1578 |
'campaign_name' => [ 'type' => 'string' ], |
| 1579 |
'entries' => [ 'type' => 'integer' ], |
| 1580 |
'revenue' => [ 'type' => 'number' ], |
| 1581 |
'is_default' => [ 'type' => 'boolean' ], |
| 1582 |
'created_at' => [ 'type' => 'string' ], |
| 1583 |
'modified_at' => [ 'type' => 'string' ], |
| 1584 |
'edit_url' => [ 'type' => 'string' ], |
| 1585 |
], |
| 1586 |
], |
| 1587 |
], |
| 1588 |
], |
| 1589 |
], |
| 1590 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 1591 |
return $runtime->list_forms( $input ); |
| 1592 |
}, |
| 1593 |
'meta' => self::build_meta( 'list', 1.0, true, false, true ), |
| 1594 |
], |
| 1595 |
|
| 1596 |
$ns . 'get-form' => [ |
| 1597 |
'label' => __( 'Get donation form', 'suredonation' ), |
| 1598 |
'description' => __( 'Returns a single donation form by ID with campaign association, status, and edit URL.', 'suredonation' ), |
| 1599 |
'category' => 'suredonation', |
| 1600 |
'permission_callback' => $perm_read, |
| 1601 |
'input_schema' => [ |
| 1602 |
'type' => 'object', |
| 1603 |
'required' => [ 'id' ], |
| 1604 |
'properties' => [ |
| 1605 |
'id' => [ |
| 1606 |
'type' => 'integer', |
| 1607 |
'description' => __( 'The donation form ID.', 'suredonation' ), |
| 1608 |
], |
| 1609 |
], |
| 1610 |
], |
| 1611 |
'output_schema' => [ |
| 1612 |
'type' => 'object', |
| 1613 |
'properties' => [ |
| 1614 |
'id' => [ 'type' => 'integer' ], |
| 1615 |
'title' => [ 'type' => 'string' ], |
| 1616 |
'status' => [ 'type' => 'string' ], |
| 1617 |
'campaign_id' => [ 'type' => 'integer' ], |
| 1618 |
'campaign_name' => [ 'type' => 'string' ], |
| 1619 |
'entries' => [ 'type' => 'integer' ], |
| 1620 |
'revenue' => [ 'type' => 'number' ], |
| 1621 |
'is_default' => [ 'type' => 'boolean' ], |
| 1622 |
'created_at' => [ 'type' => 'string' ], |
| 1623 |
'modified_at' => [ 'type' => 'string' ], |
| 1624 |
'edit_url' => [ 'type' => 'string' ], |
| 1625 |
], |
| 1626 |
], |
| 1627 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 1628 |
return $runtime->get_form( $input ); |
| 1629 |
}, |
| 1630 |
'meta' => self::build_meta( 'read', 1.0, true, false, true ), |
| 1631 |
], |
| 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 |
]; |
| 1780 |
} |
| 1781 |
|
| 1782 |
/** |
| 1783 |
* Get analytics ability configurations. |
| 1784 |
* |
| 1785 |
* @param Runtime $runtime Runtime instance. |
| 1786 |
* @param callable $perm_read Read permission closure. |
| 1787 |
* @return array<string, array<string, mixed>> Analytics abilities. |
| 1788 |
*/ |
| 1789 |
private static function get_analytics_abilities( $runtime, $perm_read ) { |
| 1790 |
$ns = SUREDONATION_ABILITY_API_NAMESPACE; |
| 1791 |
|
| 1792 |
return [ |
| 1793 |
$ns . 'get-donation-trends' => [ |
| 1794 |
'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' ), |
| 1796 |
'category' => 'suredonation', |
| 1797 |
'permission_callback' => $perm_read, |
| 1798 |
'input_schema' => [ |
| 1799 |
'type' => 'object', |
| 1800 |
'properties' => [ |
| 1801 |
'after' => [ |
| 1802 |
'type' => 'string', |
| 1803 |
'default' => '', |
| 1804 |
'description' => __( 'Start date (YYYY-MM-DD). Empty defaults to 30 days ago.', 'suredonation' ), |
| 1805 |
], |
| 1806 |
'before' => [ |
| 1807 |
'type' => 'string', |
| 1808 |
'default' => '', |
| 1809 |
'description' => __( 'End date (YYYY-MM-DD). Empty defaults to today.', 'suredonation' ), |
| 1810 |
], |
| 1811 |
'group' => [ |
| 1812 |
'type' => 'string', |
| 1813 |
'enum' => [ 'day', 'week', 'month' ], |
| 1814 |
'default' => 'day', |
| 1815 |
'description' => __( 'Group results by time period.', 'suredonation' ), |
| 1816 |
], |
| 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 |
], |
| 1834 |
], |
| 1835 |
'output_schema' => [ |
| 1836 |
'type' => 'object', |
| 1837 |
'properties' => [ |
| 1838 |
'trends' => [ |
| 1839 |
'type' => 'array', |
| 1840 |
'items' => [ |
| 1841 |
'type' => 'object', |
| 1842 |
'properties' => [ |
| 1843 |
'period' => [ 'type' => 'string' ], |
| 1844 |
'donation_count' => [ 'type' => 'integer' ], |
| 1845 |
'total_amount' => [ 'type' => 'number' ], |
| 1846 |
], |
| 1847 |
], |
| 1848 |
], |
| 1849 |
'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 |
], |
| 1860 |
], |
| 1861 |
'execute_callback' => static function ( $input ) use ( $runtime ) { |
| 1862 |
return $runtime->get_donation_trends( $input ); |
| 1863 |
}, |
| 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 ), |
| 2073 |
], |
| 2074 |
]; |
| 2075 |
} |
| 2076 |
} |
| 2077 |
|