Case Study:

U2C Mobile Self-Service Portal

Project Overview

U2C Mobile is a customer portal and admin CMS built with Laravel and Livewire for a mobile carrier managing customer plans, add-ons, SIM cards, and billing. The platform serves customers who self-manage their accounts through a post-login portal.

The portal integrates with an Amdocs BSS API ecosystem for all customer data operations — plan management, product orders, payments, and SIM workflows — while AWS Cognito handles customer authentication independently from Laravel's own admin auth stack.

The Challenge

Amdocs BSS integration. All customer data lives in an external Amdocs BSS platform, not a local database. Every customer-facing action — viewing a plan, purchasing an add-on, swapping a SIM — requires composing and sequencing API calls against 10+ distinct Amdocs endpoints, each returning deeply nested response structures that must be flattened into view-ready data.

Dual authentication systems. Admin and staff accounts use standard Laravel session-based auth. Customer accounts authenticate via AWS Cognito's USER_PASSWORD_AUTH flow, which returns a separate JWT token that must be maintained in Laravel's session and injected into every subsequent Amdocs API request. Bridging two fundamentally different auth systems in one middleware stack required careful layering.

Multi-step purchase workflows. Purchasing an add-on or swapping a plan is not a single API call. It requires a three-step cart-to-order-to-payment sequence, each step depending on the response of the last. Partial failures at any step must be handled gracefully without leaving the customer in an inconsistent state.

Role separation on a shared codebase. Admins, staff, and customers all use the same Laravel application. Middleware, route groups, and Livewire component guards must ensure a customer can never reach an admin route and vice versa, even across Livewire's AJAX lifecycle.

Authentication & Authorization Stack

The application runs two parallel authentication systems. Admin and staff accounts use Laravel Breeze's standard session guard. Customer accounts authenticate through AWS Cognito and are issued a JWT that persists in the Laravel session for the duration of the portal session.

Every customer portal request passes through a four-layer middleware chain:

authCheckTokenExpiryCustomerMiddleware → (route handler)

  • auth — standard Laravel session guard. Ensures the user has a valid Laravel session before any further checks run.
  • CheckTokenExpiry — reads token_expires_at from the session. If the Cognito JWT has expired, the session is flushed, the user is logged out, and they are redirected to the customer login with a flash error. All token expiry events are logged to the info_only log channel.
  • CustomerMiddleware — loads the authenticated user's role and verifies it is customer. Non-customer roles (admin, staff) are redirected to /admin, preventing privilege escalation across route groups.
  • APITokenMiddleware — applied to internal API endpoints. Validates a Bearer token from the Authorization header against a config value, returning HTTP 401 on mismatch.

Additional security measures:

  • IpWhitelistMiddleware — protects the internal API surface. Reads an IP whitelist from config and returns HTTP 403 with the client IP logged for any non-whitelisted request.
  • Rate limiting — 5 requests per minute on the customer login route, enforced via Laravel's built-in rate limiter.
  • Session regeneration — session ID is regenerated after successful Cognito authentication to prevent session fixation attacks.
  • Secure logging — passwords and Cognito tokens are explicitly redacted from all log output. The info_only log channel records auth events, token validity, and API interactions without exposing credentials.
Middleware Purpose Trigger
authLaravel session guardAll authenticated routes
CheckTokenExpiryCognito JWT expiration checkAll customer portal routes
CustomerMiddlewareRole-based portal access guardAll customer portal routes
APITokenMiddlewareBearer token validation for internal APIInternal API routes
IpWhitelistMiddlewareIP allowlist enforcementProtected API surface
SetLocaleURL-prefix locale detection (en/es)All public frontend routes
Rate limiter (5/min)Brute-force protection on loginCustomer login route

Amdocs API Integration

All customer data is stored and managed in an Amdocs BSS platform. The portal consumes 10+ distinct API endpoints — there is no local customer database to fall back on. Every page load that displays customer data requires one or more live API calls.

HasApiAccessToken Trait

All Livewire components and service classes that make outbound API calls use a shared HasApiAccessToken trait. The trait retrieves the authenticated customer's Cognito JWT from the Laravel session and injects it as a Bearer token into every HTTP request, ensuring no component needs to handle token retrieval independently.

Data Transformation

Amdocs API responses are deeply nested structures. Each response is flattened in a dedicated transformation layer before reaching the Blade view — extracting fields from nested arrays, resolving status codes to human-readable labels, and reformatting dates and prices. Views consume flat, typed arrays rather than raw API payloads.

