Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ussd Laravel Package

moffhub/ussd

Enterprise-grade Laravel USSD framework for building scalable menus and flows across African providers (Safaricom/Africa’s Talking, Airtel, MTN, generic). Includes menu/forms/wizards, session recovery, security, analytics, caching, and pluggable data providers.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require moffhub/ussd
    php artisan vendor:publish --provider="Moffhub\Ussd\UssdServiceProvider"
    php artisan migrate  # Optional (for session/analytics persistence)
    
  2. Route Definition (in routes/api.php):

    Route::post('/ussd', [UssdController::class, 'handle']);
    
  3. Basic Controller (app/Http/Controllers/UssdController.php):

    use Moffhub\Ussd\UssdFramework;
    use Moffhub\Ussd\Menus\SimpleMenu;
    
    public function handle(Request $request) {
        $framework = new UssdFramework(['default_menu' => 'main']);
        $framework->registerMenu('main', new SimpleMenu('Welcome', ['1' => 'Option 1'], [
            '1' => fn($session, $framework) => $framework->navigateToMenuWithResponse('balance')
        ]));
        return response($framework->handle($request)->formatForNetwork());
    }
    

First Use Case: Simple Menu Flow

Create a 2-step menu (main → balance) with:

  1. SimpleMenu for the main menu
  2. Navigation to a balance menu using navigateToMenuWithResponse()
  3. Return the response with proper headers

Implementation Patterns

Core Workflow

  1. Request Handling:

    $framework = new UssdFramework(config());
    $response = $framework->handle($request);
    return response($response->formatForNetwork());
    
  2. Menu Registration:

    $framework->registerMenu('menu_name', new SimpleMenu('Title', ['1' => 'Option'], [
        '1' => function($session, $framework) {
            // Handle selection
        }
    ]));
    
  3. Session Management:

    $session->set('user_id', 123);
    $userId = $session->get('user_id');
    

Common Patterns

1. Menu Composition

  • Simple Menus: For linear flows
    new SimpleMenu('Title', ['1' => 'Option'], ['1' => callback])
    
  • Forms: For multi-step data collection
    $form = new FormMenu('Registration');
    $form->addField(new FormField('name', 'Enter name:', [Validators::required()]));
    $form->setOnComplete(function($session, $data) { ... });
    
  • Pagination: For large datasets
    new PaginatedMenu('Products', $products, ['items_per_page' => 5])
    

2. Navigation

  • Explicit Navigation:
    $framework->navigateToMenu('menu_name');
    $response = $framework->navigateToMenuWithResponse('menu_name');
    
  • Conditional Logic:
    $menu = new ConditionalMenu($defaultMenu);
    $menu->addCondition(fn($session) => $session->getUserData('is_admin'), $adminMenu);
    

3. Data Integration

  • Database Providers:
    $provider = new DatabaseDataProvider(Product::class);
    $menu = new PaginatedMenu('Products', $provider);
    
  • API Integration:
    $provider = new ApiDataProvider('https://api.example.com', [], ['token' => '...']);
    

4. Provider-Specific Logic

  • Auto-Detection:
    $provider = ProviderFactory::detect($request);
    
  • Custom Providers:
    ProviderFactory::register('custom', CustomProvider::class);
    

Integration Tips

  1. Leverage Enums for menu names:
    enum AppMenu { case Main; case Balance; }
    $framework->registerMenu(AppMenu::Main, ...);
    
  2. Use MenuBuilder for fluent menu creation:
    (new MenuBuilder('menu'))
        ->title('Welcome')
        ->option('1', 'Option', fn($s, $f) => $f->navigateToMenuWithResponse('next'))
        ->build();
    
  3. Combine with Laravel Services:
    // In menu action
    $user = User::find($session->getUserData('user_id'));
    

Gotchas and Tips

Common Pitfalls

  1. Session Recovery:

    • Ensure grace_period is set appropriately (default: 600s)
    • Use enable_intelligent_recovery for complex flows
    • Gotcha: Session data isn't automatically preserved across provider restarts
  2. Provider Field Mappings:

    • Safaricom: phoneNumber, text, sessionId
    • Airtel: msisdn, input/text, transactionId
    • MTN: msisdn, UserAnswer, sessionId
    • Gotcha: Incorrect field names cause silent failures
  3. Rate Limiting:

    • Default limits may be too strict for production
    • Tip: Use database-backed whitelists for critical numbers
  4. Input Sanitization:

    • Enable strict_mode in production to block suspicious input
    • Gotcha: Disabled sanitization can lead to injection vulnerabilities

Debugging Tips

  1. Log Provider Requests:
    $framework->setLogger(new SingleChannelLogger('ussd.log'));
    
  2. Inspect Session State:
    dd($session->getAll());
    
  3. Validate Menu Names:
    if (!$framework->hasMenu('menu_name')) {
        throw new \InvalidArgumentException("Menu not registered");
    }
    

Configuration Quirks

  1. Caching:
    • Menus are cached by default (TTL: 3600s)
    • Tip: Disable for development:
      $framework = new UssdFramework(['cache_enabled' => false]);
      
  2. Analytics:
    • Requires ussd_analytics table
    • Tip: Disable if not needed:
      $framework = new UssdFramework(['analytics_enabled' => false]);
      
  3. i18n:
    • Language detection uses Accept-Language header
    • Gotcha: Fallback to en if not specified

Extension Points

  1. Custom Validators:
    Validators::custom(fn($value) => $value > 100, 'Must be > 100');
    
  2. Provider Extensions:
    class CustomProvider extends AbstractUssdProvider {
        public function getSessionId(Request $request) {
            return $request->input('custom_session_id');
        }
    }
    
  3. Response Modifiers:
    $response->setHeader('X-Custom-Header', 'value');
    

Performance Tips

  1. Database Queries:
    • Use DatabaseDataProvider with eager loading:
      new DatabaseDataProvider(Product::class, fn() => Product::with('category')->whereActive(true))
      
  2. Caching Strategies:
    • Cache paginated data:
      $menu = new PaginatedMenu('Products', $provider, ['cache_ttl' => 300]);
      
  3. Menu Optimization:
    • Avoid complex logic in menu actions (use services instead)
    • Tip: Pre-load menus during framework initialization

Security Best Practices

  1. Whitelist Critical Numbers:
    $rateLimiter->addToWhitelist('+254712345678', 'Admin access');
    
  2. Audit Logging:
    • Enable audit_logging in config
    • Tip: Use UssdAuditLog model for admin panels
  3. Session Expiry:
    • Set session_expiry (default: 3600s) to match business needs

Provider-Specific Notes

Provider Quirks Tips
Safaricom Strict session ID format Use sessionId field
Airtel Transaction ID required Validate transactionId in responses
MTN UserAnswer may be empty Handle null values in input
Generic Field name flexibility Configure field_mappings in provider

Testing Tips

  1. Mock Providers:
    $provider = Mockery::mock(AbstractUssdProvider::class);
    $framework->setProvider($provider);
    
  2. Session Testing:
    $session = $framework->getSession();
    $session->set('test', 'value');
    $this->assertEquals('value', $session->get('test'));
    
  3. Menu Validation:
    $this->assertTrue($framework->hasMenu('test'));
    $this->assertInstanceOf
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony
spatie/flare-daemon-runtime