Skip to main content

Table of Contents


Overview

Base URL: https://pro.chargily.net/api/v1 Chargily Pro provides a RESTful API for managing mobile top-ups and digital voucher sales. The API supports multiple authentication methods and includes comprehensive validation middleware.

Key Features

  • Mobile top-up operations (Ooredoo, Djezzy, Mobilis)
  • Digital voucher/card sales
  • Wallet & balance management
  • Webhook notifications
  • Multi-tenant support
  • Sandbox mode for testing

Authentication

All API requests require authentication using an API Public Key. Header:
X-Authorization: {api_key}
Example:
curl -X GET https://pro.chargily.net/api/v1/user/balance \
  -H "X-Authorization: {your-api-key-here}"

Getting Your API Public Key

API Public Keys are generated through your account dashboard. Each API Public Key is unique and associated with your user account.

Rate Limiting

  • Limit: 2,400 requests per minute
  • Applied to: All API routes
  • Response on limit: HTTP 429 Too Many Requests

Error Handling

Standard Error Response

{
  "message": "Error description",
  "status": false
}

HTTP Status Codes

CodeMeaningCommon Causes
200OKSuccessful GET request
201CreatedSuccessful POST request
202AcceptedRequest accepted for processing
400Bad RequestInvalid request format
401UnauthorizedInvalid/missing authentication
402Payment RequiredInsufficient balance
403ForbiddenMode inactive, permission denied
404Not FoundResource doesn’t exist
409ConflictDuplicate request
410GoneSubscription expired
413Payload Too LargePending request exists (cooldown)
422Unprocessable EntityValidation failed
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side error

Endpoints

User Management Endpoints

Get User Balance

Returns current balance and bonus for authenticated user.
GET /api/v1/user/balance
X-Authorization: {api_key}
Authentication: API Public Key required Response (200 OK):
{
  "balance": 150000,
  "bonus": 5000
}
Note: Amounts are in cents (divide by 100 for actual value) Example:
  • balance: 150000 = 1,500.00 DZD
  • bonus: 5000 = 50.00 DZD

Get All-Time User Bonus

Returns total bonus earned by user.
GET /api/v1/user/getAllTimeUserBonus
X-Authorization: {api_key}
Response (200 OK):
{
  "allTimeBonus": 25000
}
Note: Amount in cents (25000 = 250.00 DZD)

Mobile Topup (Flexy/Airtime) Endpoints

Create Topup Request

Creates a new mobile top-up request.
POST /api/v1/topup/requests
X-Authorization: {api_key}
Content-Type: application/json
Authentication: API Public Key required Middleware Checks:
  • API subscription active
  • Operator valid
  • Mode valid and active
  • Request timestamp recent (less than 30 seconds)
  • Sufficient balance
  • No duplicate pending request (3-minute cooldown)
Request Body:
{
  "request_number": "REQ-2024-001",
  "customer_name": "Ahmed Benali",
  "phone_number": "555123456",
  "value": 200,
  "operator": "ooredoo",
  "mode": "normal",
  "country_code": "DZ",
  "webhook_url": "https://your-app.net/webhooks/topup",
  "created_at": "2024-01-15 10:30:00"
}
Validation Rules:
  • request_number: Required, unique identifier for your system
  • customer_name: Required, string
  • phone_number: Required, must match operator format
  • value: Required, integer, between 10 and 5000
  • operator: Required, one of: ooredoo, djezzy, mobilis
  • mode: (Bundle) Required, valid mode for operator (e.g., “prepaid”, “postpaid”)
  • country_code: Required (e.g., “DZ”)
  • webhook_url: Required, valid URL
  • created_at: Required, datetime, max 30 seconds old
Response (201 Created):
{
  "message": "request sent successfully",
  "status": true
}
Response (402 Payment Required):
{
  "message": "Insufficient balance",
  "status": false
}
Response (413 Payload Too Large):
{
  "message": "Request already pending",
  "status": false
}
Response (422 Unprocessable Entity):
{
  "message": "Invalid phone number format for operator"
}

