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.