メインコンテンツへスキップ

Rubyライブラリ (omise-ruby)

omise-ruby gemは、優れたRailsとの統合、慣用的なRubyコード、堅牢なエラーハンドリングを備えた、Omise APIへの包括的なRubyインターフェースを提供します。

インストール

Bundlerを使用(推奨)

Gemfileに追加:

gem 'omise', '~> 0.11.0'

次に実行:

bundle install

RubyGemsを使用

gem install omise

要件

  • Ruby 2.6以上(Ruby 3.xを含む)
  • Bundler(Railsアプリケーション用)
  • API呼び出しのためのアクティブなインターネット接続

クイックスタート

基本的な構成

require 'omise'

# APIキーで構成
Omise.api_key = 'skey_test_123456789'
Omise.api_version = '2019-05-29'

# オプション: APIエンドポイントを設定(テスト用)
# Omise.api_url = 'https://api.omise.co'

Rails構成

config/initializers/omise.rbにイニシャライザを作成:

# config/initializers/omise.rb
Omise.api_key = ENV['OMISE_SECRET_KEY']
Omise.api_version = '2019-05-29'

# タイムアウトを構成(オプション)
Omise.timeout = 30 # 秒

# 開発環境でデバッグモードを有効化
Omise.debug = Rails.env.development?

環境変数

.envファイルに追加:

# 開発/テスト
OMISE_SECRET_KEY=skey_test_123456789
OMISE_PUBLIC_KEY=pkey_test_123456789

# 本番環境
# OMISE_SECRET_KEY=skey_live_123456789
# OMISE_PUBLIC_KEY=pkey_live_123456789

認証

ライブラリはすべてのAPI操作にシークレットキーを使用します:

# オプション1: グローバル構成(推奨)
Omise.api_key = ENV['OMISE_SECRET_KEY']

# オプション2: リクエストごとの構成
charge = Omise::Charge.retrieve('chrg_test_123', key: 'skey_test_alternate')

一般的な操作

チャージの作成

トークンを使用

# カードトークンでチャージを作成
charge = Omise::Charge.create(
amount: 100_000, # 1,000.00 THB(最小通貨単位)
currency: 'THB',
card: 'tokn_test_123',
description: 'Order #1234',
metadata: {
order_id: '1234',
customer_name: 'John Doe'
}
)

if charge.paid
puts "チャージ成功: #{charge.id}"
else
puts "チャージ失敗: #{charge.failure_message}"
end

顧客を使用

# 既存の顧客に対してチャージを作成
charge = Omise::Charge.create(
amount: 50_000,
currency: 'THB',
customer: 'cust_test_123',
description: 'サブスクリプション支払い'
)

3D セキュアで

# 3D Secureが必要になる可能性のあるチャージを作成
charge = Omise::Charge.create(
amount: 100_000,
currency: 'THB',
card: 'tokn_test_123',
return_uri: 'https://example.com/payment/callback'
)

if charge.authorized
if charge.authorize_uri
# 3D Secure用にauthorize_uriに顧客をリダイレクト
redirect_to charge.authorize_uri
else
# 3D Secureなしでチャージ完了
process_successful_payment(charge)
end
end

チャージの取得

# IDでチャージを取得
charge = Omise::Charge.retrieve('chrg_test_123')

puts "金額: #{charge.amount}"
puts "通貨: #{charge.currency}"
puts "ステータス: #{charge.status}"
puts "支払い済み: #{charge.paid}"

チャージのリスト表示

# ページネーションですべてのチャージをリスト表示
charges = Omise::Charge.list(
limit: 20,
offset: 0,
order: 'reverse_chronological'
)

charges.each do |charge|
puts "#{charge.id}: #{charge.amount} #{charge.currency}"
end

# フィルタを使用してチャージをリスト表示
recent_charges = Omise::Charge.list(
from: 1.week.ago.to_date.iso8601,
to: Date.today.iso8601
)

顧客の作成

# カードなしで顧客を作成
customer = Omise::Customer.create(
email: 'customer@example.com',
description: 'John Doe',
metadata: {
user_id: '12345',
account_type: 'premium'
}
)

puts "顧客が作成されました: #{customer.id}"

顧客へのカード保存

# カードトークンで顧客を更新
customer = Omise::Customer.retrieve('cust_test_123')
customer.update(card: 'tokn_test_456')

puts "カードが保存されました: #{customer.default_card}"

# または、1ステップでカード付きの顧客を作成
customer = Omise::Customer.create(
email: 'customer@example.com',
description: 'John Doe',
card: 'tokn_test_123'
)

