承接 begenius/laravel-ussd 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

begenius/laravel-ussd

Composer 安装命令:

composer require begenius/laravel-ussd

包简介

A Laravel package to build scalable USSD applications and workflows.

README 文档

README

Latest Version MIT License Total Downloads Tests

Build production-grade USSD applications in Laravel.

laravel-ussd is a framework-agnostic USSD engine that lets you create telecom-grade USSD services with a clean, declarative API. It supports multiple gateways (Orange, Moov, Africa's Talking, Infobip, etc.) through a driver-based architecture.

Table of Contents

Presentation

What is USSD?

USSD (Unstructured Supplementary Service Data) is a protocol used by GSM phones to communicate with service providers. It's the technology behind *123# codes — used for mobile money, balance checks, and telecom services across Africa and beyond.

What this package does

This package provides a complete framework for building USSD services:

  • Menu system — Declarative, tree-based navigation
  • Flow engine — Multi-step workflows with state machine
  • Session management — Database, Redis, or in-memory storage
  • 11 gateway drivers — Orange, Moov, Africa's Talking, Infobip, Twilio, Beem, Advanta, Hubtel, MTN, Vodacom, Airtel
  • Rate limiting — Per phone number throttling with END 429 response
  • Multi-language — Built-in EN/FR with easy extensibility
  • Validation — Input validation per flow step
  • Logging — Request/response logging
  • Simulator — Web-based testing tool
  • Artisan commandsussd:list and ussd:clean
  • Error handling — Graceful error recovery

Architecture overview

                    ┌──────────────────┐
                    │     Telephone    │
                    └────────┬─────────┘
                             │ USSD
                    ┌────────▼─────────┐
                    │  USSD Gateway    │
                    │(Orange, Moov, AT)│
                    └────────┬─────────┘
                             │ HTTP POST
                    ┌────────▼─────────┐
                    │  UssdController  │
                    └────────┬─────────┘
                             │
                    ┌────────▼─────────┐
                    │   UssdEngine     │
                    │  (Orchestrator)  │
                    └──┬────┬────┬─────┘
                       │    │    │
              ┌────────┘    │    └────────┐
              ▼             ▼             ▼
        ┌─────────┐ ┌──────────┐ ┌──────────┐
        │ Session │ │   Menu   │ │   Flow   │
        │ Manager │ │  Manager │ │  Engine  │
        └─────────┘ └──────────┘ └──────────┘

Installation

composer require begenius/laravel-ussd

Laravel will auto-discover the service provider. If you're using Laravel < 5.5, add this to config/app.php:

'providers' => [
    BeGenius\Ussd\UssdServiceProvider::class,
],

Publish configuration

php artisan vendor:publish --tag=ussd-config

Run migrations

php artisan vendor:publish --tag=ussd-migrations
php artisan migrate

Configuration

Configure the package in config/ussd.php or via environment variables:

USSD_DRIVER=default
USSD_SESSION_DRIVER=database
USSD_SESSION_LIFETIME=2
USSD_REDIS_CONNECTION=default
USSD_ROUTES_PREFIX=ussd
USSD_SIMULATOR_ENABLED=false
USSD_LOGGING_ENABLED=true
Option Default Description
default_driver default USSD gateway driver
session_driver database Session storage driver (database, redis, array)
session_lifetime 2 Session timeout (minutes)
redis_connection default Redis connection name for redis session driver
session_table ussd_sessions Database table name
routes_prefix ussd URL prefix for USSD routes
default_menu welcome Initial menu name
max_input_length 182 Max input characters
simulator_enabled false Enable web simulator

Quick Start

1. Define menus in a Service Provider

<?php

namespace App\Providers;

use BeGenius\Ussd\Facades\Ussd;
use Illuminate\Support\ServiceProvider;

class UssdServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Ussd::menu('welcome')
            ->title("Welcome to MyService")
            ->option('1', 'Check Balance')
            ->option('2', 'Transfer Money')
            ->option('3', 'Help', nextMenu: 'help');
    }
}

2. Configure your gateway

Set your USSD gateway to send POST requests to:

https://your-domain.com/ussd/callback

3. The gateway sends payloads like

{
    "sessionId": "ABC123",
    "phoneNumber": "22670000000",
    "network": "ORANGE",
    "text": "1"
}

