Overview
Connect World’s order management system handles the complete subscription lifecycle, from customer creation through payment processing to subscription activation and expiration tracking.
Order Creation Secure order processing with validation
Customer Management Automatic customer profile creation
Status Tracking Real-time order status updates
Order Entity
Order Interface
export type PaymentMethod = "stripe" | "paypal" ;
export type OrderStatus = "pending" | "completed" | "failed" ;
export interface Order {
id ?: string ;
customerId : string ;
planId : string ;
devices : number ;
months : number ;
amount : number ;
paymentMethod : PaymentMethod ;
paymentReceiptId : string ;
status : OrderStatus ;
activationDate : Date ;
expirationDate : Date ;
createdAt ?: Date ;
}
Source: src/domain/entities/Order.ts:1-17
Order Status
Payment Methods
Order Status Types Orders can have three status values:
pending : Order created but payment not yet confirmed
completed : Payment confirmed and subscription active
failed : Payment failed or order could not be processed
export type OrderStatus = "pending" | "completed" | "failed" ;
Source: src/domain/entities/Order.ts:2 Supported Payment Methods Orders support two payment gateways: export type PaymentMethod = "stripe" | "paypal" ;
Source: src/domain/entities/Order.ts:1 The payment method is validated server-side against the allowed enum values to prevent invalid payment types.
Order Creation
Factory Function
Orders are created using a factory function that automatically calculates activation and expiration dates:
export function createOrder ( data : Omit < Order , "id" | "createdAt" | "activationDate" | "expirationDate" >) : Order {
const activationDate = new Date ();
const expirationDate = new Date ();
expirationDate . setMonth ( expirationDate . getMonth () + data . months );
return {
... data ,
activationDate ,
expirationDate ,
createdAt: new Date (),
};
}
Source: src/domain/entities/Order.ts:19-30
activationDate : Set to current date/time when order is created
expirationDate : Calculated by adding the subscription duration (months) to the activation date
createdAt : Timestamp of order creation
Example: A 6-month subscription created on January 1st will:
activationDate: 2026-01-01
expirationDate: 2026-07-01
Order API Endpoint
Order Creation Endpoint
The /api/orders endpoint handles complete order creation with comprehensive security measures:
export async function POST ( req : NextRequest ) {
// Rate limit: 5 order creations per IP per 10 minutes (anti-spam)
const ip = getClientIp ( req );
if ( ! checkRateLimit ( `orders: ${ ip } ` , 5 , 10 * 60 * 1000 )) {
return NextResponse . json (
{ error: "Demasiadas solicitudes. Intenta de nuevo en unos minutos." },
{ status: 429 }
);
}
try {
const body = await req . json ();
// Honeypot: bots fill hidden fields humans don't see
if ( body . _hp && body . _hp !== "" ) {
// Silently reject — return fake success so bots don't retry aggressively
return NextResponse . json ({ orderId: "bot" , expirationDate: new Date (). toISOString () }, { status: 201 });
}
// Sanitize and validate every field
const name = sanitizeString ( body . name , 100 );
const email = sanitizeEmail ( body . email );
const phone = sanitizePhone ( body . phone );
const planId = sanitizeString ( body . planId , 50 );
const devices = sanitizeNumber ( body . devices , 1 , 10 );
const months = sanitizeNumber ( body . months , 1 , 12 );
const amount = sanitizeNumber ( body . amount , 1 , 10000 );
const paymentMethod = sanitizeEnum < PaymentMethod >( body . paymentMethod , PAYMENT_METHODS );
const paymentReceiptId = sanitizeString ( body . paymentReceiptId , 200 );
// Validation and order creation...
} catch ( error : unknown ) {
// Error handling...
}
}
Source: src/app/api/orders/route.ts:19-97
Rate Limiting
Anti-Spam Protection : Orders are rate-limited to 5 creations per IP per 10 minutes to prevent abuse.
if ( ! checkRateLimit ( `orders: ${ ip } ` , 5 , 10 * 60 * 1000 )) {
return NextResponse . json (
{ error: "Demasiadas solicitudes. Intenta de nuevo en unos minutos." },
{ status: 429 }
);
}
Source: src/app/api/orders/route.ts:21-27
Honeypot Protection
The endpoint implements honeypot fields to catch bots:
// Honeypot: bots fill hidden fields humans don't see
if ( body . _hp && body . _hp !== "" ) {
// Silently reject — return fake success so bots don't retry aggressively
return NextResponse . json ({ orderId: "bot" , expirationDate: new Date (). toISOString () }, { status: 201 });
}
Source: src/app/api/orders/route.ts:32-36
Honeypot fields are hidden in the UI. Legitimate users don’t see or fill them, but bots often auto-fill all fields. The endpoint returns a fake success response to avoid triggering aggressive bot retries.
Comprehensive Field Validation
Every field is sanitized and validated before processing:
const name = sanitizeString ( body . name , 100 );
const email = sanitizeEmail ( body . email );
const phone = sanitizePhone ( body . phone );
const planId = sanitizeString ( body . planId , 50 );
const devices = sanitizeNumber ( body . devices , 1 , 10 );
const months = sanitizeNumber ( body . months , 1 , 12 );
const amount = sanitizeNumber ( body . amount , 1 , 10000 );
const paymentMethod = sanitizeEnum < PaymentMethod >( body . paymentMethod , PAYMENT_METHODS );
const paymentReceiptId = sanitizeString ( body . paymentReceiptId , 200 );
Source: src/app/api/orders/route.ts:39-47
Validation Rules
Plan Validation const plan = PLANS . find (( p ) => p . id === planId );
if ( ! plan ) {
return NextResponse . json (
{ error: "Plan inválido." },
{ status: 400 }
);
}
Source: src/app/api/orders/route.ts:54-57
Duration Validation if ( ! ( VALID_MONTHS as readonly number []). includes ( months )) {
return NextResponse . json (
{ error: "Duración inválida." },
{ status: 400 }
);
}
Source: src/app/api/orders/route.ts:60-62
Price Validation const expectedPrice = plan . prices . find (
( p ) => p . months === months
)?. price ;
if ( expectedPrice === undefined ||
Math . abs ( amount - expectedPrice ) > 0.01 ) {
return NextResponse . json (
{ error: "Monto inválido." },
{ status: 400 }
);
}
Source: src/app/api/orders/route.ts:65-68
Device Validation if ( devices !== plan . devices ) {
return NextResponse . json (
{ error: "Número de dispositivos inválido." },
{ status: 400 }
);
}
Source: src/app/api/orders/route.ts:71-73
Price Tampering Prevention : The server validates that the submitted amount matches the expected price for the plan and duration. This prevents users from manipulating prices in the client.
Customer Management
Customer Entity
export interface Customer {
id ?: string ;
email : string ;
phone : string ;
name : string ;
createdAt ?: Date ;
}
export function createCustomer ( data : Omit < Customer , "id" | "createdAt" >) : Customer {
return {
... data ,
createdAt: new Date (),
};
}
Source: src/domain/entities/Customer.ts:1-14
Email and Phone Validation
Email Validation
Phone Validation
/** Validate and normalize email */
export function sanitizeEmail ( value : unknown ) : string {
const s = sanitizeString ( value , 254 );
const emailRe = / ^ [ a-zA-Z0-9._%+ \- ] + @ [ a-zA-Z0-9. \- ] + \. [ a-zA-Z ] {2,} $ / ;
return emailRe . test ( s ) ? s . toLowerCase () : "" ;
}
Source: src/lib/sanitize.ts:16-21 Emails are automatically converted to lowercase and must match a valid email pattern.
/** Validate phone — digits, +, spaces, dashes, parens only */
export function sanitizePhone ( value : unknown ) : string {
const s = sanitizeString ( value , 20 );
return / ^ [ \d+\- \s () ] {6,20} $ / . test ( s ) ? s : "" ;
}
Source: src/lib/sanitize.ts:23-27 Phone numbers must be 6-20 characters and can contain:
Digits (0-9)
Plus sign (+)
Dashes (-)
Spaces
Parentheses ()
Order Use Case
CreateOrderUseCase
The use case orchestrates customer and order creation:
export class CreateOrderUseCase {
constructor (
private readonly customerRepo : ICustomerRepository ,
private readonly orderRepo : IOrderRepository
) {}
async execute ( dto : CreateOrderDto ) : Promise < OrderResponseDto > {
const email = new Email ( dto . email );
const phone = new Phone ( dto . phone );
const customerData = createCustomer ({
name: dto . name ,
email: email . toString (),
phone: phone . toString (),
});
const customer = await this . customerRepo . create ( customerData );
const orderData = createOrder ({
customerId: customer . id ! ,
planId: dto . planId ,
devices: dto . devices ,
months: dto . months ,
amount: dto . amount ,
paymentMethod: dto . paymentMethod ,
paymentReceiptId: dto . paymentReceiptId ,
status: "completed" ,
});
const order = await this . orderRepo . create ( orderData );
return {
orderId: order . id ! ,
customerId: customer . id ! ,
planId: order . planId ,
devices: order . devices ,
months: order . months ,
amount: order . amount ,
paymentMethod: order . paymentMethod ,
paymentReceiptId: order . paymentReceiptId ,
status: order . status ,
activationDate: order . activationDate . toISOString (),
expirationDate: order . expirationDate . toISOString (),
};
}
}
Source: src/application/use-cases/CreateOrderUseCase.ts:9-54
Use Case Flow
Use Case Responsibilities
Value Object Validation : Wraps email and phone in value objects for domain validation
Customer Creation : Creates or retrieves customer profile
Order Creation : Creates order with activation/expiration dates
Data Persistence : Saves customer and order to repositories
Response Mapping : Converts domain entities to DTOs for API response
Order Response
Successful Order Response
return NextResponse . json ( result , { status: 201 });
Response structure:
{
"orderId" : "507f1f77bcf86cd799439011" ,
"customerId" : "507f191e810c19729de860ea" ,
"planId" : "plan-2" ,
"devices" : 2 ,
"months" : 6 ,
"amount" : 70 ,
"paymentMethod" : "stripe" ,
"paymentReceiptId" : "pi_3MtwBwLkdIwHu7ix28a3tqPa" ,
"status" : "completed" ,
"activationDate" : "2026-03-09T10:30:00.000Z" ,
"expirationDate" : "2026-09-09T10:30:00.000Z"
}
Source: src/app/api/orders/route.ts:91
Security Checklist
Rate Limiting 5 orders per IP per 10 minutes
Honeypot Hidden field catches bot submissions
Input Sanitization All fields sanitized and validated
Price Validation Server-side price verification
Plan Validation Validates plan exists in system
Device Validation Ensures devices match plan
Email Validation Regex pattern and normalization
Enum Validation Payment methods validated against enum
Best Practices
Always use the use case layer - Don’t directly create orders from API routes
Validate payment receipt IDs - Ensure payment was completed before creating order
Set status to “completed” - Only after successful payment verification
Calculate expiration dates server-side - Never trust client calculations
Store customer data - Link orders to customer profiles for history tracking
Log order creation - Include order ID, customer ID, and payment method
Handle errors gracefully - Return clear error messages for validation failures
Use repositories - Abstract database operations behind repository interfaces
Never create an order without first verifying the payment was successful. Always require a paymentReceiptId from the payment processor.
Order Lifecycle
Pending → Completed : Payment processor confirms successful payment
Pending → Failed : Payment processor reports failed transaction
Completed → Expired : System checks expiration date and marks subscription as expired
Failed → Archive : Failed orders are archived for review