Skip to main content

Dart SDK

The Omise Dart SDK provides a comprehensive server-side integration for the Omise payment API. Built for backend services, cloud functions, and CLI tools, it offers full API access with type-safe request/response models and async support.

Overviewโ€‹

The Dart SDK enables you to:

  • Full API access - Complete control over charges, customers, cards, and more
  • Server-side operations - Create charges, manage customers, handle refunds
  • Type-safe models - Fully typed request and response objects
  • Async/await support - Modern Dart async patterns
  • Null safety - Built with sound null safety
  • Webhook handling - Verify and process webhook events
  • Error handling - Comprehensive exception types

Key Featuresโ€‹

  • Null-safe Dart 2.12+ API
  • Future-based async operations
  • Complete API coverage
  • Type-safe request builders
  • Webhook signature verification
  • Automatic request retry logic
  • Custom HTTP client support
  • Comprehensive error handling

Requirementsโ€‹

  • Dart 2.12 or later (null safety)
  • Server-side or CLI application
  • Not suitable for client-side/browser use (requires secret key)

Installationโ€‹

Add to pubspec.yamlโ€‹

dependencies:
omise_dart: ^3.0.0

Install Packageโ€‹

dart pub add omise_dart

Or manually:

dart pub get

Quick Startโ€‹

1. Import the Packageโ€‹

import 'package:omise_dart/omise_dart.dart';

2. Initialize the Clientโ€‹

void main() async {
final omise = Omise(
publicKey: 'pkey_test_5xyzyx5xyzyx5xyzyx5',
secretKey: 'skey_test_5xyzyx5xyzyx5xyzyx5',
);
}

3. Create a Chargeโ€‹

Future<void> createCharge() async {
final omise = Omise(
publicKey: 'pkey_test_5xyzyx5xyzyx5xyzyx5',
secretKey: 'skey_test_5xyzyx5xyzyx5xyzyx5',
);

try {
final charge = await omise.charges.create(
amount: 100000, // 1,000.00 THB
currency: 'thb',
card: 'tokn_test_5xyzyx5xyzyx5xyzyx5',
description: 'Order #1234',
metadata: {
'order_id': '1234',
'customer_email': 'john@example.com',
},
);

print('Charge created: ${charge.id}');
print('Status: ${charge.status}');

if (charge.paid) {
print('Payment successful!');
}

} catch (error) {
print('Error: $error');
}
}

Configurationโ€‹

Client Configurationโ€‹

// Basic configuration
final omise = Omise(
publicKey: 'pkey_test_5xyzyx5xyzyx5xyzyx5',
secretKey: 'skey_test_5xyzyx5xyzyx5xyzyx5',
);

// Advanced configuration
final omise = Omise(
publicKey: 'pkey_test_5xyzyx5xyzyx5xyzyx5',
secretKey: 'skey_test_5xyzyx5xyzyx5xyzyx5',
apiVersion: '2019-05-29',
timeout: Duration(seconds: 60),
debugMode: true,
);

Environment-based Configurationโ€‹

import 'dart:io';

class Config {
static String get publicKey {
return Platform.environment['OMISE_PUBLIC_KEY'] ??
'pkey_test_5xyzyx5xyzyx5xyzyx5';
}

static String get secretKey {
return Platform.environment['OMISE_SECRET_KEY'] ??
'skey_test_5xyzyx5xyzyx5xyzyx5';
}
}

final omise = Omise(
publicKey: Config.publicKey,
secretKey: Config.secretKey,
);

Custom HTTP Clientโ€‹

import 'package:http/http.dart' as http;

final customClient = http.Client();

final omise = Omise(
publicKey: 'pkey_test_...',
secretKey: 'skey_test_...',
httpClient: customClient,
);

// Don't forget to close the client when done
void cleanup() {
customClient.close();
}

Chargesโ€‹

Create a Chargeโ€‹

// Charge a card token
Future<Charge> chargeCard(String tokenId, int amount) async {
return await omise.charges.create(
amount: amount,
currency: 'thb',
card: tokenId,
description: 'Payment for order',
returnUri: 'https://example.com/payment/callback',
);
}

