How to create a prepaid macro


I have my own payment system for the reasons of how many sites and applications use my servers and the time it will take to fix any issues.

I currently bill all customers based on a dialplan. This is calulated depending on the package they are on and the prices of each different site.

I do not wish to implement a2billing or astbill for many reasons which will break my system.

Is there a way I can make a macro that does the following.

  1. if a call isn’t local (which it should never be)
    a) it logs the number dialed, timestamp and extension
    b) at init checks the balance
  2. if a call can be dropped and a msg dictated can say “Please top up”
  3. if I can run this all in realtime also

Thank you.

I’ve been doing some research on this issue. It seems to be a well guarded secret. No worries. I’m on the case.

Update so far:
I have 2119 different dialplan entries. I have realised that since I know a customers balance based on the destination dialled I can calculate the minutes to set a timeout to make a sexy lady say “give me some juice sugar, your flat broke, please topup at”.

I have been loooking into AGI to do this but have had no luck as yet but will update this post as soon as I crack it. I believe many people like myself do not wish to have to deal with a cloggy system which means they have to change all the websites and systems they support just for one simple billing script.

For all those interested and ideas are welcome

ok after further looking into, PHPAGI is the way forward or FASTAGI.

The only problem I can for see here is the script does not tick like ajax for example or like realtime, SIP isn’t a protocol where we can simply say no further connections please. So if a user decides to call out on multiple channels at one time we will have a problem.

It surely is possible via AGI so when I write the script I will post links to it and the db structure, however, i expect most unlike myself to have a database full of dialplans so you will have to create them yourselves once I’ve written the script.

I’ll look into the multiple channels as well. I’ll try if I have the time to get as much info up by the weekend so others are able to implement this easily into their systems.

I thought I would put this up for those who do not wish to use prepaid billing from other sources. If you are familiar with the asterisk extensions.conf then this will help.

  1. I would only recommend going down this route if you have an established asterisk setup and do not wish to implement a2billing or astbilling or any other 3rd party tools which would mess up your entire setup.

  2. if like me you have a live production system and do not want to change the setup for multiple servers then this will help.

In order to get a prepaid module working you will need the following:

  1. 1 test asterisk server (best to not have any addons or features like freePBX etc, if you do then I don’t know, you may have difficulties)
  2. phpagi (type it into google)

BTW: This is not a calling card setup, but similar.

once you’ve installed phpagi ensure you have the phpagi.conf in /etc/asterisk

Copy below to /var/lib/asterisk/agi-bin/credit.agi
Alter the database settings

[code]#!/usr/bin/php -q

<?php include 'phpagi.php'; function connect_db() { mysql_connect ('', 'sperm', 'shoot-her-the-eye') or die (mysql_error()); mysql_select_db('tomb-raid-her') or die (mysql_error()); } connect_db(); # table which stores the live user details $query = 'SELECT * FROM credit WHERE \''.$argv[1].'\' IN(vpexOutboundId,vpexExtension,vpexCallerId) and packPaymentMethod = \'CREDIT\''; $rs = mysql_query($query); #print $query; #print "\n"; if(mysql_num_rows($rs)) { # this must mean we have a el cheapo customer $dialplan = array(); $row = (object) mysql_fetch_assoc($rs); # find where they are calling for($i = 0; $i < 9; $i++) { $query1 = 'SELECT * FROM dialplan WHERE destination = SUBSTR("'.$argv[2].'", 1,'.$i.')'; $dp = mysql_query($query1); #print $query1; if(mysql_num_rows($dp)) { $rw = (object) mysql_fetch_assoc($dp); $dialplan['destination'] = $rw->destination; $dialplan['description'] = $rw->description; $dialplan['peak'] = $rw->peak; $dialplan['offpeak'] = $rw->offpeak; $dialplan['connection_fee'] = $rw->connection_fee; } mysql_free_result($dp); #print "\n"; } if($row->cupkCredit > 0) { # now calulate how many seconds the caller has to make this call $rate = (in_array(date('H'),array(0,1,2,3,4,5,21,22,23))) ? $dialplan['offpeak'] : $dialplan['peak']; # uk only (VAT) + rate : because they don't pay it upfront damn el cheapos $rate = ($rate * 0.15) + $rate; # set the number of seconds $seconds = ((($row->cupkCredit/$rate) * 60) - 360); } else $seconds = 0; #print $dialplan['description'].' '.$dialplan['destination']." seconds remaining = $seconds"; # load the agi agi $agi = new AGI; $agi->verbose($dialplan['description'].' '.$dialplan['destination']." seconds remaining = $seconds"); $agi->verbose($row->cupkCredit); if($seconds <= 0) { $agi->exec('PlayBack','/var/lib/asterisk/sounds/not-enough-credit.gsm'); $agi->exec('Hangup'); exit; } else { # hangup the call # executes a macro to set a absolute timeout $agi->exec('Macro',"pre-pay|{$seconds}|{$argv[1]}|{$argv[2]}"); # but to be certain el cheapo doesn't get any more talk time to xxx girls while shooting her in the eye auto hangup $agi->set_autohangup($seconds); } } exit; ?>