Your application handles the rest.

Menus

Menus are the building blocks of your USSD application. They display options and handle navigation.

API

use BeGenius\Ussd\Facades\Ussd;

Ussd::menu('main')
    ->title("Main Menu")
    ->option('1', 'Balance', BalanceAction::class)
    ->option('2', 'Transfer', TransferFlow::class)
    ->option('3', 'Settings', nextMenu: 'settings')
    ->option('0', 'Exit');

Sub-menus

Ussd::menu('settings')
    ->title("Settings")
    ->option('1', 'Language', LanguageAction::class)
    ->option('0', 'Back', nextMenu: 'main');

Navigation flow

  • Action: An action class (invokable) executes business logic
  • Flow: A multi-step workflow
  • nextMenu: Navigate to another menu
  • No action/no nextMenu: Re-renders the current menu

Rendering

Menus are auto-rendered as CON responses with numbered options:

CON Main Menu
1. Balance
2. Transfer
3. Settings
0. Exit

Flows

Flows handle multi-step transactions like money transfers.

Creating a Flow

namespace App\Ussd\Flows;

use BeGenius\Ussd\Core\UssdContext;
use BeGenius\Ussd\Flows\Flow;
use BeGenius\Ussd\Flows\Step;
use BeGenius\Ussd\Flows\StepResult;
use BeGenius\Ussd\Responses\UssdResponse;

class TransferFlow extends Flow
{
    public function __construct()
    {
        parent::__construct('transfer', 'ask_recipient');

        $this->addStep(new class extends Step {
            public function name(): string
            {
                return 'ask_recipient';
            }

            public function handle(UssdContext $context): StepResult
            {
                return StepResult::next(
                    'ask_amount',
                    UssdResponse::continue('Enter recipient phone:')
                );
            }
        });

        $this->addStep(new class extends Step {
            public function name(): string
            {
                return 'ask_amount';
            }

            public function validate(UssdContext $context): ?string
            {
                $amount = $context->input();
                if (!is_numeric($amount) || $amount <= 0) {
                    return 'Invalid amount. Enter a positive number.';
                }
                return null;
            }

            public function handle(UssdContext $context): StepResult
            {
                $context->session()->set('amount', $context->input());

                return StepResult::next(
                    'confirm',
                    UssdResponse::continue(
                        "Confirm transfer:\n".
                        "To: {$context->session()->get('recipient')}\n".
                        "Amount: {$context->input()} FCFA\n".
                        "1. Confirm\n".
                        "2. Cancel"
                    )
                );
            }
        });

        $this->addStep(new class extends Step {
            public function name(): string
            {
                return 'confirm';
            }

            public function handle(UssdContext $context): StepResult
            {
                if ($context->input() === '1') {
                    // Process the transfer...
                    return StepResult::complete(
                        UssdResponse::end('Transfer successful!')
                    );
                }

                return StepResult::complete(
                    UssdResponse::end('Transfer cancelled.')
                );
            }
        });
    }
}

Register a flow in a menu

Ussd::menu('main')
    ->option('2', 'Transfer Money', TransferFlow::class);

Or register it independently:

$flow = new TransferFlow();
Ussd::registerFlow($flow);

Actions

Actions are invokable classes that handle a single menu selection.

namespace App\Ussd\Actions;

use BeGenius\Ussd\Core\UssdContext;
use BeGenius\Ussd\Responses\UssdResponse;

class BalanceAction
{
    public function __invoke(UssdContext $context): UssdResponse
    {
        $balance = $this->getBalance($context->session()->phoneNumber());

        return UssdResponse::end("Your balance is: {$balance} FCFA");
    }

    private function getBalance(string $phoneNumber): float
    {
        // Query your database or API
        return 15000.00;
    }
}

You can also use closures:

Ussd::menu('main')
    ->option('1', 'Balance', function (UssdContext $context) {
        return UssdResponse::end('Your balance is 5000 FCFA');
    });

Sessions

Sessions persist user state across USSD requests. The package supports three drivers.

Session data

Store temporary data during flows:

$context->session()->set('recipient', '22671234567');
$context->session()->set('amount', '5000');

