laravel-paycorp maintained by hansajith18
laravel-paycorp
Laravel integration for the Paycorp (Bancstac) payment gateway.
Supports the Sampath/Paycorp Paycenter Web 4.0 API — Hosted Page flow, Real-Time tokenised card payments, and Refund/Void operations — with full database audit logging and PCI DSS safe-handling baked in.
Features
- Hosted Page flow — redirect payer to Paycorp-hosted card entry, receive callback
- Real-Time flow — charge previously tokenised (saved) cards without redirecting
- Refund & Void — session-authenticated back-office API with AES-256 encryption
- HMAC-SHA256 request signing — every outgoing request is signed
- Auto-registered log channel —
paycorp-YYYY-MM-DD.logwith zero config required - Sandbox mode — automatic amount normalisation for the Paycorp test environment
- Full gateway audit log — all requests/responses written to
payment_gateway_logstable - PCI DSS safe — never stores raw card numbers or CVVs; only masked card data persisted
- Laravel 11 & 12 support
Requirements
- PHP
^8.2 - Laravel
^10.0,^11.0,^12.0, or^13.0 ext-openssl
Installation
composer require hansajith18/laravel-paycorp
The service provider is auto-discovered. No manual registration required.
Publish config and migrations
php artisan paycorp:publish
Or publish individually:
# Config only
php artisan vendor:publish --tag=paycorp-config
# Migrations only
php artisan vendor:publish --tag=paycorp-migrations
Then run migrations:
php artisan migrate
Configuration
Add the following to your .env file:
# ── Required ──────────────────────────────────────────────────────────────────
PAYCORP_ENDPOINT=https://paycorp-smp.prod.aws.paycorp.lk/rest/service/proxy
PAYCORP_AUTH_TOKEN=your_auth_token
PAYCORP_HMAC_SECRET=your_hmac_secret
# ── Client IDs (provided by Paycorp per currency) ──────────────────────────────
PAYCORP_CLIENT_ID_LKR=your_lkr_client_id
PAYCORP_CLIENT_ID_USD=your_usd_client_id
# ── Tokenised payment client IDs (required for saved-card flow) ────────────────
PAYCORP_TOKEN_CLIENT_ID_LKR=your_tokenize_lkr_client_id
PAYCORP_TOKEN_CLIENT_ID_USD=your_tokenize_usd_client_id
# ── Return URL (Paycorp redirects here after card entry) ───────────────────────
PAYCORP_RETURN_URL="${APP_URL}/api/payments/paycorp/return"
# ── Optional ───────────────────────────────────────────────────────────────────
PAYCORP_DEFAULT_CURRENCY=LKR
PAYCORP_SANDBOX=false
PAYCORP_API_VERSION=1.04
PAYCORP_TIMEOUT=30
PAYCORP_CONNECT_TIMEOUT=10
PAYCORP_RETRY_TIMES=2
PAYCORP_RETRY_SLEEP_MS=500
# ── Refund / Void credentials (required only if using refund features) ─────────
PAYCORP_REFUND_ENDPOINT=https://paycorp-console.prod.aws.paycorp.lk
PAYCORP_REFUND_USERNAME=your_console_username
PAYCORP_REFUND_PASSWORD=your_console_password
PAYCORP_REFUND_IV_PHRASE=your_iv_phrase
PAYCORP_REFUND_AES_SECRET=your_aes_secret
PAYCORP_REFUND_DEVICE_ID=your_device_id
# ── Logging ────────────────────────────────────────────────────────────────────
PAYCORP_LOG_CHANNEL=paycorp
PAYCORP_LOG_LEVEL=debug
PAYCORP_LOG_DAYS=14
The package auto-registers a paycorp daily log channel — no changes to config/logging.php are needed. To redirect logs to an existing channel (e.g. stack), set PAYCORP_LOG_CHANNEL=stack.
Usage
Hosted Page Flow
1 — Initialize a payment
use Hansajith18\LaravelPaycorp\PaycorpClient;
$client = app(PaycorpClient::class);
// or: $client = app('paycorp');
$response = $client->initPayment(
amountCents: 50000, // 500.00 LKR
clientRef: 'ORDER-' . $orderId,
returnUrl: route('payments.callback'),
currency: 'LKR',
);
// Redirect the user to the Paycorp-hosted payment page
return redirect($response->paymentPageUrl);
2 — Complete the payment (callback route)
use Hansajith18\LaravelPaycorp\PaycorpClient;
$client = app(PaycorpClient::class);
$reqId = $request->query('reqid');
$response = $client->completePayment($reqId, 'LKR');
if ($response->isApproved()) {
$txnRef = $response->txnReference;
$last4 = $response->cardLast4(); // e.g. "4564"
// fulfil the order…
} else {
// $response->responseCode, $response->responseText
}
Tokenization (Save a Card)
To save a card during payment, pass tokenize: true to the service-level initializePayment:
use Hansajith18\LaravelPaycorp\Services\PaycenterPaymentService;
use Hansajith18\LaravelPaycorp\Enums\PaymentPurposeEnum;
$service = app(PaycenterPaymentService::class);
$payment = $service->initializePayment(
userId: auth()->id(),
amountCents: 100, // small verification charge — auto-voided after card saved
purpose: PaymentPurposeEnum::CARD_REGISTRATION,
currency: 'LKR',
tokenize: true,
);
return redirect($payment->payment_page_url);
After the callback, the token is stored in saved_payments. The verification charge is automatically voided by the VoidTokenizationCharge queued job.
Real-Time Charge (Saved Card)
use Hansajith18\LaravelPaycorp\Models\SavedPayment;
use Hansajith18\LaravelPaycorp\Services\PaycenterPaymentService;
use Hansajith18\LaravelPaycorp\Enums\PaymentPurposeEnum;
$savedPayment = SavedPayment::where('user_id', auth()->id())->firstOrFail();
$service = app(PaycenterPaymentService::class);
$payment = $service->chargeWithSavedPayment(
userId: auth()->id(),
amountCents: 50000,
purpose: PaymentPurposeEnum::RIDE_PAYMENT,
savedPayment: $savedPayment,
currency: 'LKR',
);
// $payment->status === GatewayPaymentStatusEnum::COMPLETED on success
Refund
$response = $client->refund(
originalTxnReference: $txnReference,
amountCents: 50000,
clientRef: 'REFUND-' . $refundId,
currency: 'LKR',
comment: 'Customer request',
);
if ($response->isApproved()) {
// refund successful
}
Events
Listen to payment outcomes in your EventServiceProvider:
use Hansajith18\LaravelPaycorp\Events\PaymentSucceeded;
use Hansajith18\LaravelPaycorp\Events\PaymentFailed;
Event::listen(PaymentSucceeded::class, function ($event) {
$payment = $event->payment;
// fulfil order, send receipt, etc.
});
Event::listen(PaymentFailed::class, function ($event) {
// notify user, release reservation, etc.
});
Payable Status Auto-Update (Optional)
If you want the package to automatically update a payment_status column on a related payable model when a payment completes or fails, list the model class base-names in config/paycorp.php:
// config/paycorp.php
'payable_status_classes' => ['Order', 'Booking'],
Leave the array empty (default) and handle status updates in your own event listeners instead.
Database Tables
| Table | Purpose |
|---|---|
payments |
One row per payment attempt; tracks status, masked card info, gateway references |
payment_gateway_logs |
Full request/response audit trail per gateway operation |
saved_payments |
Tokenised cards per user (for Real-Time flow) |
Sandbox Mode
Set PAYCORP_SANDBOX=true when using the Paycorp test environment. The package automatically:
- Strips sub-unit cents (e.g.
5389.20 LKR → 5389.00 LKR) - Enforces a minimum amount of
200cents (2.00 LKR)
No code changes needed between sandbox and production.
Testing
composer test
Or directly:
./vendor/bin/phpunit
Quality Tooling
# Code style (Laravel Pint)
composer format
# Static analysis (PHPStan level 5)
composer analyse
Security
- All API requests are signed with HMAC-SHA256
- TLS verification enforced (
verify: true) — HTTP connections always use TLS - Card numbers and CVVs are never stored — only masked card data persisted
toSafeArray()explicitly excludes cardholder name, raw expiry, and internal comments- Refund credentials are AES-256-CBC encrypted before transmission
- Gateway audit logs sanitise all sensitive fields before writing to the database
Please see SECURITY.md for how to report a security vulnerability.
Changelog
See CHANGELOG.md for release history.
For maintainers: see docs/releases.md for the full release workflow guide.
License
The MIT License (MIT). See LICENSE for details.