import uuid
import datetime
from typing import Dict, List, Optional
from enum import Enum
import hashlib
import json
class PaymentStatus(Enum):
PENDING = "pending"
AUTHORIZED = "authorized"
CAPTURED = "captured"
FAILED = "failed"
REFUNDED = "refunded"
VOIDED = "voided"
class PaymentMethod(Enum):
CREDIT_CARD = "credit_card"
DEBIT_CARD = "debit_card"
BANK_TRANSFER = "bank_transfer"
DIGITAL_WALLET = "digital_wallet"
class CreditCard:
def __init__(self, card_number: str, expiry_month: int, expiry_year: int, cvv:
str, holder_name: str):
self.card_number = self._mask_card_number(card_number)
self.expiry_month = expiry_month
self.expiry_year = expiry_year
self.cvv = "***" # Store securely or don't store at all
self.holder_name = holder_name
self._validate()
def _mask_card_number(self, card_number: str) -> str:
return "X" * (len(card_number) - 4) + card_number[-4:]
def _validate(self) -> bool:
# Implement card validation logic (Luhn algorithm, expiry date check, etc.)
# Simplified for this example
if len(self.card_number.replace('X', '')) != 4:
raise ValueError("Invalid card number")
if not (1 <= self.expiry_month <= 12):
raise ValueError("Invalid expiry month")
current_year = datetime.datetime.now().year
if not (current_year <= self.expiry_year <= current_year + 10):
raise ValueError("Invalid expiry year")
return True
class Transaction:
def __init__(self,
amount: float,
currency: str,
payment_method: PaymentMethod,
payment_details: Dict,
merchant_id: str,
customer_id: Optional[str] = None,
description: Optional[str] = None):
self.transaction_id = str(uuid.uuid4())
self.amount = amount
self.currency = currency
self.payment_method = payment_method
self.payment_details = payment_details
self.merchant_id = merchant_id
self.customer_id = customer_id
self.description = description
self.status = PaymentStatus.PENDING
self.created_at = datetime.datetime.now()
self.updated_at = self.created_at
self.error_message = None
def to_dict(self) -> Dict:
return {
"transaction_id": self.transaction_id,
"amount": self.amount,
"currency": self.currency,
"payment_method": self.payment_method.value,
"merchant_id": self.merchant_id,
"customer_id": self.customer_id,
"description": self.description,
"status": self.status.value,
"created_at": self.created_at.isoformat(),
"updated_at": self.updated_at.isoformat(),
"error_message": self.error_message
}
def update_status(self, status: PaymentStatus, error_message: Optional[str] =
None):
self.status = status
self.error_message = error_message
self.updated_at = datetime.datetime.now()
class PaymentGateway:
def __init__(self, gateway_name: str, api_key: str, api_secret: str):
self.gateway_name = gateway_name
self.api_key = api_key
self.api_secret = api_secret
self.transactions: List[Transaction] = []
def authorize(self, transaction: Transaction) -> Dict:
"""
Authorize a payment (validate card, check funds, but don't capture payment)
"""
# In a real implementation, this would communicate with external payment
processor
try:
# Simulate communication with payment processor
self._simulate_external_auth(transaction)
transaction.update_status(PaymentStatus.AUTHORIZED)
self.transactions.append(transaction)
return {
"success": True,
"transaction_id": transaction.transaction_id,
"status": transaction.status.value
}
except Exception as e:
transaction.update_status(PaymentStatus.FAILED, str(e))
self.transactions.append(transaction)
return {
"success": False,
"transaction_id": transaction.transaction_id,
"status": transaction.status.value,
"error": str(e)
}
def capture(self, transaction_id: str, amount: Optional[float] = None) -> Dict:
"""
Capture previously authorized payment
"""
transaction = self._get_transaction(transaction_id)
if not transaction:
return {"success": False, "error": "Transaction not found"}
if transaction.status != PaymentStatus.AUTHORIZED:
return {"success": False, "error": f"Transaction in invalid state:
{transaction.status.value}"}
# Amount to capture (default to full transaction amount)
capture_amount = amount if amount else transaction.amount
if capture_amount > transaction.amount:
return {"success": False, "error": "Capture amount exceeds authorized
amount"}
try:
# Simulate actual capture with payment processor
self._simulate_external_capture(transaction, capture_amount)
transaction.update_status(PaymentStatus.CAPTURED)
return {
"success": True,
"transaction_id": transaction.transaction_id,
"status": transaction.status.value,
"amount_captured": capture_amount
}
except Exception as e:
transaction.update_status(PaymentStatus.FAILED, str(e))
return {
"success": False,
"transaction_id": transaction.transaction_id,
"status": transaction.status.value,
"error": str(e)
}
def void(self, transaction_id: str) -> Dict:
"""
Void (cancel) an authorized transaction that hasn't been captured
"""
transaction = self._get_transaction(transaction_id)
if not transaction:
return {"success": False, "error": "Transaction not found"}
if transaction.status != PaymentStatus.AUTHORIZED:
return {"success": False, "error": f"Cannot void transaction in state:
{transaction.status.value}"}
try:
# Simulate communication with payment processor to void
self._simulate_external_void(transaction)
transaction.update_status(PaymentStatus.VOIDED)
return {
"success": True,
"transaction_id": transaction.transaction_id,
"status": transaction.status.value
}
except Exception as e:
return {
"success": False,
"transaction_id": transaction.transaction_id,
"error": str(e)
}
def refund(self, transaction_id: str, amount: Optional[float] = None) -> Dict:
"""
Refund a captured transaction
"""
transaction = self._get_transaction(transaction_id)
if not transaction:
return {"success": False, "error": "Transaction not found"}
if transaction.status != PaymentStatus.CAPTURED:
return {"success": False, "error": f"Cannot refund transaction in
state: {transaction.status.value}"}
# Amount to refund (default to full transaction amount)
refund_amount = amount if amount else transaction.amount
if refund_amount > transaction.amount:
return {"success": False, "error": "Refund amount exceeds captured
amount"}
try:
# Simulate communication with payment processor for refund
self._simulate_external_refund(transaction, refund_amount)
transaction.update_status(PaymentStatus.REFUNDED)
return {
"success": True,
"transaction_id": transaction.transaction_id,
"status": transaction.status.value,
"amount_refunded": refund_amount
}
except Exception as e:
return {
"success": False,
"transaction_id": transaction.transaction_id,
"error": str(e)
}
def get_transaction(self, transaction_id: str) -> Optional[Dict]:
"""
Get transaction details by ID
"""
transaction = self._get_transaction(transaction_id)
if not transaction:
return None
return transaction.to_dict()
def generate_webhook_signature(self, payload: Dict, secret: str) -> str:
"""
Generate signature for webhook payloads
"""
payload_string = json.dumps(payload, sort_keys=True)
return hashlib.sha256(f"{payload_string}{secret}".encode()).hexdigest()
def _get_transaction(self, transaction_id: str) -> Optional[Transaction]:
"""
Internal method to get transaction by ID
"""
for transaction in self.transactions:
if transaction.transaction_id == transaction_id:
return transaction
return None
def _simulate_external_auth(self, transaction: Transaction):
"""
Simulate communication with payment processor for authorization
In a real implementation, this would make API calls to a payment processor
"""
# Simulate card validation
if transaction.payment_method == PaymentMethod.CREDIT_CARD:
card_details = transaction.payment_details
# Simulate some validation rules
if card_details.get('card_number', '').endswith('0000'):
raise ValueError("Card declined: test decline card")
def _simulate_external_capture(self, transaction: Transaction, amount: float):
"""
Simulate communication with payment processor for capture
"""
# Simulation logic for capture
pass
def _simulate_external_void(self, transaction: Transaction):
"""
Simulate communication with payment processor for void
"""
# Simulation logic for void
pass
def _simulate_external_refund(self, transaction: Transaction, amount: float):
"""
Simulate communication with payment processor for refund
"""
# Simulation logic for refund
pass
# Example usage
def main():
# Initialize payment gateway
gateway = PaymentGateway(
gateway_name="Example Payment Gateway",
api_key="live_api_key_123456",
api_secret="live_api_secret_abcdef"
)
# Create credit card payment
try:
card = CreditCard(
card_number="4111111111111111",
expiry_month=12,
expiry_year=2025,
cvv="123",
holder_name="John Doe"
)
# Create a transaction
transaction = Transaction(
amount=99.99,
currency="USD",
payment_method=PaymentMethod.CREDIT_CARD,
payment_details={
"card_number": card.card_number,
"expiry_month": card.expiry_month,
"expiry_year": card.expiry_year,
"holder_name": card.holder_name
},
merchant_id="merchant_12345",
customer_id="customer_6789",
description="Purchase of Premium Subscription"
)
# Process the payment
print("1. Authorizing payment...")
auth_result = gateway.authorize(transaction)
print(f"Authorization result: {auth_result}")
if auth_result["success"]:
transaction_id = auth_result["transaction_id"]
# Capture the authorized payment
print("\n2. Capturing payment...")
capture_result = gateway.capture(transaction_id)
print(f"Capture result: {capture_result}")
# Get transaction details
print("\n3. Transaction details:")
transaction_details = gateway.get_transaction(transaction_id)
print(f"Transaction details: {transaction_details}")
# Process refund
print("\n4. Refunding payment...")
refund_result = gateway.refund(transaction_id, 50.00)
print(f"Refund result: {refund_result}")
# Example of a void operation (with a new transaction)
print("\n5. Creating another transaction to demonstrate void...")
transaction2 = Transaction(
amount=199.99,
currency="USD",
payment_method=PaymentMethod.CREDIT_CARD,
payment_details={
"card_number": card.card_number,
"expiry_month": card.expiry_month,
"expiry_year": card.expiry_year,
"holder_name": card.holder_name
},
merchant_id="merchant_12345",
customer_id="customer_6789",
description="Purchase of Annual Subscription"
)
auth_result2 = gateway.authorize(transaction2)
print(f"Authorization result: {auth_result2}")
if auth_result2["success"]:
transaction_id2 = auth_result2["transaction_id"]
# Void the authorized payment
print("\n6. Voiding payment...")
void_result = gateway.void(transaction_id2)
print(f"Void result: {void_result}")
except ValueError as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
======================
Core Components
Payment Status & Method Enums
PaymentStatus: Tracks the lifecycle of a transaction (pending, authorized,
captured, failed, etc.)
PaymentMethod: Different payment methods supported (credit cards, debit cards, bank
transfers, digital wallets)
CreditCard Class
Handles credit card information with proper security (masking card numbers, never
storing CVV)
Performs basic validation on card details
In a real implementation, would include more comprehensive validation using
algorithms like Luhn's
Transaction Class
Represents a single payment transaction
Stores all relevant details (amount, currency, status, timestamps, etc.)
Each transaction has a unique ID generated using UUID
Maintains a history of status changes
PaymentGateway Class
Main service provider that handles all payment processing operations
Maintains a collection of transactions
Provides methods for the entire payment lifecycle
Payment Processing Flow
Authorization
First step in processing a payment
Validates the payment method and checks if funds are available
Does NOT actually move money at this stage
Card is only "reserved" or "authorized" for the amount
Capture
Actually charges the pre-authorized payment
Can capture the full amount or a partial amount
Moves money from customer to merchant account
Can only be done on authorized transactions
Void
Cancels an authorized transaction before it's captured
Used when you decide not to complete a transaction after authorization
No money changes hands in a voided transaction
Refund
Returns money to customer after a successful capture
Can be full or partial refund
Can only be done on captured transactions
Security Features
Card number masking (storing only last 4 digits)
Not storing sensitive data like CVV codes
Webhook signature generation to verify webhook authenticity
Status validation to prevent invalid operation sequences
Simulation Logic
The code includes "simulation" methods that would, in a real implementation,
communicate with external payment processors:
_simulate_external_auth
_simulate_external_capture
_simulate_external_void
_simulate_external_refund
In a production environment, these would make API calls to payment processors like
Stripe, PayPal, or Adyen.
Example Usage
The main() function demonstrates a complete payment flow:
Create a payment gateway instance
Create and validate card details
Create a transaction
Authorize the payment
Capture the payment
Retrieve transaction details
Process a refund
Create a second transaction to demonstrate voiding
What's Missing in a Production Implementation
For production use, you would need to add:
Proper encryption of sensitive data
More robust error handling
Database storage for transactions
Webhook handlers for payment events
Comprehensive logging
Fraud detection mechanisms
Support for 3D Secure/2-factor authentication
PCI compliance measures
Integration with actual payment processors