State
States are the core building blocks of your USSD application. Each state represents a screen or step in your USSD flow.
Creating a State
Use the Artisan command to create a new state:
php artisan ussd:state WelcomeState
This will create a new state class in app/Ussd/States/WelcomeState.php.
State Structure
A typical state class looks like this:
<?php
namespace App\Ussd\States;
use Vendor\LaravelUssd\Support\AbstractState;
use Vendor\LaravelUssd\Support\Context;
use Vendor\LaravelUssd\Support\UssdResponse;
use Vendor\LaravelUssd\Menu\Menu;
class WelcomeState extends AbstractState
{
protected function buildMenu(Context $context): Menu
{
return (new Menu())
->text('Welcome to our service!')
->option('1', 'View Balance')
->option('2', 'Transfer Money')
->option('3', 'Exit')
->expectsInput();
}
public function next(Context $context, string $input): ?string
{
// IMPORTANT: Must return a State class name (string) or null
// Never return an Action class name
return $this->decision($input)
->equal('1', BalanceState::class) // ✅ State class name
->equal('2', TransferState::class) // ✅ State class name
->equal('3', null) // End session
->any(WelcomeState::class); // ✅ State class name (default fallback)
}
}
State Methods
buildMenu()
Builds and returns a Menu instance for this state. This method is called when the user enters the state.
next()
Must return a State class name (string) or null. Processes user input and returns the fully qualified class name of the next State to navigate to, or null to end the session. Never return an Action class name.
decision()
Returns a Decision instance for creating fluent decision trees based on user input.
record
Access the Record instance for storing and retrieving session data throughout the flow.
Setting the Initial State
The initial state is the entry point of your USSD application. It's the first state that users encounter when they dial your USSD code and start a new session. This state is configured in your config/ussd.php file.
Configuration
The initial state is set using the initial_state key in your configuration file:
return [
'initial_state' => 'App\\Ussd\\States\\WelcomeState',
'state_namespace' => 'App\\Ussd\\States',
// ... other configuration
];
Important Requirements:
- The value must be a fully qualified class name (including the complete namespace)
-
The class must exist and implement the
Stateinterface -
Use double backslashes (
\\) to escape namespace separators in PHP strings
Changing the Initial State
To change the initial state, simply update the initial_state value to point to a different state class:
return [
// Change from WelcomeState to MainMenuState
'initial_state' => 'App\\Ussd\\States\\MainMenuState',
// Or use a different namespace
'initial_state' => 'App\\Services\\Ussd\\States\\HomeState',
// ... other configuration
];
How Initial State Works
The initial state plays a crucial role in the USSD session flow:
New Sessions
When a user dials your USSD code for the first time, the Machine creates a new session context and sets the current state to the configured initial state. The user then sees the menu from this state.
Session Restart
When a user chooses to restart their session (via session continuity), the Machine resets the context back to the initial state, clearing all stored data and starting fresh.
State Flow
From the initial state, users navigate through your application by selecting options or providing input, which transitions them to other states. The initial state serves as the root of your state tree.
Best Practices
- Choose a state that provides a clear main menu or welcome message as your initial state
-
Ensure your initial state always expects input (
expectsInput(true)) to allow users to navigate - Keep the initial state simple and focused - it's the first impression users have of your service
- Test your initial state thoroughly, as it's the most frequently accessed state in your application
Response Types: CON vs END
USSD responses must be prefixed with either CON (continue) or END (end session). The framework automatically determines this based on whether your menu expects user input.
CON (Continue) - Expects User Input
Use CON when you want the user to provide input. This keeps the session active and waits for the user's response.
When to Use CON:
- Displaying menus with options for the user to select
- Asking for user input (phone numbers, amounts, PINs, etc.)
- Any state where the user needs to make a selection or provide data
<?php
class WelcomeState extends AbstractState
{
protected function buildMenu(Context $context): Menu
{
return (new Menu())
->text('Welcome to our service!')
->option('1', 'View Balance')
->option('2', 'Transfer Money')
->option('3', 'Exit')
->expectsInput(true); // CON response - expects input
}
}
The response will be: CON Welcome to our service!\n1. View Balance\n2. Transfer Money\n3. Exit
END (End Session) - Information Only
Use END when you're displaying information only and don't need user input. This terminates the USSD session.
When to Use END:
- Displaying account balance or transaction results
- Showing confirmation messages (e.g., "Transfer successful")
- Error messages that don't require user action
- Final states in a flow (success/error screens)
<?php
class BalanceState extends AbstractState
{
protected function buildMenu(Context $context): Menu
{
$this->setContext($context);
$balance = $this->record->get('balance', '0.00');
return (new Menu())
->text('Account Balance')
->line('')
->text("Your balance: {$balance}")
->line('')
->text('Thank you for using our service.')
->expectsInput(false); // END response - no input needed
}
}
The response will be: END Account Balance\n\nYour balance: 1000.00\n\nThank you for using our service.
Using the response() Method
The response() method automatically determines CON or END based on the menu's expectsInput() setting:
<?php
class MyState extends AbstractState
{
protected function buildMenu(Context $context): Menu
{
$menu = (new Menu())
->text('Select an option:')
->option('1', 'Option 1')
->option('2', 'Option 2');
// Option 1: Set expectsInput on the menu
$menu->expectsInput(true); // Will generate CON response
// Option 2: Pass expectsInput to response() method
// This overrides the menu's setting
return $this->response($menu, true); // CON response
// or
return $this->response($menu, false); // END response
}
}
Direct Response Creation
You can also create responses directly using UssdResponse class for more control:
<?php
use Vendor\LaravelUssd\Support\UssdResponse;
class CustomState extends AbstractState
{
public function entry(Context $context): UssdResponse
{
// Create a CON response (expects input)
return UssdResponse::continue('Please enter your PIN:');
// Or create an END response (info only)
return UssdResponse::end('Transaction completed successfully!');
}
}
Important Notes
- CON responses keep the session alive. The user can continue interacting with your application.
- END responses terminate the session immediately. The user must start a new session to continue.
-
If you use
expectsInput(false), make sure yournext()method returnsnullsince the session will end. -
The default behavior is
expectsInput(true), so menus will generate CON responses unless explicitly set to false.
Common Mistakes
❌ Mistake 1: Returning Action Instead of State
The most common error is returning an Action class name from next() instead of a State class name.
public function next(Context $context, string $input): ?string
{
// ❌ WRONG: Returning an Action class name
return ProcessPaymentAction::class; // Error: Must return State, not Action
}
public function next(Context $context, string $input): ?string
{
$this->setContext($context);
// ✅ CORRECT: Call the action, then return a State
$action = new ProcessPaymentAction();
$result = $action->handle($context, $input);
return $result['success']
? PaymentSuccessState::class // Return State class name
: PaymentErrorState::class; // Return State class name
}
❌ Mistake 2: Returning Action Instance
Never return an Action instance from next().
public function next(Context $context, string $input): ?string
{
// ❌ WRONG: Returning an Action instance
return new ProcessPaymentAction(); // Error: Must return string (State class name)
}
❌ Mistake 3: Forgetting to Return State Class Name
Always return a fully qualified State class name as a string, or use the ::class constant.
public function next(Context $context, string $input): ?string
{
// ❌ WRONG: Returning the class without ::class or as object
return 'NextState'; // Missing namespace
return new NextState(); // Returning instance, not class name
}
public function next(Context $context, string $input): ?string
{
// ✅ CORRECT: Return State class name using ::class
return NextState::class;
// ✅ CORRECT: Or return fully qualified string
return 'App\\Ussd\\States\\NextState';
}
Remember: next() Return Type
-
Return type is
?string- a State class name or null - Always return a State class name, never an Action class name
-
Use
StateClass::classor fully qualified string like'App\\Ussd\\States\\StateClass' -
Return
nullto end the USSD session - If you need to call an Action for business logic, call it first, then return the appropriate State based on the result