A C-Based Billing System for Prepaid Cards with Administrator Controls
A console‑based billing system menages prepaid cards, tracks usage sessions, and supports administrator operations including rate configuration. The application stores card records in a singly linked list and administrator accounts in a dynamic array. All data is loaded from text files at startup and written backupon exit.
The system provides the following top‑level menu:
1. Add card
2. Query card
3. Delete card
4. Start session (log in)
5. End session (log out)
6. Top‑up
7. Refund
8. Statistics & summary
9. Administrator management
10. Billing policy configuration
0. Exit
Administrator operations (add, remove, set privileges) require authentication. Two privilege levels allow viewing statistics (level 1) or deleting cards and modifying rates (level 2).
Billing policy fields:
- Minimum billing unit (minutes)
- Charge per unit
- Rate type (0: standard, 1: night flat fee, 2: day flat fee)
- Night/day flat fees
Data persistence
Card records are stored in cards.dat with fields separated by ##:
cardId##password##status##creationTime##expireTime##totalSpent##lastUsed##usageCount##balance##deletedFlag
Administrators are stored in admins.dat similarly:
name##password##privilege##deletedFlag
Header file billing.h
#ifndef BILLING_H
#define BILLING_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
typedef struct Card {
char id[20];
char pwd[9];
int online; // 0 off, 1 on
time_t created;
time_t expires;
double total_charge;
double credit;
time_t last_active;
int sessions;
time_t login_time;
int removed;
struct Card* next;
} Card;
typedef struct Admin {
char id[9];
char pwd[9];
int level; // 0 none, 1 view stats, 2 delete & config
int deleted;
} Admin;
typedef struct AdminArray {
Admin* data;
size_t count;
size_t cap;
} AdminArray;
typedef struct {
int unit_min;
double unit_price;
int type; // 0 normal, 1 night, 2 day
double night_rate;
double day_rate;
} RatePolicy;
extern Card* cardList;
extern AdminArray admins;
extern RatePolicy rates;
void init_card_list(void);
void init_admin_list(void);
void load_cards(const char* fname);
void load_admins(const char* fname);
void save_cards(const char* fname);
void save_admins(const char* fname);
void release_all(void);
void fmt_time(time_t t, char* buf, size_t len);
time_t parse_time(const char* s);
int new_card(void);
void find_card(void);
void revoke_card(void);
void start_use(void);
void stop_use(void);
void deposit(void);
void withdraw(void);
void summary(void);
void admin_panel(void);
void rate_panel(void);
void show_main_menu(void);
void show_admin_menu(void);
void show_rate_menu(void);
#endif
Implementation billing.c (excerpts, error handling omitted for brevity)
#include "billing.h"
Card* cardList = NULL;
AdminArray admins = {NULL,0,0};
RatePolicy rates = {1,1.0,0,50.0,200.0};
void init_card_list() {
cardList = (Card*)calloc(1,sizeof(Card));
if (!cardList) exit(1);
cardList->next = NULL;
}
void init_admin_list() {
admins.cap = 4;
admins.data = (Admin*)malloc(admins.cap * sizeof(Admin));
if (!admins.data) exit(1);
admins.count = 0;
}
void load_cards(const char* fname) {
FILE* fp = fopen(fname, "r");
assert(fp);
char buf[128];
Card* tail = cardList;
while (tail->next) tail = tail->next; // find end
while (fgets(buf, sizeof(buf), fp)) {
Card* n = (Card*)calloc(1, sizeof(Card));
char *tok = strtok(buf, "##\n");
strncpy(n->id, tok, sizeof(n->id)-1);
strncpy(n->pwd, strtok(NULL, "##\n"), sizeof(n->pwd)-1);
n->online = atoi(strtok(NULL, "##\n"));
n->created = parse_time(strtok(NULL, "##\n"));
n->expires = parse_time(strtok(NULL, "##\n"));
n->total_charge = atof(strtok(NULL, "##\n"));
n->last_active = parse_time(strtok(NULL, "##\n"));
n->sessions = atoi(strtok(NULL, "##\n"));
n->credit = atof(strtok(NULL, "##\n"));
n->removed = atoi(strtok(NULL, "##\n"));
tail->next = n;
tail = n;
}
fclose(fp);
}
int new_card() {
Card* node = (Card*)calloc(1, sizeof(Card));
if (!node) return 1;
char id[20], pwd[9];
int ok = 0;
do {
printf("Enter card ID (8-18 chars): ");
scanf("%19s", id);
if (strlen(id) < 8 || strlen(id) > 18)
printf("Invalid length.\n");
else ok = 1;
} while (!ok);
strncpy(node->id, id, sizeof(node->id)-1);
do {
ok = 0;
printf("Enter password (1-8 chars): ");
scanf("%8s", pwd);
if (strlen(pwd) > 8) printf("Too long.\n");
else ok = 1;
} while (!ok);
strncpy(node->pwd, pwd, sizeof(node->pwd)-1);
printf("Opening balance: ");
scanf("%lf", &node->credit);
node->created = node->last_active = time(NULL);
node->expires = node->created + 360*24*3600; // 360 days
// append to end
Card* tail = cardList;
while (tail->next) tail = tail->next;
tail->next = node;
node->next = NULL;
printf("Card %s added.\n", node->id);
return 0;
}
void start_use() {
char id[20], pwd[9];
printf("Card ID: "); scanf("%19s", id);
printf("Password: "); scanf("%8s", pwd);
Card* cur = cardList->next;
while (cur) {
if (strcmp(cur->id, id)==0 && strcmp(cur->pwd, pwd)==0) {
if (cur->online) { printf("Already active.\n"); return; }
if (cur->credit <= 0) { printf("Insufficient balance.\n"); return; }
cur->login_time = time(NULL);
cur->online = 1;
cur->sessions++;
printf("Session started for %s\n", cur->id);
return;
}
cur = cur->next;
}
printf("Card not found or invalid password.\n");
}
void stop_use() {
char id[20], pwd[9];
printf("Card ID: "); scanf("%19s", id);
printf("Password: "); scanf("%8s", pwd);
Card* cur = cardList->next;
while (cur) {
if (strcmp(cur->id, id)==0 && strcmp(cur->pwd, pwd)==0) {
if (!cur->online) { printf("Already offline.\n"); return; }
time_t now = time(NULL);
cur->online = 0;
cur->last_active = now;
double elapsed = difftime(now, cur->login_time);
double cost = 0;
if (rates.type == 0)
cost = elapsed / (rates.unit_min * 60.0) * rates.unit_price;
else if (rates.type == 1)
cost = rates.night_rate;
else
cost = rates.day_rate;
cur->credit -= cost;
cur->total_charge += cost;
printf("Session ended. Charge: %.2f, Balance: %.2f\n", cost, cur->credit);
return;
}
cur = cur->next;
}
printf("Card not found.\n");
}
void summary() {
if (admins.count == 0) { printf("No admins – add one first.\n"); return; }
char aid[9], apwd[9];
printf("Admin name: "); scanf("%8s", aid);
printf("Admin password: "); scanf("%8s", apwd);
Admin* adm = NULL;
for (size_t i=0; i<admins.count; i++) {
if (strcmp(admins.data[i].id, aid)==0 && strcmp(admins.data[i].pwd, apwd)==0) {
adm = &admins.data[i]; break;
}
}
if (!adm) { printf("Authentication failed.\n"); return; }
if (adm->level < 1) { printf("Insufficient privilege.\n"); return; }
Card* cur = cardList->next;
double total_revenue = 0;
int total_sessions = 0, total_cards = 0;
printf("--- Card Report ---\n");
while (cur) {
total_revenue += cur->total_charge;
total_sessions += cur->sessions;
total_cards++;
char expire_str[20];
fmt_time(cur->expires, expire_str, sizeof(expire_str));
printf("%s %d %.2f %.2f %d %s\n",
cur->id, cur->online, cur->total_charge, cur->credit, cur->sessions, expire_str);
cur = cur->next;
}
printf("Total cards: %d, sessions: %d, revenue: %.2f\n", total_cards, total_sessions, total_revenue);
}
void admin_panel() {
int opt;
do {
show_admin_menu();
scanf("%d", &opt);
switch(opt) {
case 1: /* add admin */ {
Admin a; memset(&a,0,sizeof(a));
printf("Name (max 8): "); scanf("%8s", a.id);
printf("Password: "); scanf("%8s", a.pwd);
printf("Privilege (0-2): "); scanf("%d", &a.level);
if (admins.count == admins.cap) {
admins.cap *= 2;
admins.data = (Admin*)realloc(admins.data, admins.cap * sizeof(Admin));
assert(admins.data);
}
admins.data[admins.count++] = a;
printf("Admin added.\n");
} break;
case 2: /* remove admin */ {
char name[9], pwd[9];
printf("Name: "); scanf("%8s", name);
printf("Password: "); scanf("%8s", pwd);
for (size_t i=0; i<admins.count; i++) {
if (strcmp(admins.data[i].id, name)==0 && strcmp(admins.data[i].pwd, pwd)==0) {
admins.data[i].deleted = 1;
printf("Admin marked deleted.\n");
return;
}
}
printf("Not found.\n");
} break;
/* ... other cases similar */
}
} while (opt != 0);
}
// rate_panel(), save functions etc. follow the same pattern.
The program’s main loop calls show_main_menu, dispatches to the selected operation, and finally writes all lists back to the files before quitting.