├── java ├── build │ └── .dummyfile ├── examples │ └── simple_syncronisation │ │ ├── Makefile │ │ └── example.java ├── Makefile ├── documentation │ └── readme.txt └── source │ └── PLUSPEOPLE │ └── PesaPi │ ├── Payment.java │ └── Configuration.java ├── php ├── documentation │ ├── PesaPi.mwb │ ├── hosting_requirements.txt │ ├── congo_mpesa_private_examples.txt │ ├── todo.txt │ ├── ghana_airtel_private_examples.txt │ ├── readme.txt │ ├── kenya_airtel_private.txt │ ├── tanzania_mpesa_private_examples.txt │ ├── tanzania_tigo_private_examples.txt │ └── mpesa_private_examples.txt ├── examples │ ├── simple_syncronisation │ │ └── example.php │ └── show_balance │ │ └── example.php ├── cron │ └── sync.php ├── include │ └── PLUSPEOPLE │ │ ├── PesaPi │ │ ├── KenyaYuPrivate │ │ │ ├── Transaction.php │ │ │ ├── Account.php │ │ │ └── Parser.php │ │ ├── RwandaMTNPrivate │ │ │ ├── Transaction.php │ │ │ ├── Account.php │ │ │ └── Parser.php │ │ ├── GhanaMTNPrivate │ │ │ ├── Transaction.php │ │ │ ├── Account.php │ │ │ └── Parser.php │ │ ├── CongoMpesaPrivate │ │ │ ├── Transaction.php │ │ │ ├── Account.php │ │ │ └── Parser.php │ │ ├── SomaliaGolisPrivate │ │ │ ├── Transaction.php │ │ │ ├── Account.php │ │ │ └── Parser.php │ │ ├── UgandaMTNPrivate │ │ │ ├── Transaction.php │ │ │ ├── Account.php │ │ │ └── Parser.php │ │ ├── GhanaAirtelPrivate │ │ │ ├── Transaction.php │ │ │ ├── Account.php │ │ │ └── Parser.php │ │ ├── KenyaAirtelPrivate │ │ │ ├── Transaction.php │ │ │ └── Account.php │ │ ├── TanzaniaTigoPrivate │ │ │ ├── Transaction.php │ │ │ └── Account.php │ │ ├── TanzaniaMpesaPrivate │ │ │ ├── Transaction.php │ │ │ ├── ChargeCalculator.php │ │ │ └── Account.php │ │ ├── MpesaPrivate │ │ │ ├── Transaction.php │ │ │ ├── MpesaPrivate.php │ │ │ └── ChargeCalculator.php │ │ ├── Base │ │ │ ├── Utility.php │ │ │ ├── Parser.php │ │ │ ├── PrivateAccount.php │ │ │ ├── Database.php │ │ │ └── AccountFactory.php │ │ ├── MpesaPaybill │ │ │ ├── Scrubber.php │ │ │ └── Transaction.php │ │ ├── Configuration.php │ │ ├── SomaliaHormuudPrivate │ │ │ └── Account.php │ │ ├── SomaliaTelesomePrivate │ │ │ └── Account.php │ │ └── KenyaAirtelPaybill │ │ │ ├── Parser.php │ │ │ ├── Transaction.php │ │ │ └── Account.php │ │ ├── autoload.php │ │ └── SlowTemplate │ │ └── Template.php └── webroot │ ├── mpesaIPN.php │ └── smssync.php ├── simulator ├── include │ ├── WebUtility.php │ ├── Configuration.php │ └── Database.php ├── .htaccess ├── template │ ├── admin │ │ └── index.tpl │ └── index.tpl ├── simulator.sql └── webroot │ ├── index.php │ ├── admin │ └── index.php │ └── home2.php ├── composer.json ├── csharp └── source │ ├── MpesaPayment.cs │ ├── Configuration.cs │ └── PesaPi.cs ├── README.md └── changelog.txt /java/build/.dummyfile: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /php/documentation/PesaPi.mwb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluspeople/pesaPi/HEAD/php/documentation/PesaPi.mwb -------------------------------------------------------------------------------- /simulator/include/WebUtility.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluspeople/pesaPi/HEAD/simulator/include/WebUtility.php -------------------------------------------------------------------------------- /php/documentation/hosting_requirements.txt: -------------------------------------------------------------------------------- 1 | PHP & mysql 2 | 3 | 4 | PHP as cli module 5 | 6 | curl module 7 | mysql module 8 | xml module 9 | xmlreader module 10 | -------------------------------------------------------------------------------- /java/examples/simple_syncronisation/Makefile: -------------------------------------------------------------------------------- 1 | run: example.class 2 | java -classpath ../../build:. example 3 | all: example.class 4 | example.class: example.java 5 | javac -classpath ../../build/ example.java 6 | -------------------------------------------------------------------------------- /php/documentation/congo_mpesa_private_examples.txt: -------------------------------------------------------------------------------- 1 | ARGENT RECU du 243881260264 le 13/01/2015 12:51:37 2 | Du compte: 1000624832 3 | Montant: 0.20 USD 4 | Frais: 0.00 USD 5 | Ref: 181346285 6 | Solde Disponible: 0.20 USD 7 | -------------------------------------------------------------------------------- /java/Makefile: -------------------------------------------------------------------------------- 1 | all: source/PLUSPEOPLE/PesaPi/Configuration.java source/PLUSPEOPLE/PesaPi/PesaPi.java source/PLUSPEOPLE/PesaPi/Payment.java 2 | javac -d build/ source/PLUSPEOPLE/PesaPi/Configuration.java source/PLUSPEOPLE/PesaPi/PesaPi.java source/PLUSPEOPLE/PesaPi/Payment.java 3 | 4 | -------------------------------------------------------------------------------- /java/examples/simple_syncronisation/example.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple PesaPi syncronisation example 3 | */ 4 | import PLUSPEOPLE.PesaPi.*; 5 | 6 | public class example { 7 | public static void main(String[] args) { 8 | PesaPi pi = new PesaPi(); 9 | pi.forceSyncronisation(); 10 | } 11 | } -------------------------------------------------------------------------------- /php/examples/simple_syncronisation/example.php: -------------------------------------------------------------------------------- 1 | forceSyncronisation(); 8 | 9 | ?> -------------------------------------------------------------------------------- /php/examples/show_balance/example.php: -------------------------------------------------------------------------------- 1 | availableBalance() . "\n"; 8 | 9 | ?> -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pluspeople/pesapi", 3 | "description": "Mobile money middleware", 4 | "version": "1.0.0", 5 | "authors": [ 6 | { 7 | "name": "Michael Pedersen", 8 | "email": "michael@pluspeople.dk" 9 | } 10 | ], 11 | "minimum-stability": "dev", 12 | "require": { 13 | "php": ">=5.3.0" 14 | }, 15 | "autoload": { 16 | "psr-0": { 17 | "PLUSPEOPLE": "php/include/" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /php/documentation/todo.txt: -------------------------------------------------------------------------------- 1 | - Detection that the site is down. 2 | 3 | - disbursements. 4 | 5 | - fees-capture 6 | 7 | - Automatic detection of first time use of key asking people to change password. 8 | 9 | - auto update 10 | 11 | - scrubbing of xslt + triangulation. 12 | 13 | - feedback system 14 | 15 | - IPN support 16 | 17 | - YU cash support 18 | 19 | - MTL Rwanda support 20 | 21 | - Airtel Ghana support 22 | 23 | - Support for multiple feeds to same account. 24 | -------------------------------------------------------------------------------- /simulator/.htaccess: -------------------------------------------------------------------------------- 1 | php_value session.cache_limiter none 2 | php_value arg_seperator.output "&" 3 | php_flag register_globals Off 4 | php_value magic_quotes_gpc On 5 | 6 | RewriteEngine on 7 | 8 | RewriteCond %{REQUEST_URI} ^/ke/$ 9 | RewriteRule ^(.*)$ /ke/Default.aspx [L] 10 | 11 | RewriteCond %{REQUEST_URI} ^/ke/[Dd]efault.aspx 12 | RewriteRule ^(.*)$ webroot/index.php [L] 13 | 14 | RewriteCond %{REQUEST_URI} ^/ke/Main/home2.aspx 15 | RewriteRule ^(.*)$ webroot/home2.php [L] 16 | 17 | RewriteCond %{REQUEST_URI} !webroot/ 18 | RewriteRule ^(.*)$ webroot/$1 [L] 19 | -------------------------------------------------------------------------------- /simulator/template/admin/index.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | PesaPi-simulator Admin 4 | 5 | 6 | 7 |

Pesapi Admin - create a new payment

