PHP Library (omise-php)
The omise-php library provides a PSR-compliant PHP interface to the Omise API with excellent Laravel and Symfony integration, modern PHP features, and comprehensive error handling.
Installationโ
Using Composer (Recommended)โ
composer require omise/omise-php
composer.jsonโ
{
"require": {
"omise/omise-php": "^2.15"
}
}
Requirementsโ
- PHP 7.2 or higher (including PHP 8.x)
- cURL extension
- JSON extension
- Composer for dependency management
Quick Startโ
Basic Configurationโ
<?php
require_once 'vendor/autoload.php';
// Configure with your API keys
define('OMISE_SECRET_KEY', 'skey_test_123456789');
define('OMISE_PUBLIC_KEY', 'pkey_test_123456789');
// Set API version
define('OMISE_API_VERSION', '2019-05-29');
Laravel Configurationโ
Publish the configuration file:
php artisan vendor:publish --provider="Omise\OmiseServiceProvider"
Configure in .env:
# config/services.php
OMISE_SECRET_KEY=skey_test_123456789
OMISE_PUBLIC_KEY=pkey_test_123456789
OMISE_API_VERSION=2019-05-29
Configure the service:
<?php
// config/services.php
return [
'omise' => [
'secret_key' => env('OMISE_SECRET_KEY'),
'public_key' => env('OMISE_PUBLIC_KEY'),
'api_version' => env('OMISE_API_VERSION', '2019-05-29'),
],
];
Symfony Configurationโ
# config/packages/omise.yaml
omise:
secret_key: '%env(OMISE_SECRET_KEY)%'
public_key: '%env(OMISE_PUBLIC_KEY)%'
api_version: '2019-05-29'
Authenticationโ
The library uses your secret key for all API operations:
<?php
require_once 'vendor/autoload.php';
// Option 1: Global configuration
define('OMISE_SECRET_KEY', getenv('OMISE_SECRET_KEY'));
define('OMISE_PUBLIC_KEY', getenv('OMISE_PUBLIC_KEY'));
// Option 2: Per-request configuration
$charge = OmiseCharge::retrieve('chrg_test_123', 'skey_test_alternate');
Common Operationsโ
Creating a Chargeโ
With a Tokenโ
<?php
require_once 'vendor/autoload.php';
define('OMISE_SECRET_KEY', getenv('OMISE_SECRET_KEY'));
try {
$charge = OmiseCharge::create([
'amount' => 100000, // 1,000.00 THB (in smallest currency unit)
'currency' => 'THB',
'card' => 'tokn_test_123',
'description' => 'Order #1234',
'metadata' => [
'order_id' => '1234',
'customer_name' => 'John Doe'
]
]);
if ($charge['paid']) {
echo "Charge successful: " . $charge['id'];
} else {
echo "Charge failed: " . $charge['failure_message'];
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
With Type Declarations (PHP 7.4+)โ
<?php
declare(strict_types=1);
class PaymentService
{
public function createCharge(
int $amount,
string $currency,
string $token,
array $metadata = []
): array {
return OmiseCharge::create([
'amount' => $amount,
'currency' => $currency,
'card' => $token,
'metadata' => $metadata
]);
}
}
// Usage
$service = new PaymentService();
$charge = $service->createCharge(
100000,
'THB',
'tokn_test_123',
['order_id' => '1234']
);
With a Customerโ
<?php
// Create a charge for an existing customer
$charge = OmiseCharge::create([
'amount' => 50000,
'currency' => 'THB',
'customer' => 'cust_test_123',
'description' => 'Subscription payment'
]);
With 3D Secureโ
<?php
// Create a charge that may require 3D Secure
$charge = OmiseCharge::create([
'amount' => 100000,
'currency' => 'THB',
'card' => 'tokn_test_123',
'return_uri' => 'https://example.com/payment/callback'
]);
if ($charge['authorized']) {
if (isset($charge['authorize_uri'])) {
// Redirect customer to authorize_uri for 3D Secure
header('Location: ' . $charge['authorize_uri']);
exit;
} else {
// Charge completed without 3D Secure
processSuccessfulPayment($charge);
}
}
Retrieving a Chargeโ
<?php
// Retrieve a charge by ID
$charge = OmiseCharge::retrieve('chrg_test_123');
echo "Amount: " . $charge['amount'] . PHP_EOL;
echo "Currency: " . $charge['currency'] . PHP_EOL;
echo "Status: " . $charge['status'] . PHP_EOL;
echo "Paid: " . ($charge['paid'] ? 'Yes' : 'No') . PHP_EOL;
Listing Chargesโ
<?php
// List all charges with pagination
$charges = OmiseCharge::retrieve([
'limit' => 20,
'offset' => 0,
'order' => 'reverse_chronological'
]);
foreach ($charges['data'] as $charge) {
echo $charge['id'] . ": " . $charge['amount'] . " " . $charge['currency'] . PHP_EOL;
}
// List charges with filters
$weekAgo = (new DateTime('-7 days'))->format('Y-m-d');
$today = (new DateTime())->format('Y-m-d');
$recentCharges = OmiseCharge::retrieve([
'from' => $weekAgo,
'to' => $today
]);
Creating a Customerโ
<?php
// Create a customer without a card
$customer = OmiseCustomer::create([
'email' => 'customer@example.com',
'description' => 'John Doe',
'metadata' => [
'user_id' => '12345',
'account_type' => 'premium'
]
]);
echo "Customer created: " . $customer['id'];
Saving a Card to a Customerโ
<?php
// Update customer with a card token
$customer = OmiseCustomer::retrieve('cust_test_123');
$customer = OmiseCustomer::update('cust_test_123', [
'card' => 'tokn_test_456'
]);
echo "Card saved: " . $customer['default_card'];
// Or create customer with card in one step
$customer = OmiseCustomer::create([
'email' => 'customer@example.com',
'description' => 'John Doe',
'card' => 'tokn_test_123'
]);
Listing Customer Cardsโ
<?php
$customer = OmiseCustomer::retrieve('cust_test_123');
foreach ($customer['cards']['data'] as $card) {
echo $card['brand'] . " ending in " . $card['last_digits'] . PHP_EOL;
echo "Expires: " . $card['expiration_month'] . "/" . $card['expiration_year'] . PHP_EOL;
}
Creating a Refundโ
<?php
// Full refund
$refund = OmiseRefund::create('chrg_test_123', [
'amount' => null // null for full refund
]);
// Partial refund
$refund = OmiseRefund::create('chrg_test_123', [
'amount' => 25000, // 250.00 THB
'metadata' => [
'reason' => 'customer_request',
'ticket_id' => 'TICKET-123'
]
]);
echo "Refund " . $refund['id'] . ": " . $refund['amount'] . " " . $refund['currency'];
Creating a Transferโ
<?php
// Create a transfer to your bank account
$transfer = OmiseTransfer::create([
'amount' => 500000, // 5,000.00 THB
'recipient' => 'recp_test_123',
'metadata' => [
'payout_id' => 'PAYOUT-456'
]
]);
echo "Transfer " . $transfer['id'] . ": " . $transfer['amount'];
Alternative Payment Methodsโ
Creating a Sourceโ
<?php
// Prompt Pay QR
$source = OmiseSource::create([
'type' => 'promptpay',
'amount' => 100000,
'currency' => 'THB'
]);
// Display QR code to customer
echo "QR Code URL: " . $source['scannable_code']['image']['download_uri'];
// Create charge with source
$charge = OmiseCharge::create([
'amount' => 100000,
'currency' => 'THB',
'source' => $source['id'],
'return_uri' => 'https://example.com/payment/callback'
]);
Internet Bankingโ
<?php
// Internet Banking
$source = OmiseSource::create([
'type' => 'internet_banking_scb',
'amount' => 100000,
'currency' => 'THB'
]);
$charge = OmiseCharge::create([
'amount' => 100000,
'currency' => 'THB',
'source' => $source['id'],
'return_uri' => 'https://example.com/payment/callback'
]);
// Redirect customer to authorize_uri
header('Location: ' . $charge['authorize_uri']);
exit;
Mobile Bankingโ
<?php
// Mobile Banking (SCB Easy)
$source = OmiseSource::create([
'type' => 'mobile_banking_scb',
'amount' => 100000,
'currency' => 'THB'
]);
$charge = OmiseCharge::create([
'amount' => 100000,
'currency' => 'THB',
'source' => $source['id'],
'return_uri' => 'https://example.com/payment/callback'
]);
Installmentsโ
<?php
// Installment payment
$source = OmiseSource::create([
'type' => 'installment_kbank',
'amount' => 100000,
'currency' => 'THB',
'installment_term' => 6 // 6 months
]);
$charge = OmiseCharge::create([
'amount' => 100000,
'currency' => 'THB',
'source' => $source['id'],
'return_uri' => 'https://example.com/payment/callback'
]);
Error Handlingโ
The library throws exceptions for different error types:
<?php
require_once 'vendor/autoload.php';
try {
$charge = OmiseCharge::create([
'amount' => 100000,
'currency' => 'THB',
'card' => 'tokn_test_123'
]);
} catch (OmiseException $e) {
// Get error details
echo "Error: " . $e->getMessage() . PHP_EOL;
echo "HTTP Status: " . $e->getHttpStatus() . PHP_EOL;
echo "Error Code: " . $e->getOmiseCode() . PHP_EOL;
// Handle specific errors
if ($e->getOmiseCode() === 'authentication_failure') {
echo "Invalid API key";
} elseif ($e->getOmiseCode() === 'invalid_card') {
echo "Card declined";
}
}
Handling Specific Card Errorsโ
<?php
function handleCardError(OmiseException $e): string
{
$errorMessages = [
'insufficient_fund' => 'Insufficient funds on card',
'stolen_or_lost_card' => 'Card reported as stolen or lost',
'invalid_security_code' => 'Invalid CVV code',
'payment_cancelled' => 'Payment was cancelled'
];
return $errorMessages[$e->getOmiseCode()] ?? "Card error: " . $e->getMessage();
}
try {
$charge = OmiseCharge::create([
'amount' => 100000,
'currency' => 'THB',
'card' => $token
]);
} catch (OmiseException $e) {
$errorMessage = handleCardError($e);
// Display error to user
}
Laravel Integrationโ
Controller Exampleโ
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use App\Models\Order;
use App\Models\Payment;
class PaymentController extends Controller
{
public function __construct()
{
define('OMISE_SECRET_KEY', config('services.omise.secret_key'));
define('OMISE_PUBLIC_KEY', config('services.omise.public_key'));
}
public function create(Request $request, $orderId)
{
$order = Order::findOrFail($orderId);
$request->validate([
'omise_token' => 'required|string',
]);
try {
$charge = \OmiseCharge::create([
'amount' => (int)($order->total * 100),
'currency' => 'THB',
'card' => $request->omise_token,
'description' => "Order #{$order->id}",
'metadata' => [
'order_id' => $order->id,
'customer_email' => $order->email
],
'return_uri' => route('payment.callback')
]);
// Save payment record
Payment::create([
'order_id' => $order->id,
'charge_id' => $charge['id'],
'amount' => $order->total,
'status' => $charge['status'],
'paid' => $charge['paid']
]);
if ($charge['paid']) {
$order->update(['payment_status' => 'paid']);
return redirect()->route('orders.show', $order)
->with('success', 'Payment successful!');
} elseif (isset($charge['authorize_uri'])) {
// 3D Secure required
return redirect($charge['authorize_uri']);
} else {
return back()->with('error', $charge['failure_message']);
}
} catch (\OmiseException $e) {
Log::error('Omise error: ' . $e->getMessage());
return back()->with('error', 'Payment failed. Please try again.');
}
}
public function callback(Request $request)
{
$chargeId = $request->query('id');
try {
$charge = \OmiseCharge::retrieve($chargeId);
$payment = Payment::where('charge_id', $charge['id'])->firstOrFail();
$payment->update([
'status' => $charge['status'],
'paid' => $charge['paid']
]);
if ($charge['paid']) {
$payment->order->update(['payment_status' => 'paid']);
return redirect()->route('orders.show', $payment->order)
->with('success', 'Payment successful!');
} else {
return redirect()->route('payment.form', $payment->order)
->with('error', $charge['failure_message']);
}
} catch (\Exception $e) {
return redirect()->route('home')
->with('error', 'Payment verification failed.');
}
}
}
Model Integrationโ
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Payment extends Model
{
protected $fillable = [
'order_id', 'charge_id', 'amount', 'currency', 'status',
'paid', 'failure_code', 'failure_message', 'refund_id', 'refund_amount'
];
protected $casts = [
'amount' => 'decimal:2',
'refund_amount' => 'decimal:2',
'paid' => 'boolean'
];
public function __construct()
{
parent::__construct();
define('OMISE_SECRET_KEY', config('services.omise.secret_key'));
}
public function order()
{
return $this->belongsTo(Order::class);
}
public function charge(string $token): array
{
$charge = \OmiseCharge::create([
'amount' => (int)($this->amount * 100),
'currency' => $this->currency,
'card' => $token,
'description' => "Order #{$this->order->id}",
'metadata' => $this->getChargeMetadata()
]);
$this->update([
'charge_id' => $charge['id'],
'status' => $charge['status'],
'paid' => $charge['paid']
]);
return $charge;
}
public function refund(?float $refundAmount = null): array
{
if (!$this->charge_id) {
throw new \Exception('Cannot refund payment without charge_id');
}
$refund = \OmiseRefund::create($this->charge_id, [
'amount' => $refundAmount ? (int)($refundAmount * 100) : null
]);
$this->update([
'refund_id' => $refund['id'],
'refund_amount' => $refund['amount'] / 100,
'status' => 'refunded'
]);
return $refund;
}
public function refreshStatus(): void
{
if (!$this->charge_id) {
return;
}
$charge = \OmiseCharge::retrieve($this->charge_id);
$this->update([
'status' => $charge['status'],
'paid' => $charge['paid'],
'failure_code' => $charge['failure_code'] ?? null,
'failure_message' => $charge['failure_message'] ?? null
]);
}
protected function getChargeMetadata(): array
{
return [
'order_id' => $this->order->id,
'customer_email' => $this->order->email,
'customer_name' => $this->order->customer_name
];
}
}
Queue Job for Async Processingโ
<?php
namespace App\Jobs;
use App\Models\Payment;
use App\Mail\PaymentConfirmed;
use App\Mail\PaymentFailed;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Log;
class ProcessChargeJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 3;
public $backoff = [60, 120, 240];
protected $paymentId;
protected $token;
public function __construct(int $paymentId, string $token)
{
$this->paymentId = $paymentId;
$this->token = $token;
define('OMISE_SECRET_KEY', config('services.omise.secret_key'));
}
public function handle()
{
$payment = Payment::findOrFail($this->paymentId);
try {
$charge = $payment->charge($this->token);
if ($charge['paid']) {
Mail::to($payment->order->email)
->send(new PaymentConfirmed($payment));
} else {
Mail::to($payment->order->email)
->send(new PaymentFailed($payment, $charge['failure_message']));
}
} catch (\OmiseException $e) {
$payment->update([
'status' => 'failed',
'failure_message' => $e->getMessage()
]);
Mail::to($payment->order->email)
->send(new PaymentFailed($payment, $e->getMessage()));
Log::error('Charge processing failed', [
'payment_id' => $this->paymentId,
'error' => $e->getMessage()
]);
}
}
}
Service Providerโ
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class OmiseServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('omise', function ($app) {
return new \OmiseClient(
config('services.omise.secret_key'),
config('services.omise.public_key')
);
});
}
public function boot()
{
if ($this->app->runningInConsole()) {
$this->publishes([
__DIR__.'/../config/omise.php' => config_path('omise.php'),
], 'config');
}
}
}
Best Practicesโ
1. Use Environment Variables for Keysโ
<?php
// Never hardcode API keys
// Bad
define('OMISE_SECRET_KEY', 'skey_test_123456789');
// Good
define('OMISE_SECRET_KEY', getenv('OMISE_SECRET_KEY'));
// Better (Laravel)
define('OMISE_SECRET_KEY', config('services.omise.secret_key'));
// Validate keys exist
if (empty(OMISE_SECRET_KEY)) {
throw new \RuntimeException('OMISE_SECRET_KEY is not set');
}
2. Handle Idempotencyโ
<?php
class PaymentService
{
public function createIdempotentCharge(
int $amount,
string $currency,
string $token,
string $orderId
): array {
$idempotencyKey = sprintf('order-%s-%s', $orderId, time());
// Store idempotency key in cache/database
\Cache::put("charge:idempotency:{$orderId}", $idempotencyKey, 3600);
return OmiseCharge::create([
'amount' => $amount,
'currency' => $currency,
'card' => $token
], $idempotencyKey);
}
}
3. Use Type Declarations (PHP 7.4+)โ
<?php
declare(strict_types=1);
class ChargeService
{
private string $secretKey;
public function __construct(string $secretKey)
{
$this->secretKey = $secretKey;
define('OMISE_SECRET_KEY', $secretKey);
}
public function createCharge(
int $amount,
string $currency,
string $token,
array $metadata = []
): array {
return OmiseCharge::create([
'amount' => $amount,
'currency' => $currency,
'card' => $token,
'metadata' => $metadata
]);
}
}
4. Store Minimal Dataโ
<?php
class Payment
{
private string $chargeId;
private ?array $chargeCache = null;
public function getCharge(): array
{
if ($this->chargeCache === null) {
$this->chargeCache = \OmiseCharge::retrieve($this->chargeId);
}
return $this->chargeCache;
}
public function refreshCharge(): array
{
$this->chargeCache = null;
return $this->getCharge();
}
}
5. Validate Before API Callsโ
<?php
class ChargeValidator
{
public function validate(int $amount, string $currency): void
{
if ($amount < 2000) {
throw new \InvalidArgumentException('Amount must be at least 20 THB');
}
$allowedCurrencies = ['THB', 'USD', 'SGD', 'JPY'];
if (!in_array($currency, $allowedCurrencies)) {
throw new \InvalidArgumentException("Currency {$currency} not supported");
}
}
}
// Usage
$validator = new ChargeValidator();
$validator->validate(100000, 'THB');
$charge = OmiseCharge::create([...]);
6. Implement Loggingโ
<?php
use Psr\Log\LoggerInterface;
class PaymentService
{
private LoggerInterface $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function createCharge(array $params): array
{
$this->logger->info('Creating charge', [
'amount' => $params['amount'],
'currency' => $params['currency']
]);
try {
$charge = OmiseCharge::create($params);
$this->logger->info('Charge created', [
'charge_id' => $charge['id'],
'paid' => $charge['paid']
]);
return $charge;
} catch (\OmiseException $e) {
$this->logger->error('Charge failed', [
'error' => $e->getMessage(),
'code' => $e->getOmiseCode()
]);
throw $e;
}
}
}
Testingโ
PHPUnit Examplesโ
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use Mockery;
class PaymentServiceTest extends TestCase
{
protected function tearDown(): void
{
Mockery::close();
}
public function testCreateCharge()
{
// Mock OmiseCharge
$chargeMock = Mockery::mock('alias:OmiseCharge');
$chargeMock->shouldReceive('create')
->once()
->with([
'amount' => 100000,
'currency' => 'THB',
'card' => 'tokn_test_123'
])
->andReturn([
'id' => 'chrg_test_123',
'amount' => 100000,
'currency' => 'THB',
'paid' => true,
'status' => 'successful'
]);
$service = new \App\Services\PaymentService();
$charge = $service->createCharge(100000, 'THB', 'tokn_test_123');
$this->assertEquals('chrg_test_123', $charge['id']);
$this->assertTrue($charge['paid']);
}
public function testCreateChargeWithError()
{
$this->expectException(\OmiseException::class);
$chargeMock = Mockery::mock('alias:OmiseCharge');
$chargeMock->shouldReceive('create')
->once()
->andThrow(new \OmiseException('insufficient_fund'));
$service = new \App\Services\PaymentService();
$service->createCharge(100000, 'THB', 'tokn_test_123');
}
}
Laravel Feature Testsโ
<?php
namespace Tests\Feature;
use Tests\TestCase;
use App\Models\Order;
use App\Models\Payment;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
class PaymentControllerTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
config(['services.omise.secret_key' => 'skey_test_123']);
}
public function testCreatePayment()
{
$order = Order::factory()->create(['total' => 1000.00]);
$chargeMock = Mockery::mock('alias:OmiseCharge');
$chargeMock->shouldReceive('create')
->once()
->andReturn([
'id' => 'chrg_test_123',
'paid' => true,
'status' => 'successful'
]);
$response = $this->post("/payment/{$order->id}", [
'omise_token' => 'tokn_test_123'
]);
$response->assertRedirect(route('orders.show', $order));
$this->assertDatabaseHas('payments', [
'order_id' => $order->id,
'charge_id' => 'chrg_test_123',
'paid' => true
]);
}
}
Troubleshootingโ
SSL Certificate Errorsโ
<?php
// Update CA bundle (not recommended for production)
define('OMISE_CA_BUNDLE_PATH', '/path/to/cacert.pem');
// Or disable SSL verification (only for testing!)
// This is NOT recommended for production
Connection Timeoutsโ
<?php
// Set custom timeout
define('OMISE_API_TIMEOUT', 60); // seconds
Debug Modeโ
<?php
// Enable debug mode
define('OMISE_DEBUG', true);
// This will output request/response details
Webhook Signature Verificationโ
<?php
function verifyWebhookSignature(string $payload, string $signature, string $secret): bool
{
$expectedSignature = hash_hmac('sha256', $payload, $secret);
return hash_equals($signature, $expectedSignature);
}
// Usage
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_OMISE_SIGNATURE'] ?? '';
if (!verifyWebhookSignature($payload, $signature, getenv('OMISE_WEBHOOK_SECRET'))) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($payload, true);
// Process event
FAQโ
How do I handle webhooks in Laravel?โ
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class WebhookController extends Controller
{
public function omise(Request $request)
{
// Verify signature
$payload = $request->getContent();
$signature = $request->header('Omise-Signature');
if (!$this->verifySignature($payload, $signature)) {
return response()->json(['error' => 'Invalid signature'], 401);
}
$event = $request->json()->all();
switch ($event['key']) {
case 'charge.complete':
$this->handleChargeComplete($event['data']);
break;
case 'refund.create':
$this->handleRefundCreate($event['data']);
break;
}
return response()->json(['status' => 'ok']);
}
protected function verifySignature(string $payload, string $signature): bool
{
$expected = hash_hmac('sha256', $payload, config('services.omise.webhook_secret'));
return hash_equals($signature, $expected);
}
protected function handleChargeComplete(array $chargeData): void
{
$charge = \OmiseCharge::retrieve($chargeData['id']);
$payment = Payment::where('charge_id', $charge['id'])->first();
if ($payment) {
$payment->update([
'status' => $charge['status'],
'paid' => $charge['paid']
]);
}
}
}
How do I test payments without real charges?โ
<?php
// Use test API keys
define('OMISE_SECRET_KEY', 'skey_test_123456789');
// Use test card tokens
// Successful: tokn_test_5086xl7ddjbases4sq3i
// Declined: tokn_test_no1
$charge = OmiseCharge::create([
'amount' => 100000,
'currency' => 'THB',
'card' => 'tokn_test_5086xl7ddjbases4sq3i'
]);
// Check test mode
var_dump($charge['livemode'] === false);
How do I handle currency conversion?โ
<?php
class Money
{
private int $amountSatang;
public function __construct(float $amountBaht)
{
$this->amountSatang = (int)($amountBaht * 100);
}
public function getSatang(): int
{
return $this->amountSatang;
}
public function getBaht(): float
{
return $this->amountSatang / 100;
}
public static function fromSatang(int $amountSatang): self
{
$instance = new self(0);
$instance->amountSatang = $amountSatang;
return $instance;
}
}
// Usage
$money = new Money(1000.00); // 1000.00 THB
$charge = OmiseCharge::create([
'amount' => $money->getSatang(), // 100000 satang
'currency' => 'THB',
'card' => 'tokn_test_123'
]);
How do I implement retry logic?โ
<?php
function createChargeWithRetry(array $params, int $maxRetries = 3): array
{
$attempt = 0;
while ($attempt < $maxRetries) {
try {
return OmiseCharge::create($params);
} catch (\OmiseException $e) {
$attempt++;
if ($attempt >= $maxRetries) {
throw $e;
}
// Exponential backoff
sleep(2 ** $attempt);
}
}
}
How do I handle partial refunds?โ
<?php
function refundCharge(string $chargeId, ?float $refundAmount = null): array
{
// Get current charge
$charge = \OmiseCharge::retrieve($chargeId);
// Calculate refundable amount
$refundable = $charge['amount'] - $charge['refunded'];
if ($refundAmount !== null) {
$refundAmountSatang = (int)($refundAmount * 100);
if ($refundAmountSatang > $refundable) {
throw new \Exception("Refund amount exceeds refundable amount");
}
} else {
$refundAmountSatang = null; // Full refund
}
return \OmiseRefund::create($chargeId, [
'amount' => $refundAmountSatang
]);
}
How do I save multiple cards for a customer?โ
<?php
function addCardToCustomer(string $customerId, string $token): array
{
return \OmiseCustomer::update($customerId, [
'card' => $token
]);
}
function listCustomerCards(string $customerId): array
{
$customer = \OmiseCustomer::retrieve($customerId);
return $customer['cards']['data'];
}
function chargeSpecificCard(string $customerId, string $cardId, int $amount): array
{
return \OmiseCharge::create([
'amount' => $amount,
'currency' => 'THB',
'customer' => $customerId,
'card' => $cardId
]);
}
How do I implement subscription billing?โ
<?php
class SubscriptionManager
{
private string $customerId;
private int $planAmount;
public function __construct(string $customerId, int $planAmount)
{
$this->customerId = $customerId;
$this->planAmount = $planAmount;
}
public function chargeMonthly(): array
{
return \OmiseCharge::create([
'amount' => $this->planAmount,
'currency' => 'THB',
'customer' => $this->customerId,
'description' => 'Subscription ' . date('F Y')
]);
}
public function handleFailedPayment(array $charge): void
{
// Send notification
// Update subscription status
// Retry after grace period
}
}
// Laravel Command
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Subscription;
class ChargeSubscriptions extends Command
{
protected $signature = 'subscriptions:charge';
public function handle()
{
$subscriptions = Subscription::where('status', 'active')->get();
foreach ($subscriptions as $subscription) {
$manager = new SubscriptionManager(
$subscription->customer_id,
$subscription->plan_amount
);
try {
$charge = $manager->chargeMonthly();
$subscription->update(['last_charge_date' => now()]);
} catch (\OmiseException $e) {
$manager->handleFailedPayment($charge);
}
}
}
}
Related Resourcesโ
- Omise API Documentation
- omise-php GitHub Repository
- Packagist Package
- Omise Dashboard
- Webhooks Guide
- Testing Guide
Next Stepsโ
Supportโ
If you encounter issues with the PHP library:
- Check the GitHub Issues
- Review the API documentation
- Contact support@omise.co with:
- PHP version
- omise-php library version
- Error message and stack trace
- Steps to reproduce