Get Topup Request Status

Retrieves the status of a specific topup request.
GET /api/v1/topup/requests/{request_number}
X-Authorization: {api_key}
URL Parameters:
  • request_number (string): Your unique request identifier
Response (200 OK):
{
  "topupQueue": {
    "id": 123,
    "request_number": "REQ-2024-001",
    "customer_name": "Ahmed Benali",
    "phone_number": "555123456",
    "value": 200,
    "status": "sent",
    "created_at": "2024-01-15T10:30:00.000000Z",
    "updated_at": "2024-01-15T10:31:00.000000Z"
  }
}
Possible Status Values:
  • pending: Awaiting processing
  • sent: Successfully completed
  • failed: Operation failed
  • rejected: Request rejected
  • expired: Request timed out

Get All Sent Operations

Returns all successfully sent topup operations for the authenticated user.
GET /api/v1/topup/sent
X-Authorization: {api_key}
Response (200 OK):
{
  "topupQueue": {
    "REQ-2024-001": "sent",
    "REQ-2024-003": "sent",
    "REQ-2024-007": "sent"
  }
}

Check Existing Operations

Checks which request numbers exist in the system.
POST /api/v1/topup/checkExistingOperation
Content-Type: application/json
Request Body:
{
  "ids": ["REQ-2024-001", "REQ-2024-002", "REQ-2024-999"]
}
Response (200 OK):
{
  "ids": ["REQ-2024-001", "REQ-2024-002"]
}
Note: Returns only IDs that exist in the database

Resend Webhook for Processing Requests

Triggers webhook resend for specific request numbers.
POST /api/v1/topup/recheckProcessingRequests
Content-Type: application/json
Request Body:
{
  "ids": ["REQ-2024-001", "REQ-2024-002"]
}
Response (202 Accepted):
{
  "message": "webhook sent successfully"
}
Response (400 Bad Request):
{
  "message": "Invalid IDs provided"
}

Topup Configuration Endpoints

List Operators

Returns all active mobile operators.
GET /api/v1/topup/operators
Authentication: None required Response (200 OK):
{
  "operators": [
    {
      "id": 1,
      "name": "Ooredoo",
      "logo": "https://storage.pro.chargily.net/operators/ooredoo.png",
      "country_code": "DZ",
      "first_number": ["05", "06", "07"],
      "number_length": [9],
      "discount": 2.5,
      "mode_discount": 1.5
    },
    {
      "id": 2,
      "name": "Djezzy",
      "logo": "https://storage.pro.chargily.net/operators/djezzy.png",
      "country_code": "DZ",
      "first_number": ["077", "078", "079"],
      "number_length": [9],
      "discount": 2.0,
      "mode_discount": 1.0
    }
  ]
}
Fields:
  • first_number: Array of valid phone number prefixes
  • number_length: Array of valid phone number lengths
  • discount: Percentage discount for regular recharges
  • mode_discount: Percentage discount for mode-based recharges

List Modes

Returns all active recharge modes.
GET /api/v1/topup/modes
Authentication: None required Response (200 OK):
{
  "modes": [
    {
      "id": 5,
      "mode": "switch",
      "operator": "ooredoo",
      "value": 200,
      "amount": 205.00,
      "discount": 1.5,
      "operator_id": 1,
      "is_active": true,
      "country_code": "DZ"
    },
    {
      "id": 12,
      "mode": "legend",
      "operator": "djezzy",
      "value": 500,
      "amount": 510.00,
      "discount": 2.0,
      "operator_id": 2,
      "is_active": true,
      "country_code": "DZ"
    }
  ]
}
Fields:
  • mode: Name of the bundle (e.g., “PixX1000”, “Sama Mix”)
  • value: Face value of the recharge
  • amount: Actual amount charged (including fees/discounts)
  • discount: Percentage discount for this mode