// Charge a customer's default card
Future<Charge> chargeCustomer(String customerId, int amount) async {
return await omise.charges.create(
amount: amount,
currency: 'thb',
customer: customerId,
description: 'Subscription payment',
);
}

// Charge with a source
Future<Charge> chargeSource(String sourceId, int amount) async {
return await omise.charges.create(
amount: amount,
currency: 'thb',
source: sourceId,
returnUri: 'https://example.com/payment/callback',
);
}

Create Charge with Metadataโ€‹

Future<Charge> createChargeWithMetadata() async {
return await omise.charges.create(
amount: 100000,
currency: 'thb',
card: 'tokn_test_5xyzyx5xyzyx5xyzyx5',
description: 'Order #1234',
metadata: {
'order_id': '1234',
'customer_email': 'john@example.com',
'customer_name': 'John Doe',
'shipping_method': 'express',
},
);
}

Retrieve a Chargeโ€‹

Future<Charge> getCharge(String chargeId) async {
return await omise.charges.retrieve(chargeId);
}

// Check charge status
Future<void> checkChargeStatus(String chargeId) async {
final charge = await omise.charges.retrieve(chargeId);

print('Charge ID: ${charge.id}');
print('Status: ${charge.status}');
print('Paid: ${charge.paid}');
print('Amount: ${charge.amount}');

if (charge.authorized) {
print('Charge is authorized');
}

if (charge.captured) {
print('Charge is captured');
}

if (charge.reversed) {
print('Charge is reversed');
}
}

List Chargesโ€‹

Future<ChargeList> listCharges({
int limit = 20,
int offset = 0,
}) async {
return await omise.charges.list(
limit: limit,
offset: offset,
);
}

// List all charges
Future<void> listAllCharges() async {
var offset = 0;
const limit = 100;

while (true) {
final charges = await omise.charges.list(
limit: limit,
offset: offset,
);

for (final charge in charges.data) {
print('Charge: ${charge.id} - ${charge.amount}');
}

if (charges.data.length < limit) break;
offset += limit;
}
}

Update a Chargeโ€‹

Future<Charge> updateCharge(String chargeId) async {
return await omise.charges.update(
chargeId,
description: 'Updated description',
metadata: {
'updated_at': DateTime.now().toIso8601String(),
},
);
}

Capture a Chargeโ€‹

Future<Charge> captureCharge(String chargeId) async {
return await omise.charges.capture(chargeId);
}

// Partial capture
Future<Charge> partialCapture(String chargeId, int amount) async {
return await omise.charges.capture(
chargeId,
captureAmount: amount,
);
}

Reverse a Chargeโ€‹

Future<Charge> reverseCharge(String chargeId) async {
return await omise.charges.reverse(chargeId);
}

Customersโ€‹

Create a Customerโ€‹

Future<Customer> createCustomer({
required String email,
String? description,
Map<String, dynamic>? metadata,
}) async {
return await omise.customers.create(
email: email,
description: description,
metadata: metadata,
);
}

// Usage
final customer = await createCustomer(
email: 'john@example.com',
description: 'John Doe',
metadata: {
'phone': '+66812345678',
'address': '123 Wireless Road, Bangkok',
},
);

Retrieve a Customerโ€‹

Future<Customer> getCustomer(String customerId) async {
return await omise.customers.retrieve(customerId);
}

Update a Customerโ€‹

Future<Customer> updateCustomer(String customerId) async {
return await omise.customers.update(
customerId,
email: 'newemail@example.com',
description: 'Updated customer',
metadata: {
'last_updated': DateTime.now().toIso8601String(),
},
);
}

List Customersโ€‹

Future<CustomerList> listCustomers({
int limit = 20,
int offset = 0,
}) async {
return await omise.customers.list(
limit: limit,
offset: offset,
);
}

Delete a Customerโ€‹

Future<DeletedCustomer> deleteCustomer(String customerId) async {
return await omise.customers.delete(customerId);
}

