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

Pythonライブラリ(omise-python)

omise-python ライブラリは、Django と Flask のサポート、非同期操作、型ヒント、包括的なエラーハンドリングを備えた、Omise API への Pythonic なインターフェースを提供します。

インストール

pip の使用(推奨)

pip install omise

requirements.txt を使用した pip

# requirements.txt
omise>=0.11.0
pip install -r requirements.txt

Poetry の使用

poetry add omise

要件

  • Python 3.6 以降(Python 3.11+ を含む)
  • requests ライブラリ(自動的にインストールされます)
  • オプション:非同期サポート用の aiohttp

クイックスタート

基本的な構成

import omise

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

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

Django の構成

Django 設定に追加:

# settings.py
import os

OMISE_SECRET_KEY = os.environ.get('OMISE_SECRET_KEY')
OMISE_PUBLIC_KEY = os.environ.get('OMISE_PUBLIC_KEY')
OMISE_API_VERSION = '2019-05-29'

# apps.py または __init__.py で初期化
import omise
omise.api_secret = OMISE_SECRET_KEY
omise.api_version = OMISE_API_VERSION

Flask の構成

# config.py
import os

class Config:
OMISE_SECRET_KEY = os.environ.get('OMISE_SECRET_KEY')
OMISE_PUBLIC_KEY = os.environ.get('OMISE_PUBLIC_KEY')
OMISE_API_VERSION = '2019-05-29'

# app.py
from flask import Flask
import omise

app = Flask(__name__)
app.config.from_object('config.Config')

omise.api_secret = app.config['OMISE_SECRET_KEY']
omise.api_version = app.config['OMISE_API_VERSION']

環境変数

.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 操作にシークレットキーを使用します:

import omise

# オプション 1:グローバル構成(推奨)
omise.api_secret = os.environ['OMISE_SECRET_KEY']

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

一般的な操作

チャージの作成

トークンを使用

import omise

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

if charge.paid:
print(f"チャージ成功: {charge.id}")
else:
print(f"チャージ失敗: {charge.failure_message}")

型ヒントを使用

from typing import Dict, Any
import omise

def create_charge(
amount: int,
currency: str,
token: str,
metadata: Dict[str, Any]
) -> omise.Charge:
"""指定されたパラメータでチャージを作成します。"""
charge = omise.Charge.create(
amount=amount,
currency=currency,
card=token,
metadata=metadata
)
return charge

# 使用方法
charge = create_charge(
amount=100000,
currency='THB',
token='tokn_test_123',
metadata={'order_id': '1234'}
)

顧客を使用

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

3D セキュアで

# 3D セキュアが必要な可能性があるチャージを作成
charge = omise.Charge.create(
amount=100000,
currency='THB',
card='tokn_test_123',
return_uri='https://example.com/payment/callback'
)

if charge.authorized:
if charge.authorize_uri:
# 3D セキュアのために顧客を authorize_uri にリダイレクト
return redirect(charge.authorize_uri)
else:
# 3D セキュアなしでチャージが完了
process_successful_payment(charge)

チャージの取得

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

print(f"金額: {charge.amount}")
print(f"通貨: {charge.currency}")
print(f"ステータス: {charge.status}")
print(f"支払済: {charge.paid}")

チャージのリスト表示

from datetime import datetime, timedelta

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

for charge in charges:
print(f"{charge.id}: {charge.amount} {charge.currency}")

# フィルター付きでチャージをリスト表示
week_ago = (datetime.now() - timedelta(days=7)).date().isoformat()
today = datetime.now().date().isoformat()

recent_charges = omise.Charge.list(
from_date=week_ago,
to_date=today
)

顧客の作成

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

print(f"顧客作成: {customer.id}")

顧客へのカード保存

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

