.NETライブラリ(Omise.Net)
Omise.Netライブラリは、async/awaitパターン、LINQサポート、優れたASP.NET Core統合を備えた、Omise APIへの最新のC#インターフェースを提供します。
インストール
NuGetパッケージマネージャーの使用
dotnet add package Omise.Net
パッケージマネージャーコンソールの使用
Install-Package Omise.Net
.csprojの使用
<ItemGroup>
<PackageReference Include="Omise.Net" Version="1.0.0" />
</ItemGroup>
要件
- .NET Core 3.1+または.NET 5+、.NET 6+、.NET 7+
- C# 8.0+(nullable参照型用)
- ASP.NET Core(Webアプリケーション用)
クイックスタート
基本的な構成
using Omise;
var client = new Client(
publicKey: "pkey_test_123456789",
secretKey: "skey_test_123456789"
);
依存性注入を使用(ASP.NET Core)
// Program.csまたはStartup.cs
using Omise;
builder.Services.AddSingleton<IClient>(sp =>
new Client(
publicKey: builder.Configuration["Omise:PublicKey"],
secretKey: builder.Configuration["Omise:SecretKey"]
)
);
構成(appsettings.json)
{
"Omise": {
"PublicKey": "pkey_test_123456789",
"SecretKey": "skey_test_123456789",
"ApiVersion": "2019-05-29"
}
}
環境変数
# 開発/テスト
OMISE_SECRET_KEY=skey_test_123456789
OMISE_PUBLIC_KEY=pkey_test_123456789
# 本番環境
# OMISE_SECRET_KEY=skey_live_123456789
# OMISE_PUBLIC_KEY=pkey_live_123456789
一般的な操作
チャージの作成
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 = "注文 #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.Id}");
}
else
{
Console.WriteLine($"チャージ失敗: {charge.FailureMessage}");
}
レコード型を使用(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);
}
3Dセキュアで
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);
}
チャージの取得
var charge = await client.Charges.GetAsync("chrg_test_123");
Console.WriteLine($"金額: {charge.Amount}");
Console.WriteLine($"通貨: {charge.Currency}");
Console.WriteLine($"ステータス: {charge.Status}");
Console.WriteLine($"支払い済み: {charge.Paid}");
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}");
}
顧客の作成
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.Id}");
顧客へのカード保存
// カードトークンで顧客を更新
var updateRequest = new UpdateCustomerRequest
{
Card = "tokn_test_456"
};
var customer = await client.Customers.UpdateAsync("cust_test_123", updateRequest);
Console.WriteLine($"カード保存: {customer.DefaultCard}");
// またはカード付きで顧客を作成
var createRequest = new CreateCustomerRequest
{
Email = "customer@example.com",
Card = "tokn_test_123"
};
customer = await client.Customers.CreateAsync(createRequest);
顧客カードのリスト表示
var customer = await client.Customers.GetAsync("cust_test_123");
foreach (var card in customer.Cards.Data)
{
Console.WriteLine($"{card.Brand} 末尾 {card.LastDigits}");
Console.WriteLine($"有効期限: {card.ExpirationMonth}/{card.ExpirationYear}");
}
払い戻しの作成
// 全額返金
var refund = await client.Refunds.CreateAsync("chrg_test_123");
// 部分払い戻し
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.Id}: {refund.Amount} {refund.Currency}");
転送の作成
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.Id}: {transfer.Amount}");
代替決済方法
ソースの作成
// PromptPay QR
var request = new CreateSourceRequest
{
Type = SourceType.PromptPay,
Amount = 100000,
Currency = "THB"
};
var source = await client.Sources.CreateAsync(request);
Console.WriteLine($"QRコードURL: {source.ScannableCode.Image.DownloadUri}");
// ソースを使用してチャージを作成
var chargeRequest = new CreateChargeRequest
{
Amount = 100000,
Currency = "THB",
Source = source.Id,
ReturnUri = "https://example.com/payment/callback"
};
var charge = await client.Charges.CreateAsync(chargeRequest);
インターネットバンキング
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);
// charge.AuthorizeUriにリダイレクト
インストール
var sourceRequest = new CreateSourceRequest
{
Type = SourceType.InstallmentKBank,
Amount = 100000,
Currency = "THB",
InstallmentTerm = 6 // 6ヶ月
};
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);
エラーハンドリング
using Omise.Exceptions;
try
{
var charge = await client.Charges.CreateAsync(request);
}
catch (OmiseException ex)
{
Console.WriteLine($"エラー: {ex.Message}");
Console.WriteLine($"ステータスコード: {ex.StatusCode}");
Console.WriteLine($"エラーコード: {ex.Code}");
switch (ex.Code)
{
case "authentication_failure":
throw new InvalidOperationException("無効なAPIキー");
case "invalid_card":
throw new InvalidOperationException("カードが拒否されました");
case "insufficient_fund":
throw new InvalidOperationException("残高不足");
default:
throw;
}
}
catch (HttpRequestException ex)
{
Console.WriteLine($"ネットワークエラー: {ex.Message}");
}
カスタムエラーハンドラー
public class PaymentErrorHandler
{
private static readonly Dictionary<string, string> ErrorMessages = new()
{
["insufficient_fund"] = "カードの残高不足",
["stolen_or_lost_card"] = "カードが盗難または紛失として報告されています",
["invalid_security_code"] = "無効なCVVコード",
["payment_cancelled"] = "支払いがキャンセルされました"
};
public static string GetErrorMessage(OmiseException ex)
{
return ErrorMessages.TryGetValue(ex.Code, out var message)
? message
: ex.Message;
}
}
ASP.NET Core統合
コントローラーの例
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("注文が見つかりません");
}
var chargeRequest = new CreateChargeRequest
{
Amount = (long)(order.Total * 100),
Currency = "THB",
Card = request.OmiseToken,
Description = $"注文 #{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, "支払いが失敗しました");
return StatusCode(500, new { Error = "支払いが失敗しました" });
}
}
[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, "支払いコールバックが失敗しました");
return Redirect("/payment?error=verification_failed");
}
}
}
サービスの実装
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);
}
}
バックグラウンドサービス
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, "サブスクリプション課金エラー");
}
}
}
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 = $"サブスクリプション {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, "サブスクリプション {SubscriptionId} の課金失敗", subscription.Id);
}
}
}
}
// Program.csに登録
builder.Services.AddHostedService<SubscriptionChargeService>();
ベストプラクティス
1. 依存性注入の使用
// Program.cs
builder.Services.AddSingleton<IClient>(sp =>
{
var configuration = sp.GetRequiredService<IConfiguration>();
var secretKey = configuration["Omise:SecretKey"]
?? throw new InvalidOperationException("Omise:SecretKeyが構成されていません");
return new Client(
publicKey: configuration["Omise:PublicKey"],
secretKey: secretKey
);
});
2. Optionsパターンの使用
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. 冪等性の実装
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. 金額用の値オブジェクトの使用
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. ロギングの実装
public class PaymentService
{
private readonly IClient _client;
private readonly ILogger<PaymentService> _logger;
public async Task<Charge> CreateChargeAsync(CreateChargeRequest request)
{
_logger.LogInformation(
"チャージ作成中: Amount={Amount}, Currency={Currency}",
request.Amount,
request.Currency
);
try
{
var charge = await _client.Charges.CreateAsync(request);
_logger.LogInformation(
"チャージ作成済み: ChargeId={ChargeId}, Paid={Paid}",
charge.Id,
charge.Paid
);
return charge;
}
catch (OmiseException ex)
{
_logger.LogError(
ex,
"チャージ失敗: Code={Code}, Message={Message}",
ex.Code,
ex.Message
);
throw;
}
}
}
6. リトライロジックにPollyを使用
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);
テスト
xUnitと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()
{
// 準備
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);
// 実行
var charge = await _paymentService.CreateChargeAsync(
new CreateChargeRequest
{
Amount = 100000,
Currency = "THB",
Card = "tokn_test_123"
}
);
// 検証
Assert.Equal("chrg_test_123", charge.Id);
Assert.True(charge.Paid);
}
[Fact]
public async Task CreateCharge_ShouldThrowOmiseException_WhenCardDeclined()
{
// 準備
_mockClient
.Setup(c => c.Charges.CreateAsync(It.IsAny<CreateChargeRequest>()))
.ThrowsAsync(new OmiseException("insufficient_fund", "残高不足"));
// 実行と検証
await Assert.ThrowsAsync<OmiseException>(() =>
_paymentService.CreateChargeAsync(new CreateChargeRequest
{
Amount = 100000,
Currency = "THB",
Card = "tokn_test_123"
})
);
}
}
統合テスト
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()
{
// 準備
var request = new CreatePaymentRequest
{
OmiseToken = "tokn_test_123"
};
// 実行
var response = await _client.PostAsJsonAsync("/api/payment/1", request);
// 検証
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<ChargeResponse>();
Assert.NotNull(result);
Assert.True(result.Success);
}
}
トラブルシューティング
SSL証明書の問題
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
var httpClient = new HttpClient(handler);
var client = new Client("pkey_test_123", "skey_test_123", httpClient);
タイムアウト構成
var httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(60)
};
var client = new Client("pkey_test_123", "skey_test_123", httpClient);
デバッグログ
// appsettings.Development.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Omise": "Debug"
}
}
}
ウェブフック署名検証
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("ウェブフックシークレットが構成されていません");
}
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);
}
}
// コントローラー
[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 = "無効な署名" });
}
var webhookEvent = JsonSerializer.Deserialize<WebhookEvent>(payload);
// イベントを処理
return Ok(new { Status = "ok" });
}
よくある質問
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
);
}
}
実際の課金なしで支払いをテストする方法は?
// テストAPIキーを使用
var client = new Client("pkey_test_123", "skey_test_123");
// テストトークンを使用
var charge = await client.Charges.CreateAsync(new CreateChargeRequest
{
Amount = 100000,
Currency = "THB",
Card = "tokn_test_5086xl7ddjbases4sq3i"
});
Console.WriteLine($"テストモード: {!charge.Livemode}");
サブスクリプション請求を実装する方法は?
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 = $"サブスクリプション {DateTime.Now:MMMM yyyy}"
});
}
}
部分払い戻しを処理する方法は?
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("払い戻し金額が払い戻し可能金額を超えています");
}
return await _client.Refunds.CreateAsync(chargeId, new CreateRefundRequest
{
Amount = refundAmountSatang
});
}
return await _client.Refunds.CreateAsync(chargeId);
}
顧客に複数のカードを保存する方法は?
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
});
}
関連リソース
次のステップ
サポート
.NETライブラリで問題が発生した場合:
- GitHub Issuesを確認してください
- APIドキュメントを確認してください
- support@omise.coに以下の情報をご連絡ください:
- .NETバージョン
- Omise.Netライブラリバージョン
- エラーメッセージとスタックトレース
- 再現手順