顧客カードのリスト表示

customer = Omise::Customer.retrieve('cust_test_123')

customer.cards.each do |card|
puts "#{card.brand} 末尾 #{card.last_digits}"
puts "有効期限: #{card.expiration_month}/#{card.expiration_year}"
end

払い戻しの作成

# 全額返金
refund = Omise::Refund.create(
charge: 'chrg_test_123',
amount: nil # 全額返金の場合はnil
)

# 部分払い戻し
refund = Omise::Refund.create(
charge: 'chrg_test_123',
amount: 25_000, # 250.00 THB
metadata: {
reason: 'customer_request',
ticket_id: 'TICKET-123'
}
)

puts "払い戻し #{refund.id}: #{refund.amount} #{refund.currency}"

転送の作成

# 銀行口座への転送を作成
transfer = Omise::Transfer.create(
amount: 500_000, # 5,000.00 THB
recipient: 'recp_test_123',
metadata: {
payout_id: 'PAYOUT-456'
}
)

puts "転送 #{transfer.id}: #{transfer.amount}"

代替決済方法

ソースの作成

# PromptPay QR
source = Omise::Source.create(
type: 'promptpay',
amount: 100_000,
currency: 'THB'
)

# 顧客にQRコードを表示
puts "QRコードURL: #{source.scannable_code.image.download_uri}"

# ソースでチャージを作成
charge = Omise::Charge.create(
amount: 100_000,
currency: 'THB',
source: source.id,
return_uri: 'https://example.com/payment/callback'
)

インターネットバンキング

# インターネットバンキング
source = Omise::Source.create(
type: 'internet_banking_scb',
amount: 100_000,
currency: 'THB'
)

charge = Omise::Charge.create(
amount: 100_000,
currency: 'THB',
source: source.id,
return_uri: 'https://example.com/payment/callback'
)

# 顧客をauthorize_uriにリダイレクト
redirect_to charge.authorize_uri

モバイルバンキング

# モバイルバンキング (SCB Easy)
source = Omise::Source.create(
type: 'mobile_banking_scb',
amount: 100_000,
currency: 'THB'
)

charge = Omise::Charge.create(
amount: 100_000,
currency: 'THB',
source: source.id,
return_uri: 'https://example.com/payment/callback'
)

インストール

# 分割払い
source = Omise::Source.create(
type: 'installment_kbank',
amount: 100_000,
currency: 'THB',
installment_term: 6 # 6ヶ月
)

charge = Omise::Charge.create(
amount: 100_000,
currency: 'THB',
source: source.id,
return_uri: 'https://example.com/payment/callback'
)

エラーハンドリング

ライブラリは異なるエラーに対して特定の例外タイプを発生させます:

begin
charge = Omise::Charge.create(
amount: 100_000,
currency: 'THB',
card: 'tokn_test_123'
)
rescue Omise::AuthenticationError => e
# 無効なAPIキー
puts "認証失敗: #{e.message}"

rescue Omise::InvalidRequestError => e
# 無効なパラメータ
puts "無効なリクエスト: #{e.message}"
puts "ステータスコード: #{e.http_status}"

rescue Omise::CardError => e
# カード拒否
puts "カードエラー: #{e.message}"
puts "失敗コード: #{e.code}"

rescue Omise::APIError => e
# 一般的なAPIエラー
puts "APIエラー: #{e.message}"

rescue Omise::ConnectionError => e
# ネットワークエラー
puts "接続失敗: #{e.message}"

rescue Omise::Error => e
# すべてのOmiseエラーのキャッチオール
puts "Omiseエラー: #{e.message}"
end

特定のカードエラーの処理

begin
charge = Omise::Charge.create(amount: 100_000, currency: 'THB', card: token)

rescue Omise::CardError => e
case e.code
when 'insufficient_fund'
flash[:error] = 'カードの残高不足'
when 'stolen_or_lost_card'
flash[:error] = 'カードが盗難または紛失として報告されています'
when 'invalid_security_code'
flash[:error] = '無効なCVVコード'
when 'payment_cancelled'
flash[:error] = '支払いがキャンセルされました'
else
flash[:error] = "カードエラー: #{e.message}"
end

redirect_to payment_path
end

Railsとの統合

コントローラーの例

# app/controllers/payments_controller.rb
class PaymentsController < ApplicationController
def create
@order = Order.find(params[:order_id])