print(f"カード保存: {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')

for card in customer.cards:
print(f"{card.brand} 末尾 {card.last_digits}")
print(f"有効期限: {card.expiration_month}/{card.expiration_year}")

払い戻しの作成

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

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

print(f"払い戻し {refund.id}: {refund.amount} {refund.currency}")

転送の作成

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

print(f"転送 {transfer.id}: {transfer.amount}")

代替決済方法

ソースの作成

# PromptPay QR
source = omise.Source.create(
type='promptpay',
amount=100000,
currency='THB'
)

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

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

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

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

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

# 顧客を authorize_uri にリダイレクト
return redirect(charge.authorize_uri)

モバイルバンキング

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

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

分割払い

# 分割払い
source = omise.Source.create(
type='installment_kbank',
amount=100000,
currency='THB',
installment_term=6 # 6ヶ月
)

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

エラーハンドリング

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

import omise
from omise.errors import (
OmiseError,
AuthenticationError,
InvalidRequestError,
CardError,
APIError,
ConnectionError
)

try:
charge = omise.Charge.create(
amount=100000,
currency='THB',
card='tokn_test_123'
)
except AuthenticationError as e:
# 無効な API キー
print(f"認証失敗: {e}")

except InvalidRequestError as e:
# 無効なパラメータ
print(f"無効なリクエスト: {e}")
print(f"ステータスコード: {e.http_status}")

except CardError as e:
# カード拒否
print(f"カードエラー: {e}")
print(f"失敗コード: {e.code}")

except APIError as e:
# 一般的な API エラー
print(f"API エラー: {e}")

except ConnectionError as e:
# ネットワークエラー
print(f"接続失敗: {e}")

except OmiseError as e:
# すべての Omise エラーのキャッチオール
print(f"Omise エラー: {e}")

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

from omise.errors import CardError

try:
charge = omise.Charge.create(amount=100000, currency='THB', card=token)

except CardError as e:
error_messages = {
'insufficient_fund': 'カードの残高不足',
'stolen_or_lost_card': '盗難または紛失として報告されたカード',
'invalid_security_code': '無効な CVV コード',
'payment_cancelled': '支払いがキャンセルされました'
}

message = error_messages.get(e.code, f"カードエラー: {e}")
return {'error': message}, 400

Django 統合

ビューの例

# views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.views.decorators.http import require_http_methods
import omise
from omise.errors import OmiseError

from .models import Order, Payment

@require_http_methods(["POST"])
def create_payment(request, order_id):
"""注文の支払いチャージを作成します。"""
order = get_object_or_404(Order, id=order_id)
token = request.POST.get('omise_token')

try:
charge = omise.Charge.create(
amount=int(order.total * 100), # 最小単位に変換
currency='THB',
card=token,
description=f"注文 #{order.id}",
metadata={
'order_id': str(order.id),
'customer_email': order.email
},
return_uri=request.build_absolute_uri(
reverse('payment_callback')
)
)

# 支払いレコードを保存
payment = Payment.objects.create(
order=order,
charge_id=charge.id,
amount=order.total,
status=charge.status,
paid=charge.paid
)

if charge.paid:
order.payment_status = 'paid'
order.save()
messages.success(request, '支払いが成功しました!')
return redirect('order_detail', order_id=order.id)
elif charge.authorize_uri:
# 3D セキュアが必要
return redirect(charge.authorize_uri)
else:
messages.error(request, charge.failure_message)
return redirect('payment_form', order_id=order.id)

except OmiseError as e:
logger.error(f"Omise エラー: {e}")
messages.error(request, '支払いが失敗しました。もう一度お試しください。')
return redirect('payment_form', order_id=order.id)

@require_http_methods(["GET"])
def payment_callback(request):
"""3D セキュア後の支払いコールバックを処理します。"""
charge_id = request.GET.get('id')

try:
charge = omise.Charge.retrieve(charge_id)
payment = Payment.objects.get(charge_id=charge.id)

payment.status = charge.status
payment.paid = charge.paid
payment.save()

if charge.paid:
payment.order.payment_status = 'paid'
payment.order.save()
messages.success(request, '支払いが成功しました!')
return redirect('order_detail', order_id=payment.order.id)
else:
messages.error(request, charge.failure_message)
return redirect('payment_form', order_id=payment.order.id)

except omise.OmiseError as e:
messages.error(request, '支払いの検証に失敗しました。')
return redirect('home')

モデル統合

# models.py
from django.db import models
from django.core.exceptions import ValidationError
import omise
from omise.errors import OmiseError

class Payment(models.Model):
"""Omise チャージを追跡するための支払いモデル。"""

STATUS_CHOICES = [
('pending', '保留中'),
('successful', '成功'),
('failed', '失敗'),
('refunded', '返金済み'),
]

order = models.ForeignKey('Order', on_delete=models.CASCADE)
charge_id = models.CharField(max_length=100, unique=True, null=True)
amount = models.DecimalField(max_digits=10, decimal_places=2)
currency = models.CharField(max_length=3, default='THB')
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
paid = models.BooleanField(default=False)
failure_code = models.CharField(max_length=100, blank=True)
failure_message = models.TextField(blank=True)
refund_id = models.CharField(max_length=100, blank=True)
refund_amount = models.DecimalField(max_digits=10, decimal_places=2, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

class Meta:
ordering = ['-created_at']

def charge(self, token: str) -> omise.Charge:
"""指定されたトークンでチャージを作成します。"""
charge = omise.Charge.create(
amount=int(self.amount * 100),
currency=self.currency,
card=token,
description=f"注文 #{self.order.id}",
metadata=self.get_charge_metadata()
)

self.charge_id = charge.id
self.status = charge.status
self.paid = charge.paid
self.save()

return charge

def refund(self, refund_amount: float = None) -> omise.Refund:
"""この支払いの払い戻しを作成します。"""
if not self.charge_id:
raise ValueError("charge_id なしで支払いを払い戻すことはできません")

refund = omise.Refund.create(
charge=self.charge_id,
amount=int(refund_amount * 100) if refund_amount else None
)

self.refund_id = refund.id
self.refund_amount = refund.amount / 100
self.status = 'refunded'
self.save()

return refund

def refresh_status(self) -> None:
"""Omise から支払いステータスを更新します。"""
if not self.charge_id:
return

charge = omise.Charge.retrieve(self.charge_id)
self.status = charge.status
self.paid = charge.paid
self.failure_code = charge.failure_code or ''
self.failure_message = charge.failure_message or ''
self.save()

def get_charge_metadata(self) -> dict:
"""チャージのメタデータを取得します。"""
return {
'order_id': str(self.order.id),
'customer_email': self.order.email,
'customer_name': self.order.customer_name
}

非同期処理用の Celery タスク

# tasks.py
from celery import shared_task
from django.core.mail import send_mail
import omise
from omise.errors import OmiseError, CardError

from .models import Payment

@shared_task(bind=True, max_retries=3)
def process_charge(self, payment_id: int, token: str):
"""非同期でチャージを処理します。"""
try:
payment = Payment.objects.get(id=payment_id)
charge = payment.charge(token)

if charge.paid:
# 成功メールを送信
send_mail(
'支払い確認',
f'{payment.amount} {payment.currency} の支払いが成功しました。',
'noreply@example.com',
[payment.order.email],
fail_silently=False,
)
else:
# 失敗メールを送信
send_mail(
'支払い失敗',
f'支払いが失敗しました: {charge.failure_message}',
'noreply@example.com',
[payment.order.email],
fail_silently=False,
)

except CardError as e:
payment.status = 'failed'
payment.failure_message = str(e)
payment.save()

except OmiseError as e:
# ネットワークエラーで再試行
raise self.retry(exc=e, countdown=60)

@shared_task
def refresh_payment_statuses():
"""保留中の支払いステータスを更新します。"""
pending_payments = Payment.objects.filter(status='pending')

for payment in pending_payments:
try:
payment.refresh_status()
except OmiseError:
continue

Flask 統合

アプリケーション設定

# app.py
from flask import Flask, request, render_template, redirect, url_for, flash
from flask_sqlalchemy import SQLAlchemy
import omise
from omise.errors import OmiseError, CardError
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://localhost/myapp'
db = SQLAlchemy(app)

# Omise を構成
omise.api_secret = os.environ.get('OMISE_SECRET_KEY')
omise.api_version = '2019-05-29'

@app.route('/payment/<int:order_id>', methods=['GET'])
def payment_form(order_id):
"""支払いフォームを表示します。"""
order = Order.query.get_or_404(order_id)
return render_template('payment.html', order=order)

@app.route('/payment/<int:order_id>', methods=['POST'])
def create_payment(order_id):
"""支払いを処理します。"""
order = Order.query.get_or_404(order_id)
token = request.form.get('omise_token')

try:
charge = omise.Charge.create(
amount=int(order.total * 100),
currency='THB',
card=token,
description=f"注文 #{order.id}",
metadata={
'order_id': str(order.id),
'customer_email': order.email
},
return_uri=url_for('payment_callback', _external=True)
)

# 支払いを保存
payment = Payment(
order_id=order.id,
charge_id=charge.id,
amount=order.total,
status=charge.status,
paid=charge.paid
)
db.session.add(payment)
db.session.commit()

if charge.paid:
flash('支払いが成功しました!', 'success')
return redirect(url_for('order_detail', order_id=order.id))
elif charge.authorize_uri:
return redirect(charge.authorize_uri)
else:
flash(charge.failure_message, 'error')
return redirect(url_for('payment_form', order_id=order.id))

except CardError as e:
flash(f'カードエラー: {e}', 'error')
return redirect(url_for('payment_form', order_id=order.id))

except OmiseError as e:
app.logger.error(f"Omise エラー: {e}")
flash('支払いが失敗しました。もう一度お試しください。', 'error')
return redirect(url_for('payment_form', order_id=order.id))

@app.route('/payment/callback')
def payment_callback():
"""支払いコールバックを処理します。"""
charge_id = request.args.get('id')

try:
charge = omise.Charge.retrieve(charge_id)
payment = Payment.query.filter_by(charge_id=charge.id).first_or_404()

payment.status = charge.status
payment.paid = charge.paid
db.session.commit()

if charge.paid:
flash('支払いが成功しました!', 'success')
return redirect(url_for('order_detail', order_id=payment.order_id))
else:
flash(charge.failure_message, 'error')
return redirect(url_for('payment_form', order_id=payment.order_id))

except OmiseError as e:
flash('支払いの検証に失敗しました。', 'error')
return redirect(url_for('home'))

非同期サポート

asyncio の使用

import asyncio
import aiohttp
import omise

async def create_charge_async(amount: int, currency: str, token: str):
"""非同期でチャージを作成します。"""
# 注:omise-python にはネイティブの非同期サポートがありません
# 並行操作には run_in_executor を使用します
loop = asyncio.get_event_loop()
charge = await loop.run_in_executor(
None,
omise.Charge.create,
amount,
currency,
token
)
return charge

async def process_multiple_charges(charges_data):
"""複数のチャージを同時に処理します。"""
tasks = [
create_charge_async(
data['amount'],
data['currency'],
data['token']
)
for data in charges_data
]
results = await asyncio.gather(*tasks, return_exceptions=True)
return results

# 使用方法
async def main():
charges_data = [
{'amount': 100000, 'currency': 'THB', 'token': 'tokn_test_1'},
{'amount': 200000, 'currency': 'THB', 'token': 'tokn_test_2'},
]
results = await process_multiple_charges(charges_data)

asyncio.run(main())

ベストプラクティス

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

import os
from dotenv import load_dotenv

load_dotenv()

# キーがない場合はエラーを発生させる
OMISE_SECRET_KEY = os.environ['OMISE_SECRET_KEY']
if not OMISE_SECRET_KEY:
raise ValueError("OMISE_SECRET_KEY 環境変数が設定されていません")

omise.api_secret = OMISE_SECRET_KEY

2. べき等性の処理

import uuid
import omise

def create_idempotent_charge(amount: int, currency: str, token: str, order_id: str):
"""べき等性キー付きでチャージを作成します。"""
idempotency_key = f"order-{order_id}-{uuid.uuid4()}"

charge = omise.Charge.create(
amount=amount,
currency=currency,
card=token,
headers={'Idempotency-Key': idempotency_key}
)
return charge

3. コネクションプーリングの使用

import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

# 再試行戦略を構成
retry_strategy = Retry(
total=3,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE", "POST"],
backoff_factor=1
)

adapter = HTTPAdapter(max_retries=retry_strategy, pool_connections=10, pool_maxsize=10)
session = requests.Session()
session.mount("https://", adapter)

# omise でカスタムセッションを使用
omise.api_session = session

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

class Payment:
"""完全なオブジェクトではなく、ID のみを保存します。"""

def __init__(self, charge_id: str):
self.charge_id = charge_id
self._charge = None

@property
def charge(self):
"""必要なときにチャージを遅延ロードします。"""
if self._charge is None and self.charge_id:
self._charge = omise.Charge.retrieve(self.charge_id)
return self._charge

def refresh(self):
"""チャージデータを更新します。"""
self._charge = None
return self.charge

5. API 呼び出し前の検証

from decimal import Decimal

def validate_charge_params(amount: Decimal, currency: str):
"""API 呼び出し前にチャージパラメータを検証します。"""
if amount < Decimal('20.00'):
raise ValueError("金額は最低 20 THB 以上である必要があります")

if currency not in ['THB', 'USD', 'SGD', 'JPY']:
raise ValueError(f"通貨 {currency} はサポートされていません")

return True

def create_validated_charge(amount: Decimal, currency: str, token: str):
"""検証付きでチャージを作成します。"""
validate_charge_params(amount, currency)

charge = omise.Charge.create(
amount=int(amount * 100),
currency=currency,
card=token
)
return charge

6. ロギングの実装

import logging
import omise

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def create_charge_with_logging(amount: int, currency: str, token: str):
"""包括的なロギング付きでチャージを作成します。"""
logger.info(f"チャージ作成中: amount={amount}, currency={currency}")

try:
charge = omise.Charge.create(
amount=amount,
currency=currency,
card=token
)
logger.info(f"チャージ作成: {charge.id}, paid={charge.paid}")
return charge

except omise.OmiseError as e:
logger.error(f"チャージ失敗: {e}", exc_info=True)
raise

テスト

Pytest の例

# test_payments.py
import pytest
import omise
from unittest.mock import patch, Mock

@pytest.fixture
def mock_charge():
"""モックチャージオブジェクト。"""
charge = Mock()
charge.id = 'chrg_test_123'
charge.amount = 100000
charge.currency = 'THB'
charge.paid = True
charge.status = 'successful'
return charge

def test_create_charge(mock_charge):
"""チャージ作成をテストします。"""
with patch('omise.Charge.create', return_value=mock_charge):
charge = omise.Charge.create(
amount=100000,
currency='THB',
card='tokn_test_123'
)

assert charge.id == 'chrg_test_123'
assert charge.paid is True

def test_create_charge_with_error():
"""エラー付きのチャージ作成をテストします。"""
with patch('omise.Charge.create', side_effect=omise.CardError('insufficient_fund', '残高不足')):
with pytest.raises(omise.CardError) as exc_info:
omise.Charge.create(
amount=100000,
currency='THB',
card='tokn_test_123'
)

assert 'insufficient_fund' in str(exc_info.value)

@pytest.fixture
def payment_service():
"""支払いサービスフィクスチャ。"""
return PaymentService()

def test_process_payment(payment_service, mock_charge):
"""支払い処理をテストします。"""
with patch('omise.Charge.create', return_value=mock_charge):
result = payment_service.process_payment(
amount=1000.00,
token='tokn_test_123'
)

assert result['success'] is True
assert result['charge_id'] == 'chrg_test_123'

API テスト用の VCR.py の使用

# test_integration.py
import pytest
import vcr
import omise

# VCR を構成
my_vcr = vcr.VCR(
cassette_library_dir='tests/fixtures/vcr_cassettes',
record_mode='once',
filter_headers=['authorization'],
)

@my_vcr.use_cassette('create_charge.yaml')
def test_create_charge_integration():
"""VCR を使用した統合テスト。"""
charge = omise.Charge.create(
amount=100000,
currency='THB',
card='tokn_test_123'
)

assert charge.paid is True
assert charge.amount == 100000

モックデコレータパターン

from unittest.mock import patch
from functools import wraps

def mock_omise(func):
"""Omise API 呼び出しをモックするデコレータ。"""
@wraps(func)
def wrapper(*args, **kwargs):
with patch('omise.Charge.create') as mock_create:
mock_charge = Mock()
mock_charge.id = 'chrg_test_123'
mock_charge.paid = True
mock_create.return_value = mock_charge

return func(*args, **kwargs)
return wrapper

@mock_omise
def test_payment_flow():
"""デコレータを使用した支払いフローのテスト。"""
charge = omise.Charge.create(amount=100000, currency='THB', card='tokn_test_123')
assert charge.paid is True

トラブルシューティング

SSL 証明書エラー

import certifi
import omise

# SSL 検証に certifi を使用
omise.ca_bundle = certifi.where()

# または SSL 検証を無効化(本番環境では非推奨)
# omise.verify_ssl = False

接続タイムアウト

import omise

# 遅い接続のためにタイムアウトを増やす
omise.timeout = 60 # 秒

# またはリクエストごとに
charge = omise.Charge.create(
amount=100000,
currency='THB',
card='tokn_test_123',
timeout=60
)

デバッグモード

import logging
import omise

# デバッグロギングを有効化
logging.basicConfig(level=logging.DEBUG)
omise.debug = True

# これは次の情報を出力します:
# - リクエスト URL とメソッド
# - リクエストヘッダー
# - リクエストボディ
# - レスポンスステータス
# - レスポンスボディ

ウェブフック署名検証

import hmac
import hashlib

def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
"""ウェブフック署名を検証します。"""
expected_signature = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()

return hmac.compare_digest(signature, expected_signature)

# Flask の例
@app.route('/webhooks/omise', methods=['POST'])
def omise_webhook():
"""Omise ウェブフックを処理します。"""
payload = request.get_data()
signature = request.headers.get('Omise-Signature')

if not verify_webhook_signature(payload, signature, os.environ['OMISE_WEBHOOK_SECRET']):
return {'error': '無効な署名'}, 401

event = request.get_json()
# イベントを処理
return {'status': 'ok'}

よくある質問

Django でウェブフックを処理するにはどうすればよいですか?

# views.py
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
import json
import hmac
import hashlib

@csrf_exempt
def omise_webhook(request):
"""Omise ウェブフックを処理します。"""
if request.method != 'POST':
return JsonResponse({'error': 'メソッドは許可されていません'}, status=405)

# 署名を検証
payload = request.body
signature = request.headers.get('Omise-Signature')

expected_signature = hmac.new(
settings.OMISE_WEBHOOK_SECRET.encode(),
payload,
hashlib.sha256
).hexdigest()

if not hmac.compare_digest(signature, expected_signature):
return JsonResponse({'error': '無効な署名'}, status=401)

# イベントを処理
event = json.loads(payload)

if event['key'] == 'charge.complete':
handle_charge_complete(event['data'])
elif event['key'] == 'refund.create':
handle_refund_create(event['data'])

return JsonResponse({'status': 'ok'})

def handle_charge_complete(charge_data):
"""チャージ完了を処理します。"""
charge = omise.Charge.retrieve(charge_data['id'])
payment = Payment.objects.get(charge_id=charge.id)
payment.status = charge.status
payment.paid = charge.paid
payment.save()

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

import os
import omise

# テスト API キーを使用
omise.api_secret = 'skey_test_123456789'

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

# テストチャージを作成
charge = omise.Charge.create(
amount=100000,
currency='THB',
card='tokn_test_5086xl7ddjbases4sq3i'
)

# テストモードを確認
print(f"テストモード: {not charge.livemode}")

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

from decimal import Decimal

class Payment:
"""通貨換算付きの支払い。"""

def __init__(self, amount_baht: Decimal):
self.amount_baht = amount_baht

@property
def amount_satang(self) -> int:
"""バーツをサタンに換算します。"""
return int(self.amount_baht * 100)

@staticmethod
def from_satang(amount_satang: int) -> 'Payment':
"""サタンから支払いを作成します。"""
amount_baht = Decimal(amount_satang) / 100
return Payment(amount_baht)

# 使用方法
payment = Payment(Decimal('1000.00')) # 1000.00 THB
charge = omise.Charge.create(
amount=payment.amount_satang, # 100000 サタン
currency='THB',
card='tokn_test_123'
)

再試行ロジックを実装するにはどうすればよいですか?

import time
from typing import Optional
import omise
from omise.errors import ConnectionError

def create_charge_with_retry(
amount: int,
currency: str,
token: str,
max_retries: int = 3
) -> Optional[omise.Charge]:
"""指数バックオフ再試行付きでチャージを作成します。"""
for attempt in range(max_retries):
try:
charge = omise.Charge.create(
amount=amount,
currency=currency,
card=token
)
return charge

except ConnectionError as e:
if attempt < max_retries - 1:
wait_time = 2 ** attempt # 1、2、4 秒
time.sleep(wait_time)
else:
raise

# または tenacity ライブラリを使用
from tenacity import retry, stop_after_attempt, wait_exponential

@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10)
)
def create_charge_tenacity(amount: int, currency: str, token: str):
"""tenacity 再試行付きでチャージを作成します。"""
return omise.Charge.create(amount=amount, currency=currency, card=token)

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