Cardsโ€‹

Create a Cardโ€‹

Future<Card> addCardToCustomer(
String customerId,
String tokenId,
) async {
return await omise.customers.cards.create(
customerId,
card: tokenId,
);
}

List Customer Cardsโ€‹

Future<CardList> listCustomerCards(String customerId) async {
return await omise.customers.cards.list(customerId);
}

// Get default card
Future<Card?> getDefaultCard(String customerId) async {
final customer = await omise.customers.retrieve(customerId);

if (customer.defaultCard != null) {
return await omise.customers.cards.retrieve(
customerId,
customer.defaultCard!,
);
}

return null;
}

Update a Cardโ€‹

Future<Card> updateCard(
String customerId,
String cardId, {
String? name,
int? expirationMonth,
int? expirationYear,
}) async {
return await omise.customers.cards.update(
customerId,
cardId,
name: name,
expirationMonth: expirationMonth,
expirationYear: expirationYear,
);
}

Delete a Cardโ€‹

Future<DeletedCard> deleteCard(
String customerId,
String cardId,
) async {
return await omise.customers.cards.delete(customerId, cardId);
}

Refundsโ€‹

Create a Refundโ€‹

Future<Refund> createRefund(String chargeId) async {
// Full refund
return await omise.refunds.create(chargeId);
}

// Partial refund
Future<Refund> partialRefund(String chargeId, int amount) async {
return await omise.refunds.create(
chargeId,
amount: amount,
);
}

// Refund with metadata
Future<Refund> refundWithReason(
String chargeId,
String reason,
) async {
return await omise.refunds.create(
chargeId,
metadata: {
'reason': reason,
'refunded_by': 'system',
'refunded_at': DateTime.now().toIso8601String(),
},
);
}

Retrieve a Refundโ€‹

Future<Refund> getRefund(String chargeId, String refundId) async {
return await omise.refunds.retrieve(chargeId, refundId);
}

List Refundsโ€‹

Future<RefundList> listRefunds(String chargeId) async {
return await omise.refunds.list(chargeId);
}

// List all refunds for a charge
Future<void> listAllRefunds(String chargeId) async {
final refunds = await omise.refunds.list(chargeId);

for (final refund in refunds.data) {
print('Refund: ${refund.id}');
print('Amount: ${refund.amount}');
print('Status: ${refund.status}');
}
}

Sourcesโ€‹

Create a Sourceโ€‹

// Internet Banking
Future<Source> createInternetBankingSource(int amount) async {
return await omise.sources.create(
amount: amount,
currency: 'thb',
type: 'internet_banking_bay',
);
}

// PromptPay
Future<Source> createPromptPaySource(int amount) async {
return await omise.sources.create(
amount: amount,
currency: 'thb',
type: 'promptpay',
);
}

// TrueMoney Wallet
Future<Source> createTrueMoneySource(
int amount,
String phoneNumber,
) async {
return await omise.sources.create(
amount: amount,
currency: 'thb',
type: 'truemoney',
phoneNumber: phoneNumber,
);
}

// Installments
Future<Source> createInstallmentSource(
int amount,
int installmentTerms,
) async {
return await omise.sources.create(
amount: amount,
currency: 'thb',
type: 'installment_bay',
installmentTerms: installmentTerms,
);
}

Retrieve a Sourceโ€‹

Future<Source> getSource(String sourceId) async {
return await omise.sources.retrieve(sourceId);
}

// Poll source until it's paid
Future<Source> waitForSourcePayment(
String sourceId, {
Duration interval = const Duration(seconds: 3),
Duration timeout = const Duration(minutes: 10),
}) async {
final deadline = DateTime.now().add(timeout);

while (DateTime.now().isBefore(deadline)) {
final source = await omise.sources.retrieve(sourceId);

if (source.status == 'successful' || source.status == 'failed') {
return source;
}

await Future.delayed(interval);
}

throw TimeoutException('Source payment timeout');
}

Tokensโ€‹

Create a Tokenโ€‹