Get Mode Details

Returns details for a specific mode.
GET /api/v1/topup/modes/{id}
URL Parameters:
  • id (integer): Mode ID
Response (200 OK):
{
  "mode": {
    "id": 5,
    "mode": "normal",
    "operator": "ooredoo",
    "value": 200,
    "amount": 205.00,
    "discount": 1.5,
    "operator_id": 1,
    "is_active": true,
    "country_code": "DZ"
  }
}

Vouchers/Gift Cards Endpoints

List All Vouchers

Returns all available digital vouchers, gift and prepaid cards.
GET /api/v1/voucher/vouchers
Authentication: None required Response (200 OK):
{
  "cards": [
    {
      "id": 1,
      "name": "Google Play 1000 DZD",
      "image": "https://storage.pro.chargily.net/cards/google-play.png",
      "value": 1000,
      "amount": 1050.00,
      "redeem": "Play Store",
      "discount": 5.0,
      "card_category_id": 1,
      "card_category_name": "Gaming",
      "country_code": "DZ",
      "category_image": "https://storage.pro.chargily.net/categories/gaming.png",
      "out_of_stock": false
    },
    {
      "id": 2,
      "name": "iTunes 500 DZD",
      "image": "https://storage.pro.chargily.net/cards/itunes.png",
      "value": 500,
      "amount": 525.00,
      "redeem": "App Store",
      "discount": 5.0,
      "card_category_id": 2,
      "card_category_name": "Entertainment",
      "country_code": "DZ",
      "category_image": "https://storage.pro.chargily.net/categories/entertainment.png",
      "out_of_stock": true
    }
  ]
}
Fields:
  • value: Face value of the card
  • amount: Selling price
  • redeem: Where to redeem the card
  • out_of_stock: Boolean indicating availability

Get Voucher Details

Returns details for a specific voucher.
GET /api/v1/voucher/vouchers/{id}
URL Parameters:
  • id (integer): Voucher ID
Response (200 OK):
{
  "id": 1,
  "name": "Google Play 1000 DZD",
  "image": "https://storage.pro.chargily.net/cards/google-play.png",
  "value": 1000,
  "amount": 1050.00,
  "redeem": "Play Store",
  "discount": 5.0,
  "card_category_id": 1,
  "card_category_name": "Gaming",
  "country_code": "DZ",
  "category_image": "https://storage.pro.chargily.net/categories/gaming.png",
  "out_of_stock": false
}
Response (404 Not Found):
{
  "message": "Voucher not found"
}

List Card Categories

Returns all voucher categories.
GET /api/v1/voucher/categories
Authentication: None required Response (200 OK):
{
  "categories": [
    {
      "id": 1,
      "name": "Idoom ADSL/Fibre",
      "image": "https://storage.pro.chargily.net/categories/idoom-adsl-fibre.png",
      "redeem_link": "https://paiement.at.dz/index.php?p=voucher_internet",
      "redeem_link_description": "Recharge your internet subscription",
      "country_code": "DZ"
    },
    {
      "id": 2,
      "name": "Gaming",
      "image": "https://storage.pro.chargily.net/categories/gaming.png",
      "redeem_link": "https://play.google.net/redeem",
      "redeem_link_description": "Enter code in Play Store",
      "country_code": "DZ"
    },
    {
      "id": 3,
      "name": "Entertainment",
      "image": "https://storage.pro.chargily.net/categories/entertainment.png",
      "redeem_link": "https://www.apple.net/redeem",
      "redeem_link_description": "Redeem in App Store",
      "country_code": "DZ"
    }
  ]
}

Get Cards by Category

Returns all cards in a specific category.
GET /api/voucher/categories/{name_or_id}
URL Parameters:
  • name_or_id: Category ID (integer) or category name (string)
