emre-tarhan / kuveytturk-vpos-laravel-sdk
Kuveyt Türk Sanal Pos (Virtual POS) Laravel SDK with 3D Secure 2.0 support
Package info
github.com/emre-tarhan/kuveytturk-vpos-laravel-sdk
pkg:composer/emre-tarhan/kuveytturk-vpos-laravel-sdk
Requires
- php: ^8.1|^8.2|^8.3|^8.4
- illuminate/support: ^12.0|^13.0
- laravel/framework: ^12.0|^13.0
Requires (Dev)
- mockery/mockery: ^1.6
- orchestra/testbench: ^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
This package is not auto-updated.
Last update: 2026-04-17 23:14:46 UTC
README
A comprehensive Laravel SDK for Kuveyt Türk Virtual POS (Sanal Pos) integration with full 3D Secure 2.0 support.
Features
- ✅ 3D Secure 2.0 Full Support — Two-phase payment flow
- ✅ Two-Phase Payment: Card verification (Kart Doğrulama) + Provision (Ödeme Alma)
- ✅ Installment (Taksit) Support — Up to 24 installments
- ✅ Deferred Payment (Harcama Öteleme) — Flexible payment scheduling
- ✅ Hash Data Validation — SHA1-based request authentication
- ✅ Test & Production Environments — Separate endpoints
- ✅ Sale / Cancel / Refund / Partial Refund Operations — Full transaction lifecycle
- ✅ WCF SOAP Service Integration — For cancel/refund operations
- ✅ Comprehensive Error Handling — All Kuveyt Türk error codes
- ✅ Laravel 12.x & 13.x Compatible
- ✅ PHP 8.1, 8.2, 8.3, 8.4 Support
Installation
composer require emre-tarhan/kuveytturk-vpos-laravel-sdk
Publish Configuration
php artisan vendor:publish --tag=kuveytturk-vpos-config
Or use the install command:
php artisan kuveytturk:install
This will publish the config file to config/kuveytturk-vpos.php.
Configuration
Add your Kuveyt Türk credentials to .env:
KUVEYTTURK_MERCHANT_ID=your_merchant_id KUVEYTTURK_CUSTOMER_ID=your_customer_id KUVEYTTURK_USERNAME=your_api_username KUVEYTTURK_PASSWORD=your_api_password KUVEYTTURK_ENVIRONMENT=test # or 'production'
Or configure in config/kuveytturk-vpos.php:
return [ 'merchant_id' => env('KUVEYTTURK_MERCHANT_ID', ''), 'customer_id' => env('KUVEYTTURK_CUSTOMER_ID', ''), 'username' => env('KUVEYTTURK_USERNAME', ''), 'password' => env('KUVEYTTURK_PASSWORD', ''), 'environment' => env('KUVEYTTURK_ENVIRONMENT', 'test'), 'timeout' => env('KUVEYTTURK_TIMEOUT', 60), 'connect_timeout' => env('KUVEYTTURK_CONNECT_TIMEOUT', 30), ];
Usage
Basic Sale (Complete 3D Flow)
use EmreTarhan\KuveytTurkVPos\Services\KuveytTurkVPos; use EmreTarhan\KuveytTurkVPos\Dto\SaleRequest; use EmreTarhan\KuveytTurkVPos\Enums\CurrencyCode; // Inject via service container or resolve from app $vpos = app(KuveytTurkVPos::class); $request = new SaleRequest( merchantOrderId: 'ORDER-' . time(), amount: 100.00, // 100 TL - will be converted to 10000 internally okUrl: 'https://yoursite.com/payment/success', failUrl: 'https://yoursite.com/payment/fail', cardHolderName: 'John Doe', cardNumber: '5188961939192544', cardExpireDateYear: '2025', cardExpireDateMonth: '06', cardCVV2: '929', email: 'customer@example.com', clientIp: request()->ip(), billAddrCity: 'Istanbul', billAddrCountry: '792', // Turkey country code billAddrLine1: 'Sample Street No:123', billAddrPostCode: '34000', billAddrState: '34', Cc: 'John Doe', subscriber: 'customer@example.com', currencyCode: CurrencyCode::TRY, installmentCount: 0, // 0 = no installment, 1-24 for installment ); $response = $vpos->sale($request); if ($response->isSuccessful()) { // Payment successful echo "Transaction approved! Order ID: " . $response->orderId; } else { // Payment failed echo "Payment failed: " . $response->responseMessage; }
Two-Phase Payment (Manual Control)
If you need more control over the payment flow:
Phase 1: Card Verification
use EmreTarhan\KuveytTurkVPos\Dto\VerifyCardRequest; $verifyRequest = new VerifyCardRequest( merchantOrderId: 'ORDER-' . time(), amount: 150.00, okUrl: 'https://yoursite.com/payment/success', failUrl: 'https://yoursite.com/payment/fail', cardHolderName: 'John Doe', cardNumber: '5188961939192544', cardExpireDateYear: '2025', cardExpireDateMonth: '06', cardCVV2: '929', email: 'customer@example.com', clientIp: request()->ip(), billAddrCity: 'Istanbul', billAddrCountry: '792', billAddrLine1: 'Sample Street No:123', billAddrPostCode: '34000', billAddrState: '34', Cc: 'John Doe', subscriber: 'customer@example.com', currencyCode: CurrencyCode::TRY, installmentCount: 3, // 3 months installment ); $verifyResponse = $vpos->verifyCard($verifyRequest); // Store MD value for Phase 2 $mdValue = $verifyResponse->md; // You'll need this for provision $orderId = $verifyResponse->orderId;
Phase 2: Provision (Complete Payment)
use EmreTarhan\KuveytTurkVPos\Dto\ProvisionRequest; $provisionRequest = new ProvisionRequest( merchantOrderId: 'ORDER-' . time(), amount: 150.00, md: $mdValueFromPhase1, okUrl: 'https://yoursite.com/payment/success', failUrl: 'https://yoursite.com/payment/fail', currencyCode: CurrencyCode::TRY, installmentCount: 3, ); $provisionResponse = $vpos->provision($provisionRequest); if ($provisionResponse->isSuccessful()) { // Payment completed echo "Provision successful! RRN: " . $provisionResponse->rrn; }
Cancel Transaction (Same-Day)
use EmreTarhan\KuveytTurkVPos\Dto\CancelRequest; use EmreTarhan\KuveytTurkVPos\Enums\CurrencyCode; $cancelRequest = new CancelRequest( merchantOrderId: 'ORDER-12345', amount: 100.00, provisionNumber: '123456', // From original sale rrn: '789012345', // Reference number stan: '654321', // System trace number orderId: 'original-order-id', currencyCode: CurrencyCode::TRY, ); $cancelResponse = $vpos->cancel($cancelRequest); if ($cancelResponse->isSuccessful()) { echo "Transaction cancelled successfully!"; }
Refund (After Batch Close)
use EmreTarhan\KuveytTurkVPos\Dto\RefundRequest; $refundRequest = new RefundRequest( merchantOrderId: 'ORDER-12345', amount: 100.00, provisionNumber: '123456', rrn: '789012345', stan: '654321', orderId: 'original-order-id', currencyCode: CurrencyCode::TRY, ); $refundResponse = $vpos->refund($refundRequest); if ($refundResponse->isSuccessful()) { echo "Refund processed successfully!"; }
Partial Refund
use EmreTarhan\KuveytTurkVPos\Dto\PartialRefundRequest; $partialRefundRequest = new PartialRefundRequest( merchantOrderId: 'ORDER-12345', amount: 50.00, // Partial amount provisionNumber: '123456', rrn: '789012345', stan: '654321', orderId: 'original-order-id', currencyCode: CurrencyCode::TRY, ); $partialRefundResponse = $vpos->partialRefund($partialRefundRequest); if ($partialRefundResponse->isSuccessful()) { echo "Partial refund processed!"; }
Response Objects
SaleResponse
| Property | Description |
|---|---|
isSuccessful() |
Boolean - true if transaction approved |
responseCode |
Kuveyt Türk response code ("00" = success) |
responseMessage |
Human-readable response message |
orderId |
Bank's order ID |
provisionNumber |
Authorization number |
rrn |
Reference number |
stan |
System trace number |
transactionTime |
Transaction timestamp |
merchantOrderId |
Your order ID |
md |
3D Secure MD value |
Error Handling
use EmreTarhan\KuveytTurkVPos\Services\KuveytTurkVPos; use EmreTarhan\KuveytTurkVPos\Exceptions\KuveytTurkVPosException; use EmreTarhan\KuveytTurkVPos\Enums\ErrorCode; try { $response = $vpos->sale($request); if ($response->isSuccessful()) { // Handle success } else { // Handle business failure $errorCode = ErrorCode::fromString($response->responseCode); echo "Error: " . $errorCode->label(); } } catch (KuveytTurkVPosException $e) { // Handle connection/validation errors echo "Error: " . $e->getMessage(); }
Error Codes
| Code | Turkish | English |
|---|---|---|
| 00 | Otorizasyon verildi | Transaction approved |
| 01 | Geçersiz kart numarası | Invalid card number |
| 05 | Yetersiz bakiye | Insufficient balance |
| 12 | Geçersiz üye işyeri | Invalid merchant |
| 14 | Geçersiz işlem / CVV | Invalid transaction / CVV |
| 17 | Kart limiti aşıldı | Card limit exceeded |
| 54 | Kart süresi dolmuş | Card expired |
| 59 | Sahtekarlık şüphesi | Fraud suspected |
| 70 | Mükerrer sipariş | Duplicate order |
| 91 | Zaman aşımı | Timeout |
| 96 | Sistem hatası | System error |
| 99 | Hashdata uyuşmazlığı | Hash mismatch |
Amount Format
All amounts must be provided as actual values (e.g., 100.50 for 100 TL 50 kurus).
The SDK automatically converts to bank's format (amount × 100, e.g., 10050 for 100.50 TL).
// Both work the same: $request = new SaleRequest(..., amount: 100.50, ...); $request = new SaleRequest(..., amount: '100.50', ...); $request = new SaleRequest(..., amount: 10050, ...); // Already in bank's format
Test Environment
Use the following test card for sandbox testing:
| Field | Value |
|---|---|
| Card Number | 5188 9619 3919 2544 |
| CVV | 929 |
| Expiry | 06/25 |
| 3D Password | 123456 |
Test Endpoints:
- Card Verification:
https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home/ThreeDModelPayGate - Provision:
https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home/ThreeDModelProvisionGate
Production Endpoints:
- Card Verification:
https://sanalpos.kuveytturk.com.tr/ServiceGateWay/Home/ThreeDModelPayGate - Provision:
https://sanalpos.kuveytturk.com.tr/ServiceGateWay/Home/ThreeDModelProvisionGate
Testing
composer test
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
The MIT License (MIT). Please see LICENSE for more information.
Support
For issues and questions, please open an issue on GitHub:
https://github.com/emre-tarhan/kuveytturk-vpos-laravel-sdk-laravel-sdk
Email: post@emretarhan.net