├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── payhere.php ├── configure.php ├── database ├── factories │ └── ModelFactory.php └── migrations │ └── create_payhere_table.php.stub ├── resources └── views │ ├── .gitkeep │ └── recurring.blade.php ├── routes └── web.php └── src ├── Api ├── Authorize.php ├── Capture.php ├── Charge.php ├── Checkout.php ├── PreApproval.php ├── Recurring.php ├── Refund.php ├── Retrieve.php └── Subscription.php ├── Commands └── PayHereCommand.php ├── Controllers ├── CallbackController.php └── FormController.php ├── Events ├── AuthorizeCallbackEvent.php ├── CheckoutCallbackEvent.php ├── PreApprovalCallbackEvent.php └── RecurringCallbackEvent.php ├── Exceptions └── PayHereException.php ├── Helpers ├── PayHereClient.php └── PayHereRestClient.php ├── PayHere.php ├── PayHereFacade.php └── PayHereServiceProvider.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-payhere` will be documented in this file. 4 | 5 | ## 1.0.0 - 202X-XX-XX 6 | 7 | - initial release 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) lahirulhr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel PayHere 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/lahirulhr/laravel-payhere.svg?style=flat-square)](https://packagist.org/packages/lahirulhr/laravel-payhere) 4 | [![GitHub Tests Action Status](https://img.shields.io/github/workflow/status/lahirulhr/laravel-payhere/run-tests?label=tests)](https://github.com/lahirulhr/laravel-payhere/actions?query=workflow%3Arun-tests+branch%3Amain) 5 | [![GitHub Code Style Action Status](https://img.shields.io/github/workflow/status/lahirulhr/laravel-payhere/Check%20&%20fix%20styling?label=code%20style)](https://github.com/lahirulhr/laravel-payhere/actions?query=workflow%3A"Check+%26+fix+styling"+branch%3Amain) 6 | [![Total Downloads](https://img.shields.io/packagist/dt/lahirulhr/laravel-payhere.svg?style=flat-square)](https://packagist.org/packages/lahirulhr/laravel-payhere) 7 | 8 |
9 | 10 | [](http://laravel.com) 11 |
12 | [](http://payhere.lk) 13 | 14 |
15 | 16 | Laravel - PayHere was made to manage PayHere payment gateway on your laravel application with ease. currently this package supports all 17 | available methods on official PayHere documentation. 18 | 19 | Read official [Documentation](https://support.payhere.lk/api-&-mobile-sdk/payhere-checkout) for more information 20 | 21 | #### Available API methods 22 | ✔️ Checkout API 23 | ✔️ Recurring API 24 | ✔️ Preapproval API 25 | ✔️ Charging API 26 | ✔️ Retrieval API 27 | ✔️ Subscription Manager API 28 | ✔️ Refund API 29 | ✔️ Authorize API 30 | ✔️ Capture API 31 | 32 | 33 | ###### Basic Usage 34 | ```php 35 | 36 | // adding payment details 37 | $data = [ 38 | 'first_name' => 'Lahiru', 39 | 'last_name' => 'Tharaka', 40 | 'email' => 'lahirulhr@gmail.com', 41 | 'phone' => '+94761234567', 42 | 'address' => 'Main Rd', 43 | 'city' => 'Anuradhapura', 44 | 'country' => 'Sri lanka', 45 | 'order_id' => '45552525005', 46 | 'items' => 'Smart band MI 4 - BLACK', 47 | 'currency' => 'LKR', 48 | 'amount' => 4960.00, 49 | ]; 50 | 51 | // creating checkout page & redirect user 52 | 53 | return PayHere::checkOut() 54 | ->data($data) 55 | ->successUrl('www.visanduma.com/payment-success') 56 | ->failUrl('www.visanduma.com/payment-fail') 57 | ->renderView(); 58 | 59 | ``` 60 | 61 | ## Installation 62 | 63 | You can install the package via composer: 64 | 65 | ```bash 66 | composer require lahirulhr/laravel-payhere 67 | ``` 68 | 69 | You can publish the config file with: 70 | ```bash 71 | php artisan vendor:publish --provider="Lahirulhr\PayHere\PayHereServiceProvider" --tag="laravel-payhere-config" 72 | ``` 73 | 74 | This is the contents of the published config file: 75 | 76 | ```php 77 | return [ 78 | 79 | 80 | /* 81 | PayHere action url. usually, 82 | for production https://www.payhere.lk 83 | for testing https://sandbox.payhere.lk 84 | remember to update api when production 85 | * */ 86 | 87 | 'api_endpoint' => env('PAYHERE_API'), 88 | 89 | 90 | /* 91 | PayHere merchant ID can be found in their dashboard 92 | https://www.payhere.lk/account/settings/domain-credentials 93 | * */ 94 | 95 | 'merchant_id' => env('PAYHERE_MERCHANT_ID'), 96 | 97 | /* 98 | Merchant Secret is specific to each App/Domain. it can be generated for your domain/app as follows 99 | https://www.payhere.lk/account/settings/domain-credentials 100 | *Click 'Add Domain/App' > Fill details > Click 'Request to Allow' 101 | *Wait for the approval for your domain 102 | *Copy the Merchant Secret for your domain/app to .env file 103 | * */ 104 | 'merchant_secret' => env('PAYHERE_MERCHANT_SECRET'), 105 | 106 | 107 | /* 108 | Follow PayHere official instructions to obtain 'app_id' and 'app_secret'. 109 | NOTE: you dont need to generate "Authorization code". it will be automatically generate by this package 110 | *Sign in to your PayHere account & go to Settings > Business Apps section 111 | *Click 'Create App' button & enter an app name & comma seperated domains to whilelist 112 | *Tick the permission 'Payment Retrieval API' 113 | *Click 'Add Business App' button to create the app 114 | *Once the app is created click 'View Credential' button in front of the created app 115 | *Copy the 'App ID' & 'App Secret' values 116 | * */ 117 | 'app_id' => env('PAYHERE_APP_ID'), 118 | 'app_secret' => env('PAYHERE_APP_SECRET'), 119 | ``` 120 | 121 | ## Usage 122 | 123 | 124 | ###### [Checkout API](https://support.payhere.lk/api-&-mobile-sdk/payhere-checkout) 125 | Checkout API lets you integrate PayHere with your website, web application or any other application in code level. 126 | It offers a simple HTML Form to initiate a payment request and redirect 127 | your customer to PayHere Payment Gateway to securely process the payment. 128 | 129 | ```php 130 | // in your controller 131 | 132 | use Lahirulhr\PayHere\PayHere; 133 | 134 | // prepair posting data 135 | 136 | $data = [ 137 | 'first_name' => 'Lahiru', 138 | 'last_name' => 'Tharaka', 139 | 'email' => 'lahirulhr@gmail.com', 140 | 'phone' => '+94761234567', 141 | 'address' => 'Main Rd', 142 | 'city' => 'Anuradhapura', 143 | 'country' => 'Sri lanka', 144 | 'order_id' => '45552525005', 145 | 'items' => 'Smart band MI 4 - BLACK', 146 | 'currency' => 'LKR', 147 | 'amount' => 4960.00, 148 | ]; 149 | 150 | // creating checkout page & ridirect the user 151 | 152 | return PayHere::checkOut() 153 | ->data($data) 154 | ->setOptionalData() // Set optional data. see PayHere documantaion for available values 155 | ->successUrl('www.visanduma.com/success') 156 | ->failUrl('www.visanduma.com/fail') 157 | ->renderView(); 158 | ``` 159 | 160 | #### Handling the server callback 161 | PayHere will be notified your application with response data using public url POST request callback. 162 | then this package will emit a new event with their 163 | callback data. you just need to listen on an event and do anything you want with payload data. 164 | 165 | #### Available Events 166 | * AuthorizeCallbackEvent 167 | * CheckoutCallbackEvent 168 | * PreapprovalCallbackEvent 169 | * RecurringCallbackEvent 170 | 171 | Example: 172 | ```php 173 | 174 | // define listners in your EventServiceProvider.php 175 | 176 | class EventServiceProvider extends ServiceProvider 177 | { 178 | 179 | use Lahirulhr\PayHere\Events\CheckoutCallbackEvent; 180 | 181 | /** 182 | * The event listener mappings for the application. 183 | * 184 | * @var array 185 | */ 186 | protected $listen = [ 187 | CheckoutCallbackEvent::class => [ 188 | // register listeners to do something with callback 189 | SomeListener::class 190 | ], 191 | ]; 192 | 193 | } 194 | 195 | class SomeListener{ 196 | 197 | //.... 198 | 199 | public function handle($event) 200 | { 201 | // you can access payhere callback data using $event->payload 202 | Log::info($event->payload); 203 | } 204 | 205 | } 206 | 207 | ``` 208 | 209 | 210 | ###### [Recurring API](https://support.payhere.lk/api-&-mobile-sdk/payhere-recurring) 211 | Recurring API is accept data array same as checkout API. the only things you need to change checkOut() method 212 | to recurring(). 213 | 214 | 215 | ```php 216 | return PayHere::recurring() 217 | ->data($data) 218 | ->setOptionalData() // Set optional data. see PayHere documantaion for available values 219 | ->successUrl('www.visanduma.com/success') 220 | ->failUrl('www.visanduma.com/fail') 221 | ->chargeMonthly(2) 222 | ->forYears() 223 | ->renderView(); 224 | ``` 225 | 226 | The following options are available for making adjustment to recurring period 227 | 228 | ```php 229 | // Charging interval (Recurrence) 230 | PayHere::recurring()->chargeWeekly(2) // charge per specific period of weeks .the default value is one week 231 | PayHere::recurring()->chargeMonthly(3) // charge per specific period of months .the default value is one month 232 | PayHere::recurring()->chargeAnnually() // charge per specific period of years .the default value is one year 233 | 234 | // Duration to charge 235 | PayHere::recurring()->forWeeks(6) // set duratoin by weeks .the default value is one week 236 | PayHere::recurring()->forMonths(3) // set duratoin by months .the default value is one month 237 | PayHere::recurring()->forYears() // set duratoin by years .the default value is one year 238 | PayHere::recurring()->forForever() // set charging period to infinity. 239 | ``` 240 | 241 | ```php 242 | // use this event to recieve server callback. see above example on Checkout API 243 | RecurringCallbackEvent::class 244 | ``` 245 | 246 | ###### [Preapproval API](https://support.payhere.lk/api-&-mobile-sdk/payhere-preapproval) 247 | 248 | Use same as checkout method 249 | 250 | ```php 251 | return PayHere::preapproval() 252 | ->data($data) 253 | ->setOptionalData() // Set optional data. see PayHere documantaion for available values 254 | ->successUrl('www.visanduma.com/payment-success') 255 | ->failUrl('www.visanduma.com/payment-fail') 256 | ->renderView(); 257 | ``` 258 | 259 | ```php 260 | // use this event to recieve server callback. see above example on Checkout API 261 | PreapprovalCallbackEvent::class 262 | ``` 263 | 264 | ###### [Charging API](https://support.payhere.lk/api-&-mobile-sdk/payhere-charging) 265 | 266 | Charging API lets you charge your preapproved customers programatically on demand using the encrypted tokens. it will return response data array on success 267 | or return PayHereException if any error. 268 | 269 | ```php 270 | $data = [ 271 | "type" => "PAYMENT", 272 | "order_id" => "Order12345", 273 | "items" => "Taxi Hire 123", 274 | "currency" => "LKR", 275 | "amount" => 345.67, 276 | ]; 277 | 278 | $response = PayHere::charge() 279 | ->byToken("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") // customer token 280 | ->withData($data) 281 | ->submit(); 282 | ``` 283 | 284 | 285 | ###### [Retrieval API](https://support.payhere.lk/api-&-mobile-sdk/payhere-retrieval) 286 | 287 | Retrieval API lets you retrieve the details of the Successful payments processed through your PayHere 288 | 289 | ```php 290 | $info = PayHere::retrieve() 291 | ->orderId("od-43784658374534") // order number that you use to charge from customer 292 | ->submit(); 293 | ``` 294 | 295 | ##### [Subscription Manager](https://support.payhere.lk/api-&-mobile-sdk/payhere-subscription) 296 | Subscription Manager API lets you view, retry & cancel your subscription customers programmatically you subscribed from Recurring API. 297 | 298 | ```php 299 | // get all subscriptions 300 | $subscriptions = PayHere::subscription()->getAll(); 301 | 302 | // get payment details of specific subscription 303 | $paymentInfo = PayHere::subscription() 304 | ->getPaymentsOfSubscription("420075032251"); // subscription ID 305 | 306 | // retry on failed supscription payments 307 | PayHere::subscription() 308 | ->retry("420075032251"); // subscription ID 309 | 310 | // cancel a subscription 311 | PayHere::subscription() 312 | ->cancel("420075032251"); // subscription ID 313 | ``` 314 | 315 | 316 | ###### [Refund API](https://support.payhere.lk/api-&-mobile-sdk/payhere-refund) 317 | Refund API lets you refund your existing payment programmatically. 318 | 319 | ```php 320 | PayHere::refund() 321 | ->makePaymentRefund('320027150501') // payment_id 322 | ->note("Out of stock") // note for refund 323 | ->submit(); 324 | ``` 325 | 326 | ###### [Authorize API](https://support.payhere.lk/api-&-mobile-sdk/payhere-authorize) 327 | Authorize API allows you to get your customer authorization for Hold on Card payments. this method will redirect user to payment page 328 | 329 | ```php 330 | // use same $data as Checkout method 331 | 332 | return PayHere::authorize() 333 | ->data($data) 334 | ->successUrl('www.visanduma.com/success') 335 | ->failUrl('www.visanduma.com/fail') 336 | ->renderView(); 337 | 338 | ``` 339 | 340 | ```php 341 | // use this event to recieve server callback. see above example on Checkout API 342 | AuthorizeCallbackEvent::class 343 | ``` 344 | 345 | ###### [Capture API](https://support.payhere.lk/api-&-mobile-sdk/payhere-capture) 346 | 347 | Capture API lets you capture your authorized Hold on Card payments programmatically on demand using the authorization 348 | tokens you retrieved from Payment Authorize API. 349 | 350 | ```php 351 | $response = PayHere::capture() 352 | ->usingToken('e34f3059-7b7d-4b62-a57c-784beaa169f4') // authorization token 353 | ->amount(100) // charging amount 354 | ->reason("reason for capture") 355 | ->submit(); 356 | ``` 357 | 358 | --- 359 | 360 | ## TODO 361 | 362 | - [x] ~~Events for server callbacks~~ 363 | - [ ] Custom payment redirection page 364 | - [ ] Custom Error types 365 | - [ ] Response Data Objects 366 | - [ ] Optional Data handling 367 | - [ ] Inbuilt subscription database 368 | - [ ] Server callback log 369 | 370 | ## Testing 371 | 372 | ```bash 373 | composer test 374 | ``` 375 | 376 | ## Changelog 377 | 378 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 379 | 380 | ## Contributing 381 | 382 | Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. 383 | 384 | ## Security Vulnerabilities 385 | 386 | Please review [our security policy](../../security/policy) on how to report security vulnerabilities. 387 | 388 | ## Credits 389 | 390 | - [LaHiRu](https://github.com/lahirulhr) 391 | - [All Contributors](../../contributors) 392 | 393 | ## License 394 | 395 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 396 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lahirulhr/laravel-payhere", 3 | "description": "API integration for PayHere payment gateway in Sri Lanka", 4 | "keywords": [ 5 | "lahirulhr", 6 | "laravel", 7 | "laravel-payhere" 8 | ], 9 | "homepage": "https://github.com/lahirulhr/laravel-payhere", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "LaHiRu", 14 | "email": "lahirulhr@gmail.com", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": "^8.1", 20 | "guzzlehttp/guzzle": "^7.8", 21 | "illuminate/contracts": "^9.0|^10.0|^11.0", 22 | "spatie/laravel-package-tools": "^1.9.2" 23 | }, 24 | "require-dev": { 25 | "laravel/pint": "^1.16", 26 | "nunomaduro/collision": "^6.0", 27 | "orchestra/testbench": "^8.0", 28 | "pestphp/pest": "^1.23", 29 | "pestphp/pest-plugin-laravel": "^1.4", 30 | "phpunit/phpunit": "^9.6.0" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "Lahirulhr\\PayHere\\": "src", 35 | "Lahirulhr\\PayHere\\Database\\Factories\\": "database/factories" 36 | } 37 | }, 38 | "autoload-dev": { 39 | "psr-4": { 40 | "Lahirulhr\\PayHere\\Tests\\": "tests" 41 | } 42 | }, 43 | "scripts": { 44 | "test": "vendor/bin/pest", 45 | "test-coverage": "vendor/bin/pest coverage" 46 | }, 47 | "config": { 48 | "sort-packages": true, 49 | "allow-plugins": { 50 | "pestphp/pest-plugin": true 51 | } 52 | }, 53 | "extra": { 54 | "laravel": { 55 | "providers": [ 56 | "Lahirulhr\\PayHere\\PayHereServiceProvider" 57 | ], 58 | "aliases": { 59 | "PayHere": "Lahirulhr\\PayHere\\PayHereFacade" 60 | } 61 | } 62 | }, 63 | "minimum-stability": "dev", 64 | "prefer-stable": true 65 | } 66 | -------------------------------------------------------------------------------- /config/payhere.php: -------------------------------------------------------------------------------- 1 | env('PAYHERE_API'), 18 | 19 | /* 20 | PayHere merchant ID can be found in their dashboard 21 | https://www.payhere.lk/account/settings/domain-credentials 22 | * */ 23 | 24 | 'merchant_id' => env('PAYHERE_MERCHANT_ID'), 25 | 26 | /* 27 | Merchant Secret is specific to each App/Domain. it can be generated for your domain/app as follows 28 | https://www.payhere.lk/account/settings/domain-credentials 29 | *Click 'Add Domain/App' > Fill details > Click 'Request to Allow' 30 | *Wait for the approval for your domain 31 | *Copy the Merchant Secret for your domain/app to .env file 32 | * */ 33 | 'merchant_secret' => env('PAYHERE_MERCHANT_SECRET'), 34 | 35 | /* 36 | Follow PayHere official instructions to obtain 'app_id' and 'app_secret'. 37 | NOTE: you don't need to generate "Authorization code". it will be automatically generate by this package 38 | *Sign in to your PayHere account & go to Settings > Business Apps section 39 | *Click 'Create App' button & enter an app name & comma separated domains to whitelist 40 | *Tick the permission 'Payment Retrieval API' 41 | *Click 'Add Business App' button to create the app 42 | *Once the app is created click 'View Credential' button in front of the created app 43 | *Copy the 'App ID' & 'App Secret' values 44 | * */ 45 | 'app_id' => env('PAYHERE_APP_ID'), 46 | 'app_secret' => env('PAYHERE_APP_SECRET'), 47 | 48 | ]; 49 | -------------------------------------------------------------------------------- /configure.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | "); 121 | writeln("Namespace : {$vendorNamespace}\\{$className}"); 122 | writeln("Class name : {$className}"); 123 | writeln('------'); 124 | 125 | writeln('This script will replace the above values in all relevant files in the project directory.'); 126 | 127 | if (! confirm('Modify files?', true)) { 128 | exit(1); 129 | } 130 | 131 | $files = (str_starts_with(strtoupper(PHP_OS), 'WIN') ? replaceForWindows() : replaceForAllOtherOSes()); 132 | 133 | foreach ($files as $file) { 134 | replace_in_file($file, [ 135 | ':author_name' => $authorName, 136 | ':author_username' => $authorUsername, 137 | 'author@domain.com' => $authorEmail, 138 | ':vendor_name' => $vendorName, 139 | ':vendor_slug' => $vendorSlug, 140 | 'VendorName' => $vendorNamespace, 141 | ':package_name' => $packageName, 142 | ':package_slug' => $packageSlug, 143 | 'Skeleton' => $className, 144 | 'skeleton' => $packageSlug, 145 | ':package_description' => $description, 146 | ]); 147 | 148 | match (true) { 149 | str_contains($file, 'src/Skeleton.php') => rename($file, './src/'.$className.'.php'), 150 | str_contains($file, 'src/SkeletonServiceProvider.php') => rename($file, './src/'.$className.'ServiceProvider.php'), 151 | str_contains($file, 'src/SkeletonFacade.php') => rename($file, './src/'.$className.'Facade.php'), 152 | str_contains($file, 'src/Commands/SkeletonCommand.php') => rename($file, './src/Commands/'.$className.'Command.php'), 153 | str_contains($file, 'database/migrations/create_skeleton_table.php.stub') => rename($file, './database/migrations/create_'.$packageSlugWithoutPrefix.'_table.php.stub'), 154 | str_contains($file, 'config/skeleton.php') => rename($file, './config/'.$packageSlugWithoutPrefix.'.php'), 155 | default => [], 156 | }; 157 | } 158 | 159 | confirm('Execute `composer install` and run tests?') && run('composer install && composer test'); 160 | 161 | confirm('Let this script delete itself?', true) && unlink(__FILE__); 162 | -------------------------------------------------------------------------------- /database/factories/ModelFactory.php: -------------------------------------------------------------------------------- 1 | id(); 13 | 14 | // add fields 15 | 16 | $table->timestamps(); 17 | }); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /resources/views/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Visanduma/laravel-payhere/72ce0aa87b495517e3b2d66633e588c86d417e61/resources/views/.gitkeep -------------------------------------------------------------------------------- /resources/views/recurring.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ config('app.name') }} 8 | 9 | 10 |
11 |
12 |
13 |