Response (200 OK):
[
    {
      "id": 1,
      "name": "Google Play 1000 DZD",
      "image": "https://storage.pro.chargily.net/cards/google-play.png",
      "value": 1000,
      "amount": 1050.00,
      "redeem": "Play Store",
      "discount": 5.0,
      "card_category_id": 1,
      "card_category_name": "Gaming",
      "country_code": "DZ",
      "category_image": "https://storage.pro.chargily.net/categories/gaming.png",
      "out_of_stock": false
    }
]
Response (404 Not Found):
{
  "message": "No vouchers found in this category"
}

Create Voucher Request

Purchases a digital voucher.
POST /api/voucher/requests
X-Authorization: {api_key}
Content-Type: application/json
Authentication: API Key required Middleware Checks:
  • API subscription active
  • Voucher category active
  • Sufficient inventory
  • Sufficient balance
Request Body:
{
  "quantity": 1,
  "voucher_name": "Google Play 1000 DZD",
  "value": "1000",
  "customer_name": "Ahmed Benali",
  "request_number": "VCHR-2024-001",
  "country_code": "dz"
}
Validation Rules:
  • quantity: Required, integer, > 0
  • voucher_name: Required, string
  • value: Required, string (face value)
  • customer_name: Required, string
  • request_number: Required, unique identifier
  • country_code: Required, string
Response (201 Created):
{
  "message": "request sent successfully",
  "voucher_serial": "XXXX-XXXX-XXXX-XXXX",
  "voucher_key": "ABCD-1234-EFGH-5678",
  "status": true
}
Response (402 Payment Required):
{
  "message": "Insufficient balance"
}
Side Effects:
  • Deducts amount from user balance
  • Assigns voucher from inventory
  • Marks voucher as sold
  • Awards bonus points
Sandbox Mode: For testing users (karaOdin, karaOdin2, kyesmine, developer), returns encrypted/test voucher data without consuming inventory.

Get All Successful Vouchers

Returns all successfully sold vouchers for the authenticated user.
GET /api/voucher/sold
X-Authorization: {api_key}
Response (200 OK):
{
  "voucherRequests": {
    "VCHR-2024-001": "ABCD-1234-EFGH-5678",
    "VCHR-2024-002": "IJKL-9012-MNOP-3456"
  }
}
Response (404 Not Found):
{
  "message": "No vouchers found"
}

Add Voucher to Inventory (Internal)

Adds a new voucher code to inventory.
POST /api/voucher/inventory
Content-Type: application/json
Authentication: None (should be restricted in production) Request Body:
{
  "voucher_id": 1,
  "voucher_key": "ABCD-1234-EFGH-5678",
  "voucher_serial": "XXXX-XXXX-XXXX-XXXX"
}
Response (201 Created):
{
  "message": "Voucher stored successfully",
  "status": true
}
Side Effects:
  • Increments card quantity
  • Makes voucher available for purchase

Deposit Endpoints

Get All Deposits

Returns deposit history for authenticated user.
GET /api/deposit/all
X-Authorization: {api_key}
Authentication: API Key required Response (200 OK):
{
  "deposits": [
    {
      "id": 1,
      "user_id": 1,
      "amount": 10000.00,
      "status": "approved",
      "created_at": "2024-01-10T12:00:00.000000Z",
      "updated_at": "2024-01-10T12:30:00.000000Z"
    },
    {
      "id": 2,
      "user_id": 1,
      "amount": 5000.00,
      "status": "pending",
      "created_at": "2024-01-15T10:00:00.000000Z",
      "updated_at": "2024-01-15T10:00:00.000000Z"
    }
  ]
}
Possible Status Values:
  • pending: Awaiting approval
  • approved: Deposit confirmed
  • rejected: Deposit declined

Get Sum of Approved Deposits

Returns total of all approved deposits for authenticated user.
GET /api/deposit/sumDeposits
X-Authorization: {api_key}
Response (200 OK):
{
  "total": 50000.00
}

Webhooks

Webhook Notifications

