File manager - Edit - /home/u816558632/domains/postills.com/public_html/public/knox.tar
Back
pesapal/index.php 0000644 00000000000 15002202657 0007776 0 ustar 00 pesapal/.gitignore 0000644 00000000131 15002202657 0010153 0 ustar 00 # Created by .ignore support plugin (hsz.mobi) composer.lock composer.phar vendor/ .idea/ pesapal/composer.json 0000644 00000002033 15002202657 0010710 0 ustar 00 { "name": "mudassar1/pesapal", "description": "A laravel package that integrates into the pesapal api", "keywords": [ "pesapal", "laravel", "laravel 5", "laravel 6", "laravel 7", "laravel 8", "laravel 9", "MPESA", "payments", "VISA", "Mastercard", "Orange Money" ], "homepage": "https://github.com/knox2/pesapal", "license": "MIT", "authors": [ { "name": "Timothy Radier", "email": "timothyradier@gmail.com", "role": "Developer" } ], "minimum-stability": "stable", "require": { "php": ">=7|^8.0|^8.1|^8.2", "illuminate/support": "^5.5|^6.0|^7.0|^8.0|^9.0|^10.0" }, "autoload": { "psr-4": { "Knox\\Pesapal\\": "src/" } }, "scripts": { "post-update-cmd": [ "php artisan vendor:publish --tag=pesapal --force" ] }, "extra": { "laravel": { "providers": [ "Knox\\Pesapal\\PesapalServiceProvider" ], "aliases": { "Pesapal": "Knox\\Pesapal\\Facades\\Pesapal" } } } } pesapal/README.md 0000644 00000012627 15002202657 0007457 0 ustar 00 # Pesapal Laravel 5,6,7,8,9,10 API Laravel 5,6,7,8,9,10 Package for the Pesapal API ## Installation ### Add this package using Composer From the command line inside your project directory, simply type: `composer require knox/pesapal` ### Update your config (for Laravel 5.4 and below) Add the service provider to the providers array in config/app.php: `Knox\Pesapal\PesapalServiceProvider::class,` Add the facade to the aliases array in config/app.php: `'Pesapal' => Knox\Pesapal\Facades\Pesapal::class,` ### Publish the package configuration (for Laravel 5.4 and below) Publish the configuration file and migrations by running the provided console command: `php artisan vendor:publish --provider="Knox\Pesapal\PesapalServiceProvider"` ## Setup ### Pesapal IPN For the url of the route use /pesapal-ipn eg mysite.com/pesapal-ipn as the IPN on the Pesapal Merchant settings dashboard ### Environmental Variables PESAPAL\_CONSUMER\_KEY `pesapal consumer key`<br/> PESAPAL\_CONSUMER\_SECRET `pesapal consumer secret`<br/> PESAPAL\_CURRENCY `ISO code for the currency`<br/> PESAPAL\_IPN `controller method to call for instant notifications IPN as relative path from App\Http\Controllers\ eg "TransactionController@confirmation"`<br/> PESAPAL\_CALLBACK_ROUTE `route name to handle the callback eg Route::get('donepayment', ['as' => 'paymentsuccess', 'uses'=>'PaymentsController@paymentsuccess']); The route name is "paymentsuccess"`<br/> <b>NB: The controller method accepts 4 function parameters, Example:</b> ```php public function confirmation($trackingid,$status,$payment_method,$merchant_reference) { $payments = Payments::where('tracking',$trackingid)->first(); $payments -> payment_status = $status; $payments -> payment_method = $payment_method; $payments -> save(); } ``` ### Config <b>live</b> - Live or Demo environment<br/> The ENV Variables can also be set from here. ## Usage At the top of your controller include the facade<br/> `use Pesapal;` ### Example Code...Better Example..Haha Assuming you have a Payment Model <br/> ```php use Pesapal; use Illuminate\Http\Request; use App\Http\Requests; use Illuminate\Support\Facades\Auth; use App\Payment; class PaymentsController extends Controller { public function payment(){//initiates payment $payments = new Payment; $payments -> businessid = Auth::guard('business')->id(); //Business ID $payments -> transactionid = Pesapal::random_reference(); $payments -> status = 'NEW'; //if user gets to iframe then exits, i prefer to have that as a new/lost transaction, not pending $payments -> amount = 10; $payments -> save(); $details = array( 'amount' => $payments -> amount, 'description' => 'Test Transaction', 'type' => 'MERCHANT', 'first_name' => 'Fname', 'last_name' => 'Lname', 'email' => 'test@test.com', 'phonenumber' => '254-723232323', 'reference' => $payments -> transactionid, 'height'=>'400px', //'currency' => 'USD' ); $iframe=Pesapal::makePayment($details); return view('payments.business.pesapal', compact('iframe')); } public function paymentsuccess(Request $request)//just tells u payment has gone thru..but not confirmed { $trackingid = $request->input('tracking_id'); $ref = $request->input('merchant_reference'); $payments = Payment::where('transactionid',$ref)->first(); $payments -> trackingid = $trackingid; $payments -> status = 'PENDING'; $payments -> save(); //go back home $payments=Payment::all(); return view('payments.business.home', compact('payments')); } //This method just tells u that there is a change in pesapal for your transaction.. //u need to now query status..retrieve the change...CANCELLED? CONFIRMED? public function paymentconfirmation(Request $request) { $trackingid = $request->input('pesapal_transaction_tracking_id'); $merchant_reference = $request->input('pesapal_merchant_reference'); $pesapal_notification_type= $request->input('pesapal_notification_type'); //use the above to retrieve payment status now.. $this->checkpaymentstatus($trackingid,$merchant_reference,$pesapal_notification_type); } //Confirm status of transaction and update the DB public function checkpaymentstatus($trackingid,$merchant_reference,$pesapal_notification_type){ $status=Pesapal::getMerchantStatus($merchant_reference); $payments = Payment::where('trackingid',$trackingid)->first(); $payments -> status = $status; $payments -> payment_method = "PESAPAL";//use the actual method though... $payments -> save(); return "success"; } } ``` #### Example ENV ``` PESAPAL_IPN=PaymentsController@paymentconfirmation PESAPAL_LIVE=true PESAPAL_CALLBACK_ROUTE=paymentsuccess ``` #### Example View ``` {!! $iframe !!} ``` #### Example Routes Relevant routes example, to help exclude entire webhooks route group in Csrf check in VerifyCsrfToken Middleware<br/> ```php Route::group(['prefix' => '/webhooks'], function () { //PESAPAL Route::get('donepayment', ['as' => 'paymentsuccess', 'uses'=>'PaymentsController@paymentsuccess']); Route::get('paymentconfirmation', 'PaymentsController@paymentconfirmation'); }); ``` #### All Done Feel free to report any issues pesapal/src/Exceptions/PesapalException.php 0000644 00000000313 15002202657 0015052 0 ustar 00 <?php namespace Knox\Pesapal\Exceptions; use Exception; class PesapalException extends Exception { public function __construct($message) { parent::__construct($message); } } pesapal/src/Contracts/PesapalContract.php 0000644 00000000763 15002202657 0014521 0 ustar 00 <?php /** * Created by PhpStorm. * User: mxgel * Date: 11/15/16 * Time: 4:47 AM */ namespace Knox\Pesapal\Contracts; /** * Interface PesapalContract * @package Knox\Pesapal\Contracts */ interface PesapalContract { /** * Pending payment */ const PESAPAL_STATUS_PENDING = 'pending'; /** * Failed payment */ const PESAPAL_STATUS_FAILED = 'failed'; /** * Successfully completed payment */ const PESAPAL_STATUS_COMPLETED = 'completed'; } pesapal/src/PesapalServiceProvider.php 0000644 00000001464 15002202657 0014116 0 ustar 00 <?php namespace Knox\Pesapal; use Illuminate\Support\ServiceProvider; class PesapalServiceProvider extends ServiceProvider { /** * Bootstrap the application services. * * @return void */ public function boot() { // Publish config $configPath = __DIR__ . '/config/pesapal.php'; $this->publishes([$configPath => config_path('pesapal.php')], 'config'); /* * replace the register include with the below */ //$this->loadRoutesFrom(__DIR__.'/routes.php'); } /** * Register the application services. * * @return void */ public function register() { include __DIR__ . '/routes.php'; $this->app->singleton('pesapal',function () { return new Pesapal; }); } } pesapal/src/Pesapal.php 0000644 00000024061 15002202657 0011060 0 ustar 00 <?php namespace Knox\Pesapal; use Knox\Pesapal\Contracts\PesapalContract; use Knox\Pesapal\Exceptions\PesapalException; use Knox\Pesapal\OAuth\OAuthConsumer; use Knox\Pesapal\OAuth\OAuthRequest; use Knox\Pesapal\OAuth\OAuthSignatureMethod_HMAC_SHA1; use Route; /** * Class Pesapal * * @package Knox\Pesapal */ class Pesapal implements PesapalContract { /** * Processes the payment to pesapal * * @return pesapal_tracking_id */ private $callback_route = ''; /** * @param $params * * @return string * @throws \Knox\Pesapal\Exceptions\PesapalException */ public function makePayment($params) { $defaults = [ // the defaults will be overidden if set in $params 'amount' => '1', 'description' => 'sample description', 'type' => 'MERCHANT', 'reference' => $this->random_reference(), 'first_name' => 'John', 'last_name' => 'Doe', 'email' => 'johndoe@example.com', 'currency' => 'KES', 'phonenumber' => '254712345678', 'width' => '100%', 'height' => '100%', ]; if (!array_key_exists('currency', $params)) { if (config('pesapal.currency') != null) { $params['currency'] = config('pesapal.currency'); } } $params = array_merge($defaults, $params); if (!config('pesapal.callback_route')) { throw new PesapalException("callback route not provided"); } else { if (!Route::has(config('pesapal.callback_route'))) { throw new PesapalException("callback route does not exist"); } } $token = NULL; $consumer_key = config('pesapal.consumer_key'); $consumer_secret = config('pesapal.consumer_secret'); $signature_method = new OAuthSignatureMethod_HMAC_SHA1(); $iframelink = $this->api_link('PostPesapalDirectOrderV4'); $callback_url = url('/') . '/pesapal-callback'; //redirect url, the page that will handle the response from pesapal. $post_xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?> <PesapalDirectOrderInfo xmlns:xsi=\"http://www.w3.org/2001/XMLSchemainstance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" Amount=\"" . $params['amount'] . "\" Description=\"" . $params['description'] . "\" Type=\"" . $params['type'] . "\" Reference=\"" . $params['reference'] . "\" FirstName=\"" . $params['first_name'] . "\" LastName=\"" . $params['last_name'] . "\" Currency=\"" . $params['currency'] . "\" Email=\"" . $params['email'] . "\" PhoneNumber=\"" . $params['phonenumber'] . "\" xmlns=\"http://www.pesapal.com\" />"; $post_xml = htmlentities($post_xml); $consumer = new OAuthConsumer($consumer_key, $consumer_secret); $iframe_src = OAuthRequest::from_consumer_and_token($consumer, $token, "GET", $iframelink, $params); $iframe_src->set_parameter("oauth_callback", $callback_url); $iframe_src->set_parameter("pesapal_request_data", $post_xml); $iframe_src->sign_request($signature_method, $consumer, $token); return '<iframe src="' . $iframe_src . '" width="' . $params['width'] . '" height="' . $params['height'] . '" scrolling="auto" frameBorder="0"> <p>Unable to load the payment page</p> </iframe>'; } /** * @param $pesapalNotification * @param $pesapal_merchant_reference * @param $pesapalTrackingId */ function redirectToIPN($pesapalNotification, $pesapal_merchant_reference, $pesapalTrackingId) { $consumer_key = config('pesapal.consumer_key'); $consumer_secret = config('pesapal.consumer_secret'); $statusrequestAPI = $this->api_link('querypaymentdetails'); if ($pesapalNotification == "CHANGE" && $pesapalTrackingId != '') { $token = $params = NULL; $consumer = new OAuthConsumer($consumer_key, $consumer_secret); $signature_method = new OAuthSignatureMethod_HMAC_SHA1(); //get transaction status $request_status = OAuthRequest::from_consumer_and_token($consumer, $token, "GET", $statusrequestAPI, $params); $request_status->set_parameter("pesapal_merchant_reference", $pesapal_merchant_reference); $request_status->set_parameter("pesapal_transaction_tracking_id", $pesapalTrackingId); $request_status->sign_request($signature_method, $consumer, $token); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $request_status); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); if (defined('CURL_PROXY_REQUIRED')) { if (CURL_PROXY_REQUIRED == 'True') { $proxy_tunnel_flag = (defined('CURL_PROXY_TUNNEL_FLAG') && strtoupper(CURL_PROXY_TUNNEL_FLAG) == 'FALSE') ? false : true; curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, $proxy_tunnel_flag); curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); curl_setopt($ch, CURLOPT_PROXY, CURL_PROXY_SERVER_DETAILS); } } $response = curl_exec($ch); $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $raw_header = substr($response, 0, $header_size - 4); $headerArray = explode("\r\n\r\n", $raw_header); $header = $headerArray[count($headerArray) - 1]; //transaction status $elements = preg_split("/=/", substr($response, $header_size)); //$status = $elements[1]; $components = explode(',', $elements[1]); $transaction_id = $components[0]; $payment_method = $components[1]; $merchant_reference = $components[3]; $status = $components[2]; curl_close($ch); if ($status == 'PENDING') { sleep(60); redirectToIPN($pesapalNotification, $pesapal_merchant_reference, $pesapalTrackingId); } //UPDATE YOUR DB TABLE WITH NEW STATUS FOR TRANSACTION WITH pesapal_transaction_tracking_id $pesapalTrackingId $separator = explode('@', config('pesapal.ipn')); $controller = $separator[0]; $method = $separator[1]; $class = '\App\Http\Controllers\\' . $separator[0]; $payment = new $class(); $payment->$method($transaction_id, $status, $payment_method, $merchant_reference); if ($status != "PENDING") { $resp = "pesapal_notification_type=$pesapalNotification&pesapal_transaction_tracking_id=$pesapalTrackingId&pesapal_merchant_reference=$pesapal_merchant_reference"; ob_start(); echo $resp; ob_flush(); exit; } } } /** * @param $pesapal_merchant_reference * * @return mixed */ function getMerchantStatus($pesapal_merchant_reference) { $consumer_key = config('pesapal.consumer_key'); $consumer_secret = config('pesapal.consumer_secret'); $statusrequestAPI = $this->api_link('querypaymentstatusbymerchantref'); $token = $params = NULL; $consumer = new OAuthConsumer($consumer_key, $consumer_secret); $signature_method = new OAuthSignatureMethod_HMAC_SHA1(); //get transaction status $request_status = OAuthRequest::from_consumer_and_token($consumer, $token, "GET", $statusrequestAPI, $params); $request_status->set_parameter("pesapal_merchant_reference", $pesapal_merchant_reference); $request_status->sign_request($signature_method, $consumer, $token); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $request_status); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); if (defined('CURL_PROXY_REQUIRED')) { if (CURL_PROXY_REQUIRED == 'True') { $proxy_tunnel_flag = (defined('CURL_PROXY_TUNNEL_FLAG') && strtoupper(CURL_PROXY_TUNNEL_FLAG) == 'FALSE') ? false : true; curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, $proxy_tunnel_flag); curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); curl_setopt($ch, CURLOPT_PROXY, CURL_PROXY_SERVER_DETAILS); } } $response = curl_exec($ch); $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $raw_header = substr($response, 0, $header_size - 4); $headerArray = explode("\r\n\r\n", $raw_header); $header = $headerArray[count($headerArray) - 1]; //transaction status $elements = preg_split("/=/", substr($response, $header_size)); $status = $elements[1]; curl_close($ch); return $status; } /** * @param string $prefix * @param int $length * * @return string */ public function random_reference($prefix = 'PESAPAL', $length = 15) { $keyspace = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; $str = ''; $max = mb_strlen($keyspace, '8bit') - 1; for ($i = 0; $i < $length; ++$i) { $str .= $keyspace[random_int(0, $max)]; } return $prefix . $str; } /** * Get API path * @param null $path * @return string */ public function api_link($path = null) { $live = 'https://www.pesapal.com/api/'; $demo = 'https://demo.pesapal.com/api/'; return (config('pesapal.live') ? $live : $demo) . $path; } } pesapal/src/PesapalAPIController.php 0000644 00000002233 15002202657 0013453 0 ustar 00 <?php namespace Knox\Pesapal; use Knox\Pesapal\Exceptions\PesapalException; use App\Http\Controllers\Controller; use Pesapal; class PesapalAPIController extends Controller { function handleCallback() { $merchant_reference = request('pesapal_merchant_reference'); $tracking_id = request('pesapal_transaction_tracking_id'); $route = config('pesapal.callback_route'); return redirect()->route( $route, array('tracking_id' => $tracking_id, 'merchant_reference' => $merchant_reference) ); } function handleIPN() { if (request('pesapal_notification_type') && request('pesapal_merchant_reference') && request('pesapal_transaction_tracking_id')) { $notification_type = request('pesapal_notification_type'); $merchant_reference = request('pesapal_merchant_reference'); $tracking_id = request('pesapal_transaction_tracking_id'); Pesapal::redirectToIPN($notification_type, $merchant_reference, $tracking_id); } else { throw new PesapalException("incorrect parameters in request"); } } // Test bleeding edge } pesapal/src/OAuth/OAuthSignatureMethod.php 0000644 00000001055 15002202657 0014554 0 ustar 00 <?php /** * Created by PhpStorm. * User: mxgel * Date: 11/14/16 * Time: 2:27 AM */ namespace Knox\Pesapal\OAuth; /** * Class OAuthSignatureMethod * * @package Knox\Pesapal\OAuth */ class OAuthSignatureMethod { /** * @param $request * @param $consumer * @param $token * @param $signature * * @return bool */ public function check_signature(&$request, $consumer, $token, $signature) { $built = $this->build_signature($request, $consumer, $token); return $built == $signature; } } pesapal/src/OAuth/OAuthSignatureMethod_HMAC_SHA1.php 0000644 00000001704 15002202657 0016121 0 ustar 00 <?php /** * Created by PhpStorm. * User: mxgel * Date: 11/14/16 * Time: 2:28 AM */ namespace Knox\Pesapal\OAuth; /** * Class OAuthSignatureMethod_HMAC_SHA1 * * @package Knox\Pesapal\OAuth */ class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod { /** * @return string */ function get_name() { return "HMAC-SHA1"; } /** * @param $request * @param $consumer * @param $token * * @return string */ public function build_signature($request, $consumer, $token) { $base_string = $request->get_signature_base_string(); $request->base_string = $base_string; $key_parts = [ $consumer->secret, ($token) ? $token->secret : "", ]; $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); $key = implode('&', $key_parts); return base64_encode(hash_hmac('sha1', $base_string, $key, true)); } } pesapal/src/OAuth/Exceptions/OAuthException.php 0000644 00000000427 15002202657 0015533 0 ustar 00 <?php /** * Created by PhpStorm. * User: mxgel * Date: 11/14/16 * Time: 2:23 AM */ namespace Knox\Pesapal\OAuth\Exceptions; use Exception; /** * Class OAuthException * * @package Knox\Pesapal\OAuth\Exceptions */ class OAuthException extends Exception { // Pass } pesapal/src/OAuth/OAuthDataStore.php 0000644 00000002274 15002202657 0013344 0 ustar 00 <?php /** * Created by PhpStorm. * User: mxgel * Date: 11/14/16 * Time: 2:33 AM */ namespace Knox\Pesapal\OAuth; /** * Class OAuthDataStore * * @package Knox\Pesapal\OAuth */ class OAuthDataStore { /** * @param $consumer_key */ function lookup_consumer($consumer_key) { // implement me } /** * @param $consumer * @param $token_type * @param $token */ function lookup_token($consumer, $token_type, $token) { // implement me } /** * @param $consumer * @param $token * @param $nonce * @param $timestamp */ function lookup_nonce($consumer, $token, $nonce, $timestamp) { // implement me } /** * @param $consumer */ function new_request_token($consumer) { // return a new token attached to this consumer } /** * @param $token * @param $consumer */ function new_access_token($token, $consumer) { // return a new access token attached to this consumer // for the user associated with this token if the request token // is authorized // should also invalidate the request token } } pesapal/src/OAuth/OAuthConsumer.php 0000644 00000001401 15002202657 0013240 0 ustar 00 <?php /** * Created by PhpStorm. * User: mxgel * Date: 11/14/16 * Time: 2:24 AM */ namespace Knox\Pesapal\OAuth; /** * Class OAuthConsumer * * @package Knox\Pesapal\OAuth */ class OAuthConsumer { /** * @var */ public $key; /** * @var */ public $secret; /** * OAuthConsumer constructor. * * @param $key * @param $secret * @param null $callback_url */ function __construct($key, $secret, $callback_url = NULL) { $this->key = $key; $this->secret = $secret; $this->callback_url = $callback_url; } /** * @return string */ function __toString() { return "OAuthConsumer[key=$this->key,secret=$this->secret]"; } } pesapal/src/OAuth/OAuthSignatureMethod_PLAINTEXT.php 0000644 00000001720 15002202657 0016203 0 ustar 00 <?php /** * Created by PhpStorm. * User: mxgel * Date: 11/14/16 * Time: 2:29 AM */ namespace Knox\Pesapal\OAuth; /** * Class OAuthSignatureMethod_PLAINTEXT * * @package Knox\Pesapal\OAuth */ class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod { /** * @return string */ public function get_name() { return "PLAINTEXT"; } /** * @param $request * @param $consumer * @param $token * * @return mixed */ public function build_signature($request, $consumer, $token) { $sig = [ OAuthUtil::urlencode_rfc3986($consumer->secret), ]; if ($token) { array_push($sig, OAuthUtil::urlencode_rfc3986($token->secret)); } else { array_push($sig, ''); } $raw = implode("&", $sig); // for debug purposes $request->base_string = $raw; return OAuthUtil::urlencode_rfc3986($raw); } } pesapal/src/OAuth/OAuthServer.php 0000644 00000014225 15002202657 0012723 0 ustar 00 <?php /** * Created by PhpStorm. * User: mxgel * Date: 11/14/16 * Time: 2:31 AM */ namespace Knox\Pesapal\OAuth; use Knox\Pesapal\OAuth\Exceptions\OAuthException; /** * Class OAuthServer * * @package Knox\Pesapal\OAuth */ class OAuthServer { /** * @var int */ protected $timestamp_threshold = 300; // in seconds, five minutes /** * @var float */ protected $version = 1.0; // hi blaine /** * @var array */ protected $signature_methods = []; /** * @var */ protected $data_store; /** * OAuthServer constructor. * * @param $data_store */ function __construct($data_store) { $this->data_store = $data_store; } /** * @param $signature_method */ public function add_signature_method($signature_method) { $this->signature_methods[$signature_method->get_name()] = $signature_method; } // high level functions /** * process a request_token request * returns the request token on success */ public function fetch_request_token(&$request) { $this->get_version($request); $consumer = $this->get_consumer($request); // no token required for the initial token request $token = NULL; $this->check_signature($request, $consumer, $token); $new_token = $this->data_store->new_request_token($consumer); return $new_token; } /** * process an access_token request * returns the access token on success */ public function fetch_access_token(&$request) { $this->get_version($request); $consumer = $this->get_consumer($request); // requires authorized request token $token = $this->get_token($request, $consumer, "request"); $this->check_signature($request, $consumer, $token); $new_token = $this->data_store->new_access_token($token, $consumer); return $new_token; } /** * verify an api call, checks all the parameters */ public function verify_request(&$request) { $this->get_version($request); $consumer = $this->get_consumer($request); $token = $this->get_token($request, $consumer, "access"); $this->check_signature($request, $consumer, $token); return [ $consumer, $token, ]; } // Internals from here /** * version 1 */ private function get_version(&$request) { $version = $request->get_parameter("oauth_version"); if (!$version) { $version = 1.0; } if ($version && $version != $this->version) { throw new OAuthException("OAuth version '$version' not supported"); } return $version; } /** * figure out the signature with some defaults */ private function get_signature_method(&$request) { $signature_method = @$request->get_parameter("oauth_signature_method"); if (!$signature_method) { $signature_method = "PLAINTEXT"; } if (!in_array($signature_method, array_keys($this->signature_methods)) ) { throw new OAuthException( "Signature method '$signature_method' not supported " . "try one of the following: " . implode(", ", array_keys($this->signature_methods)) ); } return $this->signature_methods[$signature_method]; } /** * try to find the consumer for the provided request's consumer key */ private function get_consumer(&$request) { $consumer_key = @$request->get_parameter("oauth_consumer_key"); if (!$consumer_key) { throw new OAuthException("Invalid consumer key"); } $consumer = $this->data_store->lookup_consumer($consumer_key); if (!$consumer) { throw new OAuthException("Invalid consumer"); } return $consumer; } /** * try to find the token for the provided request's token key */ private function get_token(&$request, $consumer, $token_type = "access") { $token_field = @$request->get_parameter('oauth_token'); $token = $this->data_store->lookup_token( $consumer, $token_type, $token_field ); if (!$token) { throw new OAuthException("Invalid $token_type token: $token_field"); } return $token; } /** * all-in-one function to check the signature on a request * should guess the signature method appropriately */ private function check_signature(&$request, $consumer, $token) { // this should probably be in a different method $timestamp = @$request->get_parameter('oauth_timestamp'); $nonce = @$request->get_parameter('oauth_nonce'); $this->check_timestamp($timestamp); $this->check_nonce($consumer, $token, $nonce, $timestamp); $signature_method = $this->get_signature_method($request); $signature = $request->get_parameter('oauth_signature'); $valid_sig = $signature_method->check_signature( $request, $consumer, $token, $signature ); if (!$valid_sig) { throw new OAuthException("Invalid signature"); } } /** * check that the timestamp is new enough */ private function check_timestamp($timestamp) { // verify that timestamp is recentish $now = time(); if ($now - $timestamp > $this->timestamp_threshold) { throw new OAuthException( "Expired timestamp, yours $timestamp, ours $now" ); } } /** * check that the nonce is not repeated */ private function check_nonce($consumer, $token, $nonce, $timestamp) { // verify that the nonce is uniqueish $found = $this->data_store->lookup_nonce( $consumer, $token, $nonce, $timestamp ); if ($found) { throw new OAuthException("Nonce already used: $nonce"); } } } pesapal/src/OAuth/OAuthRequest.php 0000644 00000023115 15002202657 0013103 0 ustar 00 <?php /** * Created by PhpStorm. * User: mxgel * Date: 11/14/16 * Time: 2:31 AM */ namespace Knox\Pesapal\OAuth; use Knox\Pesapal\OAuth\Exceptions\OAuthException; /** * Class OAuthRequest * * @package Knox\Pesapal\OAuth */ class OAuthRequest { /** * @var array|null */ private $parameters; /** * @var */ private $http_method; /** * @var */ private $http_url; // for debug purposes /** * @var */ public $base_string; /** * @var string */ public static $version = '1.0'; /** * @var string */ public static $POST_INPUT = 'php://input'; /** * OAuthRequest constructor. * * @param $http_method * @param $http_url * @param null $parameters */ function __construct($http_method, $http_url, $parameters = NULL) { @$parameters or $parameters = []; $this->parameters = $parameters; $this->http_method = $http_method; $this->http_url = $http_url; } /** * attempt to build up a request from what was passed to the server */ public static function from_request($http_method = NULL, $http_url = NULL, $parameters = NULL) { $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") ? 'http' : 'https'; @$http_url or $http_url = $scheme . '://' . $_SERVER['HTTP_HOST'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI']; @$http_method or $http_method = $_SERVER['REQUEST_METHOD']; // We weren't handed any parameters, so let's find the ones relevant to // this request. // If you run XML-RPC or similar you should use this to provide your own // parsed parameter-list if (!$parameters) { // Find request headers $request_headers = OAuthUtil::get_headers(); // Parse the query-string to find GET parameters $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']); // It's a POST request of the proper content-type, so parse POST // parameters and add those overriding any duplicates from GET if ($http_method == "POST" && @strstr($request_headers["Content-Type"], "application/x-www-form-urlencoded") ) { $post_data = OAuthUtil::parse_parameters( file_get_contents(self::$POST_INPUT) ); $parameters = array_merge($parameters, $post_data); } // We have a Authorization-header with OAuth data. Parse the header // and add those overriding any duplicates from GET or POST if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") { $header_parameters = OAuthUtil::split_header( $request_headers['Authorization'] ); $parameters = array_merge($parameters, $header_parameters); } } return new OAuthRequest($http_method, $http_url, $parameters); } /** * pretty much a helper function to set up the request */ public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters = NULL) { @$parameters or $parameters = []; $defaults = [ "oauth_version" => OAuthRequest::$version, "oauth_nonce" => OAuthRequest::generate_nonce(), "oauth_timestamp" => OAuthRequest::generate_timestamp(), "oauth_consumer_key" => $consumer->key, ]; if ($token) { $defaults['oauth_token'] = $token->key; } $parameters = array_merge($defaults, $parameters); return new OAuthRequest($http_method, $http_url, $parameters); } /** * @param $name * @param $value * @param bool $allow_duplicates */ public function set_parameter($name, $value, $allow_duplicates = true) { if ($allow_duplicates && isset($this->parameters[$name])) { // We have already added parameter(s) with this name, so add to the list if (is_scalar($this->parameters[$name])) { // This is the first duplicate, so transform scalar (string) // into an array so we can add the duplicates $this->parameters[$name] = [$this->parameters[$name]]; } $this->parameters[$name][] = $value; } else { $this->parameters[$name] = $value; } } /** * @param $name * * @return mixed|null */ public function get_parameter($name) { return isset($this->parameters[$name]) ? $this->parameters[$name] : null; } /** * @return array|null */ public function get_parameters() { return $this->parameters; } /** * @param $name */ public function unset_parameter($name) { unset($this->parameters[$name]); } /** * The request parameters, sorted and concatenated into a normalized string. * * @return string */ public function get_signable_parameters() { // Grab all parameters $params = $this->parameters; // Remove oauth_signature if present // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.") if (isset($params['oauth_signature'])) { unset($params['oauth_signature']); } return OAuthUtil::build_http_query($params); } /** * Returns the base string of this request * * The base string defined as the method, the url * and the parameters (normalized), each urlencoded * and the concated with &. */ public function get_signature_base_string() { $parts = [ $this->get_normalized_http_method(), $this->get_normalized_http_url(), $this->get_signable_parameters(), ]; $parts = OAuthUtil::urlencode_rfc3986($parts); return implode('&', $parts); } /** * just uppercases the http method */ public function get_normalized_http_method() { return strtoupper($this->http_method); } /** * parses the url and rebuilds it to be * scheme://host/path */ public function get_normalized_http_url() { $parts = parse_url($this->http_url); if (array_key_exists('port', $parts)) { $port = @$parts['port']; } else { $port = false; } $scheme = $parts['scheme']; $host = $parts['host']; $path = @$parts['path']; $port or $port = ($scheme == 'https') ? '443' : '80'; if (($scheme == 'https' && $port != '443') || ($scheme == 'http' && $port != '80') ) { $host = "$host:$port"; } return "$scheme://$host$path"; } /** * builds a url usable for a GET request */ public function to_url() { $post_data = $this->to_postdata(); $out = $this->get_normalized_http_url(); if ($post_data) { $out .= '?' . $post_data; } return $out; } /** * builds the data one would send in a POST request */ public function to_postdata() { return OAuthUtil::build_http_query($this->parameters); } /** * builds the Authorization: header */ public function to_header() { $out = 'Authorization: OAuth realm=""'; $total = []; foreach ($this->parameters as $k => $v) { if (substr($k, 0, 5) != "oauth") { continue; } if (is_array($v)) { throw new OAuthException('Arrays not supported in headers'); } $out .= ',' . OAuthUtil::urlencode_rfc3986($k) . '="' . OAuthUtil::urlencode_rfc3986($v) . '"'; } return $out; } /** * @return string */ public function __toString() { return $this->to_url(); } /** * @param $signature_method * @param $consumer * @param $token */ public function sign_request($signature_method, $consumer, $token) { $this->set_parameter( "oauth_signature_method", $signature_method->get_name(), false ); $signature = $this->build_signature($signature_method, $consumer, $token); $this->set_parameter("oauth_signature", $signature, false); } /** * @param $signature_method * @param $consumer * @param $token * * @return mixed */ public function build_signature($signature_method, $consumer, $token) { $signature = $signature_method->build_signature($this, $consumer, $token); return $signature; } /** * util function: current timestamp */ private static function generate_timestamp() { return time(); } /** * util function: current nonce */ private static function generate_nonce() { mt_srand((double)microtime() * 10000);//optional for php 4.2.0 and up. $charid = strtoupper(md5(uniqid(rand(), true))); $hyphen = chr(45);// "-" $uuid = chr(123)// "{" . substr($charid, 0, 8) . $hyphen . substr($charid, 8, 4) . $hyphen . substr($charid, 12, 4) . $hyphen . substr($charid, 16, 4) . $hyphen . substr($charid, 20, 12) . chr(125);// "}" return $uuid; } } pesapal/src/OAuth/OAuthUtil.php 0000644 00000013765 15002202657 0012402 0 ustar 00 <?php /** * Created by PhpStorm. * User: mxgel * Date: 11/14/16 * Time: 2:33 AM */ namespace Knox\Pesapal\OAuth; /** * Class OAuthUtil * * @package Knox\Pesapal\OAuth */ class OAuthUtil { /** * @param $input * * @return array|mixed|string */ public static function urlencode_rfc3986($input) { if (is_array($input)) { return array_map([ self::class, 'urlencode_rfc3986', ], $input); } else { if (is_scalar($input)) { return str_replace( '+', ' ', str_replace('%7E', '~', rawurlencode($input)) ); } else { return ''; } } } // This decode function isn't taking into consideration the above // modifications to the encoding process. However, this method doesn't // seem to be used anywhere so leaving it as is. /** * @param $string * * @return string */ public static function urldecode_rfc3986($string) { return urldecode($string); } // Utility function for turning the Authorization: header into // parameters, has to do some unescaping // Can filter out any non-oauth parameters if needed (default behaviour) /** * @param $header * @param bool $only_allow_oauth_parameters * * @return array */ public static function split_header($header, $only_allow_oauth_parameters = true) { $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/'; $offset = 0; $params = []; while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) { $match = $matches[0]; $header_name = $matches[2][0]; $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0]; if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) { $params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content); } $offset = $match[1] + strlen($match[0]); } if (isset($params['realm'])) { unset($params['realm']); } return $params; } // helper to try to sort out headers for people who aren't running apache /** * @return array|false */ public static function get_headers() { if (function_exists('apache_request_headers')) { // we need this to get the actual Authorization: header // because apache tends to tell us it doesn't exist return apache_request_headers(); } // otherwise we don't have apache and are just going to have to hope // that $_SERVER actually contains what we need $out = []; foreach ($_SERVER as $key => $value) { if (substr($key, 0, 5) == "HTTP_") { // this is chaos, basically it is just there to capitalize the first // letter of every word that is not an initial HTTP and strip HTTP // code from przemek $key = str_replace( " ", "-", ucwords(strtolower(str_replace("_", " ", substr($key, 5)))) ); $out[$key] = $value; } } return $out; } // This function takes a input like a=b&a=c&d=e and returns the parsed // parameters like this // array('a' => array('b','c'), 'd' => 'e') /** * @param $input * * @return array */ public static function parse_parameters($input) { if (!isset($input) || !$input) { return []; } $pairs = split('&', $input); $parsed_parameters = []; foreach ($pairs as $pair) { $split = split('=', $pair, 2); $parameter = OAuthUtil::urldecode_rfc3986($split[0]); $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : ''; if (isset($parsed_parameters[$parameter])) { // We have already recieved parameter(s) with this name, so add to the list // of parameters with this name if (is_scalar($parsed_parameters[$parameter])) { // This is the first duplicate, so transform scalar (string) into an array // so we can add the duplicates $parsed_parameters[$parameter] = [$parsed_parameters[$parameter]]; } $parsed_parameters[$parameter][] = $value; } else { $parsed_parameters[$parameter] = $value; } } return $parsed_parameters; } /** * @param $params * * @return string */ public static function build_http_query($params) { if (!$params) { return ''; } // Urlencode both keys and values $keys = OAuthUtil::urlencode_rfc3986(array_keys($params)); $values = OAuthUtil::urlencode_rfc3986(array_values($params)); $params = array_combine($keys, $values); // Parameters are sorted by name, using lexicographical byte value ordering. // Ref: Spec: 9.1.1 (1) uksort($params, 'strcmp'); $pairs = []; foreach ($params as $parameter => $value) { if (is_array($value)) { // If two or more parameters share the same name, they are sorted by their value // Ref: Spec: 9.1.1 (1) natsort($value); foreach ($value as $duplicate_value) { $pairs[] = $parameter . '=' . $duplicate_value; } } else { $pairs[] = $parameter . '=' . $value; } } // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61) // Each name-value pair is separated by an '&' character (ASCII code 38) return implode('&', $pairs); } } pesapal/src/OAuth/OAuthToken.php 0000644 00000002001 15002202657 0012522 0 ustar 00 <?php /** * Created by PhpStorm. * User: mxgel * Date: 11/14/16 * Time: 2:26 AM */ namespace Knox\Pesapal\OAuth; /** * Class OAuthToken * * @package Knox\Pesapal\OAuth */ class OAuthToken { // access tokens and request tokens /** * @var */ public $key; /** * @var */ public $secret; /** * @param string $key - the token * @param string $secret - the token secret */ function __construct($key, $secret) { $this->key = $key; $this->secret = $secret; } /** * generates the basic string serialization of a token that a server * would respond to request_token and access_token calls with */ function to_string() { return "oauth_token=" . OAuthUtil::urlencode_rfc3986($this->key) . "&oauth_token_secret=" . OAuthUtil::urlencode_rfc3986($this->secret); } /** * @return string */ function __toString() { return $this->to_string(); } } pesapal/src/OAuth/OAuthSignatureMethod_RSA_SHA1.php 0000644 00000005730 15002202657 0016041 0 ustar 00 <?php /** * Created by PhpStorm. * User: mxgel * Date: 11/14/16 * Time: 2:30 AM */ namespace Knox\Pesapal\OAuth; use Knox\Pesapal\OAuth\Exceptions\OAuthException; /** * Class OAuthSignatureMethod_RSA_SHA1 * * @package Knox\Pesapal\OAuth */ class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod { /** * @return string */ public function get_name() { return "RSA-SHA1"; } /** * @param $request * * @throws \Knox\Pesapal\OAuth\Exceptions\OAuthException */ protected function fetch_public_cert(&$request) { // not implemented yet, ideas are: // (1) do a lookup in a table of trusted certs keyed off of consumer // (2) fetch via http using a url provided by the requester // (3) some sort of specific discovery code based on request // // either way should return a string representation of the certificate throw new OAuthException("fetch_public_cert not implemented"); } /** * @param $request * * @throws \Knox\Pesapal\OAuth\Exceptions\OAuthException */ protected function fetch_private_cert(&$request) { // not implemented yet, ideas are: // (1) do a lookup in a table of trusted certs keyed off of consumer // // either way should return a string representation of the certificate throw new OAuthException("fetch_private_cert not implemented"); } /** * @param $request * @param $consumer * @param $token * * @return string */ public function build_signature(&$request, $consumer, $token) { $base_string = $request->get_signature_base_string(); $request->base_string = $base_string; // Fetch the private key cert based on the request $cert = $this->fetch_private_cert($request); // Pull the private key ID from the certificate $privatekeyid = openssl_get_privatekey($cert); // Sign using the key $ok = openssl_sign($base_string, $signature, $privatekeyid); // Release the key resource openssl_free_key($privatekeyid); return base64_encode($signature); } /** * @param $request * @param $consumer * @param $token * @param $signature * * @return bool */ public function check_signature(&$request, $consumer, $token, $signature) { $decoded_sig = base64_decode($signature); $base_string = $request->get_signature_base_string(); // Fetch the public key cert based on the request $cert = $this->fetch_public_cert($request); // Pull the public key ID from the certificate $publickeyid = openssl_get_publickey($cert); // Check the computed signature against the one passed in the query $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); // Release the key resource openssl_free_key($publickeyid); return $ok == 1; } } pesapal/src/routes.php 0000644 00000000346 15002202657 0011014 0 ustar 00 <?php Route::get('pesapal-callback',['as'=>'pesapal-callback', 'uses'=>'Knox\Pesapal\PesapalAPIController@handleCallback']); Route::get('pesapal-ipn', ['as'=>'pesapal-ipn', 'uses'=>'Knox\Pesapal\PesapalAPIController@handleIPN']); pesapal/src/config/pesapal.php 0000644 00000001605 15002202657 0012364 0 ustar 00 <?php return [ /* * Pesapal consumer key */ 'consumer_key' => env('PESAPAL_CONSUMER_KEY'), /* * Pesapal consumer secret */ 'consumer_secret' => env('PESAPAL_CONSUMER_SECRET'), /* * ISO code for the currency */ 'currency' => env('PESAPAL_CURRENCY', 'KES'), /* * controller method to call for instant notifications IPN as relative path from App\Http\Controllers\ * eg "TransactionController@confirmation" */ 'ipn' => env('PESAPAL_IPN'), /* * Pesapal environment */ 'live' => env('PESAPAL_LIVE', true), /* * Route name to handle the callback * eg Route::get('donepayment', ['as' => 'paymentsuccess', 'uses'=>'PaymentsController@paymentsuccess']); * The route name is "paymentsuccess" */ 'callback_route' => env('PESAPAL_CALLBACK_ROUTE'), ]; pesapal/src/Facades/Pesapal.php 0000644 00000000303 15002202657 0012377 0 ustar 00 <?php namespace Knox\Pesapal\Facades; use Illuminate\Support\Facades\Facade; class Pesapal extends Facade { protected static function getFacadeAccessor() { return 'pesapal'; } } pesapal/788122/index.php 0000644 00000000000 15002202657 0010551 0 ustar 00
| ver. 1.4 |
Github
|
.
| PHP 8.2.28 | Generation time: 0 |
proxy
|
phpinfo
|
Settings