Processing payment ...

14 |

Please wait few seconds to continue. you will be automatically redirect to payment page

15 |
16 |
17 | @foreach($data as $key => $value) 18 | 19 | @endforeach 20 |
21 |
22 |
23 | 24 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | withoutMiddleware(\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class) 8 | ->name('payhere.callback'); 9 | 10 | Route::get('payhere/form', FormController::class); 11 | -------------------------------------------------------------------------------- /src/Api/Authorize.php: -------------------------------------------------------------------------------- 1 | form_data['authorization_token'] = $authorization_token; 14 | 15 | return $this; 16 | } 17 | 18 | public function amount(float $amount) 19 | { 20 | $this->form_data['amount'] = $amount; 21 | 22 | return $this; 23 | } 24 | 25 | public function reason(string $reason = '') 26 | { 27 | $this->form_data['deduction_details'] = $reason; 28 | 29 | return $this; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Api/Charge.php: -------------------------------------------------------------------------------- 1 | form_data['customer_token'] = $token; 14 | 15 | return $this; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Api/Checkout.php: -------------------------------------------------------------------------------- 1 | required_data['recurrence'] = "$weeks Week"; 10 | 11 | return $this; 12 | } 13 | 14 | public function chargeMonthly($months = 1) 15 | { 16 | $this->required_data['recurrence'] = "$months Month"; 17 | 18 | return $this; 19 | } 20 | 21 | public function chargeAnnually($years = 1) 22 | { 23 | $this->required_data['recurrence'] = "$years Year"; 24 | 25 | return $this; 26 | } 27 | 28 | public function forWeeks($weeks = 1) 29 | { 30 | $this->required_data['duration'] = "$weeks Week"; 31 | 32 | return $this; 33 | } 34 | 35 | public function forMonths($months = 1) 36 | { 37 | $this->required_data['duration'] = "$months Month"; 38 | 39 | return $this; 40 | } 41 | 42 | public function forYears($years = 1) 43 | { 44 | $this->required_data['duration'] = "$years Year"; 45 | 46 | return $this; 47 | } 48 | 49 | public function forForever() 50 | { 51 | $this->required_data['duration'] = 'Forever'; 52 | 53 | return $this; 54 | } 55 | 56 | public function renderView() 57 | { 58 | $action = $this->getFullApiUrl(); 59 | $data = $this->getFormData(); 60 | 61 | return view('payhere::recurring', compact('action', 'data')); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Api/Refund.php: -------------------------------------------------------------------------------- 1 | form_data['payment_id'] = $payment_id; 14 | 15 | return $this; 16 | } 17 | 18 | public function makePaymentAuthorizationRefund(string $authorization_token) 19 | { 20 | $this->form_data['authorization_token'] = $authorization_token; 21 | 22 | return $this; 23 | } 24 | 25 | public function note(string $note) 26 | { 27 | $this->form_data['description'] = $note; 28 | 29 | return $this; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Api/Retrieve.php: -------------------------------------------------------------------------------- 1 | url = $this->url.trim($order_id); 16 | 17 | return $this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Api/Subscription.php: -------------------------------------------------------------------------------- 1 | method = 'get'; 12 | $this->url = 'merchant/v1/subscription'; 13 | 14 | return $this->submit(); 15 | } 16 | 17 | public function getPaymentsOfSubscription(string $subscription_id) 18 | { 19 | $this->method = 'get'; 20 | $this->url = "merchant/v1/subscription/$subscription_id/payments"; 21 | 22 | return $this->submit(); 23 | } 24 | 25 | public function retry(string $subscription_id) 26 | { 27 | $this->method = 'post'; 28 | $this->url = 'merchant/v1/subscription/retry'; 29 | $this->form_data['subscription_id'] = $subscription_id; 30 | 31 | return $this->submit(); 32 | } 33 | 34 | public function cancel(string $subscription_id) 35 | { 36 | $this->method = 'post'; 37 | $this->url = 'merchant/v1/subscription/cancel'; 38 | $this->form_data['subscription_id'] = $subscription_id; 39 | 40 | return $this->submit(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Commands/PayHereCommand.php: -------------------------------------------------------------------------------- 1 | comment('All done'); 16 | 17 | return self::SUCCESS; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Controllers/CallbackController.php: -------------------------------------------------------------------------------- 1 | all())); 29 | 30 | break; 31 | 32 | case Checkout::getCallbackKey(): 33 | event(new CheckoutCallbackEvent($request->all())); 34 | 35 | break; 36 | 37 | case Recurring::getCallbackKey(): 38 | event(new RecurringCallbackEvent($request->all())); 39 | 40 | break; 41 | 42 | case PreApproval::getCallbackKey(): 43 | event(new PreApprovalCallbackEvent($request->all())); 44 | 45 | break; 46 | 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Controllers/FormController.php: -------------------------------------------------------------------------------- 1 | get('payload')); 12 | 13 | return view('payhere::recurring', [ 14 | 'action' => $payload['action'], 15 | 'data' => $payload['data'], 16 | ]); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Events/AuthorizeCallbackEvent.php: -------------------------------------------------------------------------------- 1 | payload = $payload; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Events/CheckoutCallbackEvent.php: -------------------------------------------------------------------------------- 1 | payload = $payload; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Events/PreApprovalCallbackEvent.php: -------------------------------------------------------------------------------- 1 | payload = $payload; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Events/RecurringCallbackEvent.php: -------------------------------------------------------------------------------- 1 | payload = $payload; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Exceptions/PayHereException.php: -------------------------------------------------------------------------------- 1 | json(['error' => true, 'message' => $this->getMessage()]); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Helpers/PayHereClient.php: -------------------------------------------------------------------------------- 1 | required_data = $array; 22 | 23 | return $this; 24 | } 25 | 26 | public function successUrl($url) 27 | { 28 | $this->success_url = $url; 29 | 30 | return $this; 31 | } 32 | 33 | public function failUrl($url) 34 | { 35 | $this->fail_url = $url; 36 | 37 | return $this; 38 | } 39 | 40 | private function setNotifyUrl() 41 | { 42 | $this->notify_url = route('payhere.callback', $this->getCallbackKey()); 43 | } 44 | 45 | public function notifyUrl(string $url) 46 | { 47 | $this->notify_url = $url; 48 | 49 | return $this; 50 | } 51 | 52 | public function setOptionalData($data) 53 | { 54 | $this->optional_data = $data; 55 | } 56 | 57 | private function authData() 58 | { 59 | return [ 60 | 'merchant_id' => config('payhere.merchant_id'), 61 | 'return_url' => $this->success_url, 62 | 'cancel_url' => $this->fail_url, 63 | 'notify_url' => $this->notify_url, 64 | ]; 65 | } 66 | 67 | public function getFormData() 68 | { 69 | $this->setNotifyUrl(); 70 | $hash = ['hash' => $this->getHashKey()]; 71 | 72 | return array_merge($this->authData(), $this->required_data, $this->optional_data, $hash); 73 | } 74 | 75 | public function getFullApiUrl() 76 | { 77 | return str(config('payhere.api_endpoint')) 78 | ->finish('/') 79 | ->append($this->url) 80 | ->toString(); 81 | } 82 | 83 | public function getFormUrl(): string 84 | { 85 | $action = $this->getFullApiUrl(); 86 | $data = $this->getFormData(); 87 | 88 | $payload = encrypt([ 89 | 'action' => $action, 90 | 'data' => $data, 91 | ]); 92 | 93 | return url('payhere/form?payload='.$payload); 94 | } 95 | 96 | public function renderView() 97 | { 98 | 99 | return redirect($this->getFormUrl()); 100 | 101 | } 102 | 103 | public static function getCallbackKey() 104 | { 105 | return base64_encode(get_called_class()); 106 | } 107 | 108 | public function getHashKey() 109 | { 110 | $vars = $this->authData(); 111 | 112 | $amount = $this->required_data['amount'] ?? 0; 113 | 114 | return strtoupper(md5( 115 | $vars['merchant_id'] 116 | .$this->required_data['order_id'] 117 | .number_format($amount, 2, '.', '') 118 | .$this->required_data['currency'] 119 | .strtoupper(md5(config('payhere.merchant_secret')))) 120 | ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Helpers/PayHereRestClient.php: -------------------------------------------------------------------------------- 1 | form_data = $data; 20 | 21 | return $this; 22 | } 23 | 24 | public function getAccessToken() 25 | { 26 | $url = $this->apiUrl('merchant/v1/oauth/token'); 27 | 28 | $data = Http::asForm() 29 | ->withToken($this->generateAuthCode(), 'Basic') 30 | ->post($url, [ 31 | 'grant_type' => 'client_credentials', 32 | ]); 33 | 34 | return $data->json()['access_token'] ?? null; 35 | } 36 | 37 | public function generateAuthCode() 38 | { 39 | return base64_encode(config('payhere.app_id').':'.config('payhere.app_secret')); 40 | } 41 | 42 | public function cachedAccessToken() 43 | { 44 | return Cache::remember('payhere-access-token', now()->addDays(7), function () { 45 | return $this->getAccessToken(); 46 | }); 47 | } 48 | 49 | public function generateHash($orderId, $amount, $currency = 'LKR') 50 | { 51 | return strtoupper( 52 | md5( 53 | config('payhere.merchant_id'). 54 | $orderId. 55 | number_format($amount, 2, '.', ''). 56 | $currency. 57 | strtoupper(md5(config('payhere.merchant_secret'))) 58 | ) 59 | ); 60 | } 61 | 62 | /** 63 | * @throws PayHereException 64 | */ 65 | public function submit() 66 | { 67 | if ($this->method == 'post') { 68 | $client = Http::asJson() 69 | ->withToken($this->cachedAccessToken()) 70 | ->post($this->apiUrl($this->url), $this->form_data); 71 | } else { 72 | $client = Http::withToken($this->cachedAccessToken()) 73 | ->get($this->apiUrl($this->url), $this->form_data); 74 | } 75 | 76 | $output = $client->json(); 77 | 78 | if (! $output) { 79 | throw new PayHereException('No data from API !'); 80 | } 81 | 82 | if (array_key_exists('error', $output)) { 83 | throw new PayHereException($output['error_description']); 84 | } 85 | 86 | if (array_key_exists('status', $output) && $output['status'] < 0) { 87 | throw new PayHereException($output['msg']); 88 | } 89 | 90 | return $output; 91 | } 92 | 93 | private function apiUrl($path = '') 94 | { 95 | return str(config('payhere.api_endpoint')) 96 | ->finish('/') 97 | ->append($path) 98 | ->toString(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/PayHere.php: -------------------------------------------------------------------------------- 1 | name('laravel-payhere') 20 | ->hasRoute('web') 21 | ->hasConfigFile() 22 | ->hasViews(); 23 | // ->hasMigration('create_laravel-payhere_table') 24 | // ->hasCommand(PayHereCommand::class); 25 | } 26 | } 27 | --------------------------------------------------------------------------------