.NET Library (Omise.Net)
The Omise.Net library provides a modern C# interface to the Omise API with async/await patterns, LINQ support, and excellent ASP.NET Core integration.
Installationโ
Using NuGet Package Managerโ
dotnet add package Omise.Net
Using Package Manager Consoleโ
Install-Package Omise.Net
Using .csprojโ
<ItemGroup>
<PackageReference Include="Omise.Net" Version="1.0.0" />
</ItemGroup>
Requirementsโ
- .NET Core 3.1+ or .NET 5+, .NET 6+, .NET 7+
- C# 8.0+ (for nullable reference types)
- ASP.NET Core (for web applications)
Quick Startโ
Basic Configurationโ
using Omise;
var client = new Client(
publicKey: "pkey_test_123456789",
secretKey: "skey_test_123456789"
);
With Dependency Injection (ASP.NET Core)โ
// Program.cs or Startup.cs
using Omise;
builder.Services.AddSingleton<IClient>(sp =>
new Client(
publicKey: builder.Configuration["Omise:PublicKey"],
secretKey: builder.Configuration["Omise:SecretKey"]
)
);
Configuration (appsettings.json)โ
{
"Omise": {
"PublicKey": "pkey_test_123456789",
"SecretKey": "skey_test_123456789",
"ApiVersion": "2019-05-29"
}
}
Environment Variablesโ
# Development/Test
OMISE_SECRET_KEY=skey_test_123456789
OMISE_PUBLIC_KEY=pkey_test_123456789
# Production
# OMISE_SECRET_KEY=skey_live_123456789
# OMISE_PUBLIC_KEY=pkey_live_123456789
Common Operationsโ
Creating a Chargeโ
using Omise;
using Omise.Models;
var client = new Client("pkey_test_123", "skey_test_123");
var request = new CreateChargeRequest
{
Amount = 100000, // 1,000.00 THB
Currency = "THB",
Card = "tokn_test_123",
Description = "Order #1234",
Metadata = new Dictionary<string, object>
{
["order_id"] = "1234",
["customer_name"] = "John Doe"
}
};
var charge = await client.Charges.CreateAsync(request);
if (charge.Paid)
{
Console.WriteLine($"Charge successful: {charge.Id}");
}
else
{
Console.WriteLine($"Charge failed: {charge.FailureMessage}");
}
With Record Types (C# 9.0+)โ
public record CreateChargeModel(
long Amount,
string Currency,
string Token,
Dictionary<string, object>? Metadata = null
);
public async Task<Charge> CreateChargeAsync(CreateChargeModel model)
{
var request = new CreateChargeRequest
{
Amount = model.Amount,
Currency = model.Currency,
Card = model.Token,
Metadata = model.Metadata ?? new Dictionary<string, object>()
};
return await _client.Charges.CreateAsync(request);
}
With 3D Secureโ
public async Task<ChargeResult> CreateSecureChargeAsync(
long amount,
string token,
string returnUri)
{
var request = new CreateChargeRequest
{
Amount = amount,
Currency = "THB",
Card = token,
ReturnUri = returnUri
};
var charge = await _client.Charges.CreateAsync(request);
if (charge.Authorized)
{
if (!string.IsNullOrEmpty(charge.AuthorizeUri))
{
return new ChargeResult { RedirectUri = charge.AuthorizeUri };
}
else
{
return new ChargeResult { Success = true, Charge = charge };
}
}
throw new Exception(charge.FailureMessage);
}
Retrieving a Chargeโ
var charge = await client.Charges.GetAsync("chrg_test_123");
Console.WriteLine($"Amount: {charge.Amount}");
Console.WriteLine($"Currency: {charge.Currency}");
Console.WriteLine($"Status: {charge.Status}");
Console.WriteLine($"Paid: {charge.Paid}");
Listing Charges with LINQโ
var request = new ListChargesRequest
{
Limit = 20,
Offset = 0,
Order = Ordering.ReverseChronological
};
var charges = await client.Charges.ListAsync(request);
var paidCharges = charges.Data
.Where(c => c.Paid)
.OrderByDescending(c => c.Created)
.ToList();
foreach (var charge in paidCharges)
{
Console.WriteLine($"{charge.Id}: {charge.Amount} {charge.Currency}");
}
Creating a Customerโ
var request = new CreateCustomerRequest
{
Email = "customer@example.com",
Description = "John Doe",
Metadata = new Dictionary<string, object>
{
["user_id"] = "12345",
["account_type"] = "premium"
}
};
var customer = await client.Customers.CreateAsync(request);
Console.WriteLine($"Customer created: {customer.Id}");
Saving a Card to a Customerโ
// Update customer with card token
var updateRequest = new UpdateCustomerRequest
{
Card = "tokn_test_456"
};
var customer = await client.Customers.UpdateAsync("cust_test_123", updateRequest);
Console.WriteLine($"Card saved: {customer.DefaultCard}");
// Or create customer with card
var createRequest = new CreateCustomerRequest
{
Email = "customer@example.com",
Card = "tokn_test_123"
};
customer = await client.Customers.CreateAsync(createRequest);
Listing Customer Cardsโ
var customer = await client.Customers.GetAsync("cust_test_123");
foreach (var card in customer.Cards.Data)
{
Console.WriteLine($"{card.Brand} ending in {card.LastDigits}");
Console.WriteLine($"Expires: {card.ExpirationMonth}/{card.ExpirationYear}");
}
Creating a Refundโ
// Full refund
var refund = await client.Refunds.CreateAsync("chrg_test_123");
// Partial refund
var request = new CreateRefundRequest
{
Amount = 25000, // 250.00 THB
Metadata = new Dictionary<string, object>
{
["reason"] = "customer_request",
["ticket_id"] = "TICKET-123"
}
};
refund = await client.Refunds.CreateAsync("chrg_test_123", request);
Console.WriteLine($"Refund {refund.Id}: {refund.Amount} {refund.Currency}");
Creating a Transferโ
var request = new CreateTransferRequest
{
Amount = 500000, // 5,000.00 THB
Recipient = "recp_test_123",
Metadata = new Dictionary<string, object>
{
["payout_id"] = "PAYOUT-456"
}
};
var transfer = await client.Transfers.CreateAsync(request);
Console.WriteLine($"Transfer {transfer.Id}: {transfer.Amount}");
Alternative Payment Methodsโ
Creating a Sourceโ
// Prompt Pay QR
var request = new CreateSourceRequest
{
Type = SourceType.PromptPay,
Amount = 100000,
Currency = "THB"
};
var source = await client.Sources.CreateAsync(request);
Console.WriteLine($"QR Code URL: {source.ScannableCode.Image.DownloadUri}");
// Create charge with source
var chargeRequest = new CreateChargeRequest
{
Amount = 100000,
Currency = "THB",
Source = source.Id,
ReturnUri = "https://example.com/payment/callback"
};
var charge = await client.Charges.CreateAsync(chargeRequest);
Internet Bankingโ
var sourceRequest = new CreateSourceRequest
{
Type = SourceType.InternetBankingSCB,
Amount = 100000,
Currency = "THB"
};
var source = await client.Sources.CreateAsync(sourceRequest);
var chargeRequest = new CreateChargeRequest
{
Amount = 100000,
Currency = "THB",
Source = source.Id,
ReturnUri = "https://example.com/payment/callback"
};
var charge = await client.Charges.CreateAsync(chargeRequest);
// Redirect to charge.AuthorizeUri
Installmentsโ
var sourceRequest = new CreateSourceRequest
{
Type = SourceType.InstallmentKBank,
Amount = 100000,
Currency = "THB",
InstallmentTerm = 6 // 6 months
};
var source = await client.Sources.CreateAsync(sourceRequest);
var chargeRequest = new CreateChargeRequest
{
Amount = 100000,
Currency = "THB",
Source = source.Id,
ReturnUri = "https://example.com/payment/callback"
};
var charge = await client.Charges.CreateAsync(chargeRequest);
Error Handlingโ
using Omise.Exceptions;
try
{
var charge = await client.Charges.CreateAsync(request);
}
catch (OmiseException ex)
{
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine($"Status Code: {ex.StatusCode}");
Console.WriteLine($"Error Code: {ex.Code}");
switch (ex.Code)
{
case "authentication_failure":
throw new InvalidOperationException("Invalid API key");
case "invalid_card":
throw new InvalidOperationException("Card was declined");
case "insufficient_fund":
throw new InvalidOperationException("Insufficient funds");
default:
throw;
}
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Network error: {ex.Message}");
}
Custom Error Handlerโ
public class PaymentErrorHandler
{
private static readonly Dictionary<string, string> ErrorMessages = new()
{
["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"
};
public static string GetErrorMessage(OmiseException ex)
{
return ErrorMessages.TryGetValue(ex.Code, out var message)
? message
: ex.Message;
}
}
ASP.NET Core Integrationโ
Controller Exampleโ
using Microsoft.AspNetCore.Mvc;
using Omise;
using Omise.Models;
[ApiController]
[Route("api/[controller]")]
public class PaymentController : ControllerBase
{
private readonly IClient _omiseClient;
private readonly IPaymentService _paymentService;
private readonly ILogger<PaymentController> _logger;
public PaymentController(
IClient omiseClient,
IPaymentService paymentService,
ILogger<PaymentController> logger)
{
_omiseClient = omiseClient;
_paymentService = paymentService;
_logger = logger;
}
[HttpPost("{orderId}")]
public async Task<ActionResult<ChargeResponse>> CreatePayment(
int orderId,
[FromBody] CreatePaymentRequest request)
{
try
{
var order = await _paymentService.GetOrderAsync(orderId);
if (order == null)
{
return NotFound("Order not found");
}
var chargeRequest = new CreateChargeRequest
{
Amount = (long)(order.Total * 100),
Currency = "THB",
Card = request.OmiseToken,
Description = $"Order #{order.Id}",
Metadata = new Dictionary<string, object>
{
["order_id"] = order.Id.ToString(),
["customer_email"] = order.Email
},
ReturnUri = Url.Action(
nameof(PaymentCallback),
"Payment",
null,
Request.Scheme
)
};
var charge = await _omiseClient.Charges.CreateAsync(chargeRequest);
await _paymentService.SavePaymentAsync(new Payment
{
OrderId = order.Id,
ChargeId = charge.Id,
Amount = order.Total,
Status = charge.Status,
Paid = charge.Paid
});
if (charge.Paid)
{
await _paymentService.MarkOrderPaidAsync(order.Id);
return Ok(new ChargeResponse { Success = true, Charge = charge });
}
else if (!string.IsNullOrEmpty(charge.AuthorizeUri))
{
return Ok(new ChargeResponse { RedirectUri = charge.AuthorizeUri });
}
else
{
return BadRequest(new { Error = charge.FailureMessage });
}
}
catch (OmiseException ex)
{
_logger.LogError(ex, "Payment failed");
return StatusCode(500, new { Error = "Payment failed" });
}
}
[HttpGet("callback")]
public async Task<IActionResult> PaymentCallback([FromQuery] string id)
{
try
{
var charge = await _omiseClient.Charges.GetAsync(id);
await _paymentService.UpdatePaymentStatusAsync(charge.Id, charge.Status, charge.Paid);
if (charge.Paid)
{
return Redirect($"/orders/{charge.Metadata["order_id"]}?success=true");
}
else
{
return Redirect($"/payment?error={charge.FailureMessage}");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Payment callback failed");
return Redirect("/payment?error=verification_failed");
}
}
}
Service Implementationโ
public interface IPaymentService
{
Task<Order?> GetOrderAsync(int orderId);
Task SavePaymentAsync(Payment payment);
Task UpdatePaymentStatusAsync(string chargeId, string status, bool paid);
Task MarkOrderPaidAsync(int orderId);
}
public class PaymentService : IPaymentService
{
private readonly ApplicationDbContext _context;
private readonly IClient _omiseClient;
public PaymentService(ApplicationDbContext context, IClient omiseClient)
{
_context = context;
_omiseClient = omiseClient;
}
public async Task<Order?> GetOrderAsync(int orderId)
{
return await _context.Orders.FindAsync(orderId);
}
public async Task SavePaymentAsync(Payment payment)
{
_context.Payments.Add(payment);
await _context.SaveChangesAsync();
}
public async Task UpdatePaymentStatusAsync(string chargeId, string status, bool paid)
{
var payment = await _context.Payments
.FirstOrDefaultAsync(p => p.ChargeId == chargeId);
if (payment != null)
{
payment.Status = status;
payment.Paid = paid;
await _context.SaveChangesAsync();
}
}
public async Task MarkOrderPaidAsync(int orderId)
{
var order = await _context.Orders.FindAsync(orderId);
if (order != null)
{
order.PaymentStatus = "paid";
await _context.SaveChangesAsync();
}
}
public async Task<Charge> ChargeCustomerAsync(string customerId, decimal amount)
{
var request = new CreateChargeRequest
{
Amount = (long)(amount * 100),
Currency = "THB",
Customer = customerId
};
return await _omiseClient.Charges.CreateAsync(request);
}
public async Task<Refund> RefundChargeAsync(string chargeId, decimal? amount = null)
{
var request = amount.HasValue
? new CreateRefundRequest { Amount = (long)(amount.Value * 100) }
: null;
return await _omiseClient.Refunds.CreateAsync(chargeId, request);
}
}
Background Serviceโ
using Microsoft.Extensions.Hosting;
public class SubscriptionChargeService : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<SubscriptionChargeService> _logger;
public SubscriptionChargeService(
IServiceProvider serviceProvider,
ILogger<SubscriptionChargeService> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
await ChargeSubscriptionsAsync();
await Task.Delay(TimeSpan.FromHours(24), stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error charging subscriptions");
}
}
}
private async Task ChargeSubscriptionsAsync()
{
using var scope = _serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
var omiseClient = scope.ServiceProvider.GetRequiredService<IClient>();
var subscriptions = await context.Subscriptions
.Where(s => s.Status == "active")
.ToListAsync();
foreach (var subscription in subscriptions)
{
try
{
var request = new CreateChargeRequest
{
Amount = subscription.PlanAmount,
Currency = "THB",
Customer = subscription.CustomerId,
Description = $"Subscription {DateTime.Now:MMMM yyyy}"
};
var charge = await omiseClient.Charges.CreateAsync(request);
if (charge.Paid)
{
subscription.LastChargeDate = DateTime.UtcNow;
await context.SaveChangesAsync();
}
}
catch (OmiseException ex)
{
_logger.LogError(ex, "Failed to charge subscription {SubscriptionId}", subscription.Id);
}
}
}
}
// Register in Program.cs
builder.Services.AddHostedService<SubscriptionChargeService>();
Best Practicesโ
1. Use Dependency Injectionโ
// Program.cs
builder.Services.AddSingleton<IClient>(sp =>
{
var configuration = sp.GetRequiredService<IConfiguration>();
var secretKey = configuration["Omise:SecretKey"]
?? throw new InvalidOperationException("Omise:SecretKey not configured");
return new Client(
publicKey: configuration["Omise:PublicKey"],
secretKey: secretKey
);
});
2. Use Options Patternโ
public class OmiseOptions
{
public string PublicKey { get; set; } = string.Empty;
public string SecretKey { get; set; } = string.Empty;
public string ApiVersion { get; set; } = "2019-05-29";
}
// Program.cs
builder.Services.Configure<OmiseOptions>(
builder.Configuration.GetSection("Omise")
);
builder.Services.AddSingleton<IClient>(sp =>
{
var options = sp.GetRequiredService<IOptions<OmiseOptions>>().Value;
return new Client(options.PublicKey, options.SecretKey);
});
3. Implement Idempotencyโ
public async Task<Charge> CreateIdempotentChargeAsync(
CreateChargeRequest request,
string orderId)
{
var idempotencyKey = $"order-{orderId}-{Guid.NewGuid()}";
return await _client.Charges.CreateAsync(request, new RequestOptions
{
IdempotencyKey = idempotencyKey
});
}
4. Use Value Objects for Moneyโ
public record Money
{
public decimal Amount { get; init; }
public string Currency { get; init; }
public long ToSmallestUnit() => Currency switch
{
"JPY" => (long)Amount,
_ => (long)(Amount * 100)
};
public static Money FromSmallestUnit(long amount, string currency) => currency switch
{
"JPY" => new Money { Amount = amount, Currency = currency },
_ => new Money { Amount = amount / 100m, Currency = currency }
};
}
5. Implement Loggingโ
public class PaymentService
{
private readonly IClient _client;
private readonly ILogger<PaymentService> _logger;
public async Task<Charge> CreateChargeAsync(CreateChargeRequest request)
{
_logger.LogInformation(
"Creating charge: Amount={Amount}, Currency={Currency}",
request.Amount,
request.Currency
);
try
{
var charge = await _client.Charges.CreateAsync(request);
_logger.LogInformation(
"Charge created: ChargeId={ChargeId}, Paid={Paid}",
charge.Id,
charge.Paid
);
return charge;
}
catch (OmiseException ex)
{
_logger.LogError(
ex,
"Charge failed: Code={Code}, Message={Message}",
ex.Code,
ex.Message
);
throw;
}
}
}
6. Use Polly for Retry Logicโ
using Polly;
using Polly.Extensions.Http;
// Program.cs
var retryPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.WaitAndRetryAsync(3, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
builder.Services.AddHttpClient<IClient, Client>()
.AddPolicyHandler(retryPolicy);
Testingโ
Unit Testing with xUnit and Moqโ
using Xunit;
using Moq;
using Omise;
using Omise.Models;
public class PaymentServiceTests
{
private readonly Mock<IClient> _mockClient;
private readonly PaymentService _paymentService;
public PaymentServiceTests()
{
_mockClient = new Mock<IClient>();
_paymentService = new PaymentService(_mockClient.Object);
}
[Fact]
public async Task CreateCharge_ShouldReturnSuccessfulCharge()
{
// Arrange
var expectedCharge = new Charge
{
Id = "chrg_test_123",
Amount = 100000,
Currency = "THB",
Paid = true,
Status = "successful"
};
_mockClient
.Setup(c => c.Charges.CreateAsync(It.IsAny<CreateChargeRequest>()))
.ReturnsAsync(expectedCharge);
// Act
var charge = await _paymentService.CreateChargeAsync(
new CreateChargeRequest
{
Amount = 100000,
Currency = "THB",
Card = "tokn_test_123"
}
);
// Assert
Assert.Equal("chrg_test_123", charge.Id);
Assert.True(charge.Paid);
}
[Fact]
public async Task CreateCharge_ShouldThrowOmiseException_WhenCardDeclined()
{
// Arrange
_mockClient
.Setup(c => c.Charges.CreateAsync(It.IsAny<CreateChargeRequest>()))
.ThrowsAsync(new OmiseException("insufficient_fund", "Insufficient funds"));
// Act & Assert
await Assert.ThrowsAsync<OmiseException>(() =>
_paymentService.CreateChargeAsync(new CreateChargeRequest
{
Amount = 100000,
Currency = "THB",
Card = "tokn_test_123"
})
);
}
}
Integration Testingโ
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
public class PaymentControllerIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
private readonly HttpClient _client;
public PaymentControllerIntegrationTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
_client = factory.CreateClient();
}
[Fact]
public async Task CreatePayment_ShouldReturn200_WhenChargeSuccessful()
{
// Arrange
var request = new CreatePaymentRequest
{
OmiseToken = "tokn_test_123"
};
// Act
var response = await _client.PostAsJsonAsync("/api/payment/1", request);
// Assert
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<ChargeResponse>();
Assert.NotNull(result);
Assert.True(result.Success);
}
}
Troubleshootingโ
SSL Certificate Issuesโ
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
var httpClient = new HttpClient(handler);
var client = new Client("pkey_test_123", "skey_test_123", httpClient);
Timeout Configurationโ
var httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(60)
};
var client = new Client("pkey_test_123", "skey_test_123", httpClient);
Debug Loggingโ
// appsettings.Development.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Omise": "Debug"
}
}
}
Webhook Signature Verificationโ
using System.Security.Cryptography;
using System.Text;
public class WebhookVerificationService
{
private readonly string _webhookSecret;
public WebhookVerificationService(IConfiguration configuration)
{
_webhookSecret = configuration["Omise:WebhookSecret"]
?? throw new InvalidOperationException("Webhook secret not configured");
}
public bool VerifySignature(string payload, string signature)
{
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_webhookSecret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
var expectedSignature = BitConverter.ToString(hash).Replace("-", "").ToLower();
return signature.Equals(expectedSignature, StringComparison.OrdinalIgnoreCase);
}
}
// Controller
[HttpPost("webhooks/omise")]
public async Task<IActionResult> OmiseWebhook()
{
using var reader = new StreamReader(Request.Body);
var payload = await reader.ReadToEndAsync();
var signature = Request.Headers["Omise-Signature"].ToString();
if (!_webhookService.VerifySignature(payload, signature))
{
return Unauthorized(new { Error = "Invalid signature" });
}
var webhookEvent = JsonSerializer.Deserialize<WebhookEvent>(payload);
// Process event
return Ok(new { Status = "ok" });
}
FAQโ
How do I handle webhooks in ASP.NET Core?โ
[ApiController]
[Route("api/webhooks")]
public class WebhooksController : ControllerBase
{
private readonly IClient _omiseClient;
private readonly IPaymentService _paymentService;
[HttpPost("omise")]
public async Task<IActionResult> OmiseWebhook([FromBody] WebhookEvent webhookEvent)
{
switch (webhookEvent.Key)
{
case "charge.complete":
await HandleChargeComplete(webhookEvent.Data);
break;
case "refund.create":
await HandleRefundCreate(webhookEvent.Data);
break;
}
return Ok(new { Status = "ok" });
}
private async Task HandleChargeComplete(dynamic chargeData)
{
var charge = await _omiseClient.Charges.GetAsync((string)chargeData.id);
await _paymentService.UpdatePaymentStatusAsync(
charge.Id,
charge.Status,
charge.Paid
);
}
}
How do I test payments without real charges?โ
// Use test API keys
var client = new Client("pkey_test_123", "skey_test_123");
// Use test tokens
var charge = await client.Charges.CreateAsync(new CreateChargeRequest
{
Amount = 100000,
Currency = "THB",
Card = "tokn_test_5086xl7ddjbases4sq3i"
});
Console.WriteLine($"Test mode: {!charge.Livemode}");
How do I implement subscription billing?โ
public class SubscriptionManager
{
private readonly IClient _client;
public async Task<Charge> ChargeMonthlyAsync(string customerId, long amount)
{
return await _client.Charges.CreateAsync(new CreateChargeRequest
{
Amount = amount,
Currency = "THB",
Customer = customerId,
Description = $"Subscription {DateTime.Now:MMMM yyyy}"
});
}
}
How do I handle partial refunds?โ
public async Task<Refund> RefundChargeAsync(string chargeId, decimal? refundAmount = null)
{
var charge = await _client.Charges.GetAsync(chargeId);
var refundable = charge.Amount - charge.Refunded;
if (refundAmount.HasValue)
{
var refundAmountSatang = (long)(refundAmount.Value * 100);
if (refundAmountSatang > refundable)
{
throw new InvalidOperationException("Refund amount exceeds refundable amount");
}
return await _client.Refunds.CreateAsync(chargeId, new CreateRefundRequest
{
Amount = refundAmountSatang
});
}
return await _client.Refunds.CreateAsync(chargeId);
}
How do I save multiple cards for a customer?โ
public async Task<Card> AddCardToCustomerAsync(string customerId, string token)
{
var customer = await _client.Customers.UpdateAsync(customerId, new UpdateCustomerRequest
{
Card = token
});
return customer.Cards.Data.First();
}
public async Task<List<Card>> ListCustomerCardsAsync(string customerId)
{
var customer = await _client.Customers.GetAsync(customerId);
return customer.Cards.Data.ToList();
}
public async Task<Charge> ChargeSpecificCardAsync(
string customerId,
string cardId,
long amount)
{
return await _client.Charges.CreateAsync(new CreateChargeRequest
{
Amount = amount,
Currency = "THB",
Customer = customerId,
Card = cardId
});
}
Related Resourcesโ
- Omise API Documentation
- Omise.Net GitHub Repository
- NuGet Package
- Omise Dashboard
- Webhooks Guide
- Testing Guide
Next Stepsโ
Supportโ
If you encounter issues with the .NET library:
- Check the GitHub Issues
- Review the API documentation
- Contact support@omise.co with:
- .NET version
- Omise.Net library version
- Error message and stack trace
- Steps to reproduce