// Note: Tokens should be created client-side for PCI compliance
// This is only for server-to-server scenarios

Future<Token> createToken({
required String name,
required String number,
required int expirationMonth,
required int expirationYear,
required String securityCode,
}) async {
return await omise.tokens.create(
name: name,
number: number,
expirationMonth: expirationMonth,
expirationYear: expirationYear,
securityCode: securityCode,
);
}

Retrieve a Tokenโ€‹

Future<Token> getToken(String tokenId) async {
return await omise.tokens.retrieve(tokenId);
}

Transfersโ€‹

Create a Transferโ€‹

Future<Transfer> createTransfer(int amount) async {
return await omise.transfers.create(
amount: amount,
);
}

// Transfer with recipient
Future<Transfer> transferToRecipient(
String recipientId,
int amount,
) async {
return await omise.transfers.create(
amount: amount,
recipient: recipientId,
);
}

Retrieve a Transferโ€‹

Future<Transfer> getTransfer(String transferId) async {
return await omise.transfers.retrieve(transferId);
}

List Transfersโ€‹

Future<TransferList> listTransfers({
int limit = 20,
int offset = 0,
}) async {
return await omise.transfers.list(
limit: limit,
offset: offset,
);
}

Update a Transferโ€‹

Future<Transfer> updateTransfer(String transferId) async {
return await omise.transfers.update(
transferId,
amount: 50000, // Update transfer amount
);
}

Destroy a Transferโ€‹

Future<DeletedTransfer> deleteTransfer(String transferId) async {
return await omise.transfers.destroy(transferId);
}

Recipientsโ€‹

Create a Recipientโ€‹

Future<Recipient> createRecipient({
required String name,
required String email,
required String type,
required Map<String, dynamic> bankAccount,
}) async {
return await omise.recipients.create(
name: name,
email: email,
type: type,
bankAccount: bankAccount,
);
}

// Usage
final recipient = await createRecipient(
name: 'John Doe',
email: 'john@example.com',
type: 'individual',
bankAccount: {
'brand': 'bbl',
'number': '1234567890',
'name': 'John Doe',
},
);

Retrieve a Recipientโ€‹

Future<Recipient> getRecipient(String recipientId) async {
return await omise.recipients.retrieve(recipientId);
}

List Recipientsโ€‹

Future<RecipientList> listRecipients({
int limit = 20,
int offset = 0,
}) async {
return await omise.recipients.list(
limit: limit,
offset: offset,
);
}

Update a Recipientโ€‹

Future<Recipient> updateRecipient(String recipientId) async {
return await omise.recipients.update(
recipientId,
name: 'Updated Name',
email: 'newemail@example.com',
);
}

Delete a Recipientโ€‹

Future<DeletedRecipient> deleteRecipient(String recipientId) async {
return await omise.recipients.delete(recipientId);
}

Webhooksโ€‹

Verify Webhook Signatureโ€‹

import 'dart:convert';
import 'package:crypto/crypto.dart';

class WebhookHandler {
final String secretKey;

WebhookHandler(this.secretKey);

bool verifySignature(String payload, String signature) {
final hmac = Hmac(sha256, utf8.encode(secretKey));
final digest = hmac.convert(utf8.encode(payload));
final expectedSignature = base64.encode(digest.bytes);

return signature == expectedSignature;
}

WebhookEvent parseEvent(String payload) {
final json = jsonDecode(payload);
return WebhookEvent.fromJson(json);
}
}

// Usage with shelf (Dart web server)
import 'package:shelf/shelf.dart';

Response handleWebhook(Request request) async {
final payload = await request.readAsString();
final signature = request.headers['x-omise-signature'];

if (signature == null) {
return Response.unauthorized('Missing signature');
}

final handler = WebhookHandler('skey_test_...');

if (!handler.verifySignature(payload, signature)) {
return Response.unauthorized('Invalid signature');
}

final event = handler.parseEvent(payload);
await processWebhookEvent(event);

return Response.ok('OK');
}

Handle Webhook Eventsโ€‹