8 | 9 |
10 | Create new payment:
11 | Day & Time:
12 |
13 | Phone:
14 |
15 | Name:
16 |
17 | Amount:
18 |
19 |
20 | 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /php/documentation/ghana_airtel_private_examples.txt: -------------------------------------------------------------------------------- 1 | This file contains various example messages of SMS notifcations used on an Airtel private account in Ghana. 2 | All messages are sent from: AIRTELMONEY 3 | 4 | ----------------------------------- 5 | Notification for when you transfer money to another individual. 6 | 7 | $example = 'Trans. ID: 313821006060 8 | You have sent 20GHS to 233261234567. Your available balance is 250.67GHS.'; 9 | 10 | ----------------------------------- 11 | Notification for when you pay to a merchant/utility account 12 | 13 | $example = 'TransID: 3131001000477 14 | Transaction successful, you have paid 50.00GHS to reference code 0261234567'; 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /csharp/source/MpesaPayment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace PLUSPEOPLE.Pesapi 7 | { 8 | public class MpesaPayment 9 | { 10 | public int id { get; set; } 11 | public int type { get; set; } 12 | public int trasfer_direction { get; set; } 13 | public string reciept { get; set; } 14 | public DateTime time { get; set; } 15 | public string phonenumber { get; set; } 16 | public string name { get; set; } 17 | public string account { get; set; } 18 | public int status { get; set; } 19 | public long amount { get; set; } 20 | public long post_balance { get; set; } 21 | public string note { get; set; } 22 | 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PesaPI 2 | ======= 3 | PesaPI is an unofficial open source API for mobile money systems, released under the BSD(lite) license. 4 | The system currently support: 5 | * Kenya: Mpesa paybill accounts 6 | * Kenya: Mpesa buygoods (lipa na mpesa) accounts 7 | * Kenya: Mpesa private accounts 8 | * Kenya: Airtel private accounts 9 | * Kenya: Airtel paybill accounts (experimental) 10 | * Ghana: Airtel private accounts (experimental) 11 | * Rwanda: MNT private accounts (experimental) 12 | * Tanzania: Mpesa private accounts 13 | * Tanzania: Tigo private accounts 14 | * Somalia: Golis Sahal private accounts (experimental) 15 | * Uganda: MTN private accounts (experimental) 16 | 17 | The API supports both PUSH and PULL versions. 18 | The PHP version of the API is generally the most mature and recomended at this point - the system is build using Mysql and Curl. 19 | 20 | For further information please visit http://www.pesapi.com or the the public mailing-list "pesaPi" on Google-groups: http://groups.google.com/group/pesapi 21 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | Version 0.1.0: 2 | - Added author credentials to each file 3 | - Added support for PERSONAL Mpesa accounts utilizing smssync ( http://smssync.ushahidi.com ) by Henry Addo 4 | - Added a concept of Accounts - allowing PesaPi to handle multiple accounts. 5 | - Major restructuring of the file-layout to prepare for multiple account types. 6 | - Added missing .java file 7 | - Started writing a changelog (this file), to prepare the notes that will eventually go into the Git-commit log 8 | 9 | Version 0.0.4: 10 | - PHP: an issue with the namespace path have been fixed 11 | - PHP: PesaPi now automatically changes the Mpesa Password when promted to do so. 12 | - JAVA: Initial code from Kevin Oyugi have been cleaned up and incorporated in the Java port, see readme.txt in java/documentation for usage details 13 | - CSHARP: Initial code from Jude Okello have been cleaned up and tested with Mono and incorporated in the csharp port - NOTE: not all the csharp code have been incorporated yet just the first few files. 14 | 15 | -------------------------------------------------------------------------------- /java/documentation/readme.txt: -------------------------------------------------------------------------------- 1 | The Java port of PesaPi is currently at a very very early state - mainly it is just the inital structure that have been created. 2 | 3 | In other words A LOT of help is needed if this port is to get to a production level. 4 | 5 | Currently the java-port is using Makefile (ask google if you are in doubt) for build-management so all you need to do is to enter the main java directory and type "make" and the code will be build. 6 | 7 | Example: 8 | $ cd java 9 | $ make 10 | javac -d build/ source/PLUSPEOPLE/PesaPi/Configuration.java source/PLUSPEOPLE/PesaPi/PesaPi.java source/PLUSPEOPLE/PesaPi/Payment.java 11 | Note: source/PLUSPEOPLE/PesaPi/Configuration.java uses unchecked or unsafe operations. 12 | Note: Recompile with -Xlint:unchecked for details. 13 | 14 | 15 | If you want to see an example running - you just enter into an example directory and type "make run" example: 16 | 17 | $ cd examples/simple_syncronisation/ 18 | $ make run 19 | javac -classpath ../../build/ example.java 20 | java -classpath ../../build:. example 21 | Syncromizing 22 | 23 | -------------------------------------------------------------------------------- /php/documentation/readme.txt: -------------------------------------------------------------------------------- 1 | General information: 2 | -------------------- 3 | PesaPI is an open source API for commercial accounts. 4 | 5 | In other words you need to have an (paybill) commercial account in order to use this API. 6 | Once you have a commercial account, you can access the account though this API, using the digital certificate that Mpesa provides you. 7 | 8 | Current status: 9 | --------------- 10 | The current system should be considered as a proff-of-concept, or alpha version. 11 | The system works all the way to the local database - but there are still rough edges and things pending. 12 | In any case, before using it in any production setup - you should carefully test and validate that it actually performs as you expect. 13 | 14 | 15 | 16 | ************************ 17 | Developer information 18 | ************************ 19 | 20 | Design goal of the application. 21 | 22 | 1. As robust as humanly possible for a scrubber based API. 23 | Consering a scrubber based api is generally not very unreliabe, due to changes in the original output. 24 | 25 | 2. Simple to use api on which to build applications ontop. 26 | -------------------------------------------------------------------------------- /simulator/include/Configuration.php: -------------------------------------------------------------------------------- 1 | "localhost", 15 | "DatabaseUserRead" => "root", 16 | "DatabasePasswordRead" => "ture", 17 | "DatabaseDatabaseRead" => "2011_simulator", 18 | "DatabaseHostWrite" => "localhost", 19 | "DatabaseUserWrite" => "root", 20 | "DatabasePasswordWrite" => "ture", 21 | "DatabaseDatabaseWrite" => "2011_simulator", 22 | ); 23 | 24 | ///////////////////////////////////////////// 25 | public function getConfig($argument) { 26 | return $this->configArray[$argument]; 27 | } 28 | 29 | public static function instantiate() { 30 | if (self::$singleton == null) { 31 | self::$singleton = new Configuration(); 32 | } 33 | return self::$singleton; 34 | } 35 | } 36 | 37 | ?> -------------------------------------------------------------------------------- /php/documentation/kenya_airtel_private.txt: -------------------------------------------------------------------------------- 1 | ----------------------------------- 2 | Notifications when you buy Airtime for yourself 3 | 4 | Trans. ID: 7740224211672. You have received Airtime of 5 | 10.00Ksh from your OWN account. 6 | 7 | ----------------------------------- 8 | Notification when a Person is sending you money. 9 | 10 | Trans. ID: 1440225031704 You have received 50.00Ksh from 11 | ANGELA NZIOKI. Your available 12 | balance is 155.00Ksh. 13 | 14 | ----------------------------------- 15 | Notifications when you send another person money 16 | 17 | Trans. ID: 1450335211705 You 18 | have sent 50.00Ksh to JOSEPH KARANJA. Your available 19 | balance is 10.00Ksh. 20 | 21 | ----------------------------------- 22 | Notification when you pay to a paybill number 23 | 24 | Trans. ID: 2240545115618 You have sent 50.00Ksh to 25 | UHASIBU in reference to TEST. 26 | Your available balance is 27 | 105.00Ksh. 28 | 29 | ----------------------------------- 30 | Notifications when depositing cash money into your account 31 | 32 | Trans. ID: 0231412250320 You have received 200Ksh from 33 | ART111. Your available balance is 200.00Ksh. 34 | 35 | 36 | ----------------------------------- 37 | Notification when you do a balance request 38 | 39 | Your available bal. is Ksh60.00. 40 | 41 | ----------------------------------- 42 | Notifications when an error occours. 43 | 44 | The amount you are trying to 45 | transfer is less than the 46 | minimum amount of 50.00Ksh 47 | allowed. 48 | -------------------------------------------------------------------------------- /java/source/PLUSPEOPLE/PesaPi/Payment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Payment 3 | * @author Michael Pedersen 4 | * @version 0.1 5 | * @since 2.September 2011 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of PLUSPEOPLE nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | */ 31 | package PLUSPEOPLE.PesaPi; 32 | 33 | public class Payment{ 34 | 35 | public void Payment() { 36 | 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /php/cron/sync.php: -------------------------------------------------------------------------------- 1 | forceSyncronisation(); 36 | 37 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/KenyaYuPrivate/Transaction.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\KenyaYuPrivate; 32 | 33 | class Transaction extends \PLUSPEOPLE\PesaPi\Base\Transaction { 34 | 35 | } 36 | 37 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/RwandaMTNPrivate/Transaction.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\RwandaMTNPrivate; 32 | 33 | class Transaction extends \PLUSPEOPLE\PesaPi\Base\Transaction { 34 | 35 | } 36 | 37 | ?> -------------------------------------------------------------------------------- /csharp/source/Configuration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace PLUSPEOPLE.Pesapi 7 | { 8 | public class Configuration 9 | { 10 | private static Configuration singleton ; 11 | private Dictionary configs = new Dictionary(); 12 | 13 | private Configuration() 14 | { 15 | configs.Add("ProductionMode", "off"); 16 | configs.Add("SimulationMode", "on"); 17 | configs.Add("AllowAutoUpdate", "on"); 18 | configs.Add("AllowFeedback", "on"); 19 | configs.Add("MaxCompatibility", "on"); 20 | configs.Add("MpesaCertificatePath", ""); 21 | configs.Add("MpesaLoginName", ""); 22 | configs.Add("MpesaCorporation", ""); 23 | configs.Add("MpesaInitialSyncDate", ""); 24 | configs.Add("CookieFolderPath", ""); 25 | configs.Add("DatabaseHostRead", "localhost"); 26 | configs.Add("DatabaseUserRead", ""); 27 | configs.Add("DatabasePasswordRead", ""); 28 | configs.Add("DatabaseDatabaseRead", ""); 29 | configs.Add("DatabaseHostWrite", "localhost"); 30 | configs.Add("DatabaseUserWrite", ""); 31 | configs.Add("DatabasePasswordWrite", ""); 32 | configs.Add("DatabaseDatabaseWrite", ""); 33 | configs.Add("Version", "0.0.3"); 34 | } 35 | 36 | 37 | public string GetConfig(string configName) 38 | { 39 | return this.configs[configName]; 40 | } 41 | 42 | public void SetConfig(string configName, string value) 43 | { 44 | if (this.configs.ContainsKey(configName)) 45 | { 46 | this.configs[configName] = value; 47 | } 48 | else 49 | { 50 | this.configs.Add(configName, value); 51 | } 52 | } 53 | 54 | public static Configuration Instantiate() 55 | { 56 | if (null == Configuration.singleton) 57 | { 58 | Configuration.singleton = new Configuration(); 59 | } 60 | return Configuration.singleton; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/autoload.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | 32 | // default timezone setting. - should be set on a per-server setup. 33 | date_default_timezone_set("Africa/Nairobi"); 34 | 35 | function __autoload($class) { 36 | $fileName = str_replace("\\", "/", $class) . ".php"; 37 | require_once($fileName); 38 | } 39 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/GhanaMTNPrivate/Transaction.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\GhanaMTNPrivate; 32 | 33 | class Transaction extends \PLUSPEOPLE\PesaPi\Base\Transaction { 34 | // Extended attributes 35 | const GH_MTN_PRIVATE_PAYMENT_RECEIVED = 1301; 36 | 37 | const GH_MTN_PRIVATE_UNKOWN = 1399; 38 | } 39 | 40 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/CongoMpesaPrivate/Transaction.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\CongoMpesaPrivate; 32 | 33 | class Transaction extends \PLUSPEOPLE\PesaPi\Base\Transaction { 34 | // Extended attributes 35 | const CD_MPESA_PRIVATE_PAYMENT_RECEIVED = 1401; 36 | 37 | const CD_MPESA_PRIVATE_UNKOWN = 1499; 38 | } 39 | 40 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/SomaliaGolisPrivate/Transaction.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\SomaliaGolisPrivate; 32 | 33 | class Transaction extends \PLUSPEOPLE\PesaPi\Base\Transaction { 34 | // Extended attributes 35 | const SO_GOLIS_PRIVATE_PAYMENT_RECEIVED = 1001; 36 | 37 | const SO_GOLIS_PRIVATE_UNKOWN = 1099; 38 | } 39 | 40 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/UgandaMTNPrivate/Transaction.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\UgandaMTNPrivate; 32 | 33 | class Transaction extends \PLUSPEOPLE\PesaPi\Base\Transaction { 34 | // Extended attributes 35 | const UG_MTN_PRIVATE_PAYMENT_RECEIVED = 1501; 36 | const UG_MTN_PRIVATE_PAYMENT_SENT = 1502; 37 | 38 | const UG_MTN_PRIVATE_UNKOWN = 1599; 39 | } 40 | 41 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/GhanaAirtelPrivate/Transaction.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\GhanaAirtelPrivate; 32 | 33 | class Transaction extends \PLUSPEOPLE\PesaPi\Base\Transaction { 34 | const GH_AIRTEL_PAYMENT_RECEIVED = 400; 35 | const GH_AIRTEL_PAYMENT_SENT = 401; 36 | const GH_AIRTEL_AIRTIME = 402; 37 | const GH_AIRTEL_PURCHASE = 403; 38 | const GH_AIRTEL_UNKNOWN = 410; 39 | 40 | } 41 | 42 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/GhanaMTNPrivate/Account.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\GhanaMTNPrivate; 32 | 33 | class Account extends \PLUSPEOPLE\PesaPi\Base\PrivateAccount { 34 | public function getFormatedType() { 35 | return "Ghana - MTN Private"; 36 | } 37 | 38 | public function getSender() { 39 | return ""; // Unknown at this point - security risk 40 | } 41 | 42 | // Namespace mismatch workaround 43 | public function parserFactory() { 44 | return new Parser(); 45 | } 46 | 47 | } 48 | 49 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/CongoMpesaPrivate/Account.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\CongoMpesaPrivate; 32 | 33 | class Account extends \PLUSPEOPLE\PesaPi\Base\PrivateAccount { 34 | public function getFormatedType() { 35 | return "Congo - Vodaphone MPESA Private"; 36 | } 37 | 38 | public function getSender() { 39 | return ""; // unknown at this point - security risk 40 | } 41 | 42 | // Namespace mismatch workaround 43 | public function parserFactory() { 44 | return new Parser(); 45 | } 46 | } 47 | 48 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/SomaliaGolisPrivate/Account.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\SomaliaGolisPrivate; 32 | 33 | class Account extends \PLUSPEOPLE\PesaPi\Base\PrivateAccount { 34 | public function getFormatedType() { 35 | return "Somalia - Golis Sahal Private"; 36 | } 37 | 38 | public function getSender() { 39 | return ""; // unknown at this point - security risk 40 | } 41 | 42 | // Namespace mismatch workaround 43 | public function parserFactory() { 44 | return new Parser(); 45 | } 46 | } 47 | 48 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/KenyaAirtelPrivate/Transaction.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\KenyaAirtelPrivate; 32 | 33 | class Transaction extends \PLUSPEOPLE\PesaPi\Base\Transaction { 34 | // Extended attributes 35 | const KE_AIRTEL_PRIVATE_PAYMENT_RECEIVED = 801; 36 | const KE_AIRTEL_PRIVATE_PAYMENT_SENT = 802; 37 | const KE_AIRTEL_PRIVATE_PAYBILL_PAID = 803; 38 | const KE_AIRTEL_PRIVATE_AIRTIME_YOU = 804; 39 | const KE_AIRTEL_PRIVATE_DEPOSIT = 805; 40 | const KE_AIRTEL_PRIVATE_WITHDRAW = 806; 41 | const KE_AIRTEL_PRIVATE_BALANCE_REQUEST = 807; 42 | 43 | const KE_AIRTEL_PRIVATE_UNKOWN = 899; 44 | } 45 | 46 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/TanzaniaTigoPrivate/Transaction.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\TanzaniaTigoPrivate; 32 | 33 | class Transaction extends \PLUSPEOPLE\PesaPi\Base\Transaction { 34 | // Extended attributes 35 | const TZ_TIGO_PRIVATE_PAYMENT_RECEIVED = 701; 36 | const TZ_TIGO_PRIVATE_PAYMENT_SENT = 702; 37 | const TZ_TIGO_PRIVATE_PAYBILL_PAID = 703; 38 | const TZ_TIGO_PRIVATE_AIRTIME_YOU = 704; 39 | const TZ_TIGO_PRIVATE_DEPOSIT = 705; 40 | const TZ_TIGO_PRIVATE_WITHDRAW = 706; 41 | const TZ_TIGO_PRIVATE_DEPOSIT_BANK = 707; 42 | const TZ_TIGO_PRIVATE_BALANCE_REQUEST = 708; 43 | 44 | const TZ_TIGO_PRIVATE_UNKOWN = 799; 45 | } 46 | 47 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/TanzaniaMpesaPrivate/Transaction.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\TanzaniaMpesaPrivate; 32 | 33 | class Transaction extends \PLUSPEOPLE\PesaPi\Base\Transaction { 34 | // Extended attributes 35 | const TZ_MPESA_PRIVATE_PAYMENT_RECEIVED = 601; 36 | const TZ_MPESA_PRIVATE_PAYMENT_SENT = 602; 37 | const TZ_MPESA_PRIVATE_PAYBILL_PAID = 603; 38 | const TZ_MPESA_PRIVATE_AIRTIME_YOU = 604; 39 | const TZ_MPESA_PRIVATE_AIRTIME_OTHER = 605; 40 | const TZ_MPESA_PRIVATE_DEPOSIT = 606; 41 | const TZ_MPESA_PRIVATE_WITHDRAW = 607; 42 | const TZ_MPESA_PRIVATE_BALANCE_REQUEST = 608; 43 | 44 | const TZ_MPESA_PRIVATE_UNKOWN = 699; 45 | } 46 | 47 | ?> -------------------------------------------------------------------------------- /php/webroot/mpesaIPN.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | set_include_path("../local_include:../include:" . get_include_path()); 32 | require_once("PLUSPEOPLE/autoload.php"); 33 | 34 | // Define constants 35 | $payloadSuccess = "OK|"; 36 | $payloadFailure = 'FAIL|'; 37 | 38 | $pesa = new PLUSPEOPLE\PesaPi\PesaPi(); 39 | 40 | // Identifier 41 | $identifier = $_GET["business_number"]; 42 | $accounts = $pesa->getAccount($identifier); 43 | 44 | if (is_object($accounts[0]) AND $accounts[0]->getIdentifier() == $identifier) { 45 | $transaction = $accounts[0]->importIPN($_GET); 46 | if (is_object($transaction)) { 47 | print $payloadSuccess; 48 | exit(); 49 | } 50 | } 51 | print $payloadSuccess; // NOT DONE 52 | ?> -------------------------------------------------------------------------------- /php/documentation/tanzania_mpesa_private_examples.txt: -------------------------------------------------------------------------------- 1 | This file contains various example messages of SMS notifcations used on an Tanzanian MPESA Private account. 2 | see also: https://www.vodacom.co.tz/mpesa/consumers/send_and_receive 3 | ----------------------------------------------------------------- 4 | 5 | Notification when a Person is sending you money. 6 | 7 | Z10DN636 Confirmed. 8 | You have received Tsh50,000 from 9 | FREDRICK KIMARO 10 | on 27/1/14 at 1:19 PM 11 | New M-PESA balance is Tsh214,676 12 | 13 | ----------------------------------------------------------------- 14 | Notifications when you send another person money 15 | 16 | X89IQ877 Confirmed. Tsh2,100 sent to MUSHI WILLIAM on 15/12/13 at 10:19 PM. New M-PESA balance is Tsh23,232. 17 | 18 | 19 | ----------------------------------------------------------------- 20 | Notifications when you buy Airtime for yourself 21 | 22 | Z92FP098 confirmed. You bought Tsh3,500 of airtime on 22/2/14 at 8:12 PM 23 | New M-PESA balance is Tsh2,720 24 | 25 | 26 | ----------------------------------------------------------------- 27 | Notifications when you buy Airtime for someone else 28 | 29 | Y24KU015 confirmed. You bought Tsh501 of airtime for 0765567959 on 1/1/14 at 10:09 PM 30 | New M-PESA balance is Tsh1,430 31 | 32 | 33 | ----------------------------------------------------------------- 34 | Notifications when you deposit money into bank - wondering if this is a paybill transaction (looks like it) 35 | 36 | Z15EE301 Confirmed. 37 | Tsh212,000 sent to business Bank for account 38 | ACB-11567341482 on 28/1/14 at 11:30 PM 39 | New M-PESA balance is Tsh225. 40 | 41 | 42 | ----------------------------------------------------------------- 43 | Notifications when you deposit money into your mpesa account 44 | 45 | BA35RS735 Confirmed. 46 | on 8/3/14 at 4:59 PM 47 | Give Tsh10,000 cash to Hilda Joseph Mushi 48 | New M-PESA balance is Tsh10,015 49 | 50 | 51 | ----------------------------------------------------------------- 52 | Notifications when you withdraw money from your account at an agent 53 | 54 | BA35RT157 Confirmed. 55 | on 8/3/14 at 5:19 PM 56 | Withdraw Tsh6,000 from 57 | 128137 - EMANUEL SHANI 58 | New M-PESA balance is Tsh3,415 59 | 60 | ----------------------------------------------------------------- 61 | Notifications when do a balance check 62 | 63 | BB43UB521 Confirmed. Your M-PESA balance was Tsh2,354 64 | on 8/3/14 at 5:26 PM. 65 | 66 | -------------------------------------------------------------------------------- /simulator/simulator.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 3.4.2 3 | -- http://www.phpmyadmin.net 4 | -- 5 | -- Host: localhost 6 | -- Generation Time: Jul 17, 2011 at 05:43 AM 7 | -- Server version: 5.5.8 8 | -- PHP Version: 5.3.6 9 | 10 | SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; 11 | SET time_zone = "+00:00"; 12 | 13 | 14 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 15 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 16 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 17 | /*!40101 SET NAMES utf8 */; 18 | 19 | -- 20 | -- Database: `2011_simulator` 21 | -- 22 | 23 | -- -------------------------------------------------------- 24 | 25 | -- 26 | -- Table structure for table `payment` 27 | -- 28 | 29 | CREATE TABLE IF NOT EXISTS `payment` ( 30 | `id` int(11) NOT NULL AUTO_INCREMENT, 31 | `type` int(11) NOT NULL, 32 | `transfer_direction` int(11) NOT NULL, 33 | `reciept` varchar(10) COLLATE latin1_danish_ci NOT NULL, 34 | `time` datetime NOT NULL, 35 | `phonenumber` varchar(45) COLLATE latin1_danish_ci NOT NULL, 36 | `name` varchar(255) COLLATE latin1_danish_ci NOT NULL, 37 | `account` varchar(255) COLLATE latin1_danish_ci NOT NULL, 38 | `status` int(11) NOT NULL, 39 | `amount` bigint(20) NOT NULL, 40 | `post_balance` bigint(20) NOT NULL, 41 | `note` varchar(255) COLLATE latin1_danish_ci NOT NULL, 42 | PRIMARY KEY (`id`), 43 | UNIQUE KEY `reciept_UNIQUE` (`reciept`,`type`), 44 | KEY `type_index` (`type`), 45 | KEY `name_index` (`name`), 46 | KEY `phone_index` (`phonenumber`), 47 | KEY `time_index` (`time`) 48 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_danish_ci AUTO_INCREMENT=6 ; 49 | 50 | -- 51 | -- Dumping data for table `payment` 52 | -- 53 | 54 | INSERT INTO `payment` (`id`, `type`, `transfer_direction`, `reciept`, `time`, `phonenumber`, `name`, `account`, `status`, `amount`, `post_balance`, `note`) VALUES 55 | (1, 1, 0, 'badf21332', '2011-07-17 02:43:36', '1234567', 'yodelay', '1234', 1, 10000, 10000, ''), 56 | (3, 1, 0, 'badf21332x', '2011-07-17 02:44:22', '1234567', 'yodelay', '1234', 1, 10000, 10000, ''), 57 | (5, 1, 0, 'BCXY8373', '2011-07-17 05:36:00', '39848585', 'Mike', '6284', 1, 300000, 300000, ''); 58 | 59 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 60 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 61 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 62 | -------------------------------------------------------------------------------- /php/documentation/tanzania_tigo_private_examples.txt: -------------------------------------------------------------------------------- 1 | This file contains various example messages of SMS notifcations used on an Tanzanian TIGO Private account. 2 | 3 | ----------------------------------------------------------------- 4 | Notification when a Person is sending you money. 5 | 6 | New balance is Tsh 138,522. 7 | You have received Tsh 50,000 8 | from CHARLES KOMBA, 9 | 0727666074. 31/01/2014 05:36 PM; with TxnId: 10 | PP141141.1843.D06413. 11 | Transact with... 12 | 13 | ----------------------------------------------------------------- 14 | Notifications when you send another person money 15 | 16 | New balance is Tsh 2,515. 17 | Money sent successfully to 18 | ANJELA KIRIA, 0745239044. 19 | Amount: Tsh 31,000. Fee: Tsh 350.TxnID: 20 | PP134304.0912.J02744. You can now send... 21 | 22 | ----------------------------------------------------------------- 23 | Notifications when you buy Airtime for yourself 24 | 25 | New balance is Tsh 2,266. Your 26 | recharge request is successful 27 | for amount Tsh 3,501. TxnId : 28 | RC140320.1332.D09804. 29 | Transact with Tigo Pesa and win from Tshs... 30 | 31 | 32 | ----------------------------------------------------------------- 33 | Notifications when you pay a bill (paybill?) 34 | 35 | Bill Transaction has been sent 36 | to Tanesco.Please wait for 37 | confirmation TxnId: 38 | BP150143.1343.E03178, Bill 39 | Number:01343392423, 40 | transaction amount : 20,000 41 | Tsh,new balance :71,073 Tsh, 42 | Company Tanesco. 43 | 44 | 45 | ----------------------------------------------------------------- 46 | Notifications when you deposit money into bank 47 | 48 | Bank payment successfull. The details are : TxnId: 49 | BP150121.1337.C09085, Ref 50 | Number:10411342553, 51 | transaction amount : 111,000 52 | Tsh , charges: 0 Tsh,new 53 | balance :2,323 Tsh, Bank Name : 54 | ACB 55 | 56 | ----------------------------------------------------------------- 57 | Notifications when you deposit money into your Tigo account 58 | 59 | New balance is Tsh 35,165. 60 | Cash-In of Tsh 35,000 61 | successful. Agent HILDA MUSHI. 62 | TxnID: 63 | CI154303.0933.G03265. You can now send money from your CRDB account to... 64 | 65 | 66 | ----------------------------------------------------------------- 67 | Notifications when you withdraw money from your Tigo account at an agent 68 | 69 | New balance is Tsh 6,022. Cash-Out to EMANUEL KIULA was successful. Amount Tsh 70 | 130,000. Charges Tsh 2500.TxnID 71 | CO150121.3223.G00889. 72 | Transact with Tigo Pesa... 73 | 74 | ----------------------------------------------------------------- 75 | Notifications when do a balance check 76 | 77 | 78 | -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/MpesaPrivate/Transaction.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\MpesaPrivate; 32 | 33 | class Transaction extends \PLUSPEOPLE\PesaPi\Base\Transaction { 34 | // Extended attributes 35 | const MPESA_PRIVATE_PAYMENT_RECEIVED = 21; 36 | const MPESA_PRIVATE_PAYMENT_SENT = 22; 37 | const MPESA_PRIVATE_DEPOSIT = 23; 38 | const MPESA_PRIVATE_WITHDRAW = 24; 39 | const MPESA_PRIVATE_WITHDRAW_ATM = 25; 40 | const MPESA_PRIVATE_PAYBILL_PAID = 26; 41 | const MPESA_PRIVATE_BUY_GOODS = 27; 42 | const MPESA_PRIVATE_AIRTIME_YOU = 28; 43 | const MPESA_PRIVATE_AIRTIME_OTHER = 29; 44 | const MPESA_PRIVATE_UNKNOWN = 30; 45 | const MPESA_PRIVATE_B2C_RECEIVED = 31; 46 | const MPESA_PRIVATE_BALANCE_REQUEST = 32; 47 | const MPESA_PRIVATE_ERROR = 33; 48 | const MPESA_PRIVATE_BUYGOODS_RECEIVED = 34; 49 | const MPESA_PRIVATE_TO_MSHWARI = 35; 50 | const MPESA_PRIVATE_FROM_MSHWARI = 36; 51 | 52 | } 53 | 54 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/Base/Utility.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\Base; 32 | 33 | class Utility { 34 | public static function numberInput($input) { 35 | $input = trim($input); 36 | $amount = 0; 37 | 38 | if (preg_match("/^[0-9,]+\.?$/", $input)) { 39 | $amount = 100 * (int)str_replace(',', '', $input); 40 | } elseif (preg_match("/^[0-9,]+\.[0-9]$/", $input)) { 41 | $amount = 10 * (int)str_replace(array('.', ','), '', $input); 42 | } elseif (preg_match("/^[0-9,]*\.[0-9][0-9]$/", $input)) { 43 | $amount = (int)str_replace(array('.', ','), '', $input); 44 | } else { 45 | $amount = (int)$input; 46 | } 47 | return $amount; 48 | } 49 | 50 | public static function dateInput($input) { 51 | $timeStamp = strtotime($input); 52 | if ($timeStamp != FALSE) { 53 | return $timeStamp; 54 | } 55 | return 0; 56 | } 57 | 58 | public static function generatePassword($length = 10, $chars = "abcdefghijkmnopqrstuvwxyz023456789") { 59 | srand((double)microtime()*1000000); 60 | 61 | $pass = '' ; 62 | for ($i = 0; $i < $length; $i++) { 63 | $num = rand() % 33; 64 | $tmp = substr($chars, $num, 1); 65 | $pass = $pass . $tmp; 66 | } 67 | 68 | return $pass; 69 | } 70 | } 71 | 72 | ?> -------------------------------------------------------------------------------- /simulator/webroot/index.php: -------------------------------------------------------------------------------- 1 | setTemplateFile('index.tpl'); 37 | session_start(); 38 | 39 | ////////////////////////////////////////////////////////////////////////////// 40 | // handle the submission 41 | if ($_SERVER["REQUEST_METHOD"] == 'POST') { 42 | if ($_POST['__VIEWSTATE'] == $_SESSION['VIEWSTATE'] AND 43 | $_POST['LoginCtrl$UserName'] == 'test' AND 44 | $_POST['LoginCtrl$Password'] == 'best' AND 45 | $_POST['LoginCtrl$txtOrganisationName'] == 'PesaPi') { 46 | 47 | if ($_GET['ReturnUrl'] != "") { 48 | WebUtility::redirect($_GET['ReturnUrl']); 49 | } else { 50 | // if no return url we pretend they gave this one - since the simulator does not have the normal "entry" one (yet) 51 | WebUtility::redirect('/ke/Main/home2.aspx?MenuID=1826'); 52 | } 53 | } 54 | } 55 | 56 | ////////////////////////////////////////////////////////////////////////////// 57 | // display the page 58 | $view = WebUtility::viewstate(152); 59 | $_SESSION['VIEWSTATE'] = $view; 60 | 61 | $slow->assign(array("VIEWSTATE" => $view)); 62 | 63 | 64 | $slow->parse(); 65 | $slow->slowPrint(); 66 | 67 | 68 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/Base/Parser.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\Base; 32 | 33 | class Parser { 34 | public function getBlankStructure() { 35 | return array("SUPER_TYPE" => 0, 36 | "TYPE" => 0, 37 | "RECEIPT" => "", 38 | "TIME" => 0, 39 | "PHONE" => "", 40 | "NAME" => "", 41 | "ACCOUNT" => "", 42 | "STATUS" => "", 43 | "AMOUNT" => 0, 44 | "BALANCE" => 0, 45 | "NOTE" => "", 46 | "COST" => 0); 47 | } 48 | 49 | public function dateInput($time, $format) { 50 | $dt = \DateTime::createFromFormat($format, $time); 51 | if ($dt !== FALSE) { 52 | return $dt->getTimestamp(); 53 | } 54 | return 0; 55 | } 56 | 57 | public function numberInput($input) { 58 | $input = trim($input); 59 | $amount = 0; 60 | 61 | if (preg_match("/^[0-9,]+\.?$/", $input)) { 62 | $amount = 100 * (int)str_replace(',', '', $input); 63 | } elseif (preg_match("/^[0-9,]+\.[0-9]$/", $input)) { 64 | $amount = 10 * (int)str_replace(array('.', ','), '', $input); 65 | } elseif (preg_match("/^[0-9,]*\.[0-9][0-9]$/", $input)) { 66 | $amount = (int)str_replace(array('.', ','), '', $input); 67 | } else { 68 | $amount = (int)$input; 69 | } 70 | return $amount; 71 | } 72 | 73 | 74 | } 75 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/MpesaPaybill/Scrubber.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\MpesaPaybill; 32 | 33 | class Scrubber { 34 | const VERSION = "1.0"; 35 | 36 | public static function numberInput($input) { 37 | $input = trim($input); 38 | $amount = 0; 39 | 40 | if (preg_match("/^[0-9,]+$/", $input)) { 41 | $amount = 100 * (int)str_replace(',', '', $input); 42 | } elseif (preg_match("/^[0-9,]+\.[0-9]$/", $input)) { 43 | $amount = 10 * (int)str_replace(array('.', ','), '', $input); 44 | } elseif (preg_match("/^[0-9,]*\.[0-9][0-9]$/", $input)) { 45 | $amount = (int)str_replace(array('.', ','), '', $input); 46 | } else { 47 | $amount = (int)$input; 48 | } 49 | return $amount; 50 | } 51 | 52 | public static function dateInput($input) { 53 | $timeStamp = strtotime($input); 54 | if ($timeStamp != FALSE) { 55 | return $timeStamp; 56 | } 57 | return 0; 58 | } 59 | 60 | public static function ScrubRows(&$data) { 61 | $result = array(); 62 | $rows = HTMLPaymentScrubber1::scrubPaymentRows($data); 63 | 64 | foreach($rows AS $row) { 65 | $transaction = HTMLPaymentScrubber1::scrubPayment($row); 66 | if ($transaction != null) { 67 | $result[] = $transaction; 68 | } 69 | } 70 | 71 | // return the reverse array - we want the oldest first 72 | return $result; 73 | } 74 | 75 | } 76 | 77 | 78 | 79 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/GhanaMTNPrivate/Parser.php: -------------------------------------------------------------------------------- 1 | 30 | Based on examples provided by Baba Musah 31 | */ 32 | namespace PLUSPEOPLE\PesaPi\GhanaMTNPrivate; 33 | 34 | class Parser extends \PLUSPEOPLE\PesaPi\Base\Parser { 35 | public function parse($input) { 36 | $result = $this->getBlankStructure(); 37 | 38 | // REFACTOR: should be split into subclasses 39 | if (strpos($input, "Payment received for GHC") !== FALSE) { 40 | $result["SUPER_TYPE"] = Transaction::MONEY_IN; 41 | $result["TYPE"] = Transaction::GH_MTN_PRIVATE_PAYMENT_RECEIVED; 42 | 43 | $temp = array(); 44 | preg_match_all("/MobileMoney Advice[\s\n\r]+Payment received for GHC([0-9\.\,]+) from ([0-9A-Za-z '\.]+)[\s\n\r]+Current Balance: GHC([0-9\.\,]+)[\s\n\r]+Available Balance: GHC([0-9\.\,]+)[\s\n\r]+Reference: (.*)[\s\n\r]+QJV/mi", $input, $temp); 45 | if (isset($temp[1][0])) { 46 | $result["RECEIPT"] = time(); // MTN does not publish reference numbers - stupid. 47 | $result["AMOUNT"] = $this->numberInput($temp[1][0]); 48 | $result["NAME"] = $temp[2][0]; 49 | $result["TIME"] = time(); 50 | $result["BALANCE"] = $this->numberInput($temp[3][0]); 51 | $result["ACCOUNT"] = trim($temp[5][0]); 52 | } 53 | 54 | } else { 55 | $result["SUPER_TYPE"] = Transaction::MONEY_NEUTRAL; 56 | $result["TYPE"] = Transaction::GH_MTN_PRIVATE_UNKOWN; 57 | } 58 | 59 | return $result; 60 | } 61 | 62 | } 63 | 64 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/CongoMpesaPrivate/Parser.php: -------------------------------------------------------------------------------- 1 | 30 | Based on examples provided by Hassan Al Jirani 31 | */ 32 | namespace PLUSPEOPLE\PesaPi\CongoMpesaPrivate; 33 | 34 | class Parser extends \PLUSPEOPLE\PesaPi\Base\Parser{ 35 | const DATE_FORMAT = "j/n/Y h:i:s"; 36 | 37 | public function parse($input) { 38 | $result = $this->getBlankStructure(); 39 | 40 | // REFACTOR: should be split into subclasses 41 | // ARGENT RECU du 243881260264 le 13/01/2015 12:51:37 Du compte: 1000624832 Montant: 0.20 USD Frais: 0.00 USD Ref: 181346285 Solde Disponible: 0.20 USD 42 | if (strpos($input, " ARGENT RECU du ") !== FALSE) { 43 | $result["SUPER_TYPE"] = Transaction::MONEY_IN; 44 | $result["TYPE"] = Transaction::CD_MPESA_PRIVATE_PAYMENT_RECEIVED; 45 | 46 | $temp = array(); 47 | preg_match_all("/ARGENT RECU du (\d+) le (\d\d?\/\d\d\/\d{4} \d\d?:\d\d:\d\d)[\s\n]+Du compte: (\d+)[\s\n]+Montant: ([0-9\.\,]+) USD[\s\n]+Frais: ([0-9\.\,]+) USD[\s\n]+Ref: (\d+)[\s\n]+Solde Disponible: ([0-9\.\,]+) USD/mi", $input, $temp); 48 | if (isset($temp[1][0])) { 49 | $result["RECEIPT"] = $temp[6][0]; 50 | $result["AMOUNT"] = $this->numberInput($temp[4][0]); 51 | $result["PHONE"] = $temp[0][0]; 52 | $result["TIME"] = $this->dateInput(Parser::DATE_FORMAT, $temp[2][0]); 53 | $result["BALANCE"] = $this->numberInput($temp[7][0]); 54 | $result["COST"] = $this->numberInput($temp[5][0]); 55 | } 56 | 57 | } else { 58 | $result["SUPER_TYPE"] = Transaction::MONEY_NEUTRAL; 59 | $result["TYPE"] = Transaction::CD_MPESA_PRIVATE_UNKOWN; 60 | } 61 | 62 | return $result; 63 | } 64 | 65 | } 66 | 67 | ?> -------------------------------------------------------------------------------- /php/webroot/smssync.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | set_include_path("../local_include:../include:" . get_include_path()); 32 | require_once("PLUSPEOPLE/autoload.php"); 33 | 34 | use \PLUSPEOPLE\PesaPi\Base\Account; 35 | 36 | // Define constants 37 | $payloadSuccess = '{ payload: { success: "true" } }'; 38 | $payloadFailure = '{ payload: { success: "false" } }'; 39 | 40 | 41 | // import payload/variables from smssync 42 | $from = @$_POST["from"]; 43 | $message = @$_POST["message"]; 44 | $secret = @$_POST["secret"]; 45 | $sent_timestamp = @$_POST["sent_timestamp"]; 46 | 47 | $config = PLUSPEOPLE\PesaPi\Configuration::instantiate(); 48 | if (!is_object($config)) { 49 | print $payloadFailure; 50 | exit(); 51 | } 52 | 53 | $pesa = new PLUSPEOPLE\PesaPi\PesaPi(); 54 | $identifier = $_GET["identifier"]; 55 | $accounts = $pesa->getAccount($identifier); 56 | 57 | if (!isset($accounts[0])) { 58 | print $payloadFailure; 59 | exit(); 60 | } 61 | $account = $accounts[0]; 62 | $settings = $account->getSettings(); 63 | 64 | if ($settings["SYNC_SECRET"] == "" OR $settings["SYNC_SECRET"] != $secret) { 65 | print $payloadFailure; 66 | exit(); 67 | } 68 | 69 | 70 | switch ($account->getType()) { 71 | case Account::TANZANIA_MPESA_PRIVATE: 72 | case Account::MPESA_PRIVATE: 73 | if ($from != "MPESA") { 74 | print $payloadSuccess; 75 | exit(); 76 | } 77 | break; 78 | } 79 | 80 | 81 | $transaction = $account->importTransaction($message); 82 | if (is_object($transaction)) { 83 | $transaction->setNote($message); 84 | $transaction->update(); 85 | print $payloadSuccess; 86 | exit(); 87 | } 88 | print $payloadFailure; 89 | 90 | 91 | 92 | 93 | 94 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/Base/PrivateAccount.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\Base; 32 | use PLUSPEOPLE\PesaPi\Base\TransactionFactory; 33 | 34 | class PrivateAccount extends Account { 35 | 36 | public function availableBalance($time = null) { 37 | $time = (int)$time; 38 | if (0 == $time) { 39 | $time = time(); 40 | } 41 | 42 | $balance = TransactionFactory::factoryOneByTime($this, $time); 43 | if (is_object($balance)) { 44 | return $balance->getPostBalance(); 45 | } 46 | return $amount; 47 | } 48 | 49 | public function locateByReceipt($receipt) { 50 | return TransactionFactory::factoryByReceipt($this, $receipt); 51 | } 52 | 53 | public function initTransaction($id, $initValues = null) { 54 | return new Transaction($id, $initValues); 55 | } 56 | 57 | public function importTransaction($message) { 58 | if ($message != "") { 59 | $parser = $this->parserFactory(); 60 | $temp = $parser->parse($message); 61 | 62 | $transaction = Transaction::createNew($this->getId(), $temp['SUPER_TYPE'], $temp['TYPE']); 63 | $transaction->setReceipt($temp['RECEIPT']); 64 | $transaction->setTime($temp["TIME"]); 65 | $transaction->setPhonenumber($temp['PHONE']); 66 | $transaction->setName($temp['NAME']); 67 | $transaction->setAccount($temp['ACCOUNT']); 68 | $transaction->setStatus($temp['STATUS']); 69 | $transaction->setAmount($temp['AMOUNT']); 70 | $transaction->setPostBalance($temp['BALANCE']); 71 | $transaction->setNote($temp['NOTE']); 72 | $transaction->setTransactionCost($temp['COST']); 73 | 74 | $transaction->update(); 75 | 76 | // Callback if needed 77 | $this->handleCallback($transaction); 78 | 79 | return $transaction; 80 | } 81 | return null; 82 | } 83 | 84 | 85 | } 86 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/Configuration.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi; 32 | 33 | class Configuration { 34 | static protected $singleton = null; 35 | 36 | protected $configArray=array( 37 | /************************************************************************* 38 | General configuration options 39 | *************************************************************************/ 40 | // Enable this feature when in production - in order to disable debuginformation 41 | "ProductionMode" => false, 42 | 43 | // Enable this feature when you want to run the API against the simulator 44 | // The simulator does not use SSL and is more easy to get up and running. 45 | "SimulationMode" => false, 46 | 47 | // Database settings follow - please note that they are repeated twice 48 | "DatabaseHostRead" => "localhost", 49 | "DatabaseUserRead" => "DB-USER", 50 | "DatabasePasswordRead" => "DB-PASSWORD", 51 | "DatabaseDatabaseRead" => "DB-DATABASE", 52 | "DatabaseHostWrite" => "localhost", 53 | "DatabaseUserWrite" => "DB-USER", 54 | "DatabasePasswordWrite" => "DB-PASSWORD", 55 | "DatabaseDatabaseWrite" => "DB-DATABASE", 56 | 57 | /************************************************************************* 58 | Payment systems configuration 59 | *************************************************************************/ 60 | "AdminEmail" => "your@email.com" 61 | 62 | 63 | ); 64 | 65 | 66 | ///////////////////////////////////////////// 67 | public function getConfig($argument) { 68 | return $this->configArray[$argument]; 69 | } 70 | 71 | public static function instantiate() { 72 | if (self::$singleton == null) { 73 | self::$singleton = new Configuration(); 74 | } 75 | return self::$singleton; 76 | } 77 | } 78 | 79 | 80 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/TanzaniaMpesaPrivate/ChargeCalculator.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\TanzaniaMpesaPrivate; 32 | 33 | class ChargeCalculator { 34 | 35 | static public function calculateCost($type, $time, $amount) { 36 | switch ($type) { 37 | case Transaction::TZ_MPESA_PRIVATE_PAYMENT_SENT: 38 | return ChargeCalculator::sendingCost($time, $amount); 39 | break; 40 | case Transaction::TZ_MPESA_PRIVATE_WITHDRAW: 41 | return ChargeCalculator::withdrawCost($time, $amount); 42 | break; 43 | case Transaction::TZ_MPESA_PRIVATE_BALANCE_REQUEST: 44 | return 6000; 45 | break; 46 | } 47 | return 0; 48 | } 49 | 50 | static protected function sendingCost($time, $amount) { 51 | if ($amount <= 99900) { 52 | return 1000; 53 | } elseif ($amount <= 299900) { 54 | return 3000; 55 | } elseif ($amount <= 499900) { 56 | return 6000; 57 | } elseif ($amount <= 999900) { 58 | return 10000; 59 | } elseif ($amount <= 1999900) { 60 | return 25000; 61 | } elseif ($amount <= 4999900) { 62 | return 30000; 63 | } elseif ($amount <= 29999900) { 64 | return 60000; 65 | } elseif ($amount <= 49999900) { 66 | return 120000; 67 | } else { 68 | return 180000; 69 | } 70 | } 71 | 72 | static protected function withdrawCost($time, $amount) { 73 | if ($amount <= 299900) { 74 | return 50000; 75 | } elseif ($amount <= 999900) { 76 | return 60000; 77 | } elseif ($amount <= 1999900) { 78 | return 120000; 79 | } elseif ($amount <= 4999900) { 80 | return 150000; 81 | } elseif ($amount <= 9999900) { 82 | return 220000; 83 | } elseif ($amount <= 19999900) { 84 | return 260000; 85 | } elseif ($amount <= 29999900) { 86 | return 420000; 87 | } elseif ($amount <= 39999900) { 88 | return 550000; 89 | } elseif ($amount <= 49999900) { 90 | return 650000; 91 | } else { 92 | return 700000; 93 | } 94 | } 95 | 96 | } -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/KenyaYuPrivate/Account.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\KenyaYuPrivate; 32 | use PLUSPEOPLE\PesaPi\Base\Database; 33 | use PLUSPEOPLE\PesaPi\Base\TransactionFactory; 34 | 35 | class Account extends \PLUSPEOPLE\PesaPi\Base\Account { 36 | 37 | public function getFormatedType() { 38 | return "Kenya - Yu"; 39 | } 40 | 41 | public function availableBalance($time = null) { 42 | $time = (int)$time; 43 | if (0 == $time) { 44 | $time = time(); 45 | } 46 | 47 | $balance = TransactionFactory::factoryOneByTime($this, $time); 48 | if (is_object($balance)) { 49 | return $balance->getPostBalance(); 50 | } 51 | return $amount; 52 | } 53 | 54 | public function locateByReceipt($receipt) { 55 | return TransactionFactory::factoryByReceipt($this, $receipt); 56 | } 57 | 58 | public function initTransaction($id, $initValues = null) { 59 | return new Transaction($id, $initValues); 60 | } 61 | 62 | public function importTransaction($message) { 63 | if ($message != "") { 64 | $parser = new Parser(); 65 | $temp = $parser->parse($message); 66 | 67 | $transaction = Transaction::createNew($this->getId(), $temp['SUPER_TYPE'], $temp['TYPE']); 68 | $transaction->setReceipt($temp['RECEIPT']); 69 | $transaction->setTime($temp["TIME"]); 70 | $transaction->setPhonenumber($temp['PHONE']); 71 | $transaction->setName($temp['NAME']); 72 | $transaction->setAccount($temp['ACCOUNT']); 73 | $transaction->setStatus($temp['STATUS']); 74 | $transaction->setAmount($temp['AMOUNT']); 75 | $transaction->setPostBalance($temp['BALANCE']); 76 | $transaction->setNote($temp['NOTE']); 77 | $transaction->setTransactionCost($temp['COSTS']); 78 | 79 | $transaction->update(); 80 | 81 | // Callback if needed 82 | $this->handleCallback($transaction); 83 | 84 | return $transaction; 85 | } 86 | return null; 87 | } 88 | 89 | } 90 | 91 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/RwandaMTNPrivate/Account.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\RwandaMTNPrivate; 32 | use PLUSPEOPLE\PesaPi\Base\Database; 33 | use PLUSPEOPLE\PesaPi\Base\TransactionFactory; 34 | 35 | class Account extends \PLUSPEOPLE\PesaPi\Base\Account { 36 | public function getFormatedType() { 37 | return "Rwanda - MTN money"; 38 | } 39 | 40 | public function availableBalance($time = null) { 41 | $time = (int)$time; 42 | if (0 == $time) { 43 | $time = time(); 44 | } 45 | 46 | $balance = TransactionFactory::factoryOneByTime($this, $time); 47 | if (is_object($balance)) { 48 | return $balance->getPostBalance(); 49 | } 50 | return $amount; 51 | } 52 | 53 | public function locateByReceipt($receipt) { 54 | return TransactionFactory::factoryByReceipt($this, $receipt); 55 | } 56 | 57 | public function initTransaction($id, $initValues = null) { 58 | return new Transaction($id, $initValues); 59 | } 60 | 61 | public function importTransaction($message) { 62 | if ($message != "") { 63 | $parser = new Parser(); 64 | $temp = $parser->parse($message); 65 | 66 | $transaction = Transaction::createNew($this->getId(), $temp['SUPER_TYPE'], $temp['TYPE']); 67 | $transaction->setReceipt($temp['RECEIPT']); 68 | $transaction->setTime($temp["TIME"]); 69 | $transaction->setPhonenumber($temp['PHONE']); 70 | $transaction->setName($temp['NAME']); 71 | $transaction->setAccount($temp['ACCOUNT']); 72 | $transaction->setStatus($temp['STATUS']); 73 | $transaction->setAmount($temp['AMOUNT']); 74 | $transaction->setPostBalance($temp['BALANCE']); 75 | $transaction->setNote($temp['NOTE']); 76 | $transaction->setTransactionCost($temp['COSTS']); 77 | 78 | $transaction->update(); 79 | 80 | // Callback if needed 81 | $this->handleCallback($transaction); 82 | 83 | return $transaction; 84 | } 85 | return null; 86 | } 87 | 88 | } 89 | 90 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/RwandaMTNPrivate/Parser.php: -------------------------------------------------------------------------------- 1 | 30 | Thanks to Josh Handley for supplying information about MTN in Rwanda 31 | */ 32 | namespace PLUSPEOPLE\PesaPi\RwandaMTNPrivate; 33 | use \PLUSPEOPLE\PesaPi\Base\Utility; 34 | use \PLUSPEOPLE\PesaPi\Base\Transaction; 35 | 36 | 37 | // I NEED MORE EXAMPLE SMS'S FROM RWANDA TO COMPLETE THIS!!! 38 | class Parser { 39 | const PAYMENT_RECEIVED = 200; 40 | const UNKNOWN = 210; 41 | 42 | public function dateInput($time) { 43 | $dt = \DateTime::createFromFormat("j/n/y h:i A", $time); 44 | return $dt->getTimestamp(); 45 | } 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, "You have received ") !== FALSE) { 62 | $result["SUPER_TYPE"] = Transaction::MONEY_IN; 63 | $result["TYPE"] = Parser::PAYMENT_RECEIVED; 64 | 65 | $temp = array(); 66 | preg_match_all("/You have received Rwf\s+([0-9\.\,]+)\s+from\s+([0-9]+)\.\r?\nReason:\s+([^\r\n])\.\r?\nYour\s+balance\s+is\s+Rwf\s+([0-9\.\,]+)\./mi", $input, $temp); 67 | if (isset($temp[1][0])) { 68 | $result["RECEIPT"] = ""; // NOT GOOD - NO RECEIPT NUMBER 69 | $result["AMOUNT"] = Utility::numberInput($temp[1][0]); 70 | $result["NAME"] = ""; 71 | $result["ACCOUNT"] = $temp[3][0]; 72 | $result["PHONE"] = $temp[2][0]; 73 | $result["TIME"] = time(); 74 | $result["BALANCE"] = Utility::numberInput($temp[4][0]); 75 | } 76 | 77 | } else { 78 | $result["SUPER_TYPE"] = Transaction::MONEY_NEUTRAL; 79 | $result["TYPE"] = PersonalParser::UNKNOWN; 80 | } 81 | 82 | return $result; 83 | } 84 | 85 | } 86 | 87 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/UgandaMTNPrivate/Account.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\UgandaMTNPrivate; 32 | use PLUSPEOPLE\PesaPi\Base\Database; 33 | use PLUSPEOPLE\PesaPi\Base\TransactionFactory; 34 | 35 | class Account extends \PLUSPEOPLE\PesaPi\Base\Account { 36 | public function getFormatedType() { 37 | return "Uganda - MTN"; 38 | } 39 | 40 | public function availableBalance($time = null) { 41 | $time = (int)$time; 42 | if (0 == $time) { 43 | $time = time(); 44 | } 45 | 46 | $balance = \PLUSPEOPLE\PesaPi\Base\TransactionFactory::factoryOneByTime($this, $time); 47 | if (is_object($balance)) { 48 | return $balance->getPostBalance(); 49 | } 50 | return 0; 51 | } 52 | 53 | public function locateByReceipt($receipt) { 54 | return TransactionFactory::factoryByReceipt($this, $receipt); 55 | } 56 | 57 | public function initTransaction($id, $initValues = null) { 58 | return new Transaction($id, $initValues); 59 | } 60 | 61 | public function importTransaction($message) { 62 | if ($message != "") { 63 | $parser = new Parser(); 64 | $temp = $parser->parse($message); 65 | 66 | $transaction = Transaction::createNew($this->getId(), $temp['SUPER_TYPE'], $temp['TYPE']); 67 | $transaction->setReceipt($temp['RECEIPT']); 68 | $transaction->setTime($temp["TIME"]); 69 | $transaction->setPhonenumber($temp['PHONE']); 70 | $transaction->setName($temp['NAME']); 71 | $transaction->setAccount($temp['ACCOUNT']); 72 | $transaction->setStatus($temp['STATUS']); 73 | $transaction->setAmount($temp['AMOUNT']); 74 | $transaction->setPostBalance($temp['BALANCE']); 75 | $transaction->setNote($temp['NOTE']); 76 | $transaction->setTransactionCost($temp['COST']); 77 | 78 | $transaction->update(); 79 | 80 | // Callback if needed 81 | $this->handleCallback($transaction); 82 | 83 | return $transaction; 84 | } 85 | return null; 86 | } 87 | 88 | } 89 | 90 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/GhanaAirtelPrivate/Account.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\GhanaAirtelPrivate; 32 | use PLUSPEOPLE\PesaPi\Base\Database; 33 | use PLUSPEOPLE\PesaPi\Base\TransactionFactory; 34 | 35 | class Account extends \PLUSPEOPLE\PesaPi\Base\Account { 36 | 37 | public function getFormatedType() { 38 | return "Ghana - Airtel money"; 39 | } 40 | 41 | public function availableBalance($time = null) { 42 | $time = (int)$time; 43 | if (0 == $time) { 44 | $time = time(); 45 | } 46 | 47 | $balance = TransactionFactory::factoryOneByTime($this, $time); 48 | if (is_object($balance)) { 49 | return $balance->getPostBalance(); 50 | } 51 | return $amount; 52 | } 53 | 54 | public function locateByReceipt($receipt) { 55 | return TransactionFactory::factoryByReceipt($this, $receipt); 56 | } 57 | 58 | public function initTransaction($id, $initValues = null) { 59 | return new Transaction($id, $initValues); 60 | } 61 | 62 | public function importTransaction($message) { 63 | if ($message != "") { 64 | $parser = new Parser(); 65 | $temp = $parser->parse($message); 66 | 67 | $transaction = Transaction::createNew($this->getId(), $temp['SUPER_TYPE'], $temp['TYPE']); 68 | $transaction->setReceipt($temp['RECEIPT']); 69 | $transaction->setTime($temp["TIME"]); 70 | $transaction->setPhonenumber($temp['PHONE']); 71 | $transaction->setName($temp['NAME']); 72 | $transaction->setAccount($temp['ACCOUNT']); 73 | $transaction->setStatus($temp['STATUS']); 74 | $transaction->setAmount($temp['AMOUNT']); 75 | $transaction->setPostBalance($temp['BALANCE']); 76 | $transaction->setNote($temp['NOTE']); 77 | $transaction->setTransactionCost($temp['COST']); 78 | 79 | $transaction->update(); 80 | 81 | // Callback if needed 82 | $this->handleCallback($transaction); 83 | 84 | return $transaction; 85 | } 86 | return null; 87 | } 88 | 89 | } 90 | 91 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/KenyaAirtelPrivate/Account.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\KenyaAirtelPrivate; 32 | use PLUSPEOPLE\PesaPi\Base\Database; 33 | use PLUSPEOPLE\PesaPi\Base\TransactionFactory; 34 | 35 | class Account extends \PLUSPEOPLE\PesaPi\Base\Account { 36 | public function getFormatedType() { 37 | return "Kenya - Airtel"; 38 | } 39 | 40 | public function availableBalance($time = null) { 41 | $time = (int)$time; 42 | if (0 == $time) { 43 | $time = time(); 44 | } 45 | 46 | $balance = \PLUSPEOPLE\PesaPi\Base\TransactionFactory::factoryOneByTime($this, $time); 47 | if (is_object($balance)) { 48 | return $balance->getPostBalance(); 49 | } 50 | return $amount; 51 | } 52 | 53 | public function locateByReceipt($receipt) { 54 | return TransactionFactory::factoryByReceipt($this, $receipt); 55 | } 56 | 57 | public function initTransaction($id, $initValues = null) { 58 | return new Transaction($id, $initValues); 59 | } 60 | 61 | public function importTransaction($message) { 62 | if ($message != "") { 63 | $parser = new Parser(); 64 | $temp = $parser->parse($message); 65 | 66 | $transaction = Transaction::createNew($this->getId(), $temp['SUPER_TYPE'], $temp['TYPE']); 67 | $transaction->setReceipt($temp['RECEIPT']); 68 | $transaction->setTime($temp["TIME"]); 69 | $transaction->setPhonenumber($temp['PHONE']); 70 | $transaction->setName($temp['NAME']); 71 | $transaction->setAccount($temp['ACCOUNT']); 72 | $transaction->setStatus($temp['STATUS']); 73 | $transaction->setAmount($temp['AMOUNT']); 74 | $transaction->setPostBalance($temp['BALANCE']); 75 | $transaction->setNote($temp['NOTE']); 76 | $transaction->setTransactionCost($temp['COST']); 77 | 78 | $transaction->update(); 79 | 80 | // Callback if needed 81 | $this->handleCallback($transaction); 82 | 83 | return $transaction; 84 | } 85 | return null; 86 | } 87 | 88 | } 89 | 90 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/TanzaniaTigoPrivate/Account.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\TanzaniaTigoPrivate; 32 | use PLUSPEOPLE\PesaPi\Base\Database; 33 | use PLUSPEOPLE\PesaPi\Base\TransactionFactory; 34 | 35 | class Account extends \PLUSPEOPLE\PesaPi\Base\Account { 36 | public function getFormatedType() { 37 | return "Tanzania - TIGO"; 38 | } 39 | 40 | public function availableBalance($time = null) { 41 | $time = (int)$time; 42 | if (0 == $time) { 43 | $time = time(); 44 | } 45 | 46 | $balance = \PLUSPEOPLE\PesaPi\Base\TransactionFactory::factoryOneByTime($this, $time); 47 | if (is_object($balance)) { 48 | return $balance->getPostBalance(); 49 | } 50 | return $amount; 51 | } 52 | 53 | public function locateByReceipt($receipt) { 54 | return TransactionFactory::factoryByReceipt($this, $receipt); 55 | } 56 | 57 | public function initTransaction($id, $initValues = null) { 58 | return new Transaction($id, $initValues); 59 | } 60 | 61 | public function importTransaction($message) { 62 | if ($message != "") { 63 | $parser = new Parser(); 64 | $temp = $parser->parse($message); 65 | 66 | $transaction = Transaction::createNew($this->getId(), $temp['SUPER_TYPE'], $temp['TYPE']); 67 | $transaction->setReceipt($temp['RECEIPT']); 68 | $transaction->setTime($temp["TIME"]); 69 | $transaction->setPhonenumber($temp['PHONE']); 70 | $transaction->setName($temp['NAME']); 71 | $transaction->setAccount($temp['ACCOUNT']); 72 | $transaction->setStatus($temp['STATUS']); 73 | $transaction->setAmount($temp['AMOUNT']); 74 | $transaction->setPostBalance($temp['BALANCE']); 75 | $transaction->setNote($temp['NOTE']); 76 | $transaction->setTransactionCost($temp['COST']); 77 | 78 | $transaction->update(); 79 | 80 | // Callback if needed 81 | $this->handleCallback($transaction); 82 | 83 | return $transaction; 84 | } 85 | return null; 86 | } 87 | 88 | } 89 | 90 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/MpesaPrivate/MpesaPrivate.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\MpesaPrivate; 32 | use PLUSPEOPLE\PesaPi\Base\Database; 33 | use PLUSPEOPLE\PesaPi\Base\TransactionFactory; 34 | 35 | class MpesaPrivate extends \PLUSPEOPLE\PesaPi\Base\Account { 36 | public function getFormatedType() { 37 | return "Kenya - MPESA Private"; 38 | } 39 | 40 | public function availableBalance($time = null) { 41 | $time = (int)$time; 42 | if (0 == $time) { 43 | $time = time(); 44 | } 45 | 46 | $balance = \PLUSPEOPLE\PesaPi\Base\TransactionFactory::factoryOneByTime($this, $time); 47 | if (is_object($balance)) { 48 | return $balance->getPostBalance(); 49 | } 50 | return $amount; 51 | } 52 | 53 | public function locateByReceipt($receipt) { 54 | return TransactionFactory::factoryByReceipt($this, $receipt); 55 | } 56 | 57 | public function initTransaction($id, $initValues = null) { 58 | return new Transaction($id, $initValues); 59 | } 60 | 61 | public function importTransaction($message) { 62 | if ($message != "") { 63 | $parser = new PersonalParser(); 64 | $temp = $parser->parse($message); 65 | 66 | $transaction = Transaction::createNew($this->getId(), $temp['SUPER_TYPE'], $temp['TYPE']); 67 | $transaction->setReceipt($temp['RECEIPT']); 68 | $transaction->setTime($temp["TIME"]); 69 | $transaction->setPhonenumber($temp['PHONE']); 70 | $transaction->setName($temp['NAME']); 71 | $transaction->setAccount($temp['ACCOUNT']); 72 | $transaction->setStatus($temp['STATUS']); 73 | $transaction->setAmount($temp['AMOUNT']); 74 | $transaction->setPostBalance($temp['BALANCE']); 75 | $transaction->setNote($temp['NOTE']); 76 | $transaction->setTransactionCost($temp['COST']); 77 | 78 | $transaction->update(); 79 | 80 | // Callback if needed 81 | $this->handleCallback($transaction); 82 | 83 | return $transaction; 84 | } 85 | return null; 86 | } 87 | 88 | } 89 | 90 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/TanzaniaMpesaPrivate/Account.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\TanzaniaMpesaPrivate; 32 | use PLUSPEOPLE\PesaPi\Base\Database; 33 | use PLUSPEOPLE\PesaPi\Base\TransactionFactory; 34 | 35 | class Account extends \PLUSPEOPLE\PesaPi\Base\Account { 36 | public function getFormatedType() { 37 | return "Tanzania - MPESA Private"; 38 | } 39 | 40 | public function availableBalance($time = null) { 41 | $time = (int)$time; 42 | if (0 == $time) { 43 | $time = time(); 44 | } 45 | 46 | $balance = \PLUSPEOPLE\PesaPi\Base\TransactionFactory::factoryOneByTime($this, $time); 47 | if (is_object($balance)) { 48 | return $balance->getPostBalance(); 49 | } 50 | return $amount; 51 | } 52 | 53 | public function locateByReceipt($receipt) { 54 | return TransactionFactory::factoryByReceipt($this, $receipt); 55 | } 56 | 57 | public function initTransaction($id, $initValues = null) { 58 | return new Transaction($id, $initValues); 59 | } 60 | 61 | public function importTransaction($message) { 62 | if ($message != "") { 63 | $parser = new Parser(); 64 | $temp = $parser->parse($message); 65 | 66 | $transaction = Transaction::createNew($this->getId(), $temp['SUPER_TYPE'], $temp['TYPE']); 67 | $transaction->setReceipt($temp['RECEIPT']); 68 | $transaction->setTime($temp["TIME"]); 69 | $transaction->setPhonenumber($temp['PHONE']); 70 | $transaction->setName($temp['NAME']); 71 | $transaction->setAccount($temp['ACCOUNT']); 72 | $transaction->setStatus($temp['STATUS']); 73 | $transaction->setAmount($temp['AMOUNT']); 74 | $transaction->setPostBalance($temp['BALANCE']); 75 | $transaction->setNote($temp['NOTE']); 76 | $transaction->setTransactionCost($temp['COST']); 77 | 78 | $transaction->update(); 79 | 80 | // Callback if needed 81 | $this->handleCallback($transaction); 82 | 83 | return $transaction; 84 | } 85 | return null; 86 | } 87 | 88 | } 89 | 90 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/SomaliaHormuudPrivate/Account.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\SomaliaHormuudPrivate; 32 | use PLUSPEOPLE\PesaPi\Base\Database; 33 | use PLUSPEOPLE\PesaPi\Base\TransactionFactory; 34 | 35 | class Account extends \PLUSPEOPLE\PesaPi\Base\Account { 36 | public function getFormatedType() { 37 | return "Somalia - Hormuud EVC Private"; 38 | } 39 | 40 | public function availableBalance($time = null) { 41 | $time = (int)$time; 42 | if (0 == $time) { 43 | $time = time(); 44 | } 45 | 46 | $balance = \PLUSPEOPLE\PesaPi\Base\TransactionFactory::factoryOneByTime($this, $time); 47 | if (is_object($balance)) { 48 | return $balance->getPostBalance(); 49 | } 50 | return $amount; 51 | } 52 | 53 | public function locateByReceipt($receipt) { 54 | return TransactionFactory::factoryByReceipt($this, $receipt); 55 | } 56 | 57 | public function initTransaction($id, $initValues = null) { 58 | return new Transaction($id, $initValues); 59 | } 60 | 61 | public function importTransaction($message) { 62 | if ($message != "") { 63 | $parser = new Parser(); 64 | $temp = $parser->parse($message); 65 | 66 | $transaction = Transaction::createNew($this->getId(), $temp['SUPER_TYPE'], $temp['TYPE']); 67 | $transaction->setReceipt($temp['RECEIPT']); 68 | $transaction->setTime($temp["TIME"]); 69 | $transaction->setPhonenumber($temp['PHONE']); 70 | $transaction->setName($temp['NAME']); 71 | $transaction->setAccount($temp['ACCOUNT']); 72 | $transaction->setStatus($temp['STATUS']); 73 | $transaction->setAmount($temp['AMOUNT']); 74 | $transaction->setPostBalance($temp['BALANCE']); 75 | $transaction->setNote($temp['NOTE']); 76 | $transaction->setTransactionCost($temp['COST']); 77 | 78 | $transaction->update(); 79 | 80 | // Callback if needed 81 | $this->handleCallback($transaction); 82 | 83 | return $transaction; 84 | } 85 | return null; 86 | } 87 | 88 | } 89 | 90 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/SomaliaTelesomePrivate/Account.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\SomaliaTelesomePrivate; 32 | use PLUSPEOPLE\PesaPi\Base\Database; 33 | use PLUSPEOPLE\PesaPi\Base\TransactionFactory; 34 | 35 | class Account extends \PLUSPEOPLE\PesaPi\Base\Account { 36 | public function getFormatedType() { 37 | return "Somalia - Telesome ZAAD Private"; 38 | } 39 | 40 | public function availableBalance($time = null) { 41 | $time = (int)$time; 42 | if (0 == $time) { 43 | $time = time(); 44 | } 45 | 46 | $balance = \PLUSPEOPLE\PesaPi\Base\TransactionFactory::factoryOneByTime($this, $time); 47 | if (is_object($balance)) { 48 | return $balance->getPostBalance(); 49 | } 50 | return $amount; 51 | } 52 | 53 | public function locateByReceipt($receipt) { 54 | return TransactionFactory::factoryByReceipt($this, $receipt); 55 | } 56 | 57 | public function initTransaction($id, $initValues = null) { 58 | return new Transaction($id, $initValues); 59 | } 60 | 61 | public function importTransaction($message) { 62 | if ($message != "") { 63 | $parser = new Parser(); 64 | $temp = $parser->parse($message); 65 | 66 | $transaction = Transaction::createNew($this->getId(), $temp['SUPER_TYPE'], $temp['TYPE']); 67 | $transaction->setReceipt($temp['RECEIPT']); 68 | $transaction->setTime($temp["TIME"]); 69 | $transaction->setPhonenumber($temp['PHONE']); 70 | $transaction->setName($temp['NAME']); 71 | $transaction->setAccount($temp['ACCOUNT']); 72 | $transaction->setStatus($temp['STATUS']); 73 | $transaction->setAmount($temp['AMOUNT']); 74 | $transaction->setPostBalance($temp['BALANCE']); 75 | $transaction->setNote($temp['NOTE']); 76 | $transaction->setTransactionCost($temp['COST']); 77 | 78 | $transaction->update(); 79 | 80 | // Callback if needed 81 | $this->handleCallback($transaction); 82 | 83 | return $transaction; 84 | } 85 | return null; 86 | } 87 | 88 | } 89 | 90 | ?> -------------------------------------------------------------------------------- /simulator/webroot/admin/index.php: -------------------------------------------------------------------------------- 1 | setTemplateFile('index.tpl'); 39 | session_start(); 40 | 41 | ////////////////////////////////////////////////////////////////////////////// 42 | // handle the submission 43 | if ($_SERVER["REQUEST_METHOD"] == 'POST') { 44 | if (isset($_POST["ok"])) { 45 | $reciept = "BCXY" . rand(1000, 9999); // need to be more random 46 | $payment = Payment::createNew($reciept, Payment::TYPE_PAYMENT_RECIEVED); 47 | if (is_object($payment)) { 48 | $amount = numberInput($_POST["amount"]); 49 | $payment->setTime(dateInput($_POST["day"] . " " . $_POST["time"])); 50 | $payment->setPhonenumber($_POST["phone"]); 51 | $payment->setName($_POST["name"]); 52 | $payment->setAccount(rand(1000,9999)); 53 | $payment->setStatus(Payment::STATUS_COMPLETED); 54 | $payment->setAmount($amount); 55 | $payment->setPostBalance($amount); 56 | $payment->update(); 57 | 58 | print "Payment created"; 59 | } 60 | } 61 | } 62 | 63 | ////////////////////////////////////////////////////////////////////////////// 64 | // display the page 65 | $slow->assign(array("DAY" => date("d-m-Y"), 66 | "TIME" => date("H:s") 67 | )); 68 | 69 | 70 | $slow->parse(); 71 | $slow->slowPrint(); 72 | 73 | function numberInput($input) { 74 | $input = trim($input); 75 | $amount = 0; 76 | 77 | if (preg_match("/^[0-9,]+$/", $input)) { 78 | $amount = 100 * (int)str_replace(',', '', $input); 79 | } elseif (preg_match("/^[0-9,]+\.[0-9]$/", $input)) { 80 | $amount = 10 * (int)str_replace(array('.', ','), '', $input); 81 | } elseif (preg_match("/^[0-9,]*\.[0-9][0-9]$/", $input)) { 82 | $amount = (int)str_replace(array('.', ','), '', $input); 83 | } else { 84 | $amount = (int)$input; 85 | } 86 | return $amount; 87 | } 88 | function dateInput($input) { 89 | $timeStamp = strtotime($input); 90 | if ($timeStamp != FALSE) { 91 | return $timeStamp; 92 | } 93 | return 0; 94 | } 95 | 96 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/UgandaMTNPrivate/Parser.php: -------------------------------------------------------------------------------- 1 | 30 | Based on examples provided by Humphrey William 31 | */ 32 | namespace PLUSPEOPLE\PesaPi\UgandaMTNPrivate; 33 | use \PLUSPEOPLE\PesaPi\Base\Utility; 34 | 35 | class Parser { 36 | public function dateInput($time) { 37 | $dt = \DateTime::createFromFormat("Y-m-d H:i:s", $time); 38 | return $dt->getTimestamp(); 39 | } 40 | 41 | public function parse($input) { 42 | $result = array("SUPER_TYPE" => 0, 43 | "TYPE" => 0, 44 | "RECEIPT" => "", 45 | "TIME" => 0, 46 | "PHONE" => "", 47 | "NAME" => "", 48 | "ACCOUNT" => "", 49 | "STATUS" => "", 50 | "AMOUNT" => 0, 51 | "BALANCE" => 0, 52 | "NOTE" => "", 53 | "COST" => 0); 54 | 55 | 56 | /* 57 | $input = "Y'ello. You have received 58 | UGX 1000000.00 from 59 | ABACUS 60 | INVESTMENTS LIMITED 61 | COMMISSION 62 | (256774656827) on your 63 | mobile money account at 64 | 2015-05-10 65 | 13:35:12.Your new 66 | balance:UGX 67 | 1068816.00. 68 | "; 69 | */ 70 | 71 | // REFACTOR: should be split into subclasses 72 | if (strpos($input, "You have received") !== FALSE) { 73 | $result["SUPER_TYPE"] = Transaction::MONEY_IN; 74 | 75 | $temp = array(); 76 | preg_match_all("/You have received[\s\n]+UGX ([0-9\.\,]+) from[\s\n]+([^\(]+)[\s\n]+\(([0-9]+)\) on your[\s\n]+mobile money account at[\s\n]+(\d{4}-\d\d-\d\d?)[\s\n]+(\d\d?:\d\d:\d\d)\.Your new[\s\n]+balance:UGX[\s\n]+([0-9\.\,]+)\./mi", $input, $temp); 77 | if (isset($temp[1][0])) { 78 | $result["TYPE"] = Transaction::UG_MTN_PRIVATE_PAYMENT_RECEIVED; 79 | 80 | $result["AMOUNT"] = Utility::numberInput($temp[1][0]); 81 | $result["NAME"] = $temp[2][0]; 82 | $result["PHONE"] = $temp[3][0]; 83 | $result["TIME"] = $this->dateInput($temp[4][0] . " " . $temp[5][0]); 84 | $result["BALANCE"] = Utility::numberInput($temp[6][0]); 85 | $result["RECEIPT"] = $result["TIME"]; // since no unique code is given we will use the timestamp 86 | } 87 | 88 | 89 | } else { 90 | $result["SUPER_TYPE"] = Transaction::MONEY_NEUTRAL; 91 | $result["TYPE"] = Transaction::UG_MTN_PRIVATE_UNKOWN; 92 | } 93 | 94 | return $result; 95 | } 96 | 97 | } 98 | 99 | ?> -------------------------------------------------------------------------------- /php/include/PLUSPEOPLE/PesaPi/KenyaAirtelPaybill/Parser.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | namespace PLUSPEOPLE\PesaPi\KenyaAirtelPaybill; 32 | 33 | class Parser { 34 | public function scrubTransactions($rawtext) { 35 | $result = array(); 36 | $temp = array(); 37 | 38 | preg_match_all('/Source Info<\/td>[\s\n\r]*<\/tr>(.+)[\s\n\r]*<\/td>/msi', $rawtext, $temp); 39 | if (isset($temp[1][0])) { 40 | $rows = array_reverse(preg_split('/<\/tr>[\s\n\r]*/', $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 |
8 | 9 | 10 |
11 |
12 | 13 |
14 | 15 |
16 |
17 | 19 |
20 |

Welcome

21 |

22 | 23 |

Welcome to the M-PESA Administration Website

24 | 25 | 26 | 27 | 74 | 75 |
28 | 29 | 30 | 32 | 36 | 37 | 38 | 40 | 44 | 45 | 46 | 48 | 52 | 53 | 54 | 55 | 58 | 59 | 60 | 61 | 64 | 65 | 66 | 67 | 70 | 71 |
31 | : 33 | 34 | 35 |
39 | : 41 | 42 |    43 |
47 | : 49 | 50 |   51 |  
  56 | 57 |
  62 | 63 |
  68 | 69 |
72 |
73 |
76 | 77 | 78 | 79 | 82 | 83 |
80 |
Proper use of this site requires popups to be enabled, and therefore pop up blockers should be switched off.  You should switch off both the Explorer SP2 popup blocker and also any toolbar popup blockers you have installed (eg Google toolbar).
The access and use of this website remains subject to the policies and guidelines of acceptable IT resource usage as may be published by Safaricom from time to time
81 |
84 | 85 |
86 |
87 | 98 |
99 | 100 | --------------------------------------------------------------------------------