The system sends webhook notifications when topup request status changes. Package: spatie/laravel-webhook-server Trigger Events:
  • Status changed to: sent, failed, rejected, expired
Webhook Request:
POST {your_webhook_url}
Content-Type: application/json
X-Signature: {hmac_signature}
Payload:
{
  "payload": {
    "id": 123,
    "request_number": "REQ-2024-001",
    "customer_name": "Ahmed Benali",
    "phone_number": "555123456",
    "value": 200,
    "username": "johndoe",
    "user_id": 1,
    "mode": "normal",
    "mode_id": 5,
    "operator": "ooredoo",
    "status": "sent",
    "created_at": "2024-01-15 10:30:00",
    "updated_at": "2024-01-15 10:31:00"
  }
}

Webhook Security

Signature Verification: The webhook includes an HMAC signature in the X-Signature header. Verify using your secret key:
$signature = hash_hmac('sha256', $payload, $user->secret);
if (!hash_equals($signature, $request->header('X-Signature'))) {
    // Invalid signature
}
Retry Logic:
  • Maximum 5 retry attempts
  • Exponential backoff between retries
  • SSL verification disabled (for development)

API Restrictions & Validations

All API requests go through automatic validation to ensure data integrity and prevent errors. Below are the restrictions you may encounter:

Subscription Status

  • Your API subscription must be active
  • Error (410 Gone): “Subscription is expired”

Authentication

  • Valid API key required in X-Authorization header
  • Error (401 Unauthorized): “Unauthorized”

Balance Requirements

  • Sufficient balance required for purchases
  • Error (402 Payment Required): “Insufficient balance”

Operator Validation

When creating topup requests:
  • Operator must exist and be active
  • Phone number length must match operator requirements
  • Phone number must start with valid prefix for the operator
Possible Errors:
  • 404: “Operator not found”
  • 422: “Invalid phone number length”
  • 422: “Invalid phone number format for operator”

Topup Mode Validation

  • Mode must exist for the specified operator
  • Mode must be active
  • Value must match mode requirements (for fixed-value modes)
Possible Errors:
  • 404: “Mode not found for operator”
  • 403: “Mode is not active”

Request Timing & Duplicates

  • Request timestamp must be within 30 seconds
  • No duplicate requests (same request_number + customer_name)
Possible Errors:
  • 400: “Request timestamp too old (max 30 seconds)”
  • 409: “Duplicate request detected”

Voucher Validation

  • Voucher category must be active and available
  • Sufficient inventory must be available for requested quantity
Possible Errors:
  • 404: “this voucher is not available”
  • 402: “Insufficient voucher inventory”

Business Logic

Topup Request Lifecycle

  1. Validation
    • Operator valid and active
    • Mode valid and active
    • Phone number format correct
    • Request timestamp recent (less than 30 seconds)
    • Sufficient user balance
  2. Duplicate Check
    • 3-minute cooldown per phone number
    • Prevents duplicate pending requests
  3. Balance Deduction
    • Amount deducted from user wallet
    • Transaction recorded
  4. Request Creation
    • Status set to “pending”
    • Queued for processing
  5. Processing
    • Automatic or manual processing
    • Status updated to “sent”/“failed”
  6. Notification
    • Webhook sent to client URL
    • Up to 5 retry attempts
  7. Finalization
    • Success: Award bonus points
    • Failure: Refund to user balance

Voucher Request Lifecycle

  1. Validation
    • Voucher category active
    • Sufficient inventory
    • Sufficient user balance
  2. Balance Deduction
    • Amount deducted from wallet
    • Transaction recorded
  3. Voucher Assignment
    • Voucher retrieved from inventory
    • Serial and key assigned
  4. Status Update
    • Voucher marked as sold
    • Inventory quantity decremented
  5. Bonus Award
    • Reward points added to bonus wallet
  6. Response
    • Serial and key returned to client

Sandbox Mode

Purpose: Testing without consuming real inventory Sandbox Users:
  • karaOdin
  • karaOdin2
  • kyesmine
  • developer
