PagaFácil

A WhatsApp payment assistant for Venezuela that guided users through collecting transfer details and a one-time bank code, with a state machine controlling the conversation.

Period
August 2025 – September 2025
Role
Engineer
Team
Taecontrol
Stack
Laravel 12, PHP ^8.4, React 19, Inertia 2, Horizon, Redis, OpenRouter, WhatsApp Cloud API, Cobra Fácil, Saloon
Status
Closed

PagaFácil started with a payment someone could describe in a WhatsApp message. I wanted to build a Spanish-language assistant for Venezuela that could ask for missing transfer details and guide the person through pago móvil without making them fill in the whole payment on a separate screen.

There was still a web app. A user signed in with a code sent by email and registered their payment methods there. When a WhatsApp message arrived, the backend checked for a default payment method. If one was missing, it sent the user to the dashboard before starting a payment conversation.

The conversation needed more control than a single prompt could provide. Collecting the recipient's details was different from capturing a bank's one-time code. Waiting for a payment result was different again. I represented those situations as states, with allowed transitions declared in a graph.

Each active state selected an agent with a specific job. The agents wrote their results into the conversation's metadata under their state names. Later agents could read the details already collected. That gave me a record to inspect when the conversation took an unexpected path.

I used Laravel jobs and events to advance the conversation. WhatsApp intake and the model calls ran on separate queues, so receiving a message did not have to wait for an LLM response. Locks prevented overlapping work on the same conversation, and a unique message identifier handled repeated webhook deliveries.

The Cobra Fácil integration supported submitting a payment and checking its status. While a transaction was pending, the conversation waited in a state without an agent. A polling command checked the provider and emitted the event that moved the conversation to a result.

The app scheduled payment checks every five seconds. The success and failure agents emitted an event that expired the conversation.

The last repository push was in September 2025. The domain no longer resolves, and the code remains public. The project taught me to distinguish a model's conversational work from the steps the application must control. An agent could ask for a missing detail. The application checked its next transition against the graph.