Future<void> processWebhookEvent(WebhookEvent event) async {
print('Received event: ${event.key}');

switch (event.key) {
case 'charge.complete':
await handleChargeComplete(event.data as Charge);
break;

case 'charge.create':
await handleChargeCreate(event.data as Charge);
break;

case 'refund.create':
await handleRefundCreate(event.data as Refund);
break;

case 'transfer.create':
await handleTransferCreate(event.data as Transfer);
break;

default:
print('Unhandled event type: ${event.key}');
}
}

Future<void> handleChargeComplete(Charge charge) async {
print('Charge completed: ${charge.id}');

if (charge.paid) {
// Update order status in your database
await updateOrderStatus(
charge.metadata['order_id'],
'paid',
);

// Send confirmation email
await sendConfirmationEmail(
charge.metadata['customer_email'],
charge.id,
);
}
}

Error Handlingโ€‹

Exception Typesโ€‹

Future<void> handlePayment() async {
try {
final charge = await omise.charges.create(
amount: 100000,
currency: 'thb',
card: 'tokn_test_...',
);

print('Charge created: ${charge.id}');

} on OmiseException catch (e) {
// API error from Omise
print('API Error: ${e.message}');
print('Code: ${e.code}');
print('Status: ${e.statusCode}');

} on NetworkException catch (e) {
// Network connectivity error
print('Network Error: ${e.message}');

} on AuthenticationException catch (e) {
// Invalid API keys
print('Authentication Error: ${e.message}');

} catch (e) {
// Unknown error
print('Unknown Error: $e');
}
}

Handle Specific Error Codesโ€‹

Future<Charge> createChargeWithErrorHandling(
String tokenId,
int amount,
) async {
try {
return await omise.charges.create(
amount: amount,
currency: 'thb',
card: tokenId,
);

} on OmiseException catch (e) {
switch (e.code) {
case 'invalid_card':
throw PaymentException('Invalid card details');

case 'insufficient_fund':
throw PaymentException('Insufficient funds');

case 'failed_processing':
throw PaymentException('Payment processing failed');

case 'invalid_security_code':
throw PaymentException('Invalid CVV');

case 'stolen_or_lost_card':
throw PaymentException('Card reported stolen or lost');

default:
throw PaymentException('Payment failed: ${e.message}');
}
}
}

class PaymentException implements Exception {
final String message;
PaymentException(this.message);

@override
String toString() => message;
}

Retry Logicโ€‹

Future<Charge> createChargeWithRetry({
required String tokenId,
required int amount,
int maxAttempts = 3,
Duration delay = const Duration(seconds: 1),
}) async {
int attempts = 0;

while (attempts < maxAttempts) {
try {
return await omise.charges.create(
amount: amount,
currency: 'thb',
card: tokenId,
);

} on NetworkException catch (e) {
attempts++;

if (attempts >= maxAttempts) {
rethrow;
}

print('Attempt $attempts failed, retrying...');
await Future.delayed(delay * attempts);

} on OmiseException catch (e) {
// Don't retry on API errors
rethrow;
}
}

throw Exception('Max retry attempts reached');
}

Best Practicesโ€‹

Securityโ€‹

// โœ… DO: Use environment variables
final omise = Omise(
publicKey: Platform.environment['OMISE_PUBLIC_KEY']!,
secretKey: Platform.environment['OMISE_SECRET_KEY']!,
);

// โŒ DON'T: Hardcode API keys
// final omise = Omise(
// publicKey: 'pkey_...',
// secretKey: 'skey_...',
// );

// โœ… DO: Validate input
Future<Charge> createCharge(Map<String, dynamic> data) async {
final amount = data['amount'] as int?;
if (amount == null || amount <= 0) {
throw ArgumentError('Invalid amount');
}

return await omise.charges.create(
amount: amount,
currency: 'thb',
card: data['token'] as String,
);
}

// โœ… DO: Use HTTPS only
// The SDK enforces HTTPS connections

// โŒ DON'T: Log sensitive data
// print('Card: ${card.number}');

// โœ… DO: Use sanitized logging
print('Charge created: ${charge.id}');

