定制 mskayali/halkode 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

mskayali/halkode

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

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

mskayali/halkode 适用场景与选型建议

mskayali/halkode 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 0 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 03 月 04 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「payment」 「sdk」 「gateway」 「yii2」 「halkode」 「ccpayment」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 mskayali/halkode 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 mskayali/halkode 我们能提供哪些服务?
定制开发 / 二次开发

基于 mskayali/halkode 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 11
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-03-04