Graceful Degradation

Every HTTP call is wrapped in a try/catch block. On failure, a user-friendly flash message is displayed via Toastr.js, and the full exception detail is written to the info_only log channel. The page does not crash — components render into an empty or error state rather than throwing an unhandled exception.

API Endpoint Purpose
customer-managementRetrieve customer account summary and plan details
party-managementView and update customer contact mediums (email, phone, address)
product-inventory-apiList customer's active products and SIM associations
productcatalogBrowse available plans and add-ons with channel/type filtering
shoppingcartCreate and manage cart with UUID line items
productorderConvert a cart to a product order
payment-intentGenerate a Stripe payment redirect URL from an order ID
paymentRetrieve payment transaction history
payment-methodRetrieve saved card details (last four digits, expiry)
account-managementBilling account details and balance

Customer Purchase Workflow

Both the Add-ons and Plan Swap features use a shared three-step cart-to-payment workflow. Intermediate state (cart ID, order ID, cart items, payment info) is stored in the Laravel session between steps so the process survives a page reload.

Step 1 — Add to Cart

A POST request to the shoppingcart API creates a new cart with UUID-identified line items representing the selected product. The returned cart ID is stored in the session.

Step 2 — Convert Cart to Order

The cart ID from Step 1 is POSTed to the productorder API. This converts the cart into a formal product order. The returned order ID is stored in the session.

Step 3 — Payment Intent

The order ID from Step 2 is POSTed to the payment-intent API, which generates a Stripe-hosted payment URL. The customer is redirected to Stripe to complete payment. On return, the session state is cleared and the portal reflects the updated plan or add-on.

Plan Swap Complexity

Plan Swap is significantly more complex than a standard add-on purchase. The cart must contain three line items submitted in a single request:

  • ADD — the new plan being purchased
  • DELETE — the customer's current plan being cancelled
  • MODIFY — the SIM product re-associated to the new plan

The three-part cart structure requires retrieving the customer's current plan and SIM details from product-inventory-api before the cart can be constructed. PlanSwap.php handles this assembly; AddOns.php handles the simpler single-item flow.

Customer Portal

After logging in through the Cognito-backed customer login, customers land on a dashboard that pulls live data from Amdocs — current plan, MSISDN, billing summary, and account status. Every section of the portal reflects real-time API data; nothing is cached from a local database.

Profile

Displays contact mediums (email, phone, address) pulled from party-management. Customers can edit and submit changes, which are sent as a PATCH request to update their record in Amdocs directly.

Orders

Paginated order history retrieved from productorder, with status filtering. Each order expands to show nested pricing detail including tax and duty-free line items.

Payments

Payment transaction history from the payment endpoint, alongside saved card details (last four digits and expiry) from payment-method.

Add-ons

The product catalog is fetched from productcatalog and filtered by channel (Mobile App) and type (Add-on) to surface only relevant products. Selecting an add-on initiates the three-step purchase workflow.

Plan Swap

Customers can browse available plans and initiate a plan change. The workflow fetches the customer's current plan and SIM details, constructs the three-part cart (ADD + DELETE + MODIFY), and routes through the standard order-to-payment flow.

SIM Swap

A dedicated SIM swap workflow handles SIM card replacement, updating the SIM association in Amdocs product inventory without requiring a plan change.

Tech Stack

Layer Technology
FrameworkLaravel 12 (PHP 8.3)
Reactive UILivewire 3.4
FrontendBootstrap 5.2, Sass/SCSS, Vite 5
AuthLaravel Breeze + AWS Cognito (USER_PASSWORD_AUTH)
External APIsAmdocs BSS Suite (10+ endpoints)
PaymentStripe (via Amdocs payment-intent)
DatabaseMySQL 8 (Docker)
ChartsChart.js via livewire-charts
NotificationsToastr.js
ExportMaatwebsite Excel
TestingPestPHP

Outcome

The platform is live and serving U2C Mobile customers. Customers can self-manage their plans, purchase add-ons, initiate SIM swaps, and review billing and payment history entirely through the portal — without contacting support for routine account changes.

The admin team manages all site content, blog posts, user accounts, and system settings independently, with no developer involvement required for day-to-day operations. Maintenance mode, logo updates, and custom scripts can all be toggled from the CMS.

The unified codebase serving both the customer portal and admin CMS keeps the operational surface minimal while supporting two distinct user audiences through a well-layered middleware and role architecture.