import omise

def refund_charge(charge_id: str, refund_amount: float = None):
"""チャージを払い戻します(部分または全額)。"""
# 現在のチャージを取得
charge = omise.Charge.retrieve(charge_id)

# 払い戻し可能金額を計算
refundable = charge.amount - charge.refunded

if refund_amount:
refund_amount_satang = int(refund_amount * 100)
if refund_amount_satang > refundable:
raise ValueError(f"払い戻し金額が払い戻し可能金額を超えています: {refundable / 100}")
else:
refund_amount_satang = None # 全額返金

# 払い戻しを作成
refund = omise.Refund.create(
charge=charge_id,
amount=refund_amount_satang
)

return refund

# 使用方法
refund = refund_charge('chrg_test_123', refund_amount=250.00)

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

import omise

def add_card_to_customer(customer_id: str, token: str) -> omise.Card:
"""顧客にカードを追加します。"""
customer = omise.Customer.retrieve(customer_id)
customer.update(card=token)
return customer.default_card

def list_customer_cards(customer_id: str):
"""顧客のすべてのカードをリスト表示します。"""
customer = omise.Customer.retrieve(customer_id)
return list(customer.cards)

def charge_specific_card(customer_id: str, card_id: str, amount: int):
"""特定のカードにチャージします。"""
charge = omise.Charge.create(
amount=amount,
currency='THB',
customer=customer_id,
card=card_id # 特定のカード ID
)
return charge

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