charge = Omise::Charge.create(
amount: (@order.total * 100).to_i, # 最小単位に変換
currency: 'THB',
card: params[:omise_token],
description: "Order ##{@order.id}",
metadata: {
order_id: @order.id,
customer_email: @order.email
},
return_uri: payment_callback_url
)

if charge.paid
@order.update!(
payment_status: 'paid',
charge_id: charge.id
)
redirect_to order_path(@order), notice: '支払いが成功しました!'
elsif charge.authorize_uri
# 3D Secureが必要
redirect_to charge.authorize_uri
else
flash[:error] = charge.failure_message
render :new
end

rescue Omise::Error => e
Rails.logger.error "Omiseエラー: #{e.message}"
flash[:error] = '支払いが失敗しました。もう一度お試しください。'
render :new
end

def callback
charge = Omise::Charge.retrieve(params[:id])
@order = Order.find_by(charge_id: charge.id)

if charge.paid
@order.update!(payment_status: 'paid')
redirect_to order_path(@order), notice: '支払いが成功しました!'
else
flash[:error] = charge.failure_message
redirect_to new_payment_path(order_id: @order.id)
end
end
end

モデル統合

# app/models/payment.rb
class Payment < ApplicationRecord
belongs_to :order

validates :amount, presence: true, numericality: { greater_than: 0 }
validates :currency, presence: true

# チャージを作成
def charge!(token)
charge = Omise::Charge.create(
amount: (amount * 100).to_i,
currency: currency,
card: token,
description: "Order ##{order.id}",
metadata: charge_metadata
)

update!(
charge_id: charge.id,
status: charge.status,
paid: charge.paid
)

charge
end

# チャージを払い戻す
def refund!(refund_amount = nil)
refund = Omise::Refund.create(
charge: charge_id,
amount: refund_amount ? (refund_amount * 100).to_i : nil
)

update!(
refund_id: refund.id,
refund_amount: refund.amount / 100.0,
status: 'refunded'
)

refund
end

# チャージステータスを更新
def refresh_status!
charge = Omise::Charge.retrieve(charge_id)
update!(
status: charge.status,
paid: charge.paid,
failure_code: charge.failure_code,
failure_message: charge.failure_message
)
end

private

def charge_metadata
{
order_id: order.id,
customer_email: order.email,
customer_name: order.customer_name
}
end
end

チャージ処理用のバックグラウンドジョブ

# app/jobs/process_charge_job.rb
class ProcessChargeJob < ApplicationJob
queue_as :payments

retry_on Omise::ConnectionError, wait: :exponentially_longer, attempts: 5
discard_on Omise::AuthenticationError

def perform(payment_id, token)
payment = Payment.find(payment_id)

charge = payment.charge!(token)

if charge.paid
OrderMailer.payment_confirmed(payment.order).deliver_later
else
OrderMailer.payment_failed(payment.order, charge.failure_message).deliver_later
end

rescue Omise::CardError => e
payment.update!(
status: 'failed',
failure_message: e.message
)
OrderMailer.payment_failed(payment.order, e.message).deliver_later
end
end

ベストプラクティス

1. キー用の環境変数を使用

# config/initializers/omise.rb
Omise.api_key = ENV.fetch('OMISE_SECRET_KEY') do
raise 'OMISE_SECRET_KEY環境変数が設定されていません'
end

# キーをバージョン管理にコミットしない
# .gitignoreに追加:
# .env
# .env.local

2. べき等性を処理

# チャージ作成にべき等性キーを使用
charge = Omise::Charge.create(
amount: 100_000,
currency: 'THB',
card: token,
headers: {
'Idempotency-Key' => "order-#{order.id}-#{Time.now.to_i}"
}
)

3. API呼び出しにバックグラウンドジョブを使用

# チャージを非同期で処理
class ChargeCustomerJob < ApplicationJob
queue_as :payments

def perform(customer_id, amount)
customer = Customer.find(customer_id)

charge = Omise::Charge.create(
amount: (amount * 100).to_i,
currency: 'THB',
customer: customer.omise_customer_id
)

# 結果を処理
rescue Omise::Error => e
# エラーを処理
end
end

4. 最小限のデータを保存

# IDのみを保存し、完全なオブジェクトは保存しない
class Order < ApplicationRecord
# 良い: IDのみを保存
def create_charge(token)
charge = Omise::Charge.create(...)
update!(charge_id: charge.id)
end

# 良い: 必要な時に取得
def charge
@charge ||= Omise::Charge.retrieve(charge_id) if charge_id
end
end

5. API呼び出し前に検証

