mosesadewale/kora-php
Composer 安装命令:
composer require mosesadewale/kora-php
包简介
Framework-agnostic PHP SDK for the Kora payment API (formerly KoraPay).
README 文档
README
Framework-agnostic PHP SDK for the Kora payment API.
Requirements
- PHP 8.2+
ext-openssl,ext-json
Installation
composer require mosesadewale/kora-php
Quick start
Get your API keys from the Kora dashboard. Use sk_test_ keys for sandbox and sk_live_ keys for production.
use Kora\Sdk\Factory; $kora = Factory::make( secretKey: getenv('KORA_SECRET_KEY') ?: '', encryptionKey: getenv('KORA_ENCRYPTION_KEY') ?: '', // required for card payments );
The SDK infers the environment from the secret key prefix: sk_test_ maps to sandbox and sk_live_ maps to live.
Webhook verification uses your Kora API secret key.
Configuration
use Kora\Sdk\Enums\Environment; use Kora\Sdk\Factory; $kora = Factory::make( secretKey: 'sk_live_...', encryptionKey: '...', // 32-byte key, required for card payments timeout: 30.0, // seconds, default 30 retryAttempts: 3, // retries on 5xx, default 3 );
If you want to be explicit, you can still pass environment: Environment::Live or Environment::Sandbox. A mismatch with the key prefix throws InvalidArgumentException at construction time.
Alternatively, build from a config object:
use Kora\Sdk\Factory; use Kora\Sdk\Support\KoraConfig; $config = new KoraConfig(secretKey: 'sk_live_...', retryAttempts: 5); $kora = Factory::fromConfig($config, logger: $psrLogger);
Resources
Charges
// Charge — returns a checkout URL for redirect-based flows, or null for direct card charges $charge = $kora->charges()->charge([ 'reference' => 'ref_' . uniqid(), 'amount' => 5000, 'currency' => 'NGN', 'customer' => ['email' => 'user@example.com', 'name' => 'Ada Okonkwo'], 'redirect_url' => 'https://yourapp.com/callback', ]); echo $charge->checkoutUrl; // string|null — null for direct card charges echo $charge->reference; echo $charge->status; // Verify a charge $charge = $kora->charges()->verify('ref_001');
Card charges
Card encryption requires
encryptionKeyto be exactly 32 bytes. ALogicExceptionis thrown if it is missing or empty.
Pass a card object inside payment_options. The SDK encrypts it transparently using AES-256-GCM before sending — your code never touches the encrypted form.
$charge = $kora->charges()->charge([ 'reference' => 'ref_' . uniqid(), 'amount' => 5000, 'currency' => 'NGN', 'customer' => ['email' => 'user@example.com'], 'payment_options' => [ 'card' => [ 'number' => '5399831111111111', 'cvv' => '100', 'expiry_month' => '10', 'expiry_year' => '31', 'pin' => '1234', ], ], ]);
Mobile Money
$charge = $kora->mobileMoney()->charge([ 'reference' => 'ref_' . uniqid(), 'amount' => 5000, 'currency' => 'KES', 'customer' => ['email' => 'user@example.com'], 'payment_options' => [ 'mobile_money' => ['operator' => 'mpesa', 'mobile_number' => '254712345678'], ], ]);
Payouts
$payout = $kora->payouts()->disburse([ 'reference' => 'payout_' . uniqid(), 'destination' => [ 'type' => 'bank_account', 'amount' => 10000, 'currency' => 'NGN', 'narration' => 'Freelancer payment', 'bank_account' => ['bank' => '058', 'account' => '0123456789'], ], ]); echo $payout->reference; echo $payout->status;
Bulk Payouts
$bulk = $kora->bulkPayouts()->disburse([ 'reference' => 'bulk_' . uniqid(), 'currency' => 'NGN', 'payouts' => [ ['reference' => 'item_1', 'amount' => 5000, 'bank_account' => ['bank' => '058', 'account' => '0123456789']], ['reference' => 'item_2', 'amount' => 3000, 'bank_account' => ['bank' => '033', 'account' => '9876543210']], ], ]); echo $bulk->reference; echo $bulk->totalAmount; // Retrieve individual payout items $items = $kora->bulkPayouts()->items($bulk->reference);
Balances
$balances = $kora->balances()->list(); foreach ($balances as $balance) { echo $balance['currency'] . ': ' . $balance['available_balance']; }
Conversions
// Fetch exchange rates $rates = $kora->conversions()->rates([ 'from' => 'USD', 'to' => 'NGN', ]); // Initiate a conversion $conversion = $kora->conversions()->initiate([ 'reference' => 'conv_' . uniqid(), 'source_currency' => 'USD', 'destination_currency' => 'NGN', 'amount' => 100, ]); echo $conversion->sourceAmount; echo $conversion->destinationAmount; echo $conversion->sourceCurrency; echo $conversion->destinationCurrency;
Refunds
$refund = $kora->refunds()->initiate([ 'reference' => 'refund_' . uniqid(), 'transaction_reference' => 'ref_001', 'amount' => 5000, ]); echo $refund->reference; echo $refund->status; // List refunds $refunds = $kora->refunds()->list();
Pool Accounts
$account = $kora->poolAccounts()->create([ 'reference' => 'pool_' . uniqid(), 'name' => 'Escrow Account', 'currency' => 'NGN', 'customer' => ['email' => 'user@example.com', 'name' => 'Ada Okonkwo'], ]); echo $account->reference; echo $account->currency;
Chargebacks
// List chargebacks $chargebacks = $kora->chargebacks()->list(); // Accept a chargeback $result = $kora->chargebacks()->accept('chb_ref_001'); // Dispute a chargeback with evidence $result = $kora->chargebacks()->dispute('chb_ref_001', [ 'reason' => 'Item was delivered on 2026-04-10', 'evidence' => 'https://cdn.yourapp.com/delivery-proof.pdf', ]);
Webhooks
$raw = file_get_contents('php://input'); $signature = $_SERVER['HTTP_X_KORAPAY_SIGNATURE'] ?? ''; if (!$kora->webhooks()->verify($raw, $signature)) { http_response_code(401); exit; } $event = $kora->webhooks()->parse($raw); match (\Kora\Sdk\Enums\WebhookEventType::tryFrom($event->type)) { \Kora\Sdk\Enums\WebhookEventType::ChargeSuccess => fulfillOrder($event->data['reference']), \Kora\Sdk\Enums\WebhookEventType::PayoutSuccess => markPaid($event->data['reference']), \Kora\Sdk\Enums\WebhookEventType::RefundSuccess => processRefund($event->data['reference']), default => null, }; http_response_code(200);
Supported event types:
| Constant | Kora event |
|---|---|
WebhookEventType::ChargeSuccess |
charge.success |
WebhookEventType::ChargeFailed |
charge.failed |
WebhookEventType::PayoutSuccess |
transfer.success |
WebhookEventType::PayoutFailed |
transfer.failed |
WebhookEventType::RefundSuccess |
refund.success |
WebhookEventType::RefundFailed |
refund.failed |
WebhookEventType::ChargebackCreated |
chargeback.created |
WebhookEventType::ChargebackWon |
chargeback.won |
WebhookEventType::ChargebackLost |
chargeback.lost |
Unknown future event types return null from tryFrom() and fall through to default.
Error handling
use Kora\Sdk\Exceptions\ApiException; use Kora\Sdk\Exceptions\AuthenticationException; use Kora\Sdk\Exceptions\DuplicateReferenceException; use Kora\Sdk\Exceptions\InsufficientFundsException; use Kora\Sdk\Exceptions\KoraException; use Kora\Sdk\Exceptions\NetworkException; use Kora\Sdk\Exceptions\ValidationException; try { $kora->payouts()->disburse($payload); } catch (DuplicateReferenceException $e) { // reference already used — check existing payout status } catch (InsufficientFundsException $e) { // wallet balance too low } catch (ValidationException $e) { // $e->errors() returns the field-level errors array logger()->warning('Kora validation', $e->errors()); } catch (AuthenticationException $e) { // invalid or revoked secret key } catch (ApiException $e) { // 5xx from Kora — $e->context() returns the raw response body logger()->error('Kora server error', ['context' => $e->context()]); } catch (NetworkException $e) { // connectivity, timeout, or non-JSON response } catch (KoraException $e) { // catch-all for any other SDK exception }
| Exception | HTTP status | Notes |
|---|---|---|
AuthenticationException |
401 | Invalid or revoked key |
ValidationException |
400 | errors() returns field-level detail |
InsufficientFundsException |
400 | Wallet balance too low |
DuplicateReferenceException |
409 | |
ApiException |
5xx | context() returns raw response body |
NetworkException |
— | Connectivity, timeout, or non-JSON response |
WebhookException |
— | Invalid payload or signature; thrown by verifyThenParse() |
Testing
Mock KoraClientInterface in your tests — no network calls, no real credentials:
use Kora\Sdk\Contracts\KoraClientInterface; use Kora\Sdk\DTOs\ChargeResponse; use Kora\Sdk\Resources\ChargesResource; $charges = $this->createMock(ChargesResource::class); $charges->method('charge')->willReturn(ChargeResponse::fromArray([ 'reference' => 'ref_001', 'status' => 'pending', 'amount' => 5000, 'currency' => 'NGN', 'checkout_url' => 'https://pay.korahq.com/checkout/ref_001', ])); $kora = $this->createMock(KoraClientInterface::class); $kora->method('charges')->willReturn($charges); // inject $kora into the class under test
Laravel
See mosesadewale/kora-laravel for the Laravel service provider, facade, and optional webhook receiver. The Laravel package verifies and dispatches a generic webhook event; your app owns event-specific jobs and listeners.
License
MIT
mosesadewale/kora-php 适用场景与选型建议
mosesadewale/kora-php 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 6 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 06 月 10 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「php」 「payments」 「sdk」 「africa」 「korapay」 「kora」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 mosesadewale/kora-php 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 mosesadewale/kora-php 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 mosesadewale/kora-php 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Dealing with payments through the Egyptian payment gateway PayMob
Librería para la gestión sencilla de pagos mediante TPV Redsys y Paypal
Alfabank REST API integration
Scanpay module for Magento 2
Zero-dependency raw PHP DNS resolver, domain-ownership verification, and intoDNS/MxToolbox-style diagnostics. Queries authoritative nameservers directly over sockets — never trusts the recursive cache for ownership checks.
Official Safaricom Daraja M-Pesa integration for Laravel built on ysg/payment-core.
统计信息
- 总下载量: 6
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 28
- 依赖项目数: 1
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-10