├── .gitignore
├── src
├── Interfaces
│ └── RecurringPaymentInterface.php
├── resources
│ └── views
│ │ ├── loader.blade.php
│ │ ├── failurl.blade.php
│ │ ├── okurl.blade.php
│ │ ├── error.blade.php
│ │ └── index.blade.php
├── Http
│ ├── Requests
│ │ └── HandleRecurringPaymentRequest.php
│ └── Controllers
│ │ ├── RecurringPaymentController.php
│ │ └── CasysController.php
├── config
│ └── casys.php
├── routes
│ └── casys.php
├── CasysServiceProvider.php
└── Service
│ ├── RecurringPayment.php
│ └── Casys.php
├── phpstan.neon
├── rector.php
├── .github
└── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── composer.json
├── tests
└── Feature
│ └── CasysTest.php
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Project exclude paths
2 | .idea
3 | vendor
4 | composer.lock
--------------------------------------------------------------------------------
/src/Interfaces/RecurringPaymentInterface.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | public function sendPayment(string $merchantID, string $rpRef, string $rpRefID, int $amount, string $password): array;
11 | }
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | includes:
2 | - vendor/larastan/larastan/extension.neon
3 | - vendor/nesbot/carbon/extension.neon
4 |
5 | parameters:
6 |
7 | paths:
8 | - src/
9 | - config/
10 | # Level 10 is the highest level
11 | level: 10
12 |
13 | ignoreErrors:
14 | - '#Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''.#'
15 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | withPaths([
9 | __DIR__ . '/src',
10 | __DIR__ . '/tests',
11 | ])
12 | // uncomment to reach your current PHP version
13 | // ->withPhpSets()
14 | ->withTypeCoverageLevel(50)
15 | ->withDeadCodeLevel(51)
16 | ->withCodeQualityLevel(72);
17 |
--------------------------------------------------------------------------------
/src/resources/views/loader.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
20 |
21 |
22 | Please wait...
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/src/Http/Requests/HandleRecurringPaymentRequest.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | public function rules(): array
15 | {
16 | return [
17 | 'merchant_id' => 'required|string',
18 | 'rp_ref' => 'required|string',
19 | 'rp_ref_id' => 'required|string',
20 | 'amount' => 'required|numeric',
21 | 'password' => 'required|string',
22 | ];
23 | }
24 |
25 | /**
26 | * Authorize the request.
27 | */
28 | public function authorize(): bool
29 | {
30 | return true;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/config/casys.php:
--------------------------------------------------------------------------------
1 | env('PAY_TO_MERCHANT'),
13 | 'MerchantName' => env('MERCHANT_NAME'),
14 | 'AmountCurrency' => env('AMOUNT_CURRENCY', 'MKD'),
15 | 'PaymentOKURL' => env('PAYMENT_OK_URL', 'paymentOKURL'),
16 | 'PaymentFailURL' => env('PAYMENT_FAIL_URL', 'paymentFailURL'),
17 | 'Password' => env('CASYS_TOKEN', 'TEST_PASS'),
18 | 'RecurrentPaymentWsdl' => env('RECURRING_PAYMENT_WSDL', 'https://www.cpay.com.mk/Recurring/RecurringPaymentsWS.wsdl'),
19 |
20 | ];
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/src/resources/views/failurl.blade.php:
--------------------------------------------------------------------------------
1 | @extends('layouts.app')
2 |
3 | @section('content')
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
Unsuccessful transaction
15 |
16 |
17 |
18 |
19 |
20 |
21 |
26 | @endsection
27 |
--------------------------------------------------------------------------------
/src/resources/views/okurl.blade.php:
--------------------------------------------------------------------------------
1 | @extends('layouts.app')
2 | @section('content')
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
Successful transaction
14 |
15 |
16 |
17 |
18 |
19 |
24 |
25 | @endsection
26 |
--------------------------------------------------------------------------------
/src/resources/views/error.blade.php:
--------------------------------------------------------------------------------
1 | @extends('layouts.app')
2 | @section('content')
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
Payment error
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
25 | @endsection
26 |
--------------------------------------------------------------------------------
/src/routes/casys.php:
--------------------------------------------------------------------------------
1 | name('loader');
19 | Route::post('payment', [CasysController::class, 'getCasys'])->name('validateAndPay');
20 | Route::post('paymentOKURL', [CasysController::class, 'success'])->name('paymentOKURL');
21 | Route::post('paymentFailURL', [CasysController::class, 'fail'])->name('paymentFailURL');
22 |
23 | Route::post('recurring-payment', [RecurringPaymentController::class, 'handleRecurringPayment'])->name('recurring.payment');
24 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kalimeromk/casys-laravel",
3 | "description": "Integration of casys payment method",
4 | "require": {
5 | "php": ">=8.0",
6 | "ext-json": "*",
7 | "ext-soap": "*"
8 | },
9 | "keywords": [
10 | "laravel",
11 | "casys"
12 | ],
13 | "license": "MIT",
14 | "authors": [
15 | {
16 | "name": "Zoran Shefot Bogoevski",
17 | "email": "zbogoevski@gmail.com"
18 | }
19 | ],
20 | "autoload": {
21 | "psr-4": {
22 | "Kalimero\\Casys\\": "src/",
23 | "Kalimero\\Casys\\Tests\\": "tests/"
24 | }
25 | },
26 | "scripts": {
27 | "post-autoload-dump": [
28 | "@php ./vendor/bin/testbench package:discover --ansi"
29 | ],
30 | "test": "vendor/bin/phpunit"
31 | },
32 | "autoload-dev": {
33 | "psr-4": {
34 | "Kalimero\\Casys\\Tests\\": "tests/"
35 | }
36 | },
37 | "extra": {
38 | "laravel": {
39 | "providers": [
40 | "Kalimero\\Casys\\CasysServiceProvider"
41 | ]
42 | }
43 | },
44 | "require-dev": {
45 | "orchestra/testbench": "^5.0|^6.0|^7.0|8.0|^9.0",
46 | "phpunit/phpunit": "^8.0|^9.0|10.0|^11.0",
47 | "mockery/mockery": "^1.4|^1.5|^1.6|^1.7",
48 | "larastan/larastan": "^3.0",
49 | "rector/rector": "^2.1.0"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Http/Controllers/RecurringPaymentController.php:
--------------------------------------------------------------------------------
1 | recurringPayment = $recurringPayment;
17 | }
18 |
19 | /**
20 | * Handle the recurring payment request.
21 | */
22 | public function handleRecurringPayment(HandleRecurringPaymentRequest $request): JsonResponse
23 | {
24 | $validated = $request->validated();
25 |
26 | $merchantId = is_string($validated['merchant_id']) ? $validated['merchant_id'] : '';
27 | $rpRef = is_string($validated['rp_ref']) ? $validated['rp_ref'] : '';
28 | $rpRefId = is_string($validated['rp_ref_id']) ? $validated['rp_ref_id'] : '';
29 | $amount = is_int($validated['amount']) ? $validated['amount'] : 0;
30 | $password = is_string($validated['password']) ? $validated['password'] : '';
31 |
32 | $response = $this->recurringPayment->sendPayment(
33 | $merchantId,
34 | $rpRef,
35 | $rpRefId,
36 | $amount,
37 | $password
38 | );
39 |
40 | return response()->json($response);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/Feature/CasysTest.php:
--------------------------------------------------------------------------------
1 | get(route('casys.index'));
16 |
17 | $response->assertStatus(200);
18 | $response->assertViewIs('casys::loader');
19 | }
20 |
21 | public function test_it_returns_casys_data_on_getCasys(): void
22 | {
23 | $client = $this->faker->word;
24 | $amount = $this->faker->randomNumber(2);
25 |
26 | $response = $this->get(route('casys.getCasys', ['client' => $client, 'amount' => $amount]));
27 |
28 | $response->assertStatus(200);
29 | $response->assertViewIs('casys::index');
30 | $response->assertViewHas('casys');
31 | }
32 |
33 | public function test_it_returns_success_view_on_success(): void
34 | {
35 | $response = $this->get(route('casys.success'));
36 |
37 | $response->assertStatus(200);
38 | $response->assertViewIs('casys::okurl');
39 | $response->assertViewHas('success', 'You transaction was successful');
40 | }
41 |
42 | public function test_it_returns_fail_view_on_fail(): void
43 | {
44 | $response = $this->get(route('casys.fail'));
45 |
46 | $response->assertStatus(200);
47 | $response->assertViewIs('casys::failurl');
48 | $response->assertViewHas('success', 'Your transaction failed');
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/CasysServiceProvider.php:
--------------------------------------------------------------------------------
1 | loadViewsFrom(__DIR__ . '/resources/views', 'casys');
19 | $this->loadRoutesFrom(__DIR__ . '/routes/casys.php');
20 |
21 | $this->publishes([
22 | __DIR__ . '/resources/views' => resource_path('views/vendor/casys'),
23 | ]);
24 | }
25 |
26 | /**
27 | * Register the service provider.
28 | */
29 | public function register(): void
30 | {
31 | $this->registerConfig();
32 |
33 | $this->app->singleton(RecurringPaymentInterface::class, function (): RecurringPayment {
34 | $wsdl = config('casys.RecurrentPaymentWsdl');
35 |
36 | // Ensure the WSDL is valid
37 | if (!is_string($wsdl) || ($wsdl === '' || $wsdl === '0')) {
38 | throw new InvalidArgumentException('The WSDL URL for the Recurrent Payment service must be a valid non-empty string.');
39 | }
40 |
41 | $soapClient = new SoapClient($wsdl);
42 |
43 | return new RecurringPayment($soapClient);
44 | });
45 | }
46 |
47 | /**
48 | * Register package config.
49 | */
50 | protected function registerConfig(): void
51 | {
52 | $this->mergeConfigFrom(__DIR__ . '/config/casys.php', 'casys');
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Http/Controllers/CasysController.php:
--------------------------------------------------------------------------------
1 | casys = $casys;
22 | }
23 |
24 | /**
25 | * Display the payment loader view.
26 | */
27 | public function index(): View|Factory|Application
28 | {
29 | /** @var view-string $viewName */
30 | $viewName = 'casys::loader';
31 | return view($viewName);
32 | }
33 |
34 | /**
35 | * Generate and display the payment data.
36 | *
37 | * @param stdClass $client An object containing client data (name, last_name, country, email).
38 | * @param float $amount The amount to be paid.
39 | */
40 | public function getCasys(stdClass $client, float $amount): View|Factory|Application
41 | {
42 | $casysData = $this->casys->getCasysData($client, $amount);
43 | /** @var view-string $viewName */
44 | $viewName = 'casys::index';
45 | return view($viewName, ['casysData' => $casysData]);
46 | }
47 |
48 | /**
49 | * Handle successful payment.
50 | */
51 | public function success(): View|Factory|Application
52 | {
53 | /** @var view-string $viewName */
54 | $viewName = 'casys::okurl';
55 | return view($viewName)->with('success', 'Your transaction was successful');
56 | }
57 |
58 | /**
59 | * Handle failed payment.
60 | */
61 | public function fail(): View|Factory|Application
62 | {
63 | /** @var view-string $viewName */
64 | $viewName = 'casys::failurl';
65 | return view($viewName)->with('error', 'Your transaction failed');
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Service/RecurringPayment.php:
--------------------------------------------------------------------------------
1 | soapClient = $soapClient;
19 | }
20 |
21 | /**
22 | * Perform a recurring payment.
23 | *
24 | * @return array{success: bool, payment_reference?: string, error_description?: string}
25 | * @throws Exception
26 | */
27 | public function sendPayment(string $merchantID, string $rpRef, string $rpRefID, int $amount, string $password): array
28 | {
29 | $params = [
30 | 'Amount' => $amount,
31 | 'MerchantID' => $merchantID,
32 | 'RPRef' => $rpRef,
33 | 'RPRefID' => $rpRefID,
34 | 'MD5' => md5($merchantID . $rpRefID . $rpRef . $amount . $password),
35 | ];
36 |
37 | try {
38 | $response = $this->soapClient->__soapCall('sendPayment', [$params]);
39 |
40 | if (is_object($response) && isset($response->Success)) {
41 | $paymentReference = '';
42 | if (isset($response->CPayPaymentRef)) {
43 | $paymentReference = is_string($response->CPayPaymentRef) ? $response->CPayPaymentRef : '';
44 | }
45 |
46 | $errorDescription = 'Unknown error';
47 | if (isset($response->ErrorDecription)) {
48 | $errorDescription = is_string($response->ErrorDecription) ? $response->ErrorDecription : 'Unknown error';
49 | }
50 |
51 | return $response->Success
52 | ? ['success' => true, 'payment_reference' => $paymentReference]
53 | : ['success' => false, 'error_description' => $errorDescription];
54 | }
55 |
56 | return ['success' => false, 'error_description' => 'Invalid response format from SOAP service'];
57 | } catch (Exception $e) {
58 | return ['success' => false, 'error_description' => $e->getMessage()];
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/resources/views/index.blade.php:
--------------------------------------------------------------------------------
1 | @extends('layouts.app')
2 |
3 | @section('content')
4 |
43 | @endsection
44 | @push('scripts')
45 |
50 | @endpush
51 |
--------------------------------------------------------------------------------
/src/Service/Casys.php:
--------------------------------------------------------------------------------
1 | , user: array, checkSumHeader: string}
16 | */
17 | public function getCasysData(stdClass $client, float $amount): array
18 | {
19 | $requiredData = [
20 | 'AmountToPay' => (int) round($amount) > 0 ? (int) round($amount) * 100 : null,
21 | 'PayToMerchant' => config('casys.PayToMerchant'),
22 | 'MerchantName' => config('casys.MerchantName'),
23 | 'AmountCurrency' => config('casys.AmountCurrency'),
24 | 'Details1' => Str::random(12),
25 | 'Details2' => 'Price ' . round($amount) . (is_string(config('casys.AmountCurrency')) ? config('casys.AmountCurrency') : ''),
26 | 'PaymentOKURL' => config('casys.PaymentOKURL'),
27 | 'PaymentFailURL' => config('casys.PaymentFailURL'),
28 | 'OriginalAmount' => (int) round($amount),
29 | 'OriginalCurrency' => config('casys.AmountCurrency'),
30 | ];
31 |
32 | $userData = [
33 | 'FirstName' => $client->name,
34 | 'LastName' => $client->last_name,
35 | 'Country' => $client->country,
36 | 'Email' => $client->email,
37 | ];
38 |
39 | $checkSumHeader = implode(',', array_merge(array_keys($requiredData), array_keys($userData)));
40 | $checkSumHeaderLengths = implode('', array_merge(array_values($requiredData), array_values($userData)));
41 | $password = config('casys.Password');
42 | $passwordString = is_string($password) ? $password : '';
43 | $checkSumHeaderParams = $checkSumHeaderLengths . md5($passwordString);
44 |
45 | foreach ($requiredData as $value) {
46 | $checkSumHeaderParams .= is_null($value) ? '' : (is_scalar($value) ? (string)$value : '');
47 | }
48 |
49 | foreach ($userData as $value) {
50 | $checkSumHeaderParams .= is_null($value) ? '' : (is_scalar($value) ? (string)$value : '');
51 | }
52 |
53 | return [
54 | 'checkSum' => $checkSumHeaderParams,
55 | 'required' => $requiredData,
56 | 'user' => $userData,
57 | 'checkSumHeader' => $checkSumHeader . $checkSumHeaderLengths,
58 | ];
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Casys Laravel Package
3 |
4 | This is a package to integrate the Casys payment gateway into Laravel. It generates complete scaffolding for simple integration, including support for recurring payments.
5 |
6 | ## Features
7 | 1. **Views**
8 | - `resources/views/vendor/casys`
9 | 2. **Configuration**
10 | - `config/casys.php`
11 | 3. **Controllers**
12 | - `/Http/Controllers/CasysController`
13 | - `/Http/Controllers/RecurringPaymentController`
14 | 4. **Recurring Payment Support**
15 | - `/Http/Services/RecurringPayment.php`
16 | - Example integration for managing recurring payments using SOAP services.
17 | 5. **Routes**
18 | - Predefined routes for standard and recurring payments.
19 |
20 | ---
21 |
22 | ## Installation
23 |
24 | Require this package by running:
25 |
26 | ```bash
27 | composer require kalimeromk/casys-laravel
28 | ```
29 |
30 | After installation, publish the package files:
31 |
32 | ```bash
33 | php artisan vendor:publish --provider="Kalimero\Casys\CasysServiceProvider"
34 | ```
35 |
36 | This will publish the following files:
37 | - `config/casys.php`
38 | - `resources/views/vendor/casys`
39 |
40 | ---
41 |
42 | ## Laravel Setup
43 |
44 | Register the route file in your `RouteServiceProvider` or add the following routes to your existing route file:
45 |
46 | ### Standard Payment Routes
47 | ```php
48 | use App\Http\Controllers\CasysController;
49 |
50 | Route::get('paymentLoader', [CasysController::class, 'index'])->name('loader');
51 | Route::post('payment', [CasysController::class, 'getCasys'])->name('validateAndPay');
52 | Route::post('paymentOKURL', [CasysController::class, 'success'])->name('paymentOKURL');
53 | Route::post('paymentFailURL', [CasysController::class, 'fail'])->name('paymentFailURL');
54 | ```
55 |
56 | ### Recurring Payment Routes
57 | ```php
58 | use KalimeroMK\Casys\Controllers\RecurringPaymentController;
59 |
60 | Route::post('/recurring-payment', [RecurringPaymentController::class, 'handleRecurringPayment'])->name('recurring.payment');
61 | ```
62 |
63 | For Laravel <=7, use the controller string syntax:
64 | ```php
65 | Route::get('paymentLoader', 'CasysController@index')->name('loader');
66 | Route::post('payment', 'CasysController@getCasys')->name('validateAndPay');
67 | Route::post('paymentOKURL', 'CasysController@success')->name('paymentOKURL');
68 | Route::post('paymentFailURL', 'CasysController@fail')->name('paymentFailURL');
69 | ```
70 |
71 | ---
72 |
73 | ## How to Use
74 |
75 | ### Configuration
76 | Add your credentials to the `.env` file:
77 | ```env
78 | PAY_TO_MERCHANT=your_merchant_id
79 | MERCHANT_NAME=your_merchant_name
80 | AMOUNT_CURRENCY=MKD
81 | PAYMENT_OK_URL=your_success_url
82 | PAYMENT_FAIL_URL=your_fail_url
83 | CASYS_TOKEN=your_token
84 | ```
85 |
86 | ### Standard Payments
87 | For standard payments, simply pass the amount and client data to the appropriate method in the `CasysController`. The views provided by the package will handle the UI. If you wish to customize the views, edit the published views in `resources/views/vendor/casys`.
88 |
89 | ---
90 |
91 | ### Recurring Payments
92 |
93 | The package includes support for recurring payments through the `RecurringPayment` class and `RecurringPaymentController`. Here’s how you can integrate it:
94 |
95 | #### **Recurring Payment Parameters**
96 | The recurring payment requires the following parameters:
97 | - **RPRef**: A string containing details about the recurring payment, formatted as:
98 | ```plaintext
99 | RPRef = RequestType,BillingCycle,MaxBCycles,BillingAmount,BillingCycleStart
100 | ```
101 | - **RPRefID**: Unique ID returned during the initial registration of the recurring transaction.
102 |
103 | #### **Example Workflow**
104 | 1. **Initial Registration**
105 | The cardholder performs the initial transaction with the `RPRef` parameter set. The system returns an `RPRefID`, which you should store for future recurring payments.
106 |
107 | 2. **Subsequent Payments**
108 | Use the stored `RPRefID` to initiate subsequent payments without user involvement.
109 |
110 | #### **Example Request**
111 | Call the `/recurring-payment` endpoint with the following payload:
112 | ```json
113 | {
114 | "merchant_id": "YourMerchantID",
115 | "rp_ref": "R,1M,12,500000,20240101",
116 | "rp_ref_id": "UniqueRPRefID",
117 | "amount": 500000,
118 | "password": "YourMerchantPassword"
119 | }
120 | ```
121 |
122 | #### **Example Recurring Payment Integration**
123 | You can use the `RecurringPayment` service class in your application:
124 |
125 | ```php
126 | use KalimeroMK\Casys\Services\RecurringPayment;
127 |
128 | $recurringPayment = new RecurringPayment();
129 |
130 | $response = $recurringPayment->sendPayment(
131 | $merchantId,
132 | $rpRef,
133 | $rpRefId,
134 | $amount,
135 | $password
136 | );
137 |
138 | if ($response['success']) {
139 | echo "Recurring payment successful! Reference: " . $response['payment_reference'];
140 | } else {
141 | echo "Recurring payment failed: " . $response['error_description'];
142 | }
143 | ```
144 |
145 | ---
146 |
147 | ### Provided Controllers
148 |
149 | #### **CasysController**
150 | Handles the standard payment flow, including:
151 | - Loading the payment page (`paymentLoader` route).
152 | - Validating and processing payments (`payment` route).
153 | - Handling success (`paymentOKURL` route) and failure (`paymentFailURL` route) callbacks.
154 |
155 | #### **RecurringPaymentController**
156 | Handles recurring payment requests via the `/recurring-payment` route. Example integration is included for making SOAP calls to the Casys gateway.
157 |
158 | ---
159 |
160 | ## Info
161 |
162 | This package is in an alpha stage and is designed to be flexible for your specific needs. **Suggestions are welcome!**
163 |
--------------------------------------------------------------------------------