$recipient = $context->session()->get('recipient');
$hasAmount  = $context->session()->has('amount');
$context->session()->forget('amount');

Session lifecycle

  1. Created — When a user dials the service code
  2. Active — During menu navigation and flow execution
  3. Expired — After session_lifetime minutes of inactivity
  4. Destroyed — After successful END response

Drivers

USSD Gateway Drivers

Built-in gateway drivers

Driver Class Gateways
default DefaultUssdDriver Generic (use as fallback)
orange OrangeDriver Orange CI, Orange SN
moov MoovDriver Moov Africa
africastalking AfricasTalkingDriver Africa's Talking
infobip InfobipDriver Infobip
twilio TwilioDriver Twilio
beem BeemDriver Beem (Tanzania)
advanta AdvantaDriver Advanta
hubtel HubtelDriver Hubtel (Ghana)
mtn MtnDriver MTN
vodacom VodacomDriver Vodacom (DRC)
airtel AirtelDriver Airtel

Set the driver in .env:

USSD_DRIVER=orange

Custom gateway driver

Create a driver for your specific gateway:

namespace App\Ussd\Drivers;

use BeGenius\Ussd\Contracts\UssdDriver;
use BeGenius\Ussd\Http\Requests\UssdRequest;
use Illuminate\Http\Request;

class OrangeDriver implements UssdDriver
{
    public function parseRequest(Request $request): UssdRequest
    {
        return new UssdRequest(
            sessionId:   $request->input('sessionId'),
            phoneNumber: $request->input('msisdn'),
            network:     'ORANGE',
            text:        $request->input('ussdText', ''),
            raw:         $request->all(),
            serviceCode: $request->input('serviceCode'),
        );
    }
}

Register your driver in the service provider:

// In UssdServiceProvider registration
$this->app->bind(UssdDriverContract::class, function ($app) {
    return new OrangeDriver();
});

Session Drivers

Built-in session drivers:

Driver Class Use case
database DatabaseSessionDriver MySQL/PostgreSQL (production)
redis RedisSessionDriver High-performance with TTL (production)
array ArraySessionDriver In-memory (testing only)

Switch driver via .env:

USSD_SESSION_DRIVER=redis
USSD_REDIS_CONNECTION=default

The Redis driver stores sessions as JSON with a configurable TTL (session_lifetime) and supports custom Redis connections.

Simulator

Test your USSD application without a real gateway:

USSD_SIMULATOR_ENABLED=true

Open http://localhost:8000/ussd/simulator in your browser.

┌─────────────────────┐
│   USSD Simulator    │
│                     │
│  CON Welcome        │
│  1. Balance         │
│  2. Transfer        │
│                     │
├─────────────────────┤
│ [Input: 1     ] [→] │
│                     │
│ Session: abc123     │
└─────────────────────┘

⚠️ Disable in production: USSD_SIMULATOR_ENABLED=false

Artisan Commands

ussd:list

List all registered menus and their options:

php artisan ussd:list

Output:

+----------------+---------+-----------------------+
| Menu           | Options | Preview               |
+----------------+---------+-----------------------+
| welcome        | 3       |  1. Check Balance     |
|                |         |  2. Transfer Money    |
|                |         |  3. Help              |
+----------------+---------+-----------------------+

ussd:clean

Purge expired sessions from storage:

php artisan ussd:clean                    # purge sessions > 2 min old
php artisan ussd:clean --minutes=5        # purge sessions > 5 min old

Rate Limiting

Protect your USSD endpoint from abuse. The built-in ThrottleUssdRequests middleware limits by phone number (with IP fallback):

// In a route file
Route::post('/ussd/callback', [UssdController::class, 'handle'])
    ->middleware('throttle:ussd');

Customize limits in AppServiceProvider:

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for('ussd', function ($job) {
    return Limit::perMinute(10)->by($job->input('phoneNumber', $job->ip()));
});

When the limit is exceeded, the middleware returns a 429 Too Many Requests response with END format.

Multi-language

Built-in translations for English and French.

__('ussd::ussd.session_expired');
// en: "Session expired. Please dial again."
// fr: "Session expirée. Veuillez recomposer le code."

Switch locale as usual:

app()->setLocale('fr');

Publish and customise:

php artisan vendor:publish --tag=ussd-lang

