Skip to main content

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โ€‹

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);
}
}
}
}

Next Stepsโ€‹

Supportโ€‹

If you encounter issues with the PHP library:

  1. Check the GitHub Issues
  2. Review the API documentation
  3. Contact support@omise.co with:
    • PHP version
    • omise-php library version
    • Error message and stack trace
    • Steps to reproduce