Performanceโ€‹

// โœ… DO: Reuse the client
class PaymentService {
static final PaymentService _instance = PaymentService._internal();
factory PaymentService() => _instance;

late final Omise omise;

PaymentService._internal() {
omise = Omise(
publicKey: Config.publicKey,
secretKey: Config.secretKey,
);
}
}

// โœ… DO: Use pagination for large lists
Future<List<Charge>> getAllCharges() async {
final allCharges = <Charge>[];
var offset = 0;
const limit = 100;

while (true) {
final charges = await omise.charges.list(
limit: limit,
offset: offset,
);

allCharges.addAll(charges.data);

if (charges.data.length < limit) break;
offset += limit;
}

return allCharges;
}

// โœ… DO: Cache frequently accessed data
class CachedCustomerService {
final Omise omise;
final Map<String, Customer> _cache = {};

CachedCustomerService(this.omise);

Future<Customer> getCustomer(String id) async {
if (_cache.containsKey(id)) {
return _cache[id]!;
}

final customer = await omise.customers.retrieve(id);
_cache[id] = customer;
return customer;
}
}

Error Handlingโ€‹

// โœ… DO: Handle errors gracefully
Future<Charge?> createChargeWithFallback(
String tokenId,
int amount,
) async {
try {
return await omise.charges.create(
amount: amount,
currency: 'thb',
card: tokenId,
);

} on OmiseException catch (e) {
logger.error('Payment failed', error: e);
await notifyAdmins(e);
return null;

} on NetworkException catch (e) {
logger.error('Network error', error: e);
await queueForRetry(tokenId, amount);
return null;
}
}

// โœ… DO: Validate webhook signatures
Future<void> handleWebhook(Request request) async {
final signature = request.headers['x-omise-signature'];

if (signature == null) {
throw UnauthorizedException('Missing signature');
}

final payload = await request.readAsString();

if (!verifySignature(payload, signature)) {
throw UnauthorizedException('Invalid signature');
}

await processWebhook(payload);
}

Testingโ€‹

Unit Testingโ€‹

import 'package:test/test.dart';
import 'package:mockito/mockito.dart';

class MockOmise extends Mock implements Omise {}

void main() {
group('Payment Tests', () {
late MockOmise mockOmise;

setUp(() {
mockOmise = MockOmise();
});

test('createCharge returns charge on success', () async {
// Arrange
final expectedCharge = Charge(
id: 'chrg_test_123',
amount: 100000,
currency: 'thb',
status: 'successful',
paid: true,
);

when(mockOmise.charges.create(
amount: anyNamed('amount'),
currency: anyNamed('currency'),
card: anyNamed('card'),
)).thenAnswer((_) async => expectedCharge);

// Act
final charge = await mockOmise.charges.create(
amount: 100000,
currency: 'thb',
card: 'tokn_test_123',
);

// Assert
expect(charge.id, equals('chrg_test_123'));
expect(charge.paid, isTrue);
});

test('createCharge throws on invalid card', () async {
// Arrange
when(mockOmise.charges.create(
amount: anyNamed('amount'),
currency: anyNamed('currency'),
card: anyNamed('card'),
)).thenThrow(
OmiseException('invalid_card', 'Invalid card'),
);

// Act & Assert
expect(
() => mockOmise.charges.create(
amount: 100000,
currency: 'thb',
card: 'tokn_invalid',
),
throwsA(isA<OmiseException>()),
);
});
});
}

Integration Testingโ€‹

import 'package:test/test.dart';

void main() {
group('Integration Tests', () {
late Omise omise;

setUp(() {
omise = Omise(
publicKey: 'pkey_test_5xyzyx5xyzyx5xyzyx5',
secretKey: 'skey_test_5xyzyx5xyzyx5xyzyx5',
);
});

test('create and retrieve charge', () async {
// Create token
final token = await omise.tokens.create(
name: 'Test User',
number: '4242424242424242',
expirationMonth: 12,
expirationYear: 2025,
securityCode: '123',
);

// Create charge
final charge = await omise.charges.create(
amount: 100000,
currency: 'thb',
card: token.id,
);

expect(charge.id, isNotEmpty);
expect(charge.amount, equals(100000));

// Retrieve charge
final retrieved = await omise.charges.retrieve(charge.id);
expect(retrieved.id, equals(charge.id));
});
});
}

