/', $temp[1][0]));
41 | foreach($rows AS $row) {
42 | $transaction = $this->scrubRow($row);
43 | if ($transaction != null) {
44 | $result[] = $transaction;
45 | }
46 | }
47 | }
48 |
49 | return $result;
50 | }
51 |
52 | public function scrubRow($rawtext) {
53 | $result = array("SUPER_TYPE" => 0,
54 | "TYPE" => 0,
55 | "RECEIPT" => "",
56 | "TIME" => 0,
57 | "PHONE" => "",
58 | "NAME" => "",
59 | "ACCOUNT" => "",
60 | "STATUS" => "",
61 | "AMOUNT" => 0,
62 | "BALANCE" => 0,
63 | "NOTE" => "",
64 | "COST" => 0);
65 |
66 | $temp = array();
67 | preg_match_all('/| ]*>(.*)<\/td>/Umsi', $rawtext, $temp);
68 |
69 | if (isset($temp[1]) AND count($temp[1]) >= 11) {
70 | if ($temp[1][9] == " - CASH RECEIVE") {
71 | $result["SUPER_TYPE"] = Transaction::MONEY_IN;
72 | $result["TYPE"] = Transaction::KE_AIRTEL_PAYBILL_PAYMENT_RECEIVED;
73 | $result["RECEIPT"] = trim(strip_tags($temp[1][3]));
74 | $result["TIME"] = strtotime(str_replace(' ', ' ', $temp[1][2]));
75 | $result["PHONE"] = trim(strip_tags($temp[1][4]));
76 | $result["NAME"] = trim(str_replace(' ', ' ', $temp[1][10]));
77 | // $result["ACCOUNT"] = "NOT DONE"; // NOT DONE
78 | $result["STATUS"] = Transaction::STATUS_COMPLETED;
79 | $result["AMOUNT"] = (int)(((double)$temp[1][8])*100);
80 | $result["BALANCE"] = (int)(((double)$temp[1][7])*100);
81 |
82 | } else {
83 | $result["SUPER_TYPE"] = Transaction::MONEY_NEUTRAL;
84 | $result["TYPE"] = Transaction::KE_AIRTEL_PAYBILL_UNKOWN;
85 | $result["NOTE"] = $rawtext;
86 | }
87 |
88 | return $result;
89 | }
90 | return null;
91 | }
92 |
93 | }
94 |
95 | ?>
--------------------------------------------------------------------------------
/php/include/PLUSPEOPLE/PesaPi/KenyaYuPrivate/Parser.php:
--------------------------------------------------------------------------------
1 |
30 | */
31 | namespace PLUSPEOPLE\PesaPi\KenyaYUPrivate;
32 | use \PLUSPEOPLE\PesaPi\Base\Utility;
33 | use \PLUSPEOPLE\PesaPi\Base\Transaction;
34 |
35 |
36 | // I NEED MORE EXAMPLE SMS'S FROM YU TO COMPLETE THIS!!!
37 | class Parser {
38 | const PAYMENT_RECEIVED = 300;
39 | const PAYMENT_SENT = 301;
40 | const DEPOSIT = 302;
41 | const UNKNOWN = 310;
42 |
43 | public function timeInput($time) {
44 | $dt = \DateTime::createFromFormat("d-m-Y H:i:s", $time);
45 | return $dt->getTimestamp();
46 | }
47 |
48 | public function parse($input) {
49 | $result = array("SUPER_TYPE" => 0,
50 | "RECEIPT" => "",
51 | "TIME" => 0,
52 | "PHONE" => "",
53 | "NAME" => "",
54 | "ACCOUNT" => "",
55 | "STATUS" => "",
56 | "AMOUNT" => 0,
57 | "BALANCE" => 0,
58 | "NOTE" => "",
59 | "COSTS" => 0);
60 |
61 | if (strpos($input, "yuCash payment sent ") !== FALSE) {
62 | $result["SUPER_TYPE"] = Transaction::MONEY_OUT;
63 | $result["TYPE"] = Parser::PAYMENT_SENT;
64 |
65 | $temp = array();
66 | preg_match_all("/KES\s+([0-9\.\,]+)\s+yuCash payment sent to\s+(.+)\s+-\s+([0-9]+)\.\s+Fees:\s+KES\s+([0-9\.\,]+)\.\s+Balance:\s+KES\s+([0-9\.\,]+)\.\s+TxnId:\s+([0-9]+)/mi", $input, $temp);
67 | if (isset($temp[1][0])) {
68 | $result["RECEIPT"] = $temp[6][0];
69 | $result["AMOUNT"] = Utility::numberInput($temp[1][0]);
70 | $result["NAME"] = $temp[2][0];
71 | $result["ACCOUNT"] = $temp[3][0];
72 | $result["TIME"] = time();
73 | $result["BALANCE"] = Utility::numberInput($temp[5][0]);
74 | $result["COSTS"] = Utility::numberInput($temp[4][0]);
75 | }
76 |
77 | } elseif (strpos($input, "Successful Deposit: ") !== FALSE) {
78 | $result["SUPER_TYPE"] = Transaction::MONEY_IN;
79 | $result["TYPE"] = Parser::DEPOSIT;
80 |
81 | $temp = array();
82 | preg_match_all("/Successful Deposit: StoreName\s+(.+)\s+AgentID\s+([0-9]+)\s+Amt\s+KES\s+([0-9\.\,]+)\s+Balance\s+
83 | KES\s+([0-9\.\,]+)\s+Date\s+(.+)\s+Txn\s+ ID\s+([0-9]+)/mi", $input, $temp);
84 | if (isset($temp[1][0])) {
85 | $result["RECEIPT"] = $temp[6][0];
86 | $result["AMOUNT"] = Utility::numberInput($temp[3][0]);
87 | $result["NAME"] = $temp[1][0];
88 | $result["ACCOUNT"] = $temp[2][0];
89 | $result["TIME"] = $this->timeInput($temp[5][0]);
90 | $result["BALANCE"] = Utility::numberInput($temp[4][0]);
91 | }
92 |
93 | } else {
94 | $result["SUPER_TYPE"] = Transaction::MONEY_NEUTRAL;
95 | $result["TYPE"] = PersonalParser::UNKNOWN;
96 | }
97 |
98 | return $result;
99 | }
100 |
101 | }
102 |
103 | ?>
--------------------------------------------------------------------------------
/java/source/PLUSPEOPLE/PesaPi/Configuration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Configuration
3 | * @author Oyugik
4 | * @author Michael Pedersen
5 | * @version 0.1
6 | * @since 2.September 2011
7 | *
8 | * Redistribution and use in source and binary forms, with or without
9 | * modification, are permitted provided that the following conditions
10 | * are met:
11 | * 1. Redistributions of source code must retain the above copyright
12 | * notice, this list of conditions and the following disclaimer.
13 | * 2. Redistributions in binary form must reproduce the above copyright
14 | * notice, this list of conditions and the following disclaimer in the
15 | * documentation and/or other materials provided with the distribution.
16 | * 3. Neither the name of PLUSPEOPLE nor the names of its contributors
17 | * may be used to endorse or promote products derived from this software
18 | * without specific prior written permission.
19 | *
20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 | * SUCH DAMAGE.
31 | */
32 | package PLUSPEOPLE.PesaPi;
33 | import java.util.Hashtable;
34 |
35 | public class Configuration{
36 | private static Configuration config;
37 | private Hashtable values = new Hashtable();
38 |
39 | public static Configuration instantiate() {
40 | if (config == null) {
41 | config = new Configuration();
42 | }
43 | return config;
44 | }
45 |
46 | public String getConfig(String argument) {
47 | return (String)values.get(argument);
48 | }
49 |
50 | private Configuration() {
51 | // Enable this feature when in production - in order to disable debuginformation
52 | values.put("ProductionMode", "yes");
53 |
54 | // Enable this feature when you want to run the API against the simulator
55 | // The simulator does not use SSL and is more easy to get up and running.
56 | values.put("SimulationMode", "yes");
57 |
58 | // Enabling this will allow the system to automatically
59 | // update the scrubbing methods in use.
60 | // Hereby ensuring the system will keep running with
61 | // minimum downtime, in case of Safaricom changing any code.
62 | values.put("AllowAutoUpdate", "yes");
63 |
64 | // Enabling this feature allows the system to give feedback regarding
65 | // errors, problems and performance.
66 | // feedback to the developers of Mpesapi - hereby making it
67 | // possible to better analyse how to improve the system further.
68 | values.put("AllowFeedback", "yes");
69 |
70 | // To ensure the system is a robust as possible you want
71 | // to keep this feature active - By doing so you enable
72 | // method triangulation to ensure it fallsback to a different
73 | // method in case one fails.
74 | // the downside is slower performance.
75 | values.put("MaxCompatibility", "yes");
76 |
77 | // Mpesa information
78 | values.put("MpesaCertificatePath", "change_this_path.pem");
79 | values.put("MpesaLoginName", "test");
80 | values.put("MpesaPassword", "best");
81 | values.put("MpesaCorporation", "PesaPi");
82 | values.put("MpesaInitialSyncData", "2011-01-01");
83 | values.put("CookieFolderPath", ".");
84 |
85 | // Database settings follow - please note that they are repeated twice
86 | values.put("DatabaseHostRead", "localhost");
87 | values.put("DatabaseUserRead", "root");
88 | values.put("DatabasePasswordRead", "***PASSWORD***");
89 | values.put("DatabaseDatabaseRead", "pesaPi");
90 | values.put("DatabaseHostWrite", "localhost");
91 | values.put("DatabaseUserWrite", "root");
92 | values.put("DatabasePasswordWrite", "***PASSWORD***");
93 | values.put("DatabaseDatabaseWrite", "pesaPi");
94 |
95 | values.put("Version", "0.0.0");
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/php/include/PLUSPEOPLE/PesaPi/KenyaAirtelPaybill/Transaction.php:
--------------------------------------------------------------------------------
1 |
30 | */
31 | namespace PLUSPEOPLE\PesaPi\KenyaAirtelPaybill;
32 |
33 | class Transaction extends \PLUSPEOPLE\PesaPi\Base\Transaction {
34 | // Extended attributes
35 | const KE_AIRTEL_PAYBILL_PAYMENT_RECEIVED = 901;
36 |
37 | const KE_AIRTEL_PAYBILL_UNKOWN = 999;
38 |
39 |
40 | public static function updateData($row, $account) {
41 | $existing = $account->locateByReceipt($row['RECEIPT'], false);
42 |
43 | if (count($existing) > 0 AND is_object($existing[0])) {
44 | if ($existing[0]->getSuperType() != $row['SUPER_TYPE']) {
45 | // NOT DONE - MAJOR ISSUE - ALERT OPERATOR..
46 | }
47 | if ($existing[0]->getType() != $row['TYPE']) {
48 | // NOT DONE - MAJOR ISSUE - ALERT OPERATOR..
49 | }
50 |
51 | // Merge the information assuming we can piece together a full picture by two half successfull notifications
52 | if ($existing[0]->getTime() == 0 AND $row['TIME'] > 0) {
53 | $existing[0]->setTime($row["TIME"]);
54 | }
55 | if (trim($existing[0]->getPhonenumber()) == "" AND !empty($row['PHONE'])) {
56 | $existing[0]->setPhonenumber($row['PHONE']);
57 | }
58 | if (trim($existing[0]->getName()) == "" AND !empty($row['NAME'])) {
59 | $existing[0]->setName($row['NAME']);
60 | }
61 | if (trim($existing[0]->getAccount()) == "" AND !empty($row['ACCOUNT'])) {
62 | $existing[0]->setAccount($row['ACCOUNT']);
63 | }
64 | if ($row['STATUS'] == "" AND $existing[0]->getStatus() != $row['STATUS']) {
65 | $existing[0]->setStatus($row['STATUS']);
66 | }
67 | if ($existing[0]->getAmount() < $row['AMOUNT']) {
68 | $existing[0]->setAmount($row['AMOUNT']);
69 | }
70 | if ($existing[0]->getPostBalance() < $row['BALANCE']) {
71 | $existing[0]->setPostBalance($row['BALANCE']);
72 | }
73 | if (trim($existing[0]->getNote()) == "" AND !empty($row['NOTE'])) {
74 | $existing[0]->setNote($row['NOTE']);
75 | }
76 |
77 | $existing[0]->update();
78 | return array($existing[0], false);
79 |
80 | } else {
81 | return array(Transaction::import($account, $row), true);
82 | }
83 | }
84 |
85 | public static function import($account, $row) {
86 | $payment = Transaction::createNew($account->getId(), $row['SUPER_TYPE'], $row['TYPE']);
87 | if (is_object($payment)) {
88 | $payment->setReceipt($row['RECEIPT']);
89 | $payment->setTime($row["TIME"]);
90 | $payment->setPhonenumber($row['PHONE']);
91 | $payment->setName($row['NAME']);
92 | $payment->setAccount($row['ACCOUNT']);
93 | $payment->setStatus($row['STATUS']);
94 | $payment->setAmount($row['AMOUNT']);
95 | $payment->setPostBalance($row['BALANCE']);
96 | $payment->setNote($row['NOTE']);
97 |
98 | $payment->update();
99 | return $payment;
100 | }
101 | return null;
102 | }
103 |
104 | }
105 |
106 | ?>
--------------------------------------------------------------------------------
/simulator/include/Database.php:
--------------------------------------------------------------------------------
1 | config = Configuration::instantiate();
45 | // if we need to use same credentials to the database then we need to use mysql_connect instead of mysql_pconnect.
46 | $this->dbId = mysql_pconnect($this->config->getConfig("DatabaseHost" . $type),$this->config->getConfig("DatabaseUser" . $type),$this->config->getConfig("DatabasePassword" . $type), true);
47 | if ($this->dbId > 0) {
48 | if (!mysql_select_db($this->config->getConfig("DatabaseDatabase" . $type), $this->dbId)) {
49 | exit;
50 | }
51 | }
52 | }
53 |
54 | // use this to check how many querys are posted.
55 | public function getQueryAmount() {
56 | return $this->queryAmount;
57 | }
58 |
59 | public static function instantiate($type = Database::TYPE_READ) {
60 | global $singletonArray;
61 |
62 | if (!isset($singletonArray["Database" . $type] )) {
63 | $singletonArray["Database" . $type] = new Database($type);
64 | }
65 | return $singletonArray["Database" . $type];
66 | }
67 |
68 | public function dbIn($input) {
69 | return addslashes($input);
70 | }
71 |
72 | public function dbOut($input) {
73 | return stripslashes($input);
74 | }
75 |
76 | public function query($input) {
77 | ++$this->queryAmount;
78 | return mysql_query($input, $this->dbId);
79 | }
80 |
81 | public function fetchObject($input) {
82 | return mysql_fetch_object($input);
83 | }
84 |
85 | public function freeResult($input) {
86 | return mysql_free_result($input);
87 | }
88 |
89 | public function insertId() {
90 | return mysql_insert_id($this->dbId);
91 | }
92 |
93 | public function affectedRows() {
94 | return mysql_affected_rows($this->dbId);
95 | }
96 |
97 | public function numRows($input) {
98 | return mysql_num_rows($input);
99 | }
100 |
101 | public function beginTransaction() {
102 | $this->transactionCount++;
103 | if ($this->transactionCount == 1) {
104 | return (bool)mysql_query("START TRANSACTION");
105 | }
106 | return true;
107 | }
108 |
109 | public function commitTransaction() {
110 | if ($this->transactionCount > 0) {
111 | $this->transactionCount--;
112 | }
113 | if ($this->transactionCount == 0) {
114 | return (bool)mysql_query("COMMIT");
115 | }
116 | return true;
117 | }
118 |
119 | public function rollbackTransaction() {
120 | if ($this->transactionCount != 0) {
121 | $this->transactionCount = 0;
122 | return (bool)mysql_query("ROLLBACK");
123 | }
124 | return true;
125 | }
126 | }
127 | ?>
--------------------------------------------------------------------------------
/php/include/PLUSPEOPLE/PesaPi/Base/Database.php:
--------------------------------------------------------------------------------
1 |
30 | */
31 | namespace PLUSPEOPLE\PesaPi\Base;
32 |
33 | class Database {
34 | ############## Properties ####################
35 | const TYPE_READ = "Read";
36 | const TYPE_WRITE = "Write";
37 |
38 | protected $config;
39 | protected $queryAmount = 0;
40 | protected $dbId = 0;
41 | protected $transactionCount = 0;
42 |
43 | ############## Methods #######################
44 | # # # # # # # # Initializer # # # # # # # # # #
45 | protected function __construct($type) {
46 | $this->config = \PLUSPEOPLE\PesaPi\Configuration::instantiate();
47 |
48 | $this->db = new \mysqli($this->config->getConfig("DatabaseHost" . $type),
49 | $this->config->getConfig("DatabaseUser" . $type),
50 | $this->config->getConfig("DatabasePassword" . $type),
51 | $this->config->getConfig("DatabaseDatabase" . $type));
52 |
53 | if ($this->db->connect_errno) {
54 | print "DB connection error";
55 | exit();
56 | }
57 | }
58 |
59 | // use this to check how many querys are posted.
60 | public function getQueryAmount() {
61 | return $this->queryAmount;
62 | }
63 |
64 | public static function instantiate($type = Database::TYPE_READ) {
65 | global $singletonArray;
66 |
67 | if (!isset($singletonArray["Database" . $type] )) {
68 | $singletonArray["Database" . $type] = new Database($type);
69 | }
70 | return $singletonArray["Database" . $type];
71 | }
72 |
73 | public function dbIn($input) {
74 | return $this->db->real_escape_string($input);
75 | }
76 |
77 | public function dbOut($input) {
78 | return $input;
79 | }
80 |
81 | public function query($input) {
82 | ++$this->queryAmount;
83 | return $this->db->query($input);
84 | }
85 |
86 | public function fetchObject($input) {
87 | return $input->fetch_object();
88 | }
89 |
90 | public function freeResult($input) {
91 | return $input->close();
92 | }
93 |
94 | public function insertId() {
95 | return $this->db->insert_id;
96 | }
97 |
98 | public function affectedRows() {
99 | return $this->db->affected_rows;
100 | }
101 |
102 | public function numRows($input) {
103 | return $input->num_rows;
104 | }
105 |
106 | public function beginTransaction() {
107 | $this->transactionCount++;
108 | if ($this->transactionCount == 1) {
109 | return (bool)$this->query("START TRANSACTION");
110 | }
111 | return true;
112 | }
113 |
114 | public function commitTransaction() {
115 | if ($this->transactionCount > 0) {
116 | $this->transactionCount--;
117 | }
118 | if ($this->transactionCount == 0) {
119 | return (bool)$this->query("COMMIT");
120 | }
121 | return true;
122 | }
123 |
124 | public function rollbackTransaction() {
125 | if ($this->transactionCount != 0) {
126 | $this->transactionCount = 0;
127 | return (bool)$this->query("ROLLBACK");
128 | }
129 | return true;
130 | }
131 | }
132 | ?>
--------------------------------------------------------------------------------
/simulator/webroot/home2.php:
--------------------------------------------------------------------------------
1 | setTemplateFile('home2.tpl');
40 | session_start();
41 |
42 | //////////////////////////////////////////////////////////////////////////////
43 | // handle the submission
44 | if ($_SERVER["REQUEST_METHOD"] == 'POST') {
45 | if ($_POST['__VIEWSTATE'] == $_SESSION['VIEWSTATE']) {
46 |
47 |
48 |
49 | }
50 | }
51 |
52 | //////////////////////////////////////////////////////////////////////////////
53 | // display the page
54 | $view = WebUtility::viewstate(1476);
55 | $_SESSION['VIEWSTATE'] = $view;
56 |
57 | // Tariff's
58 | $tariffs = array('MFI Tariff 4', 'Unkown');
59 |
60 | // page size
61 | $pagesizes = array(20, 50, 100, 500);
62 |
63 | // Search results
64 | $results = PaymentFactory::factoryAll();
65 | foreach ($results AS $result) {
66 | $slow->assign(array("RECEIPT" => $result->getReciept(),
67 | "TIME" => date("Y-m-d H:i:s", $result->getTime()),
68 | "DESCRIPTION" => "Payment received from " . $result->getPhonenumber() . " - " . $result->getName() . " Acc. " . $result->getAccount(),
69 | "STATUS" => "Completed",
70 | "AMOUNT" => number_format($result->getAmount(), 2, '.', ''),
71 | "BALANCE" => number_format($result->getPostBalance(), 2, '.', ''),
72 | "HASH" => "b142222a-59ab-2ef6-8e52-a027ca4edb21"
73 | ));
74 |
75 | $slow->parse("Result");
76 | }
77 |
78 |
79 | $slow->assign(array("VIEWSTATE" => $view,
80 | "ORGANISATION" => "MpesaPi",
81 | "USERNAME" => "Test Testson",
82 | "LOGIN_TIME" => date("Y-m-d H:i:s"),
83 | "LAST_LOGIN_TIME" => date("Y-m-d H:i:s"),
84 | "ACCOUNT_NUMBER" => '32943321-11',
85 | "TARIFF" => $tariffs[1],
86 | "SEARCH_FROM" => date("Y-m-d H:i:s"),
87 | "SEARCH_FROM_DAY" => date("Y-m-d"),
88 | "SEARCH_FROM_TIME" => "00:00",
89 | "SEARCH_UNTIL" => date("Y-m-d H:i:s"),
90 | "SEARCH_UNTIL_DAY" => date("Y-m-d"),
91 | "SEARCH_UNTIL_TIME" => "23:59",
92 | "PAGE_SIZE_INDEX" => "0",
93 | "PAGE_SIZE" => $pagesizes[0],
94 | "CHECKED_ALL" => 'checked="checked"',
95 | "CHECKED_DECLINED" => '',
96 | "CHECKED_CANCELLED" => '',
97 | "CHECKED_EXPIRED" => '',
98 | "CHECKED_PENDING" => '',
99 | "CHECKED_COMPLETED" => '',
100 | "BALANCE_UPDATED" => date("Y-m-d H:i:s"),
101 | "CURRENT_BALANCE" => " 0.00",
102 | "UNCLEARED_FUNDS" => " 0.00",
103 | "RESERVED_FUNDS" => " 0.00",
104 | "AVAILABLE_FUNDS" => " 0.00",
105 | ));
106 |
107 |
108 | $slow->parse();
109 | $slow->slowPrint();
110 |
111 |
112 | ?>
--------------------------------------------------------------------------------
/php/include/PLUSPEOPLE/PesaPi/KenyaAirtelPaybill/Account.php:
--------------------------------------------------------------------------------
1 |
30 | */
31 | namespace PLUSPEOPLE\PesaPi\KenyaAirtelPaybill;
32 | use PLUSPEOPLE\PesaPi\Base\TransactionFactory;
33 |
34 | class Account extends \PLUSPEOPLE\PesaPi\Base\Account {
35 | public function getFormatedType() {
36 | return "Kenya - Airtel paybill";
37 | }
38 |
39 | public function availableBalance($time = null) {
40 | $time = (int)$time;
41 | if (0 == $time) {
42 | $time = time();
43 | }
44 |
45 | $balance = TransactionFactory::factoryOneByTime($this, $time);
46 | if (is_object($balance)) {
47 | return $balance->getPostBalance();
48 | }
49 | return $amount;
50 | }
51 |
52 | public function locateByReceipt($receipt) {
53 | return TransactionFactory::factoryByReceipt($this, $receipt);
54 | }
55 |
56 | public function initTransaction($id, $initValues = null) {
57 | return new Transaction($id, $initValues);
58 | }
59 |
60 | public function importTransaction($message) {
61 | if ($message != "") {
62 | $parser = new Parser();
63 | $temp = $parser->parse($message);
64 |
65 | $transaction = Transaction::createNew($this->getId(), $temp['SUPER_TYPE'], $temp['TYPE']);
66 | $transaction->setReceipt($temp['RECEIPT']);
67 | $transaction->setTime($temp["TIME"]);
68 | $transaction->setPhonenumber($temp['PHONE']);
69 | $transaction->setName($temp['NAME']);
70 | $transaction->setAccount($temp['ACCOUNT']);
71 | $transaction->setStatus($temp['STATUS']);
72 | $transaction->setAmount($temp['AMOUNT']);
73 | $transaction->setPostBalance($temp['BALANCE']);
74 | $transaction->setNote($temp['NOTE']);
75 | $transaction->setTransactionCost($temp['COST']);
76 |
77 | $transaction->update();
78 |
79 | // Callback if needed
80 | $this->handleCallback($transaction);
81 |
82 | return $transaction;
83 | }
84 | return null;
85 | }
86 |
87 | public function forceSyncronisation() {
88 | // determine the start time
89 | $settings = $this->getSettings();
90 | $lastSync = $settings["LAST_SYNC"];
91 |
92 | // We keep the timestamp from just _BEFORE_ we start connecting - this way we ensure that incomming payment while
93 | // the process is in operation will be discovered at the NEXT request.
94 | $now = time();
95 |
96 | // perform file fetch
97 | $loader = new Loader($this);
98 | $pages = $loader->retrieveData($lastSync);
99 | // perform analysis/scrubbing
100 | $parser = new Parser();
101 | foreach ($pages AS $page) {
102 | $rows = $parser->scrubTransactions($page);
103 | // save data to database
104 | foreach ($rows AS $row) {
105 | $tuple = Transaction::updateData($row, $this);
106 | if ($tuple[1] AND is_object($tuple[0])) {
107 | $this->handleCallback($tuple[0]);
108 | }
109 | }
110 | }
111 |
112 | // TODO save last entry time as last sync - not "now" sometimes the statement updates are delayed
113 | $settings["LAST_SYNC"] = $now;
114 | $this->setSettings($settings);
115 | $this->update();
116 | }
117 | }
118 |
119 | ?>
--------------------------------------------------------------------------------
/php/include/PLUSPEOPLE/PesaPi/MpesaPaybill/Transaction.php:
--------------------------------------------------------------------------------
1 |
30 | */
31 | namespace PLUSPEOPLE\PesaPi\MpesaPaybill;
32 |
33 | class Transaction extends \PLUSPEOPLE\PesaPi\Base\Transaction {
34 | // Extended attributes
35 | const MPESA_PAYBILL_PAYMENT_RECIEVED = 1;
36 | const MPESA_PAYBILL_PAYMENT_CANCELLATION = 2;
37 | const MPESA_PAYBILL_PAYMENT_REFUND = 7;
38 | const MPESA_PAYBILL_FUNDS_TRANSFER = 3;
39 | const MPESA_PAYBILL_FUNDS_CANCELLATION = 4;
40 | const MPESA_PAYBILL_BUSINESS_CHARGES = 5;
41 | const MPESA_PAYBILL_BUSINESS_CHARGES_CANCELLATION = 6;
42 | const MPESA_PAYBILL_TRANSFER_FROM_UTILITY = 107;
43 | const MPESA_PAYBILL_UNKOWN = 199;
44 |
45 | public static function updateData($row, $account) {
46 | $existing = $account->locateByReceipt($row['RECEIPT'], false);
47 |
48 | if (count($existing) > 0 AND is_object($existing[0])) {
49 | if ($existing[0]->getSuperType() != $row['SUPER_TYPE']) {
50 | // NOT DONE - MAJOR ISSUE - ALERT OPERATOR..
51 | }
52 | if ($existing[0]->getType() != $row['TYPE']) {
53 | // NOT DONE - MAJOR ISSUE - ALERT OPERATOR..
54 | }
55 |
56 | // Merge the information assuming we can piece together a full picture by two half successfull notifications
57 | if ($existing[0]->getTime() == 0 AND $row['TIME'] > 0) {
58 | $existing[0]->setTime($row["TIME"]);
59 | }
60 | if (trim($existing[0]->getPhonenumber()) == "" AND !empty($row['PHONE'])) {
61 | $existing[0]->setPhonenumber($row['PHONE']);
62 | }
63 | if (trim($existing[0]->getName()) == "" AND !empty($row['NAME'])) {
64 | $existing[0]->setName($row['NAME']);
65 | }
66 | if (trim($existing[0]->getAccount()) == "" AND !empty($row['ACCOUNT'])) {
67 | $existing[0]->setAccount($row['ACCOUNT']);
68 | }
69 | if ($row['STATUS'] == "" AND $existing[0]->getStatus() != $row['STATUS']) {
70 | $existing[0]->setStatus($row['STATUS']);
71 | }
72 | if ($existing[0]->getAmount() < $row['AMOUNT']) {
73 | $existing[0]->setAmount($row['AMOUNT']);
74 | }
75 | if ($existing[0]->getPostBalance() < $row['BALANCE']) {
76 | $existing[0]->setPostBalance($row['BALANCE']);
77 | }
78 | if (trim($existing[0]->getNote()) == "" AND !empty($row['NOTE'])) {
79 | $existing[0]->setNote($row['NOTE']);
80 | }
81 |
82 | $existing[0]->update();
83 | return array($existing[0], false);
84 |
85 | } else {
86 | return array(Transaction::import($account, $row), true);
87 | }
88 | }
89 |
90 | public static function import($account, $row) {
91 | $payment = Transaction::createNew($account->getId(), $row['SUPER_TYPE'], $row['TYPE']);
92 | if (is_object($payment)) {
93 | $payment->setReceipt($row['RECEIPT']);
94 | $payment->setTime($row["TIME"]);
95 | $payment->setPhonenumber($row['PHONE']);
96 | $payment->setName($row['NAME']);
97 | $payment->setAccount($row['ACCOUNT']);
98 | $payment->setStatus($row['STATUS']);
99 | $payment->setAmount($row['AMOUNT']);
100 | $payment->setPostBalance($row['BALANCE']);
101 | $payment->setNote($row['NOTE']);
102 |
103 | $payment->update();
104 | return $payment;
105 | }
106 | return null;
107 | }
108 | }
109 |
110 | ?>
--------------------------------------------------------------------------------
/php/include/PLUSPEOPLE/PesaPi/SomaliaGolisPrivate/Parser.php:
--------------------------------------------------------------------------------
1 |
30 | Based on examples provided by Ali Saiid
31 | */
32 | namespace PLUSPEOPLE\PesaPi\SomaliaGolisPrivate;
33 |
34 | class Parser extends \PLUSPEOPLE\PesaPi\Base\Parser{
35 | const DATE_FORMAT = "n/j/Y h:i:s A";
36 |
37 | // Custom numberInput function needed as Somalia uses 3 decimal digits.
38 | public function numberInput($input) {
39 | $input = trim($input);
40 | $amount = 0;
41 |
42 | if (preg_match("/^[0-9,]+\.?$/", $input)) {
43 | $amount = 1000 * (int)str_replace(',', '', $input);
44 | } elseif (preg_match("/^[0-9,]+\.[0-9]$/", $input)) {
45 | $amount = 100 * (int)str_replace(array('.', ','), '', $input);
46 | } elseif (preg_match("/^[0-9,]*\.[0-9][0-9]$/", $input)) {
47 | $amount = 10 * (int)str_replace(array('.', ','), '', $input);
48 | } elseif (preg_match("/^[0-9,]*\.[0-9][0-9][0-9]$/", $input)) {
49 | $amount = (int)str_replace(array('.', ','), '', $input);
50 | } else {
51 | $amount = (int)$input;
52 | }
53 | return $amount;
54 | }
55 |
56 | public function parse($input) {
57 | $result = $this->getBlankStructure();
58 |
59 | // REFACTOR: should be split into subclasses
60 | // [SAHAL] Ref:302228123 confirmed. $100 Received from c/risaaq axmed(7763277) on 10/23/2014 12:07:57 PM. New A/c balance is $101.780.
61 | if (strpos($input, " Received from ") !== FALSE) {
62 | $result["SUPER_TYPE"] = Transaction::MONEY_IN;
63 | $result["TYPE"] = Transaction::SO_GOLIS_PRIVATE_PAYMENT_RECEIVED;
64 |
65 | $temp = array();
66 | preg_match_all("/.*Ref:(\d+) confirmed\. \\$([0-9\.\,]+) Received from ([^\(]+)\((\d+)\) on (\d\d?\/\d\d?\/\d{4}) (\d\d?:\d\d:\d\d [AP]M)\. New A\/c balance is \\$([0-9\.\,]+)\./mi", $input, $temp);
67 | if (isset($temp[1][0])) {
68 | $result["RECEIPT"] = $temp[1][0];
69 | $result["AMOUNT"] = $this->numberInput($temp[2][0]);
70 | $result["NAME"] = $temp[3][0];
71 | $result["PHONE"] = $temp[4][0];
72 | $result["TIME"] = $this->dateInput(Parser::DATE_FORMAT, $temp[5][0] . " " . $temp[6][0]);
73 | $result["BALANCE"] = $this->numberInput($temp[7][0]);
74 | }
75 |
76 | // [SAHAL] Tix:307013277 waxaad $1 ka heshay CABDILAAHI MIRRE AXMED MUUSE(252633659717) tar:11/6/2014 10:32:40 AM. Haraagaagu waa $55.980.
77 | } elseif (strpos($input, " ka heshay ") !== FALSE) {
78 | $result["SUPER_TYPE"] = Transaction::MONEY_IN;
79 | $result["TYPE"] = Transaction::SO_GOLIS_PRIVATE_PAYMENT_RECEIVED;
80 |
81 | $temp = array();
82 | preg_match_all("/.*Tix:(\d+) waxaad \\$([0-9\.\,]+) ka heshay ([^\(]+)\((\d+)\) tar:(\d\d?\/\d\d?\/\d{4}) (\d\d?:\d\d:\d\d [AP]M)\. Haraagaagu waa \\$([0-9\.\,]+)\./mi", $input, $temp);
83 | if (isset($temp[1][0])) {
84 | $result["RECEIPT"] = $temp[1][0];
85 | $result["AMOUNT"] = $this->numberInput($temp[2][0]);
86 | $result["NAME"] = $temp[3][0];
87 | $result["PHONE"] = $temp[4][0];
88 | $result["TIME"] = $this->dateInput(Parser::DATE_FORMAT, $temp[5][0] . " " . $temp[6][0]);
89 | $result["BALANCE"] = $this->numberInput($temp[7][0]);
90 | }
91 |
92 |
93 | } else {
94 | $result["SUPER_TYPE"] = Transaction::MONEY_NEUTRAL;
95 | $result["TYPE"] = Transaction::SO_GOLIS_PRIVATE_UNKOWN;
96 | }
97 |
98 | return $result;
99 | }
100 |
101 | }
102 |
103 | ?>
--------------------------------------------------------------------------------
/php/documentation/mpesa_private_examples.txt:
--------------------------------------------------------------------------------
1 | This file contains various example messages of SMS notifcations used on an MPESA Private account.
2 |
3 | -----------------------------------
4 | Notification for when you transfer money to MPESA from your bank account (suspected to be a general B2C status message).
5 |
6 | $example = 'DT85TH896 Confirmed.
7 | You have received Ksh3,500.00 from
8 | 501901 - KCB Money Transfer Services
9 | on 31/7/13 at 6:43 PM
10 | New M-PESA balance is Ksh11,312.00.Save & get a loan on Mshwari';
11 |
12 | -----------------------------------
13 | Notification when a Person is sending you money.
14 |
15 | Example 1.
16 | $example = 'BS49OR201 Confirmed.
17 | You have received Ksh50.00 from
18 | MICHAEL FEDERSEN 254729901555
19 | on 15/10/11 at 11:52 AM
20 | New M-PESA balance is Ksh100.00';
21 |
22 | Example 2.
23 | $example = 'BS39OR301 Confirmed.
24 | You have received Ksh350.00 from
25 | MICHAEL FEDERSEN 254729901555
26 | on 15/10/11 at 11:52 AM
27 | New M-PESA balance is Ksh400.00.Save & get a loan on Mshwari';
28 |
29 | Example 3.
30 | $example = "DT82ZD611 Confirmed.
31 | You have received Ksh5,500.00 from
32 | ALEX NDUNG'U 254723784491
33 | on 31/7/13 at 3:08 PM
34 | New M-PESA balance is Ksh5,844.00.Save & get a loan on Mshwari";
35 |
36 | Example 4.
37 | $example = "EV52AY844 Confirmed.
38 | You have received Ksh200.00 from
39 | 0RENGE ALEX 254724613573
40 | on 29/3/14 at 1:38 AM
41 | New M-PESA balance is Ksh2,927.00.PIN YAKO SIRI YAKO";
42 |
43 | $example = "EV42RB339 Confirmed.
44 | You have received Ksh200.00 from
45 | BRIAN NGANGA 254722923120
46 | on 27/3/14 at 11:04 PM
47 | New M-PESA balance is Ksh3,366.00.PIN YAKO SIRI YAKO";
48 |
49 | -----------------------------------
50 | Notifications when an error occours.
51 |
52 | Example 1.
53 | $example = 'Failed. The entered phone number is incorrect
54 | 07221889563.';
55 |
56 | $example = 'Failed. M-PESA is temporarily unable to authorise airtime purchase of Ksh100.00.
57 | Please try again later.';
58 |
59 | -----------------------------------
60 | Notifications when depositing cash money into your mpesa account
61 |
62 | $example = 'DQ94ZE762 Confirmed.
63 | on 3/7/13 at 9:07 AM
64 | Give Ksh1,000.00 cash to Digital Africa Services Jolet Supermarket
65 | New M-PESA balance is Ksh1,338.00';
66 |
67 |
68 | -----------------------------------
69 | Notifications when withdrawing money from you account at an agent
70 |
71 | ET04TG335 Confirmed.
72 | on 20/2/14 at 2:44 PM
73 | Withdraw Ksh16,000.00 from
74 | 129324 - Brothers Link Agency Vetngong Road
75 | New M-PESA balance is Ksh570.00.Save & get a loan on MShwari
76 |
77 | -----------------------------------
78 | Notifications when you send another person money
79 |
80 | $example = 'DZ12GX874 Confirmed. Ksh2,100.00 sent to BRIAN MBUGUA 0723447655 on 17/9/13 at 3:16 PM New M-PESA balance is Ksh106.00.PIN YAKO SIRI YAKO';
81 |
82 |
83 | -----------------------------------
84 | Notifications when you buy Airtime for yourself
85 |
86 | $example = 'DZ55IX312 confirmed. You bought Ksh100.00 of airtime on 21/9/13 at 5:51 PM
87 | New M-PESA balance is Ksh6.00.Safaricom only calls you from 0722000000';
88 |
89 | -----------------------------------
90 | Notification when you do a balance request
91 |
92 | $example = 'DQ91IB986 Confirmed.
93 | Your M-PESA balance was Ksh339.00
94 | on 2/7/13 at 6:46 PM.Safaricom only calls you from 0722000000';
95 |
96 | -----------------------------------
97 | Notification when you pay to a paybill number
98 |
99 | $example = 'DY28XV679 Confirmed. Ksh4,000.00 sent to KCB Paybill AC for account 1137238445 on 9/9/13 at 11:31 PM
100 | New M-PESA balance is Ksh22.00.';
101 |
102 | -----------------------------------
103 | Notification when a buygoods number receives a payment.
104 |
105 | $example = 'EA54HY643 Confirmed.
106 | on 28/9/13 at 1:14 PM
107 | Ksh50.00 received from
108 | 254729639024 MORRIS M.
109 | New Account balance is Ksh54.00';
110 |
111 |
112 | -----------------------------------
113 | Notification when you transfer to your M-Shwari account.
114 |
115 | $example = 'EB97SA431 Confirmed. Ksh50.00 transferred to M-Shwari account on 13/10/13 at 2:13 AM. M-PESA balance is Ksh4,265.00, new M-Shwari account balance is Ksh20,087.69.';
116 |
117 |
118 | -----------------------------------
119 | Notification when you transfer from your M-Shwari account.
120 |
121 | $example = 'EB87ST824 Confirmed. You have transferred Ksh50.00 from your M-Shwari account on 13/10/13 at 2:14 AM. M-Shwari balance is Ksh20,037.69. M-PESA balance is Ksh4,315.00.';
122 |
123 |
124 | -----------------------------------
125 | Notification when you receive a refund for a transaction you had paid to a "lipa na mpesa" account.
126 |
127 | EE56TY519 confirmed. Your Pay Shop transaction EE56TT315 of 10Ksh has been refunded by 971577 - JUKKA ENTERPRISES. Please contact 971577 - JUKKA ENTERPRISES for more information. Your account balance is now 47Ksh.
128 |
129 |
130 | -----------------------------------
131 | Notification when safaricom reverses a transaction that came to your account.
132 |
133 | ER30SR746 Confirmed. Transaction EQ47FM754 has been reversed. Your account balance is now Ksh5,987.00.
134 |
135 |
--------------------------------------------------------------------------------
/php/include/PLUSPEOPLE/PesaPi/GhanaAirtelPrivate/Parser.php:
--------------------------------------------------------------------------------
1 |
30 | Thanks to Henry Addo for supplying information about Airtel Money in Ghana
31 | */
32 | namespace PLUSPEOPLE\PesaPi\GhanaAirtelPrivate;
33 | use \PLUSPEOPLE\PesaPi\Base\Utility;
34 |
35 | // WE NEED MORE EXAMPLE SMS'S FROM GHANA TO COMPLETE THIS!!!
36 | class Parser {
37 |
38 | public function parse($input) {
39 | $result = array("SUPER_TYPE" => 0,
40 | "RECEIPT" => "",
41 | "TIME" => 0,
42 | "PHONE" => "",
43 | "NAME" => "",
44 | "ACCOUNT" => "",
45 | "STATUS" => "",
46 | "AMOUNT" => 0,
47 | "BALANCE" => 0,
48 | "NOTE" => "",
49 | "COST" => 0);
50 |
51 | if (strpos($input, "You have received ") !== FALSE) {
52 | $result["SUPER_TYPE"] = Transaction::MONEY_IN;
53 | $result["TYPE"] = Transaction::GH_AIRTEL_PAYMENT_RECEIVED;
54 |
55 | $temp = array();
56 | preg_match_all("/Trans\.\s+ID:\s+([0-9]+)\s+You have received ([0-9\.\,]+)GHS\s+from([0-9]+)\.\s+Your available\s+balance\s+is\s+([0-9\.\,]+)GHS/mi", $input, $temp);
57 | if (isset($temp[1][0])) {
58 | $result["RECEIPT"] = $temp[1][0];
59 | $result["AMOUNT"] = Utility::numberInput($temp[2][0]);
60 | $result["NAME"] = "";
61 | $result["PHONE"] = $temp[3][0];
62 | $result["TIME"] = time();
63 | $result["BALANCE"] = Utility::numberInput($temp[4][0]);
64 | }
65 |
66 | } elseif (strpos($input, "You have sent ") !== FALSE) {
67 | $result["SUPER_TYPE"] = Transaction::MONEY_OUT;
68 | $result["TYPE"] = Transaction::GH_AIRTEL_PAYMENT_SENT;
69 |
70 | $temp = array();
71 | preg_match_all("/Trans\.\s+ID:\s+([0-9]+)\s+You have sent ([0-9\.\,]+)GHS\s+to([0-9]+)\.\s+Your available\s+balance\s+is\s+([0-9\.\,]+)GHS/mi", $input, $temp);
72 | if (isset($temp[1][0])) {
73 | $result["RECEIPT"] = $temp[1][0];
74 | $result["AMOUNT"] = Utility::numberInput($temp[2][0]);
75 | $result["NAME"] = "";
76 | $result["PHONE"] = $temp[3][0];
77 | $result["TIME"] = time();
78 | $result["BALANCE"] = Utility::numberInput($temp[4][0]);
79 | }
80 |
81 | } elseif (strpos($input, "You have received Airtime of") != FALSE) {
82 | $result["SUPER_TYPE"] = Transaction::MONEY_OUT; // NOT CERTAIN - someone else may be giving us airtime.
83 | $result["TYPE"] = Transaction::GH_AIRTEL_AIRTIME;
84 |
85 | $temp = array();
86 | preg_match_all("/Trans\.\s+ID:\s+([0-9]+)\s+You have received Airtime of\s+GHS\s+([0-9\.\,]+)\s+from([0-9]+)\./mi", $input, $temp);
87 | if (isset($temp[1][0])) {
88 | $result["RECEIPT"] = $temp[1][0];
89 | $result["AMOUNT"] = Utility::numberInput($temp[2][0]);
90 | $result["NAME"] = "";
91 | $result["PHONE"] = $temp[3][0];
92 | $result["TIME"] = time();
93 | $result["BALANCE"] = -1; // Unkown
94 | }
95 |
96 | } elseif (strpos($input, "you have paid") != FALSE) {
97 | $result["SUPER_TYPE"] = Transaction::MONEY_OUT;
98 | $result["TYPE"] = Transaction::GH_AIRTEL_PURCHASE;
99 |
100 | $temp = array();
101 | preg_match_all("/Trans\s*ID:\s+([0-9]+)\s+Transaction successful, you have paid ([0-9\.\,]+)GHS\s+to reference code ([0-9]+)/mi", $example, $temp);
102 | if (isset($temp[1][0])) {
103 | $result["RECEIPT"] = $temp[1][0];
104 | $result["AMOUNT"] = Utility::numberInput($temp[2][0]);
105 | $result["ACCOUNT"] = $temp[3][0];
106 | $result["TIME"] = time();
107 | $result["BALANCE"] = -1; // Unkown
108 | }
109 |
110 | } else {
111 | $result["SUPER_TYPE"] = Transaction::MONEY_NEUTRAL;
112 | $result["TYPE"] = Transaction::GH_AIRTEL_UNKNOWN;
113 | }
114 |
115 | return $result;
116 | }
117 |
118 | }
119 |
120 | ?>
--------------------------------------------------------------------------------
/php/include/PLUSPEOPLE/PesaPi/MpesaPrivate/ChargeCalculator.php:
--------------------------------------------------------------------------------
1 |
30 | */
31 | namespace PLUSPEOPLE\PesaPi\MpesaPrivate;
32 |
33 | class ChargeCalculator {
34 |
35 | static public function calculateCost($type, $time, $amount) {
36 | switch ($type) {
37 | case Transaction::MPESA_PRIVATE_PAYMENT_SENT:
38 | return ChargeCalculator::sendingCost($time, $amount);
39 | break;
40 | case Transaction::MPESA_PRIVATE_WITHDRAW:
41 | return ChargeCalculator::withdrawCost($time, $amount);
42 | break;
43 |
44 | case Transaction::MPESA_PRIVATE_WITHDRAW_ATM:
45 | return ChargeCalculator::atmWithdrawCost($time, $amount);
46 | break;
47 | case Transaction::MPESA_PRIVATE_BALANCE_REQUEST:
48 | return 100;
49 | break;
50 | }
51 |
52 | return 0;
53 | }
54 |
55 | static protected function sendingCost($time, $amount) {
56 | if ($time > 140951880) {
57 | // Rates 1.Sep 2014 -
58 | if ($amount <= 4900) {
59 | return 100;
60 | } elseif ($amount <= 10000) {
61 | return 300;
62 | } elseif ($amount <= 50000) {
63 | return 1100;
64 | } elseif ($amount <= 100000) {
65 | return 1500;
66 | } elseif ($amount <= 150000) {
67 | return 2500;
68 | } elseif ($amount <= 250000) {
69 | return 4000;
70 | } elseif ($amount <= 350000) {
71 | return 5500;
72 | } elseif ($amount <= 500000) {
73 | return 6000;
74 | } elseif ($amount <= 750000) {
75 | return 7500;
76 | } elseif ($amount <= 1000000) {
77 | return 8500;
78 | } elseif ($amount <= 1500000) {
79 | return 9500;
80 | } elseif ($amount <= 2000000) {
81 | return 10000;
82 | } else {
83 | return 11000;
84 | }
85 |
86 | } else {
87 | // Rates: 8.Feb 2013 to 1.Sep 2014
88 | if ($amount <= 4900) {
89 | return 300;
90 | } elseif ($amount <= 10000) {
91 | return 500;
92 | } elseif ($amount <= 50000) {
93 | return 2700;
94 | } elseif ($amount <= 500000) {
95 | return 3300;
96 | } elseif ($amount <= 2000000) {
97 | return 5500;
98 | } elseif ($amount <= 4500000) {
99 | return 8200;
100 | } else {
101 | return 11000;
102 | }
103 | }
104 | }
105 |
106 | static protected function withdrawCost($time, $amount) {
107 | if ($time > 140951880) {
108 | // Rates 1.Sep 2014 -
109 | if ($amount <= 10000) {
110 | return 100;
111 | } elseif ($amount <= 250000) {
112 | return 2700;
113 | } elseif ($amount <= 350000) {
114 | return 4900;
115 | } elseif ($amount <= 500000) {
116 | return 6600;
117 | } elseif ($amount <= 750000) {
118 | return 8200;
119 | } elseif ($amount <= 1000000) {
120 | return 11000;
121 | } elseif ($amount <= 1500000) {
122 | return 15900;
123 | } elseif ($amount <= 2000000) {
124 | return 17600;
125 | } elseif ($amount <= 3500000) {
126 | return 18700;
127 | } elseif ($amount <= 5000000) {
128 | return 27500;
129 | } else {
130 | return 33000;
131 | }
132 |
133 | } else {
134 | // Rates: 8.Feb 2013 to 1.Sep 2014
135 | if ($amount <= 10000) {
136 | return 1000;
137 | } elseif ($amount <= 250000) {
138 | return 2700;
139 | } elseif ($amount <= 350000) {
140 | return 4900;
141 | } elseif ($amount <= 500000) {
142 | return 6600;
143 | } elseif ($amount <= 750000) {
144 | return 8200;
145 | } elseif ($amount <= 1000000) {
146 | return 11000;
147 | } elseif ($amount <= 1500000) {
148 | return 15900;
149 | } elseif ($amount <= 2000000) {
150 | return 17600;
151 | } elseif ($amount <= 3500000) {
152 | return 18700;
153 | } elseif ($amount <= 5000000) {
154 | return 27500;
155 | } else {
156 | return 33000;
157 | }
158 | }
159 | }
160 |
161 | static protected function atmWithdrawCost($time, $amount) {
162 | if ($amount <= 250000) {
163 | return 3300;
164 | } elseif ($amount <= 500000) {
165 | return 6600;
166 | } elseif ($amount <= 1000000) {
167 | return 11000;
168 | } else {
169 | return 19300;
170 | }
171 | }
172 |
173 | }
--------------------------------------------------------------------------------
/php/include/PLUSPEOPLE/SlowTemplate/Template.php:
--------------------------------------------------------------------------------
1 | template = new SlowTemplate("", false);
16 | $this->template->setTemplateFile($this->getTemplateFile());
17 | }
18 |
19 | // # # # # # # # get/set methods # # # # # # # #
20 | // do override this function so it returns the name
21 | // of the file you want to include
22 | public function getTemplateFile() {
23 | return "";
24 | }
25 |
26 | public function getCacheable() {
27 | return false;
28 | }
29 |
30 | public function getRequireActiveSubscription() {
31 | return true;
32 | }
33 |
34 | // indicates wether the output/page can be compressed or not
35 | public function getCompressable() {
36 | return true;
37 | }
38 |
39 | // Two magic levels "None" and "Login"
40 | public function getRequiredAccessLevel() {
41 | return "None";
42 | }
43 |
44 | public function getRequiredType() {
45 | return null;
46 | }
47 |
48 | public function getTemplate() {
49 | return $this->template;
50 | }
51 |
52 | public function getLoginUrl() {
53 | return "/login.php";
54 | }
55 |
56 | public function getUser() {
57 | if ($this->user == NULL) {
58 | @session_start();
59 | $userId = @(int)$_SESSION["GLOBAL_USER_ID"];
60 | if ($userId > 0) {
61 | $this->user = \ICTPrices\ProfileFactory::factoryOne($userId);
62 | }
63 | }
64 | return $this->user;
65 | }
66 |
67 | //# # # # # # # # Misc methods # # # # # # # # # #
68 | public function displaySelect($dataset, $selectedId, $prefix, $nameFunction="getFormatedName") {
69 | $slow = $this->getTemplate();
70 | $up = strtoupper($prefix);
71 | foreach ($dataset AS $data) {
72 |
73 | $slow->assign(array($up . "_VALUE" => (string)$data->getId(),
74 | $up . "_NAME" => call_user_func(array(&$data, $nameFunction)),
75 | $up . "_SELECTED" => ""));
76 | if ($data->getId() == $selectedId) {
77 | $slow->assignOne($up . "_SELECTED", 'selected="selected"');
78 | }
79 | $slow->parse($prefix);
80 | }
81 | }
82 |
83 | public function handleRequest() {
84 | //# Confirm that you actually have the required access level for this page.
85 | if ($this->getRequiredAccessLevel() != "None") {
86 | $user = $this->getUser();
87 | if ($user == NULL) {
88 | //# user not loged in
89 | WebUtility::redirect($this->getLoginUrl());
90 | exit;
91 |
92 | } else {
93 | //# loged in lets check for required accesslevels needed
94 | if ($this->getRequiredType() != null AND $this->getRequiredType() != $user->getType()) {
95 | //# user does not have access
96 | WebUtility::redirect($this->getLoginUrl());
97 | exit;
98 | }
99 | if ($this->getRequiredAccessLevel() != "Login") {
100 | $access = false;
101 | $levels = $user->getAccessLevel();
102 | foreach ($levels AS $level) {
103 | if ($this->getRequiredAccessLevel() == $level->getName()) {
104 | $access = true;
105 | break;
106 | }
107 | }
108 | if (!$access) {
109 | // user does not have access
110 | WebUtility::redirect($this->getLoginUrl());
111 | exit;
112 | }
113 | }
114 |
115 | }
116 | }
117 |
118 | if ($this->getCacheable()) {
119 | }
120 | $this->generate();
121 | }
122 |
123 |
124 | //# # # # # # # # Private/protected methods # # # #
125 | protected function checkAccess($accessName) {
126 | $user = $this->getUser();
127 | $levels = $user->getAccessLevel();
128 | $access = false;
129 | foreach ($levels AS $level) {
130 | if ($accessName == $level->getName()) {
131 | $access = true;
132 | break;
133 | }
134 | }
135 | return $access;
136 | }
137 |
138 | protected function generate() {
139 | global $singletonArray;
140 |
141 | try {
142 | if (isset($_REQUEST["AJAX"])) {
143 | $method = "ajax" . ucfirst($_REQUEST["AJAX"]);
144 | if (method_exists($this, $method)) {
145 | call_user_func_array(array($this, $method), array());
146 | } else {
147 | $this->ajax();
148 | }
149 | exit();
150 | } else {
151 | switch ($_SERVER["REQUEST_METHOD"]) {
152 | case "POST":
153 | $this->post();
154 | break;
155 | case "HEAD":
156 | $this->head();
157 | break;
158 | case "PUT":
159 | $this->put();
160 | break;
161 | case "GET":
162 | $this->get();
163 | break;
164 | }
165 | $this->request();
166 | }
167 |
168 | // free db resources
169 | if (isset($singletonArray['Database'])) {
170 | foreach ($singletonArray['Database'] AS $db) {
171 | $db->disconnect();
172 | }
173 | }
174 |
175 | //output
176 | $this->template->parse();
177 | $this->template->slowPrint();
178 |
179 | } catch(UhasibuError $ue) {
180 | # not done.
181 | print "EXCEPTION: " . $ue->getMessage();
182 | exit();
183 | }
184 | }
185 |
186 | // default implementation (ment to be overridet)
187 | public function ajax() {
188 | }
189 |
190 | public function get() {
191 | }
192 |
193 | public function post() {
194 | }
195 |
196 | public function put() {
197 | }
198 |
199 | public function head() {
200 | }
201 |
202 | public function request() {
203 | }
204 |
205 | }
206 |
207 | ?>
--------------------------------------------------------------------------------
/csharp/source/PesaPi.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | #region License Information
7 | /* Copyright (c) 2011, PLUSPEOPLE Kenya Limited.
8 | All rights reserved.
9 |
10 | Redistribution and use in source and binary forms, with or without
11 | modification, are permitted provided that the following conditions
12 | are met:
13 | 1. Redistributions of source code must retain the above copyright
14 | notice, this list of conditions and the following disclaimer.
15 | 2. Redistributions in binary form must reproduce the above copyright
16 | notice, this list of conditions and the following disclaimer in the
17 | documentation and/or other materials provided with the distribution.
18 | 3. Neither the name of PLUSPEOPLE nor the names of its contributors
19 | may be used to endorse or promote products derived from this software
20 | without specific prior written permission.
21 |
22 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 | SUCH DAMAGE.
33 | */
34 | #endregion
35 |
36 | namespace PLUSPEOPLE.Pesapi
37 | {
38 | ///
39 | /// This is the main interface to the Mpesa API.
40 | /// Features are collected here for simple interfacing by the user.
41 | ///
42 | public class PesaPi
43 | {
44 | protected int initSyncDate = 0;
45 | protected DateTime lastSyncSetting;
46 | // pesapiDataContext db = null;
47 |
48 |
49 | public PesaPi()
50 | {
51 | // initSyncDate = Settings.Default.MpesaInitialSyncDate;
52 | // lastSyncSetting = SettingFactory.FactoryByName("LastSync").value_date;
53 | // db = new pesapiDataContext(Settings.Default.pesaPiConnectionString);
54 | }
55 |
56 | ///
57 | /// This method returns the balance of the mpesa account at the specified point in time.
58 | /// If there are not transactions later than the specified time, then we can not gurantee 100%
59 | /// that is is the exact balance - since there might be a transaction prior to the specified time
60 | /// which we have not yet been informed about.
61 | /// The specified time is represented in a unix timestamp.
62 | ///
63 | ///
64 | ///
65 | public long AvailableBalance(DateTime time)
66 | {
67 | if (lastSyncSetting < time)
68 | {
69 | this.ForceSyncronisation();
70 | }
71 |
72 | long balance = 0;
73 | // long balance = db.Mpesapi_Payments.Where(payments => payments.time <= time).FirstOrDefault().post_balance;
74 |
75 | return balance;
76 | }
77 |
78 |
79 | public MpesaPayment LocateByReceipt(string reciept)
80 | {
81 | // Not done
82 | return null;
83 | }
84 |
85 |
86 | public MpesaPayment[] LocateByPhone(string phone)
87 | {
88 | return this.LocateByPhone(phone, null, null);
89 | }
90 | public MpesaPayment[] LocateByPhone(string phone, DateTime fromtime)
91 | {
92 | return this.LocateByPhone(phone, fromtime, null);
93 | }
94 | public MpesaPayment[] LocateByPhone(string phone, DateTime? fromtime, DateTime? until)
95 | {
96 | // not done
97 | return new MpesaPayment[0];
98 | }
99 |
100 |
101 |
102 | public MpesaPayment[] LocateByName(string name)
103 | {
104 | return this.LocateByName(name, null, null);
105 | }
106 | public MpesaPayment[] LocateByName(string name, DateTime fromtime)
107 | {
108 | return this.LocateByName(name, fromtime, null);
109 | }
110 | public MpesaPayment[] LocateByName(string name, DateTime? fromtime, DateTime? until)
111 | {
112 | // not done
113 | return new MpesaPayment[0];
114 | }
115 |
116 |
117 | public MpesaPayment[] LocateByAccount(string account)
118 | {
119 | return this.LocateByAccount(account, null, null);
120 | }
121 | public MpesaPayment[] LocateByAccount(string account, DateTime fromtime)
122 | {
123 | return this.LocateByAccount(account, fromtime, null);
124 | }
125 | public MpesaPayment[] LocateByAccount(string account, DateTime? fromtime, DateTime? until)
126 | {
127 | // not done
128 | return new MpesaPayment[0];
129 | }
130 |
131 |
132 | public MpesaPayment[] LocateByTimeInterval(DateTime fromtime, DateTime until, int type)
133 | {
134 | // not done
135 | return new MpesaPayment[0];
136 | }
137 |
138 |
139 | public string[] LocateName(string phone)
140 | {
141 | // not done
142 | return new string[0];
143 | }
144 |
145 | public string[] LocatePhone(string name)
146 | {
147 | // not done
148 | return new string[0];
149 | }
150 |
151 |
152 | public void ForceSyncronisation()
153 | {
154 | // not done
155 | }
156 |
157 | public int GetErrorCode()
158 | {
159 | // not done
160 | return 0;
161 | }
162 |
163 | public string GetErrorMessage()
164 | {
165 | // not done
166 | return "";
167 | }
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/php/include/PLUSPEOPLE/PesaPi/Base/AccountFactory.php:
--------------------------------------------------------------------------------
1 |
30 | */
31 | namespace PLUSPEOPLE\PesaPi\Base;
32 |
33 | class AccountFactory {
34 | ############### Properties ####################
35 | const SELECTLIST = "
36 | SELECT id,
37 | type,
38 | name,
39 | identifier,
40 | push_in,
41 | push_out,
42 | push_neutral,
43 | settings ";
44 |
45 | //# # # # # # # # misc methods # # # # # # # #
46 | static public function factoryOne($id) {
47 | $db = Database::instantiate(Database::TYPE_READ);
48 | $id = (int)$id;
49 |
50 | $query = AccountFactory::SELECTLIST . "
51 | FROM pesapi_account
52 | WHERE id = '$id' ";
53 |
54 | if ($result = $db->query($query) AND $foo = $db->fetchObject($result)) {
55 | $returnval = AccountFactory::createEntry($foo->type, $foo->id, $foo);
56 | $db->freeResult($result);
57 | return $returnval;
58 | }
59 | }
60 |
61 | static public function factoryByIdentifier($id) {
62 | $db = Database::instantiate(Database::TYPE_READ);
63 | $id = $id;
64 |
65 | if ($id != "") {
66 | $query = AccountFactory::SELECTLIST . "
67 | FROM pesapi_account
68 | WHERE identifier = '" . $db->dbIn("$id") . "' ";
69 |
70 | if ($result = $db->query($query) AND $foo = $db->fetchObject($result)) {
71 | $returnval = AccountFactory::createEntry($foo->type, $foo->id, $foo);
72 | $db->freeResult($result);
73 | return $returnval;
74 | }
75 | }
76 | return null;
77 | }
78 |
79 | static function factoryAll() {
80 | $db = Database::instantiate(Database::TYPE_READ);
81 |
82 | $query = AccountFactory::SELECTLIST . "
83 | FROM pesapi_account ";
84 |
85 | $tempArray = array();
86 | if ($result = $db->query($query)) {
87 | while($foo = $db->fetchObject($result)) {
88 | $tempArray[] = AccountFactory::createEntry($foo->type, $foo->id, $foo);
89 | }
90 | $db->freeResult($result);
91 | }
92 | return $tempArray;
93 | }
94 |
95 |
96 | public static function createEntry($type, $id, $initValues=NULL) {
97 | switch($type) {
98 | case Account::MPESA_PAYBILL:
99 | $object = new \PLUSPEOPLE\PesaPi\MpesaPaybill\MpesaPaybill($id, $initValues);
100 | break;
101 | case Account::MPESA_PRIVATE:
102 | $object = new \PLUSPEOPLE\PesaPi\MpesaPrivate\MpesaPrivate($id, $initValues);
103 | break;
104 | case Account::KENYA_YU_PRIVATE:
105 | $object = new \PLUSPEOPLE\PesaPi\KenyaYuPrivate\Account($id, $initValues);
106 | break;
107 | case Account::GHANA_AIRTEL_PRIVATE:
108 | $object = new \PLUSPEOPLE\PesaPi\GhanaAirtelPrivate\Account($id, $initValues);
109 | break;
110 | case Account::RWANDA_MTN_PRIVATE:
111 | $object = new \PLUSPEOPLE\PesaPi\RwandaMTNPrivate\Account($id, $initValues);
112 | break;
113 | case Account::TANZANIA_MPESA_PRIVATE:
114 | $object = new \PLUSPEOPLE\PesaPi\TanzaniaMpesaPrivate\Account($id, $initValues);
115 | break;
116 | case Account::TANZANIA_TIGO_PRIVATE:
117 | $object = new \PLUSPEOPLE\PesaPi\TanzaniaTigoPrivate\Account($id, $initValues);
118 | break;
119 | case Account::KENYA_AIRTEL_PRIVATE:
120 | $object = new \PLUSPEOPLE\PesaPi\KenyaAirtelPrivate\Account($id, $initValues);
121 | break;
122 | case Account::KENYA_AIRTEL_PAYBILL:
123 | $object = new \PLUSPEOPLE\PesaPi\KenyaAirtelPaybill\Account($id, $initValues);
124 | break;
125 | case Account::SOMALIA_GOLIS_PRIVATE:
126 | $object = new \PLUSPEOPLE\PesaPi\SomaliaGolisPrivate\Account($id, $initValues);
127 | break;
128 | case Account::SOMALIA_TELESOME_PRIVATE:
129 | $object = new \PLUSPEOPLE\PesaPi\SomaliaTelesomePrivate\Account($id, $initValues);
130 | break;
131 | case Account::SOMALIA_HORMUUD_PRIVATE:
132 | $object = new \PLUSPEOPLE\PesaPi\SomaliaHormuudPrivate\Account($id, $initValues);
133 | break;
134 | case Account::GHANA_MTN_PRIVATE:
135 | $object = new \PLUSPEOPLE\PesaPi\GhanaMTNPrivate\Account($id, $initValues);
136 | break;
137 | case Account::DR_CONGO_MPESA_PRIVATE:
138 | $object = new \PLUSPEOPLE\PesaPi\CongoMpesaPrivate\Account($id, $initValues);
139 | break;
140 | case Account::UGANDA_MTN_PRIVATE:
141 | $object = new \PLUSPEOPLE\PesaPi\UgandaMTNPrivate\Account($id, $initValues);
142 | break;
143 | }
144 | return $object;
145 | }
146 |
147 | }
148 | ?>
--------------------------------------------------------------------------------
/simulator/template/index.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | M-PESA Login
5 |
6 |
7 |
99 |
100 |
--------------------------------------------------------------------------------
|