class Payment < ApplicationRecord
validates :amount, numericality: { greater_than: 2000 } # 最低20 THB
validates :currency, inclusion: { in: %w[THB USD] }

def charge!(token)
raise '支払いが無効です' unless valid?

Omise::Charge.create(
amount: (amount * 100).to_i,
currency: currency,
card: token
)
end
end

6. テストモード構成

# config/environments/test.rb
Rails.application.configure do
config.after_initialize do
Omise.api_key = 'skey_test_123456789'
end
end

# テストでAPI呼び出しをスタブ化
RSpec.describe Payment do
before do
allow(Omise::Charge).to receive(:create).and_return(
double(id: 'chrg_test_123', paid: true)
)
end
end

テスト

RSpecの例

# spec/models/payment_spec.rb
require 'rails_helper'

RSpec.describe Payment, type: :model do
let(:payment) { create(:payment, amount: 1000) }

describe '#charge!' do
context '有効なトークンの場合' do
let(:charge) do
double(
id: 'chrg_test_123',
paid: true,
status: 'successful'
)
end

before do
allow(Omise::Charge).to receive(:create).and_return(charge)
end

it 'チャージを作成する' do
expect(Omise::Charge).to receive(:create).with(
hash_including(
amount: 100_000,
currency: 'THB'
)
)

payment.charge!('tokn_test_123')
end

it 'チャージIDで支払いを更新する' do
payment.charge!('tokn_test_123')
expect(payment.charge_id).to eq('chrg_test_123')
expect(payment.paid).to be true
end
end

context 'カードエラーの場合' do
before do
allow(Omise::Charge).to receive(:create).and_raise(
Omise::CardError.new('insufficient_fund', '残高不足')
)
end

it 'CardErrorを発生させる' do
expect { payment.charge!('tokn_test_123') }.to raise_error(Omise::CardError)
end
end
end
end

APIテスト用のVCR

# spec/spec_helper.rb
require 'vcr'

VCR.configure do |config|
config.cassette_library_dir = 'spec/vcr_cassettes'
config.hook_into :webmock
config.filter_sensitive_data('<OMISE_SECRET_KEY>') { ENV['OMISE_SECRET_KEY'] }
config.filter_sensitive_data('<OMISE_PUBLIC_KEY>') { ENV['OMISE_PUBLIC_KEY'] }
end

# spec/integration/charge_spec.rb
RSpec.describe 'チャージの作成' do
it 'チャージを正常に作成する', :vcr do
charge = Omise::Charge.create(
amount: 100_000,
currency: 'THB',
card: 'tokn_test_123'
)

expect(charge.paid).to be true
end
end

WebMockでのモック化

# spec/support/omise_stubs.rb
module OmiseStubs
def stub_charge_create(paid: true, amount: 100_000)
stub_request(:post, 'https://api.omise.co/charges')
.to_return(
status: 200,
body: {
object: 'charge',
id: 'chrg_test_123',
amount: amount,
currency: 'THB',
paid: paid
}.to_json,
headers: { 'Content-Type' => 'application/json' }
)
end
end

RSpec.configure do |config|
config.include OmiseStubs
end

# specでの使用方法
RSpec.describe PaymentsController do
describe 'POST #create' do
before { stub_charge_create(paid: true) }

it 'チャージを作成する' do
post :create, params: { token: 'tokn_test_123', amount: 1000 }
expect(response).to redirect_to(success_path)
end
end
end

トラブルシューティング

SSL証明書エラー

# 開発環境でSSLエラーが発生した場合
Omise.ssl_verify = false # 開発/テストのみ!

# より良い方法: CA証明書を更新
# Homebrewを使用したmacOSの場合:
# brew install curl-ca-bundle

# Ubuntu/Debianの場合:
# apt-get install ca-certificates

接続タイムアウト

# 低速接続のタイムアウトを増やす
Omise.timeout = 60 # 秒

# またはリクエストごと
charge = Omise::Charge.create(
amount: 100_000,
currency: 'THB',
card: token,
timeout: 60
)

デバッグモード

# デバッグログを有効化
Omise.debug = true

# 次のものが出力されます:
# - リクエストURLとメソッド
# - リクエストヘッダー
# - リクエストボディ
# - レスポンスステータス
# - レスポンスボディ

ウェブフック署名検証

# ウェブフック署名を検証
def verify_webhook_signature
payload = request.body.read
signature = request.headers['Omise-Signature']

expected_signature = OpenSSL::HMAC.hexdigest(
'sha256',
ENV['OMISE_WEBHOOK_SECRET'],
payload
)