Troubleshootingโ€‹

Common Issuesโ€‹

Issue: "Authentication failed" error

// Solution: Check your API keys
final omise = Omise(
publicKey: 'pkey_test_...', // Must start with pkey_
secretKey: 'skey_test_...', // Must start with skey_
);

Issue: Network timeout errors

// Solution: Increase timeout
final omise = Omise(
publicKey: 'pkey_test_...',
secretKey: 'skey_test_...',
timeout: Duration(seconds: 60),
);

Issue: SSL certificate errors

// Solution: Ensure system certificates are up to date
// For development only:
// import 'dart:io';
// HttpOverrides.global = DevHttpOverrides();

Issue: "Invalid charge" error

// Solution: Ensure amount is in smallest currency unit
final amount = 100000; // 1,000.00 THB (not 1000.00)

final charge = await omise.charges.create(
amount: amount,
currency: 'thb',
card: tokenId,
);

Frequently Asked Questionsโ€‹

Can I use this SDK in Flutter apps?

This SDK is designed for server-side use only as it requires your secret key. For Flutter apps, use the Flutter SDK which only requires your public key.

How do I handle idempotency?

Use the idempotency key parameter to ensure operations are not duplicated:

final charge = await omise.charges.create(
amount: 100000,
currency: 'thb',
card: tokenId,
idempotencyKey: 'order_1234_payment',
);

Can I use this with cloud functions?

Yes, the SDK works great with cloud functions (Firebase, AWS Lambda, etc.):

// Firebase Cloud Function
import 'package:functions_framework/functions_framework.dart';

@CloudFunction()
Future<Response> handlePayment(Request request) async {
final omise = Omise(
publicKey: Platform.environment['OMISE_PUBLIC_KEY']!,
secretKey: Platform.environment['OMISE_SECRET_KEY']!,
);

final body = await request.readAsString();
final data = jsonDecode(body);

final charge = await omise.charges.create(
amount: data['amount'],
currency: 'thb',
card: data['token'],
);

return Response.ok(jsonEncode(charge.toJson()));
}

How do I test webhooks locally?

Use a tool like ngrok to expose your local server:

dart run bin/server.dart
ngrok http 8080

Then configure the ngrok URL in your Omise dashboard.

Can I batch operations?

The SDK doesn't provide built-in batching, but you can use Future.wait:

final charges = await Future.wait([
omise.charges.create(amount: 10000, currency: 'thb', card: token1),
omise.charges.create(amount: 20000, currency: 'thb', card: token2),
omise.charges.create(amount: 30000, currency: 'thb', card: token3),
]);

How do I handle rate limiting?

Implement exponential backoff:

Future<T> withRetry<T>(Future<T> Function() operation) async {
var delay = Duration(seconds: 1);
var attempts = 0;
const maxAttempts = 5;

while (attempts < maxAttempts) {
try {
return await operation();
} on OmiseException catch (e) {
if (e.statusCode == 429) { // Rate limited
attempts++;
if (attempts >= maxAttempts) rethrow;
await Future.delayed(delay);
delay *= 2;
} else {
rethrow;
}
}
}

throw Exception('Max retries exceeded');
}

Can I use this SDK with multiple accounts?

Yes, create multiple client instances:

final omise1 = Omise(
publicKey: 'pkey_account1_...',
secretKey: 'skey_account1_...',
);

final omise2 = Omise(
publicKey: 'pkey_account2_...',
secretKey: 'skey_account2_...',
);

Next Stepsโ€‹

  1. Set up your account to get your API keys
  2. Configure webhooks to receive payment updates
  3. Test your integration with test mode
  4. Go live with production keys

Supportโ€‹

Need help with the Dart SDK?