Action

Actions encapsulate side-effect logic that can be invoked from states. They're useful for handling business logic, API calls, database operations, and other tasks that shouldn't be directly in your state classes.

Creating an Action

Use the Artisan command to create a new action:

php artisan ussd:action ProcessPayment

Action Structure

<?php

namespace App\Ussd\Actions;

use Vendor\LaravelUssd\Support\AbstractAction;
use Vendor\LaravelUssd\Support\Context;

class ProcessPayment extends AbstractAction
{
    public function handle(Context $context, string $input): mixed
    {
        // Your business logic here
        $amount = $input;
        $account = $context->data['account_number'] ?? null;

        // Process payment
        $result = $this->paymentService->process($account, $amount);

        if ($result->success) {
            $context->data['payment_id'] = $result->id;
            return ['success' => true, 'message' => 'Payment processed successfully'];
        }

        return ['success' => false, 'message' => 'Payment failed. Please try again.'];
    }
}

Using Actions in States

Actions are called from within your State's next() method to perform business logic. After calling an action, you must return a State class name, not an Action class name.

public function next(Context $context, string $input): ?string
{
    $this->setContext($context);
    
    // Call the action to perform business logic
    $action = new ProcessPayment();
    $result = $action->handle($context, $input);

    // IMPORTANT: Return a STATE class name, not an Action class name
    if ($result['success']) {
        return PaymentSuccessState::class; // ✅ Correct: State class
    }

    // Handle error case - also return a State
    return ErrorState::class; // ✅ Correct: State class
}

Common Mistake: Returning Action Instead of State

⚠️ Important: Never Return Action Class Names

The next() method must return a State class name (string), never an Action class name. Returning an Action will cause a type error:

Machine::resolveState(): Return value must be of type State, Action returned

❌ Incorrect - Returning Action Class

public function next(Context $context, string $input): ?string
{
    $this->setContext($context);
    
    // ❌ WRONG: Returning an Action class name
    return ProcessPayment::class; // This will cause an error!
    
    // ❌ WRONG: Returning an Action instance
    return new ProcessPayment(); // This will also cause an error!
}

✅ Correct - Call Action, Return State

public function next(Context $context, string $input): ?string
{
    $this->setContext($context);
    
    // ✅ CORRECT: Call the action to perform business logic
    $action = new ProcessPayment();
    $result = $action->handle($context, $input);
    
    // ✅ CORRECT: Return a STATE class name based on the result
    if ($result['success'] ?? false) {
        return PaymentSuccessState::class; // State class name
    }
    
    return PaymentErrorState::class; // State class name
}

Key Points to Remember:

  • Actions are called (invoked) within states to perform business logic
  • States are returned from next() to navigate the flow
  • The return type of next() is ?string - a State class name or null
  • Use actions for side effects (API calls, database operations), then return the appropriate next state