Skip to main content
โšก Calmops

Building Payment Processing Systems: Stripe, Square, and PayPal Integration

Introduction

Payment processing is the backbone of e-commerce and SaaS businesses. Building a robust payment system requires understanding multiple payment gateways, handling edge cases, ensuring PCI compliance, and managing failures gracefully. Many teams build payment systems without proper error handling, resulting in lost transactions and customer frustration.

This comprehensive guide covers production-grade payment processing with Stripe, Square, and PayPal, including integration patterns, error handling, and compliance.


Core Concepts

Payment Gateway

Service that processes credit card and digital payments.

Merchant Account

Bank account for receiving payments.

PCI-DSS

Payment Card Industry Data Security Standard for handling card data.

Tokenization

Converting sensitive card data into non-sensitive tokens.

Webhook

Server-to-server notification of payment events.

Idempotency

Ensuring duplicate requests produce same result.

Settlement

Process of transferring funds to merchant account.

Chargeback

Customer dispute of a transaction.

3D Secure

Authentication protocol for card payments.

Recurring Billing

Automatic periodic charges for subscriptions.


Payment Processing Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Client Application                    โ”‚
โ”‚              (Web, Mobile, Point of Sale)               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                  Payment Form                            โ”‚
โ”‚         (Stripe Elements, Square Web Payments)          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              Your Backend Server                         โ”‚
โ”‚         (Create Payment Intent, Handle Webhooks)        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚                โ”‚                โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Stripe     โ”‚  โ”‚   Square    โ”‚  โ”‚   PayPal   โ”‚
โ”‚   Gateway    โ”‚  โ”‚   Gateway   โ”‚  โ”‚   Gateway  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ”‚                โ”‚               โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              Payment Database                            โ”‚
โ”‚         (Store transactions, receipts, logs)            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Stripe Integration

Setup and Configuration

import stripe
from flask import Flask, request, jsonify
from datetime import datetime
import logging

app = Flask(__name__)
stripe.api_key = "sk_live_YOUR_KEY"
logger = logging.getLogger(__name__)

# Configuration
STRIPE_CONFIG = {
    "publishable_key": "pk_live_YOUR_KEY",
    "webhook_secret": "whsec_YOUR_SECRET",
    "api_version": "2024-04-10"
}

Create Payment Intent

@app.route("/create-payment-intent", methods=["POST"])
def create_payment_intent():
    """Create Stripe payment intent"""
    try:
        data = request.json
        
        # Validate input
        if not data.get("amount") or not data.get("currency"):
            return jsonify({"error": "Missing amount or currency"}), 400
        
        # Create payment intent
        intent = stripe.PaymentIntent.create(
            amount=int(data["amount"] * 100),  # Convert to cents
            currency=data["currency"],
            metadata={
                "order_id": data.get("order_id"),
                "customer_id": data.get("customer_id")
            },
            # Idempotency key prevents duplicate charges
            idempotency_key=data.get("idempotency_key")
        )
        
        logger.info(f"Payment intent created: {intent.id}")
        
        return jsonify({
            "client_secret": intent.client_secret,
            "payment_intent_id": intent.id
        })
    
    except stripe.error.CardError as e:
        logger.error(f"Card error: {e.user_message}")
        return jsonify({"error": e.user_message}), 400
    
    except stripe.error.RateLimitError:
        logger.error("Rate limit exceeded")
        return jsonify({"error": "Rate limit exceeded"}), 429
    
    except stripe.error.APIConnectionError:
        logger.error("API connection error")
        return jsonify({"error": "Connection error"}), 503
    
    except Exception as e:
        logger.error(f"Unexpected error: {str(e)}")
        return jsonify({"error": "Payment processing failed"}), 500

Handle Webhooks

@app.route("/webhook", methods=["POST"])
def handle_webhook():
    """Handle Stripe webhook events"""
    payload = request.get_data()
    sig_header = request.headers.get("Stripe-Signature")
    
    try:
        # Verify webhook signature
        event = stripe.Webhook.construct_event(
            payload,
            sig_header,
            STRIPE_CONFIG["webhook_secret"]
        )
    except ValueError:
        logger.error("Invalid payload")
        return jsonify({"error": "Invalid payload"}), 400
    except stripe.error.SignatureVerificationError:
        logger.error("Invalid signature")
        return jsonify({"error": "Invalid signature"}), 400
    
    # Handle events
    if event["type"] == "payment_intent.succeeded":
        payment_intent = event["data"]["object"]
        handle_payment_succeeded(payment_intent)
    
    elif event["type"] == "payment_intent.payment_failed":
        payment_intent = event["data"]["object"]
        handle_payment_failed(payment_intent)
    
    elif event["type"] == "charge.refunded":
        charge = event["data"]["object"]
        handle_refund(charge)
    
    return jsonify({"status": "success"}), 200

