mskayali/halkode
最新稳定版本:v1.0.0
Composer 安装命令:
composer require mskayali/halkode
包简介
Halkode Payment Gateway SDK for Yii2
README 文档
README
A PHP SDK for integrating the Halkode payment gateway into your application. Designed as a Yii2-compatible Composer package with clean abstraction for token storage, encryption, and multi-language response handling.
📖 Official Integration Docs: This SDK is built based on the official Halkode Integration Documentation. Refer to it for detailed API specifications, parameter descriptions, and response formats.
Table of Contents
- Requirements
- Installation
- Configuration
- Quick Start
- API Reference
- Payment Workflows
- Token Storage
- Error Handling
- Testing
- Project Structure
- License
Requirements
- PHP >= 8.1
- yiisoft/yii2 ~2.0.0
- yiisoft/yii2-httpclient ~2.0.0
Installation
composer require mskayali/halkode
Configuration
As a Yii2 Component
Add the following to your config/web.php or config/main.php:
'components' => [ 'halkode' => [ 'class' => \mskayali\halkode\Client::class, 'baseUrl' => \mskayali\halkode\Client::BASE_URL_DEV, // or BASE_URL_PROD 'merchantUID' => 'your-merchant-uid', 'apikeypublic' => 'your-api-secret-key', 'apiclientpublic' => 'your-api-client-key', 'language' => 'tr', // 'tr' or 'en' for response messages // Optional: custom token storage (see Token Storage section) // 'tokenStorage' => new RedisTokenStorage($redis), ], ],
Standalone Usage
use mskayali\halkode\Client; $client = new Client([ 'baseUrl' => Client::BASE_URL_DEV, 'merchantUID' => 'your-merchant-uid', 'apikeypublic' => 'your-api-secret-key', 'apiclientpublic' => 'your-api-client-key', ]); $services = $client->getPaymentServices();
Environment URLs
| Environment | Constant | URL |
|---|---|---|
| Development | Client::BASE_URL_DEV |
https://testapp.halkode.com.tr/ccpayment |
| Production | Client::BASE_URL_PROD |
https://app.halkode.com.tr/ccpayment |
Quick Start
use mskayali\halkode\Client; use mskayali\halkode\models\PurchaseLinkRequest; use mskayali\halkode\models\Invoice; use mskayali\halkode\models\InvoiceItem; // 1. Create client $client = new Client([ 'baseUrl' => Client::BASE_URL_DEV, 'merchantUID' => 'your-merchant-uid', 'apikeypublic' => 'your-api-secret-key', 'apiclientpublic' => 'your-api-client-key', ]); // 2. Build invoice $invoice = new Invoice([ 'invoice_id' => 'INV-' . time(), 'invoice_description' => 'Order #123', 'total' => 250.00, 'return_url' => 'https://example.com/payment/success', 'cancel_url' => 'https://example.com/payment/cancel', 'items' => [ new InvoiceItem(['name' => 'Widget', 'price' => 250, 'quantity' => 1]), ], ]); // 3. Create payment link $services = $client->getPaymentServices(); $result = $services->purchaseLink(new PurchaseLinkRequest([ 'currency_code' => 'TRY', 'invoice' => $invoice, 'name' => 'John', 'surname' => 'Doe', ])); if ($result->success) { // Redirect user to the payment page header('Location: ' . $result->data->link); }
API Reference
Available Services
All services are accessed through $client->getPaymentServices():
| Method | Endpoint | Description |
|---|---|---|
purchaseLink($request) |
/purchase/link |
Generate a hosted payment page link |
paySmart2D($request) |
/api/paySmart2D |
Direct card payment (no 3D Secure) |
paySmart3D($request) |
/api/paySmart3D |
Card payment with 3D Secure verification |
refundPayment($request) |
/api/refund |
Full or partial refund |
checkstatus($request) |
/api/checkstatus |
Query transaction status |
confirmPayment($request) |
/api/confirmPayment |
Confirm a pre-authorized payment |
getInstallment($request) |
/api/installments |
Query available installment options |
merchantCommissions($request) |
/api/commissions |
Retrieve merchant commission rates |
getPos($request) |
/api/getpos |
Query available POS terminals for a card |
getTransaction($request) |
/api/getTransactions |
Retrieve a single transaction |
allTransaction($request) |
/api/alltransaction |
List all transactions |
paymentComplete($request) |
/payment/complete |
Finalize a payment |
Response Structure
Every service method returns an object with these attributes:
$result->success; // bool — whether the operation succeeded $result->message; // string|null — localized status message (based on Client language) $result->data; // object|null — response data on success $result->error; // mixed|null — error details on failure
Payment Workflows
Workflow 1: Hosted Payment Page (Purchase Link)
Best for: redirecting users to a Halkode-hosted payment page.
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │────▶│ Server │────▶│ Halkode │────▶│ Customer │
│ (Your │ │ purchase │ │ Hosted │ │ completes│
│ App) │◀────│ Link() │◀────│ Page │◀────│ payment │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│
redirect to return_url
or cancel_url
// Step 1: Create payment link $result = $services->purchaseLink(new PurchaseLinkRequest([ 'currency_code' => 'TRY', 'invoice' => $invoice, 'name' => 'John', 'surname' => 'Doe', ])); // Step 2: Redirect user if ($result->success) { return redirect($result->data->link); } // Step 3: Handle callback (on your return_url) // Halkode redirects back with payment result in POST data $invoiceId = $_POST['invoice_id']; $status = $services->checkstatus(new CheckstatusRequest([ 'invoice_id' => $invoiceId, 'merchant_key' => $client->getMerchantUID(), ])); if ($status->success) { // Payment confirmed — update your order }
Workflow 2: Direct Card Payment (2D)
Best for: collecting card data on your own PCI-compliant form.
// Step 1: Charge the card directly $result = $services->paySmart2D(new PaySmart2DRequest([ 'cc_holder_name' => 'John Doe', 'cc_no' => '4155141122223339', 'expiry_month' => '12', 'expiry_year' => '26', 'cvv' => '555', 'currency_code' => 'TRY', 'installments_number' => 1, 'invoice_id' => 'INV-' . time(), 'invoice_description' => 'Premium subscription', 'total' => 99.00, 'name' => 'John', 'surname' => 'Doe', ])); // Step 2: Check result if ($result->success) { $orderNo = $result->data->order_no; // Save $orderNo to your database for future reference } else { $error = $result->error; // Display error to user or log }
Workflow 3: 3D Secure Payment
Best for: most production card payments — adds an extra bank verification step.
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Your │────▶│ Halkode │────▶│ Bank 3D │────▶│ Halkode │
│ Server │ │ API │ │ Verify │ │ Callback │
│ paySmart │ │ returns │ │ Page │ │ return │
│ 3D() │ │ redirect │ │ (OTP) │ │ _url │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│
┌─────▼─────┐
│ Your App │
│ handles │
│ callback │
└───────────┘
// Step 1: Initiate 3D payment $result = $services->paySmart3D(new PaySmart3DRequest([ 'cc_holder_name' => 'John Doe', 'cc_no' => '4155141122223339', 'expiry_month' => '12', 'expiry_year' => '26', 'cvv' => '555', 'currency_code' => 'TRY', 'installments_number' => 1, 'invoice_id' => 'INV-3D-' . time(), 'total' => 500.00, 'name' => 'John', 'surname' => 'Doe', 'return_url' => 'https://example.com/payment/3d-callback', 'cancel_url' => 'https://example.com/payment/cancel', ])); // Step 2: Redirect user to 3D verification if ($result->success) { return redirect($result->data->redirect_url); } // Step 3: Handle 3D callback (on your return_url) // The bank redirects back after OTP verification $invoiceId = $_POST['invoice_id'] ?? $_GET['invoice_id']; // Step 4: Verify the payment status $status = $services->checkstatus(new CheckstatusRequest([ 'invoice_id' => $invoiceId, 'merchant_key' => $client->getMerchantUID(), ])); if ($status->success && $status->data->payment_status == 1) { // Payment successful — fulfill the order } else { // Payment failed or pending }
Workflow 4: Pre-Authorization + Capture
Best for: reserving funds first, then capturing later (e.g., hotel bookings, car rentals).
// Step 1: Pre-authorize (hold funds on the card) $result = $services->paySmart2D(new PaySmart2DRequest([ 'cc_holder_name' => 'John Doe', 'cc_no' => '4155141122223339', 'expiry_month' => '12', 'expiry_year' => '26', 'cvv' => '555', 'currency_code' => 'TRY', 'installments_number' => 1, 'invoice_id' => 'PREAUTH-' . time(), 'total' => 1000.00, 'name' => 'John', 'surname' => 'Doe', 'transaction_type' => TransactionType::PreAuthorization, ])); // Step 2: Later — confirm (capture) the payment if ($result->success) { $confirmed = $services->confirmPayment(new ConfirmPaymentRequest([ 'invoice_id' => 'PREAUTH-XXX', 'total' => '1000.00', 'status' => '1', 'merchant_key' => $client->getMerchantUID(), 'hash_key' => $client->generateHash('PREAUTH-XXX'), ])); if ($confirmed->success) { // Funds captured — order finalized } }
Workflow 5: Refund (Full or Partial)
// Full refund $result = $services->refundPayment(new RefundAPIRequest([ 'invoice_id' => 'INV-ORIGINAL', 'amount' => 99.00, // Full amount 'app_id' => $client->apiclientpublic, 'app_secret' => $client->apikeypublic, 'merchant_key' => $client->getMerchantUID(), 'hash_key' => $client->generateHash('INV-ORIGINAL'), ])); // Partial refund (e.g., 30 TRY of a 99 TRY charge) $partialRefund = $services->refundPayment(new RefundAPIRequest([ 'invoice_id' => 'INV-ORIGINAL', 'amount' => 30.00, // Partial amount 'app_id' => $client->apiclientpublic, 'app_secret' => $client->apikeypublic, 'merchant_key' => $client->getMerchantUID(), 'hash_key' => $client->generateHash('INV-ORIGINAL'), ]));
Workflow 6: Transaction Monitoring & Reconciliation
// Check a specific transaction $tx = $services->getTransaction(new GetTransactionRequest([ 'invoice_id' => 'INV-001', 'merchant_key' => $client->getMerchantUID(), 'hash_key' => $client->generateHash('INV-001'), ])); // List all transactions (for daily reconciliation) $allTx = $services->allTransaction(new AllTransactionRequest([ 'merchant_key' => $client->getMerchantUID(), ])); // Check payment status (polling for async flows) $status = $services->checkstatus(new CheckstatusRequest([ 'invoice_id' => 'INV-001', 'merchant_key' => $client->getMerchantUID(), ]));
Workflow 7: Installment Query Before Payment
Best for: showing available installment options to the user before payment.
// Step 1: Query installments for the card BIN $installments = $services->getInstallment(new GetInstallmentRequest([ 'bin_number' => substr($cardNumber, 0, 6), // First 6 digits 'total' => 1200.00, 'currency_code' => 'TRY', ])); // Step 2: Display installment options to user if ($installments->success) { foreach ($installments->data as $option) { echo "Installments: {$option->installment_number} " . "— Monthly: {$option->monthly_payment} TRY " . "— Total: {$option->total_amount} TRY\n"; } } // Step 3: User selects installment count, then call paySmart2D/3D $result = $services->paySmart3D(new PaySmart3DRequest([ 'installments_number' => 6, // User's selection // ... other fields ]));
Workflow 8: POS Selection
Query available POS terminals for a card:
$pos = $services->getPos(new GetPosRequest([ 'credit_card' => '415514', // First 6 digits (BIN) 'amount' => 500.00, 'currency_code' => 'TRY', 'merchant_key' => $client->getMerchantUID(), ])); if ($pos->success) { // pos->data contains available POS terminals with: // - pos_id, card_type, card_scheme // - payable_amount, hash_key // - commission_rate, installment details }
Token Storage
The SDK automatically manages API authentication tokens. By default, tokens are stored in-memory and discarded after each request.
Default: In-Memory Storage
// No configuration needed — MemoryTokenStorage is used automatically $client = new Client([...]);
Custom: Redis Storage (Plain)
For persistent token caching across requests:
use mskayali\halkode\TokenStorageInterface; class RedisTokenStorage implements TokenStorageInterface { private \Redis $redis; public function __construct(\Redis $redis) { $this->redis = $redis; } public function get(string $key): ?string { $value = $this->redis->get($key); return $value === false ? null : $value; } public function set(string $key, string $value, int $ttl): bool { return $this->redis->setex($key, $ttl, $value); } } // Usage: $client = new Client([ // ... credentials 'tokenStorage' => new RedisTokenStorage($redis), ]);
Recommended: Encrypted Redis Storage
⚠️ Security Note: API tokens stored in Redis are sensitive credentials. If your Redis instance is shared, accessible over a network, or not configured with TLS, you should encrypt tokens before storing them. This prevents token leakage if Redis is compromised.
use mskayali\halkode\TokenStorageInterface; class EncryptedRedisTokenStorage implements TokenStorageInterface { private \Redis $redis; private string $encryptionKey; private string $cipher = 'aes-256-cbc'; /** * @param \Redis $redis Connected Redis instance * @param string $encryptionKey A 32-byte (256-bit) secret key. * Generate once with: bin2hex(random_bytes(16)) * Store securely (e.g., environment variable). */ public function __construct(\Redis $redis, string $encryptionKey) { $this->redis = $redis; $this->encryptionKey = $encryptionKey; } public function get(string $key): ?string { $encrypted = $this->redis->get($key); if ($encrypted === false) { return null; } $data = base64_decode($encrypted); $ivLength = openssl_cipher_iv_length($this->cipher); $iv = substr($data, 0, $ivLength); $ciphertext = substr($data, $ivLength); $decrypted = openssl_decrypt($ciphertext, $this->cipher, $this->encryptionKey, OPENSSL_RAW_DATA, $iv); return $decrypted === false ? null : $decrypted; } public function set(string $key, string $value, int $ttl): bool { $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->cipher)); $ciphertext = openssl_encrypt($value, $this->cipher, $this->encryptionKey, OPENSSL_RAW_DATA, $iv); $encrypted = base64_encode($iv . $ciphertext); if ($ttl > 0) { return $this->redis->setex($key, $ttl, $encrypted); } return $this->redis->set($key, $encrypted); } }
Setup:
// 1. Generate an encryption key ONCE and store it securely: // php -r "echo bin2hex(random_bytes(16));" // → e.g., "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" // 2. Store the key in an environment variable: // HALKODE_TOKEN_KEY=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 // 3. Use in your application: $redis = new \Redis(); $redis->connect('127.0.0.1', 6379); $client = new Client([ 'baseUrl' => Client::BASE_URL_PROD, 'merchantUID' => 'your-merchant-uid', 'apikeypublic' => 'your-api-secret-key', 'apiclientpublic' => 'your-api-client-key', 'tokenStorage' => new EncryptedRedisTokenStorage( $redis, getenv('HALKODE_TOKEN_KEY') ), ]);
When to use which:
| Storage | Use Case |
|---|---|
MemoryTokenStorage (default) |
Development, single-request scripts, stateless apps |
RedisTokenStorage (plain) |
Production with secured Redis (TLS, private network, AUTH) |
EncryptedRedisTokenStorage |
Production with shared/untrusted Redis, compliance requirements |
TokenStorageInterface
Any class implementing TokenStorageInterface can be passed to the client:
interface TokenStorageInterface { public function get(string $key): ?string; public function set(string $key, string $value, int $ttl): bool; }
Error Handling
Every service call returns a response object. No exceptions are thrown for API errors — errors are returned in the response:
$result = $services->paySmart2D($request); if (!$result->success) { // Option 1: Localized message echo $result->message; // e.g., "Authentication error" (en) or "Kimlik doğrulama hatası" (tr) // Option 2: Detailed error if (is_array($result->error)) { foreach ($result->error as $field => $messages) { echo "$field: " . implode(', ', $messages) . "\n"; } } else { echo $result->error; } }
Status Codes
| Code | English | Turkish |
|---|---|---|
| 200 | OK | Tamam |
| 201 | Created | Oluşturuldu |
| 400 | Bad Request | Hatalı istek |
| 401 | Authentication Error | Kimlik doğrulama hatası |
| 403 | Forbidden | Yasak |
| 404 | Resource Not Found | Kaynak bulunamadı |
| 500 | Internal Server Error | Dahili Sunucu Hatası |
Testing
Run the test suite:
composer install ./vendor/bin/phpunit --testdox
The test suite includes 108 tests with 218 assertions covering:
| Suite | Tests | Coverage |
|---|---|---|
| AutoloadTest | 36 | PSR-4 autoloading for all classes |
| MemoryTokenStorageTest | 7 | Interface, CRUD, TTL, overwrites |
| ClientTest | 17 | Constants, encryption, tokens, auth headers |
| BaseResponseTest | 8 | Language support, status codes |
| ModelTest | 26 | Validation rules, serialization |
| PaymentServicesTest | 14 | All 12 endpoint methods |
Project Structure
mskayali/halkode/
├── composer.json # Package definition & PSR-4 autoload
├── phpunit.xml # PHPUnit configuration
├── README.md
├── examples/
│ ├── all_services.php # Working examples for all 12 endpoints
│ ├── redis_token_storage.php # Custom Redis token storage
│ └── yii2_integration.php # Yii2 component & controller patterns
├── src/
│ ├── Client.php # HTTP client, encryption, token management
│ ├── TokenStorageInterface.php
│ ├── MemoryTokenStorage.php
│ ├── models/
│ │ ├── BaseResponse.php
│ │ ├── StatusCodes.php
│ │ ├── TransactionType.php
│ │ ├── Invoice.php
│ │ ├── InvoiceItem.php
│ │ ├── GetTokenRequest.php / GetTokenBody.php
│ │ ├── PurchaseLinkRequest.php / PurchaseLinkBody.php
│ │ ├── PaySmart2DRequest.php / PaySmart2DBody.php / PaySmart2DData.php
│ │ ├── PaySmart3DRequest.php / PaySmart3DBody.php
│ │ ├── RefundAPIRequest.php / RefundAPIBody.php
│ │ ├── CheckstatusRequest.php / CheckstatusBody.php
│ │ ├── ConfirmPaymentRequest.php / ConfirmPaymentBody.php
│ │ ├── GetInstallmentRequest.php / GetInstallmentBody.php
│ │ ├── MerchantCommissionsRequest.php / MerchantCommissionsBody.php
│ │ ├── GetPosRequest.php / GetPosBody.php / GetPosData.php
│ │ ├── GetTransactionRequest.php / GetTransactionBody.php
│ │ ├── AllTransactionRequest.php / AllTransactionBody.php
│ │ └── PaymentCompleteRequest.php / PaymentCompleteBody.php
│ └── services/
│ └── PaymentServices.php # All 12 API endpoint methods
└── tests/
├── bootstrap.php
├── AutoloadTest.php
├── MemoryTokenStorageTest.php
├── ClientTest.php
├── BaseResponseTest.php
├── ModelTest.php
└── PaymentServicesTest.php
License
MIT
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 4
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-03-04