texhub / alif-pay
Alif Acquiring (WebCheckout) payment gateway SDK for any PHP framework with first-class Laravel support: payments, tokenization, marketplace split-payments and webhooks.
Package info
pkg:composer/texhub/alif-pay
Requires
- php: ^8.2
- ext-curl: *
- ext-hash: *
- ext-json: *
Requires (Dev)
- illuminate/support: ^11.0 || ^12.0 || ^13.0
- phpunit/phpunit: ^11.0 || ^12.0
Suggests
- illuminate/support: Required to use the package inside a Laravel application (service provider, facade, config publishing).
README
English · Русский
A clean, framework-agnostic PHP SDK for the Alif Acquiring (WebCheckout) payment gateway — payments, tokenization and marketplace split-payments — with first-class Laravel support.
Works in plain PHP and any framework. Laravel gets auto-discovery, a config file and a facade for free.
Based on the official documentation: https://docs.acquiring.alif.tj/intro
Features
- Standard payments — Korti Milli, Alif wallet, Salom installments, cash invoices, Visa/Mastercard
- Tokenization — bind cards/wallets for repeat charges
- Marketplace — split a single payment between multiple sellers, hold & confirm delivery
- HMAC SHA256 signing — the exact double-HMAC scheme Alif requires, done for you
- Webhooks — typed callback objects + signature verification
- Pluggable HTTP transport — cURL by default; inject your own for testing
- Fully unit-tested, no network needed
- Test / Production environment switch
Installation
composer require texhub/alif-pay
Requirements: PHP ≥ 8.2 with the curl, json and hash extensions.
Quick start (plain PHP)
use TexHub\AlifPay\AlifPay; use TexHub\AlifPay\Enums\Environment; use TexHub\AlifPay\Enums\Gate; use TexHub\AlifPay\Requests\PaymentRequest; $alif = AlifPay::make( terminalId: 'YOUR_TERMINAL_ID', terminalPassword: 'YOUR_TERMINAL_PASSWORD', environment: Environment::Test, // Environment::Production when live ); $response = $alif->payments()->initiate( PaymentRequest::make('ORDER_123456', '100.50') ->gate(Gate::KortiMilli) ->callbackUrl('https://shop.tj/alif/callback') ->returnUrl('https://shop.tj/success') ->info('Оплата заказа №123456') ->phone('992900123456') ); // Send the customer to the secure payment form: header('Location: ' . $response->redirectUrl());
Environments
| Environment | Base URL |
|---|---|
Environment::Test |
https://test-web.alif.tj |
Environment::Production |
https://web.alif.tj |
In the test environment you can use Alif's test cards to simulate scenarios (blocked card, insufficient funds, …) without moving real money.
Authorization (how signing works)
Every request is authorized with an HMAC SHA256 token built from the request data:
token = HMAC_SHA256( dataToSign, HMAC_SHA256(terminal_password, terminal_id) )
The SDK builds the correct dataToSign for each operation automatically:
| Operation | dataToSign |
|---|---|
| Payment / Marketplace | terminal_id + order_id + amount + callback_url |
| Tokenization | terminal_id + phone + gate |
| Confirm delivery | terminal_id + transaction_id + amount |
| Confirm VSA/MCR delivery | terminal_id + parent_transaction_id |
You never call the signer manually — but it's available via $alif->signature() if needed.
Payments
Gateways (Gate)
| Enum | gate value |
Method |
|---|---|---|
Gate::KortiMilli |
korti_milli |
National card (default) |
Gate::Wallet |
wallet |
Alif mobi wallet |
Gate::Salom |
salom |
Salom installment |
Gate::Invoice |
invoice |
Cash invoice |
Gate::Visa |
vsa |
Visa |
Gate::Mastercard |
mcr |
Mastercard |
Gate::CybersourceCheckout |
cybersource_checkout |
Cybersource hosted checkout |
Salom installment (with invoice items)
use TexHub\AlifPay\Requests\InvoiceItem; $response = $alif->payments()->initiate( PaymentRequest::make('ORDER_345678', '1500.00') ->gate(Gate::Salom) ->callbackUrl('https://shop.tj/alif/callback') ->returnUrl('https://shop.tj/success') ->phone('992900111222') ->addInvoiceItem(new InvoiceItem( name: 'Смартфон Samsung Galaxy A54', category: 'Электроника', quantity: 1, price: '1500.00', vatPercent: '0', )) );
Cash invoice (with deadline)
$alif->payments()->initiate( PaymentRequest::make('ORDER_678900', '1200.00') ->gate(Gate::Invoice) ->callbackUrl('https://shop.tj/alif/callback') ->returnUrl('https://shop.tj/success') ->deadline('2025-11-29T07:59:59Z') );
Check status / cancel
$status = $alif->payments()->checkStatus('ORDER_123456'); $alif->payments()->cancel(transactionId: '789012', amount: '100.50');
Tokenization
use TexHub\AlifPay\Enums\TokenizationGate; use TexHub\AlifPay\Requests\TokenizationRequest; $response = $alif->tokenization()->initiate( TokenizationRequest::make('ORDER_123456', '+992900123456', TokenizationGate::Wallet) ->callbackUrl('https://shop.tj/alif/tokenize-callback') ->returnUrl('https://shop.tj/success') ->clientId('client_12345') ); header('Location: ' . $response->redirectUrl());
Available gates: KortiMilli, Wallet, Salom, Tcell, Megafon, Babilon, ZetMobile, Procard (Visa/Mastercard).
Marketplace (split-payment)
use TexHub\AlifPay\Requests\MarketplaceRequest; $response = $alif->marketplace()->initiate( MarketplaceRequest::make('MP_ORDER_123456', '500.00') ->gate(Gate::KortiMilli) ->callbackUrl('https://shop.tj/alif/mp-callback') ->returnUrl('https://shop.tj/success') ->splitTo('partner_terminal_1', '300.00') ->splitTo('partner_terminal_2', '200.00') );
The split total must equal the order amount — the SDK validates this before sending.
Funds are held until delivery is confirmed:
// All methods except Visa/Mastercard: $alif->marketplace()->confirmDelivery(transactionId: '789013', amount: '300.00'); // Visa / Mastercard: $alif->marketplace()->confirmVsaMcrDelivery(parentTransactionId: '789012'); // Status & cancellation: $alif->marketplace()->checkStatus('MP_ORDER_123456'); $alif->marketplace()->cancel(transactionId: '789013', amount: '300.00');
Webhooks (callbacks)
Alif sends a POST to your callback_url on every status change. Respond with HTTP 200 or it will retry.
Payment / marketplace callback
use TexHub\AlifPay\Enums\PaymentStatus; $callback = $alif->webhooks()->paymentCallback(file_get_contents('php://input')); // Verify authenticity before trusting it (see note below): if (! $alif->webhooks()->verifyPaymentCallback($callback)) { http_response_code(400); exit; } match ($callback->status) { PaymentStatus::Ok => markOrderPaid($callback->orderId, $callback->amount), PaymentStatus::Failed, PaymentStatus::Canceled => markOrderFailed($callback->orderId), default => null, // pending / to_approve }; http_response_code(200); echo 'OK';
For marketplace, $callback->subTransactions holds the per-partner breakdown and $callback->isMarketplace() is true.
Tokenization callback
The tokenization callback has a different structure (result code at the root, data under
payload).
$callback = $alif->webhooks()->tokenizationCallback(file_get_contents('php://input')); if ($callback->isSuccessful()) { saveToken($callback->orderId, $callback->token); // store for repeat charges } http_response_code(200);
A note on callback signature verification
Alif signs callbacks with the same HMAC scheme, but the exact concatenation order for the callback token is not published. verifyPaymentCallback() defaults to orderId . amount; if your terminal differs, pass your own signing string:
$alif->webhooks()->verifyPaymentCallback($callback, dataToSign: $yourString); // or the low-level check: $alif->webhooks()->verifyToken($yourString, $callback->token);
Error handling
The gateway always replies with HTTP 200 and a business code. The SDK turns any non-200 code into an ApiException:
use TexHub\AlifPay\Exceptions\ApiException; use TexHub\AlifPay\Exceptions\TransportException; try { $response = $alif->payments()->initiate($request); } catch (ApiException $e) { $e->apiCode; // 208, 400, 401, 403, 404, 500 $e->apiMessage; // human-readable message (RU) $e->isDuplicate(); // true for code 208 $e->isRetryable(); // true for 404 / 500 } catch (TransportException $e) { // network/connection failure }
| Code | Meaning | Retry |
|---|---|---|
| 200 | Success | — |
| 208 | Duplicate order_id | No |
| 400 | Validation error | No |
| 401 | Auth error (token) | No |
| 403 | Invalid key | No |
| 404 | Not found | Yes |
| 500 | Internal error | Yes |
Laravel
The service provider and AlifPay facade are auto-discovered. Publish the config:
php artisan vendor:publish --tag=alif-pay-config
Add credentials to .env:
ALIF_PAY_ENVIRONMENT=test ALIF_PAY_TERMINAL_ID=your_terminal_id ALIF_PAY_TERMINAL_PASSWORD=your_terminal_password ALIF_PAY_CALLBACK_URL=https://shop.tj/alif/callback ALIF_PAY_RETURN_URL=https://shop.tj/success ALIF_PAY_TIMEOUT=30
Use the facade (callback/return URL fall back to config):
use TexHub\AlifPay\Laravel\AlifPay; use TexHub\AlifPay\Enums\Gate; use TexHub\AlifPay\Requests\PaymentRequest; $response = AlifPay::payments()->initiate( PaymentRequest::make('ORDER_'.$order->id, $order->total)->gate(Gate::KortiMilli) ); return redirect()->away($response->redirectUrl());
…or resolve from the container / inject it:
public function pay(\TexHub\AlifPay\AlifPay $alif) { /* ... */ }
Example callback controller
use Illuminate\Http\Request; use TexHub\AlifPay\Laravel\AlifPay; use TexHub\AlifPay\Enums\PaymentStatus; public function callback(Request $request) { $callback = AlifPay::webhooks()->paymentCallback($request->getContent()); if ($callback->status === PaymentStatus::Ok) { Order::where('reference', $callback->orderId)->update(['status' => 'paid']); } return response('OK', 200); }
Exclude the callback route from CSRF protection (
VerifyCsrfToken::$except) since it's a server-to-serverPOST.
Testing
The SDK ships with a fake transport so you can test without hitting the network:
use TexHub\AlifPay\AlifPay; use TexHub\AlifPay\Config; use TexHub\AlifPay\Tests\Support\FakeTransport; $transport = (new FakeTransport())->willReturnJson([ 'code' => 200, 'message' => 'Успешно', 'url' => 'https://web.alif.tj/abc', ]); $alif = new AlifPay(new Config('id', 'secret'), $transport); // ... assert on $transport->lastBody / lastHeaders / lastUrl
Run the package test suite:
composer install composer test # or: vendor/bin/phpunit
Architecture
src/
├── AlifPay.php # entry point — payments()/tokenization()/marketplace()/webhooks()
├── Config.php # immutable configuration
├── Signature.php # HMAC SHA256 double-hash signer
├── Enums/ # Environment, Gate, TokenizationGate, PaymentStatus, …
├── Http/ # Transport interface, CurlTransport, Response
├── Requests/ # PaymentRequest, TokenizationRequest, MarketplaceRequest, …
├── Clients/ # PaymentClient, TokenizationClient, MarketplaceClient
├── Webhook/ # callback DTOs + WebhookHandler
├── Exceptions/ # ApiException, TransportException, …
└── Laravel/ # ServiceProvider + Facade
License
MIT © TexHub Pro — built by Mahmudi Shodmehr.