def handle_payment_succeeded(payment_intent):
    """Process successful payment"""
    logger.info(f"Payment succeeded: {payment_intent['id']}")
    
    # Update database
    order_id = payment_intent["metadata"]["order_id"]
    # db.orders.update(order_id, status="paid")
    
    # Send confirmation email
    # send_email(order_id)
    
    # Trigger fulfillment
    # trigger_fulfillment(order_id)

def handle_payment_failed(payment_intent):
    """Handle failed payment"""
    logger.error(f"Payment failed: {payment_intent['id']}")
    
    # Update database
    order_id = payment_intent["metadata"]["order_id"]
    # db.orders.update(order_id, status="payment_failed")
    
    # Notify customer
    # send_failure_email(order_id)

Recurring Billing

def create_subscription(customer_id, price_id, payment_method_id):
    """Create recurring subscription"""
    
    # Create or get customer
    customer = stripe.Customer.create(
        id=customer_id,
        payment_method=payment_method_id,
        invoice_settings={"default_payment_method": payment_method_id}
    )
    
    # Create subscription
    subscription = stripe.Subscription.create(
        customer=customer.id,
        items=[{"price": price_id}],
        payment_settings={
            "save_default_payment_method": "on_subscription"
        }
    )
    
    logger.info(f"Subscription created: {subscription.id}")
    return subscription

def cancel_subscription(subscription_id):
    """Cancel subscription"""
    subscription = stripe.Subscription.delete(subscription_id)
    logger.info(f"Subscription cancelled: {subscription_id}")
    return subscription

def update_subscription(subscription_id, price_id):
    """Update subscription price"""
    subscription = stripe.Subscription.modify(
        subscription_id,
        items=[{"price": price_id}]
    )
    logger.info(f"Subscription updated: {subscription_id}")
    return subscription

Square Integration

Setup and Configuration

from squareup.client import Client
from squareup.api.payments_api import PaymentsApi
from squareup.models import Money, CreatePaymentRequest
import uuid

# Initialize Square client
client = Client(
    access_token="YOUR_ACCESS_TOKEN",
    environment="production"
)

payments_api = client.payments

Create Payment

def create_square_payment(amount_cents, source_id, idempotency_key=None):
    """Create Square payment"""
    
    if not idempotency_key:
        idempotency_key = str(uuid.uuid4())
    
    payment_body = CreatePaymentRequest(
        source_id=source_id,
        amount_money=Money(
            amount=amount_cents,
            currency="USD"
        ),
        idempotency_key=idempotency_key,
        autocomplete=True
    )
    
    try:
        result = payments_api.create_payment(payment_body)
        
        if result.is_success():
            payment = result.result
            print(f"Payment created: {payment.id}")
            return payment
        
        elif result.is_client_error():
            print(f"Client error: {result.errors}")
            return None
        
        elif result.is_server_error():
            print(f"Server error: {result.errors}")
            return None
    
    except Exception as e:
        print(f"Error: {str(e)}")
        return None

def refund_square_payment(payment_id, amount_cents=None):
    """Refund Square payment"""
    
    refund_body = {
        "payment_id": payment_id,
        "amount_money": Money(
            amount=amount_cents,
            currency="USD"
        ) if amount_cents else None
    }
    
    try:
        result = payments_api.refund_payment(refund_body)
        
        if result.is_success():
            print(f"Refund created: {result.result.id}")
            return result.result
        
        else:
            print(f"Error: {result.errors}")
            return None
    
    except Exception as e:
        print(f"Error: {str(e)}")
        return None

PayPal Integration

Setup and Configuration

from paypalrestsdk import Api, Payment
import paypalrestsdk

# Configure PayPal
paypalrestsdk.configure({
    "mode": "live",  # sandbox or live
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
})

Create Payment

def create_paypal_payment(amount, description, return_url, cancel_url):
    """Create PayPal payment"""
    
    payment = Payment({
        "intent": "sale",
        "payer": {
            "payment_method": "paypal"
        },
        "redirect_urls": {
            "return_url": return_url,
            "cancel_url": cancel_url
        },
        "transactions": [{
            "amount": {
                "total": str(amount),
                "currency": "USD",
                "details": {
                    "subtotal": str(amount)
                }
            },
            "description": description
        }]
    })
    
    if payment.create():
        print(f"Payment created: {payment.id}")
        
        # Get approval URL
        for link in payment.links:
            if link.rel == "approval_url":
                return {
                    "payment_id": payment.id,
                    "approval_url": link.href
                }
    
    else:
        print(f"Error: {payment.error}")
        return None