Behavior:
  • Returns encrypted/test voucher data
  • Does not consume inventory
  • Does not deduct real balance
  • Useful for integration testing

Best Practices

Request Timestamps

Always include current timestamp in created_at field:
// JavaScript
{
  "created_at": new Date().toISOString().slice(0, 19).replace('T', ' ')
}
// PHP
[
    'created_at' => now()->format('Y-m-d H:i:s')
]

Webhook Implementation

Implement webhook handler to receive status updates:
Route::post('/webhooks/topup', function (Request $request) {
    // Verify signature
    $signature = hash_hmac('sha256', $request->getContent(), config('app.webhook_secret'));
    if (!hash_equals($signature, $request->header('X-Signature'))) {
        abort(401, 'Invalid signature');
    }

    // Process webhook
    $payload = $request->input('payload');
    $status = $payload['status'];
    $request_number = $payload['request_number'];

    // Update your local database
    // Send notification to customer
    // etc.

    return response()->json(['status' => 'ok']);
});

Error Handling

Always handle API errors gracefully:
try {
  const response = await fetch('/api/topup/requests', {
    method: 'POST',
    headers: {
      'X-Authorization': apiKey,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(requestData)
  });

  if (!response.ok) {
    const error = await response.json();
    if (response.status === 402) {
      // Insufficient balance - show top-up prompt
    } else if (response.status === 413) {
      // Duplicate request - show pending message
    } else {
      // Generic error handling
    }
  }

  const result = await response.json();
  // Handle success
} catch (error) {
  // Network error handling
}

Balance Management

Check balance before expensive operations:
// Check balance first
const balanceResponse = await fetch('/api/user/balance', {
  headers: { 'X-Authorization': apiKey }
});
const { balance } = await balanceResponse.json();

// Balance is in cents, convert to display
const balanceInDZD = balance / 100;

if (balanceInDZD >= requiredAmount) {
  // Proceed with operation
} else {
  // Show insufficient balance message
}

Idempotency

Use unique request_number for idempotent requests:
// Generate unique request number
const requestNumber = `REQ-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

const response = await fetch('/api/topup/requests', {
  method: 'POST',
  headers: {
    'X-Authorization': apiKey,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    request_number: requestNumber,
    // ... other fields
  })
});
This ensures duplicate API calls don’t create duplicate charges.

Testing

Testing with Sandbox Mode

Use sandbox accounts for testing:
# Sandbox usernames (no real charges)
karaOdin
karaOdin2
kyesmine
developer
These accounts return test data without consuming real inventory or balance.

Sample Integration Test

public function test_create_topup_request()
{
    $user = User::factory()->create();
    $apiKey = ApiKey::create(['user_id' => $user->id]);

    // Fund user wallet
    $user->deposit(10000); // 100.00 DZD

    $response = $this->withHeaders([
        'X-Authorization' => $apiKey->key,
    ])->postJson('/api/topup/requests', [
        'request_number' => 'TEST-001',
        'customer_name' => 'Test Customer',
        'phone_number' => '555123456',
        'value' => 200,
        'operator' => 'ooredoo',
        'mode' => 'flexy',
        'country_code' => 'DZ',
        'webhook_url' => 'https://your-app.net/webhooks/topup',
        'created_at' => now()->format('Y-m-d H:i:s'),
    ]);

    $response->assertStatus(201)
             ->assertJson([
                 'message' => 'request sent successfully',
                 'status' => true
             ]);

    $this->assertDatabaseHas('topup_queue', [
        'request_number' => 'TEST-001',
        'status' => 'pending'
    ]);
}

Support

For API support:
  • Email: [email protected]
  • Documentation: Check this file
  • Issue Tracker: Contact your account manager

Changelog

Version 1.0 (2024-01-15)

  • Initial API documentation
  • All endpoints documented
  • Authentication methods described
  • Error handling documented
  • Webhook system explained