Available keys:

Key English French
welcome Welcome Bienvenue
back Back Retour
confirm Confirm Confirmer
cancel Cancel Annuler
session_expired Session expired... Session expirée...
system_error A system error... Une erreur système...
too_many_requests Too many requests... Trop de requêtes...

Testing

composer test

The package uses PHPUnit with Orchestra Testbench.

public function it_creates_a_continue_response(): void
{
    $response = UssdResponse::continue("Welcome\n1. Balance");

    $this->assertTrue($response->isContinue());
    $this->assertEquals('CON', $response->type());
    $this->assertEquals("CON Welcome\n1. Balance\n", $response->toString());
}

Architecture

src/
├── UssdServiceProvider.php        # Laravel service provider
├── Facades/
│   └── Ussd.php                   # Facade for UssdEngine
├── Config/
│   └── ussd.php                   # Configuration file
├── Core/
│   ├── UssdEngine.php             # Main orchestrator
│   ├── UssdSession.php            # Session value object
│   └── UssdContext.php            # Request context
├── Http/
│   ├── Controllers/
│   │   ├── UssdController.php     # Gateway callback controller
│   │   └── SimulatorController.php # Web simulator
│   ├── Middleware/
│   │   └── ThrottleUssdRequests.php # Rate limiter middleware
│   └── Requests/
│       └── UssdRequest.php        # Parsed USSD request
├── Responses/
│   └── UssdResponse.php           # CON/END response builder
├── Menus/
│   ├── Menu.php                   # Menu definition
│   └── MenuOption.php             # Single menu option
├── Flows/
│   ├── Flow.php                   # Multi-step workflow
│   ├── Step.php                   # Single flow step
│   └── StepResult.php             # Step execution result
├── Contracts/
│   ├── UssdDriver.php             # Gateway driver interface
│   └── SessionDriver.php          # Session storage interface
├── Drivers/
│   ├── Gateway/                   # 11 gateway driver files
│   └── Session/
│       ├── DatabaseSessionDriver.php # DB session storage
│       ├── RedisSessionDriver.php    # Redis session storage
│       └── ArraySessionDriver.php    # In-memory session
├── Services/
│   ├── SessionManager.php         # Session lifecycle
│   └── MenuManager.php            # Menu registry
├── Exceptions/
│   ├── UssdException.php          # Base exception
│   ├── InvalidMenuException.php   # Menu not found
│   └── SessionExpiredException.php # Session timeout
└── Console/
    ├── Commands/
    │   ├── UssdListCommand.php     # ussd:list
    │   └── UssdCleanCommand.php    # ussd:clean
    └── Resources/
        └── lang/                   # Translation files (EN/FR)

Key design patterns

Pattern Usage
Orchestration UssdEngine coordinates components
Builder Fluent menu/flow API
Registry MenuManager stores menus by name
State Flow steps as state machine
Strategy Drivers interchangeable via interface
Context UssdContext carries state through pipeline
Value Object UssdRequest, UssdResponse, UssdSession

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Commit changes: git commit -am 'Add my feature'
  4. Push: git push origin feature/my-feature
  5. Open a Pull Request

Please follow PSR-12 coding standards and include tests.

Roadmap

v1.0

  • Menu system with fluent API
  • Multi-step flow engine (state machine)
  • Session management (database + Redis + array)
  • 11 gateway drivers (Orange, Moov, AT, Infobip, Twilio, Beem, Advanta, Hubtel, MTN, Vodacom, Airtel)
  • Rate limiting per phone number
  • Multi-language support (EN/FR)
  • Artisan commands (ussd:list, ussd:clean)
  • CSRF exclusion auto-configuration
  • USSD simulator
  • Request/response logging
  • Error handling
  • PHPUnit test suite
  • GitHub Actions CI

v1.1

  • Visual flow designer UI
  • Analytics dashboard
  • Webhook integration
  • REST API for session management
  • Broadcast messages
  • Push USSD (MT sessions)

v2.0

  • Visual flow designer UI
  • Webhook integration
  • REST API for session management
  • Push USSD (MT sessions)
  • Laravel Livewire component

License

laravel-ussd is open-source software licensed under the MIT license.

Built with ❤️ by BeGenius

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-07-10

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固