Now adjust this script however you need it with your tables.
The database tables are as follows

DROP TABLE IF EXISTS `tomb-raid-her`.`dialplan`;
CREATE TABLE  `tomb-raid-her`.`dialplan` (
  `destination` varchar(10) NOT NULL default '',
  `description` varchar(250) NOT NULL,
  `peak` double NOT NULL,
  `offpeak` double NOT NULL,
  `connection_fee` double NOT NULL,
  PRIMARY KEY  USING BTREE (`destination`,`description`)

DROP TABLE IF EXISTS `tomb-raid-her`.`customerpackages`;
CREATE TABLE  `tomb-raid-her`.`customerpackages` (
  `cupkPackageId` int(10) unsigned NOT NULL auto_increment,
  `cupkCustomerId` bigint(20) unsigned NOT NULL,
  `cupkActive` tinyint(1) unsigned NOT NULL,
  `cupkPackage` int(10) unsigned NOT NULL,
  `cupkLastUpdated` datetime NOT NULL,
  `cupkDateCreated` datetime NOT NULL,
  `cupkAuditInfo` text NOT NULL,
  `cupkPrice` decimal(10,2) NOT NULL,
  `cupkCredit` decimal(10,2) NOT NULL,
  PRIMARY KEY  (`cupkPackageId`)

# Make up your our package and customer tables

The next part is reall simple

on your outgoing route\context use the following, mine is below

exten => s,n,AGI(credit.agi,${ARG1},${ARG2})
;exten => s,n, do what ever else you where going to do

the app in the above script pre-pay is the following:

exten => s,1,NoOp(Making outbound call from ${ARG3} to ${ARG2})
exten => s,n,Set(TIMEOUT(absolute)=${ARG1})
exten => s,n,Answer
exten => s,n,Wait(2)
exten => s,n,Playback(not-enough-credit)
exten => s,n,Playtones(congestion)
exten => s,n,Congestion(5)
exten => h,1,NoOp(Hangup)
exten => i,1,NoOp(Invalid)
exten => t,1,NoOp(Timeout)

Thanks for all the help guys.

I know this post is really old, but I’m trying to accomplish something like that, I still can see that in your macro, you dont have the control if more of one channel is calling, did you find any solution to do this??

for example I have an account that has 100 USS if I have 3 peers that belong to that account and they call all 3 together, how do you check balance every minute??

This is a real problem: Enabling concurrent calls on the same prepaid-account.
Actually the only working solution is to disallow concurrent calls in a way, that You track down, wheter an account is acutally in use for a call or not.
The problem is, that the first call gets a static call limit (asterisk is AFAIK not able to change the values in the L-option during the call).
And even if it would be possible to re-arrange tha call-limit of the already running call - how would You determine the call limit available for the first as well as for the second (or even third) call ?

Allthough it’s possible to intercept the call with a watchdog listening on asterisk events in combination with realtime-limit updates in the customer’s database (charge the call(s) every charge-intervall) I find such solutions not satisfying as they don’t reflect any message You may have given the customer before starting the first call …

So it’s IMHO a bit like trying to fit a square peg into a round hole …