def execute_paypal_payment(payment_id, payer_id):
    """Execute PayPal payment after approval"""
    
    payment = Payment.find(payment_id)
    
    if payment.execute({"payer_id": payer_id}):
        print(f"Payment executed: {payment.id}")
        return payment
    
    else:
        print(f"Error: {payment.error}")
        return None

Error Handling & Retry Logic

import time
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 process_payment_with_retry(payment_data):
    """Process payment with automatic retry"""
    
    try:
        intent = stripe.PaymentIntent.create(
            amount=payment_data["amount"],
            currency=payment_data["currency"]
        )
        return intent
    
    except stripe.error.RateLimitError:
        logger.warning("Rate limit, retrying...")
        raise
    
    except stripe.error.APIConnectionError:
        logger.warning("Connection error, retrying...")
        raise
    
    except stripe.error.CardError as e:
        logger.error(f"Card error: {e.user_message}")
        # Don't retry card errors
        raise

# Idempotency for safety
def safe_payment_creation(payment_data, idempotency_key):
    """Create payment safely with idempotency"""
    
    # Check if payment already exists
    existing = check_payment_exists(idempotency_key)
    if existing:
        logger.info(f"Payment already exists: {existing.id}")
        return existing
    
    # Create new payment
    intent = stripe.PaymentIntent.create(
        amount=payment_data["amount"],
        currency=payment_data["currency"],
        idempotency_key=idempotency_key
    )
    
    return intent

PCI Compliance

Best Practices

# 1. Never store raw card data
# โŒ WRONG
def bad_payment_storage(card_number, cvv):
    db.save({"card": card_number, "cvv": cvv})

# โœ… CORRECT
def good_payment_storage(payment_method_id):
    # Store only the token
    db.save({"payment_method_id": payment_method_id})

# 2. Use HTTPS everywhere
# 3. Implement strong authentication
# 4. Encrypt sensitive data
# 5. Use tokenization

# 6. Implement proper logging
def log_payment_event(event_type, payment_id, status):
    """Log payment events without sensitive data"""
    logger.info(f"Event: {event_type}, Payment: {payment_id}, Status: {status}")
    # Never log card numbers, CVV, or other sensitive data

# 7. Regular security audits
# 8. Keep dependencies updated

Comparison: Stripe vs Square vs PayPal

Feature Stripe Square PayPal
Transaction Fee 2.9% + $0.30 2.6% + $0.10 2.9% + $0.30
Setup Time 1-2 days 1-2 days 1-2 days
API Quality Excellent Good Good
Documentation Excellent Good Good
Webhook Support Yes Yes Yes
Recurring Billing Yes Yes Yes
International 135+ countries Limited 200+ countries
Dispute Handling Excellent Good Good

Monitoring & Analytics

class PaymentAnalytics:
    def __init__(self):
        self.transactions = []
    
    def log_transaction(self, payment_id, amount, status, gateway):
        """Log transaction"""
        self.transactions.append({
            "payment_id": payment_id,
            "amount": amount,
            "status": status,
            "gateway": gateway,
            "timestamp": datetime.now()
        })
    
    def get_daily_revenue(self, date):
        """Calculate daily revenue"""
        daily = [
            t["amount"] for t in self.transactions
            if t["timestamp"].date() == date and t["status"] == "succeeded"
        ]
        return sum(daily)
    
    def get_failure_rate(self):
        """Calculate payment failure rate"""
        if not self.transactions:
            return 0
        
        failed = len([t for t in self.transactions if t["status"] == "failed"])
        return (failed / len(self.transactions)) * 100
    
    def get_gateway_comparison(self):
        """Compare gateway performance"""
        gateways = {}
        for t in self.transactions:
            if t["gateway"] not in gateways:
                gateways[t["gateway"]] = {"total": 0, "succeeded": 0}
            
            gateways[t["gateway"]]["total"] += 1
            if t["status"] == "succeeded":
                gateways[t["gateway"]]["succeeded"] += 1
        
        return gateways

External Resources

Official Documentation

Security & Compliance

Learning Resources


Conclusion

Building production payment systems requires careful integration with payment gateways, proper error handling, and strict compliance with PCI-DSS standards. Choose the right gateway for your use case, implement robust error handling and retry logic, and monitor transactions continuously.

Start with Stripe for best-in-class API and documentation, consider Square for lower fees, and use PayPal for international reach. Always prioritize security and compliance over convenience.

Comments