from datetime import datetime, timedelta
import omise

class SubscriptionManager:
"""サブスクリプション課金を管理します。"""

def __init__(self, customer_id: str, plan_amount: int):
self.customer_id = customer_id
self.plan_amount = plan_amount

def charge_monthly(self) -> omise.Charge:
"""月額サブスクリプションをチャージします。"""
charge = omise.Charge.create(
amount=self.plan_amount,
currency='THB',
customer=self.customer_id,
description=f"サブスクリプション {datetime.now().strftime('%B %Y')}"
)
return charge

def handle_failed_payment(self, charge: omise.Charge):
"""失敗したサブスクリプション支払いを処理します。"""
# 通知を送信
# サブスクリプションステータスを更新
# 猶予期間後に再試行
pass

# Celery での使用方法
from celery import shared_task

@shared_task
def charge_subscriptions():
"""すべてのアクティブなサブスクリプションをチャージします。"""
from myapp.models import Subscription

subscriptions = Subscription.objects.filter(status='active')

for subscription in subscriptions:
manager = SubscriptionManager(
subscription.customer_id,
subscription.plan_amount
)
try:
charge = manager.charge_monthly()
subscription.last_charge_date = datetime.now()
subscription.save()
except omise.CardError as e:
manager.handle_failed_payment(charge)

関連リソース

次のステップ

サポート

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

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