Skip to content

Prepaid Cards

The Prepaid Cards page lets you mint batches of recharge cards and redeem them against subscribers. Each card is a code + PIN pair that carries a monetary value, a number of days, an optional service, and an optional quota refill. Redeeming a card extends the subscriber’s expiry, optionally switches their service, optionally resets their quota counters, and records a transaction.

This is the page to open when an ISP sells physical/printed recharge cards through shops, or when a reseller wants to top customers up without touching their balance directly.

Sidebar → Prepaid Cards. Direct URL: /prepaid.

Requires prepaid.view. Resellers see only cards belonging to their own reseller (and their sub-resellers’) unless they have prepaid.view_all, in which case they see every card.

SectionWhat it shows
Toolbar”Prepaid Cards” title, Redeem Card button, Generate Cards button
Batches overviewUp to 4 batch stat-cards: batch ID, active count, total count, used count, and a Delete Unused button per batch
Filter barStatus dropdown (All / Available / Used), Batch dropdown (All Batches or a specific batch)
Cards tableCode, PIN, Value, Days, Service, Status, Batch, Actions
PaginationServer-side, shown when there is more than one page

The cards table is paginated server-side: 25 cards per page by default, capped at 100. Cards are listed newest-first (created_at DESC).

Click Generate Cards to open the generate modal. Cards are always created as a batch — even a batch of one. The batch is stamped with an auto-generated ID of the form BATCH-<unix-timestamp>, and each card is numbered within the batch.

The generate form sends these fields:

FieldMeaningNotes
CountHow many cards to mintRequired. Must be between 1 and 1000.
DaysDays added to the subscriber’s expiry on redemptionOptional. 0 = no expiry change.
ServiceService the subscriber is switched to on redemptionOptional. “No service change” leaves the subscriber’s service untouched.
PrefixPrefix prepended to each generated codeOptional, up to 6 characters, upper-cased in the UI.
ValueMonetary value recorded on the redemption transactionSent by the API; defaults to 0.
Quota refillWhether redemption resets quota usageSent by the API; any value greater than 0 triggers a quota reset on redemption.
Code lengthLength of the generated codeBelow 8 it falls back to 12.
PIN lengthLength of the numeric PINBelow 4 it falls back to 4.

Codes are random hex (optionally with the PREFIX- in front); PINs are random digits. Both are generated with a cryptographic random source.

On success the API returns the new batch_id, the count, and the full list of generated cards (codes and PINs included) so they can be printed or exported.

Click Redeem Card to open the redeem modal. It asks for:

  1. Card Code — the card’s code.
  2. PIN — the card’s PIN.
  3. Subscriber ID — the numeric ID of the subscriber to apply the card to.

The redeem handler looks the card up by exact code + PIN. If no card matches, it returns “Invalid card code or PIN”.

Before applying anything it runs these checks, in order:

  1. Already used — if is_used is true, redemption is rejected (“Card has already been used”).
  2. Inactive — if is_active is false, redemption is rejected (“Card is not active”).
  3. Expired — if the card has an expiry date and it is in the past (in the tenant’s timezone), redemption is rejected (“Card has expired”).
  4. Card ownership — a reseller may only redeem their own cards (or a sub-reseller’s), unless they have prepaid.view_all. Otherwise “Access denied”.
  5. Subscriber found — the target subscriber must exist.
  6. Subscriber ownership — the target subscriber must be inside the caller’s reseller tree.

If all checks pass, redemption runs as a single database transaction:

  1. The card is claimed with a guarded update (WHERE is_used = false). If a concurrent request already claimed it, the claim affects 0 rows and the whole redemption aborts with “Card has already been used” — so the grant can never apply twice.
  2. The subscriber is updated based on what the card carries:
    • Days — if the card has days, they are added to the subscriber’s expiry. If the current expiry is already in the past, the days are added from “now” instead.
    • Service — if the card has a service, the subscriber is switched to it.
    • Quota refill — if the card has a quota refill, the subscriber’s daily_quota_used and monthly_quota_used are reset to 0.
  3. A transaction record is created (type “prepaid card”) for the card’s value, attributed to the card’s reseller and linked to the subscriber, with a description noting the card code.

On success the response echoes the card’s value, days, and quota.

Each card carries two flags that drive its status:

FlagMeaning
is_usedSet to true the moment a card is successfully redeemed, along with the redeeming subscriber and the redemption time.
is_activeWhether the card is enabled. New cards are active. An inactive card cannot be redeemed.

In the table the Status column shows:

BadgeCondition
Availableis_used = false (green badge)
Usedis_used = true (gray badge)

The Status filter maps Availableis_used = false and Usedis_used = true.

There are two ways to remove cards, and both only ever touch unused cards:

  1. Delete a single card — the per-row Delete button (shown only for available cards). A used card cannot be deleted (“Cannot delete used cards”).
  2. Delete unused in a batch — the Delete Unused button on a batch stat-card removes every unused card in that batch and reports how many were deleted. Used cards in the batch are left intact.

This keeps redeemed cards on record (with their value transaction) while letting you clear out unsold stock.

The batch stat-cards summarise each batch:

  • Total — every card in the batch.
  • Used — cards where is_used = true.
  • Active — cards that are unused (is_used = false) and enabled (is_active = true) — i.e. still sellable/redeemable.

The page also exposes a batches list used to populate the Batch filter dropdown.

PermissionEffect
prepaid.viewPage loads; list cards, batches, and a single card.
prepaid.view_allReseller sees every card and can redeem any card, not just their own tree’s.
prepaid.createGenerate new card batches.
prepaid.editRedeem a card against a subscriber.
prepaid.deleteDelete a single unused card or all unused cards in a batch.

Admins have all permissions. A reseller with no permission group assigned also has all permissions (backward compatibility); a reseller with a group only has what the group grants.

  • Subscribers — redemption applies to a subscriber by ID; find the ID here.
  • Services & Plans — a card can switch the subscriber to a chosen service on redemption.
  • Billing & Invoices — each redemption writes a “prepaid card” transaction for the card’s value.
  • Resellers — cards are scoped to the reseller tree; prepaid.view_all widens that scope.
  • FUP Counters — a card’s quota refill resets the subscriber’s daily and monthly usage.