unless Rack::Utils.secure_compare(signature, expected_signature)
render json: { error: '無効な署名' }, status: :unauthorized
end
end

よくある質問

RailsでWebhookを処理するにはどうすればよいですか?

# config/routes.rb
post '/webhooks/omise', to: 'webhooks#omise'

# app/controllers/webhooks_controller.rb
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token

def omise
event = JSON.parse(request.body.read)

case event['key']
when 'charge.complete'
handle_charge_complete(event['data'])
when 'refund.create'
handle_refund_create(event['data'])
end

head :ok
end

private

def handle_charge_complete(charge_data)
charge = Omise::Charge.retrieve(charge_data['id'])
payment = Payment.find_by(charge_id: charge.id)
payment.update!(paid: charge.paid, status: charge.status)
end
end

実際のチャージなしで支払いフローをテストするにはどうすればよいですか?

# テストAPIキーを使用
Omise.api_key = 'skey_test_123456789'

# テストカードトークンを使用
# 成功: tokn_test_5086xl7ddjbases4sq3i
# 拒否: tokn_test_no1

# テストモードを確認
charge = Omise::Charge.create(...)
puts "テストモード: #{charge.livemode == false}"

通貨変換を処理するにはどうすればよいですか?

# 金額を最小通貨単位でデータベースに保存
class Payment < ApplicationRecord
# amountは整数としてデータベースに保存(サタン/セント)

def amount_baht
amount / 100.0
end

def amount_baht=(value)
self.amount = (value.to_f * 100).to_i
end
end

# 使用方法
payment = Payment.new(amount_baht: 1000.00) # 1000.00 THB
payment.amount # => 100000(サタン単位)

リトライロジックを実装するにはどうすればよいですか?

# 指数バックオフを使用
def create_charge_with_retry(params, max_retries: 3)
retries = 0

begin
Omise::Charge.create(params)
rescue Omise::ConnectionError => e
retries += 1
if retries <= max_retries
sleep(2 ** retries) # 2、4、8秒
retry
else
raise
end
end
end

# またはリトライ付きのActiveJobを使用
class ChargeJob < ApplicationJob
retry_on Omise::ConnectionError, wait: :exponentially_longer, attempts: 5

def perform(params)
Omise::Charge.create(params)
end
end

部分払い戻しを処理するにはどうすればよいですか?

charge = Omise::Charge.retrieve('chrg_test_123')

# 払い戻し可能額を確認
refundable = charge.amount - charge.refunded

# 部分払い戻しを作成
if refundable >= 25_000
refund = Omise::Refund.create(
charge: charge.id,
amount: 25_000
)
end

# 全額払い戻しされているかを確認
charge.reload
puts "全額払い戻し: #{charge.refunded == charge.amount}"

顧客に複数のカードを保存するにはどうすればよいですか?

customer = Omise::Customer.retrieve('cust_test_123')

# 最初のカードを追加
customer.update(card: 'tokn_test_111')

# 2番目のカードを追加(デフォルトを置き換え)
customer.update(card: 'tokn_test_222')

# すべてのカードをリスト表示
customer.cards.each do |card|
puts "#{card.id}: #{card.brand} #{card.last_digits}"
end

# 特定のカードにチャージ
Omise::Charge.create(
amount: 100_000,
currency: 'THB',
customer: customer.id,
card: 'card_test_111' # 特定のカードID
)

サブスクリプション請求を実装するにはどうすればよいですか?

# カード付きの顧客を作成
customer = Omise::Customer.create(
email: 'customer@example.com',
card: token
)

# 毎月チャージ
def charge_subscription(customer_id, plan_amount)
charge = Omise::Charge.create(
amount: (plan_amount * 100).to_i,
currency: 'THB',
customer: customer_id,
description: "サブスクリプション #{Date.today.strftime('%B %Y')}"
)

if charge.paid
# サブスクリプションステータスを更新
else
# 失敗した支払いを処理
end
end

# wheneverまたはcronでスケジュール
# またはSidekiqスケジューラーを使用
class SubscriptionChargeJob < ApplicationJob
def perform
Subscription.active.find_each do |subscription|
charge_subscription(subscription.customer_id, subscription.amount)
end
end
end

関連リソース

次のステップ

サポート

Rubyライブラリで問題が発生した場合:

  1. GitHub Issuesを確認してください
  2. APIドキュメントを確認してください
  3. support@omise.coに以下の情報をご連絡ください:
    • Rubyバージョン
    • omise-ruby gemバージョン
    • エラーメッセージとスタックトレース
    • 再現手順