Simple Payment API Testing

Basic payment API testing with charge, refund, and validation scenarios

LoadForge can record your browser, graphically build tests, scan your site with a wizard and more. Sign up now to run your first test.

Sign up now


This guide shows how to test payment APIs with common operations like charging, refunding, and validating payment methods.

Use Cases

  • Test payment processing under load
  • Validate payment flow success and failure scenarios
  • Test refund processing
  • Check payment method validation

Simple Implementation

from locust import task, HttpUser
import random
import json

class PaymentTestUser(HttpUser):
    def on_start(self):
        # Payment test data
        self.test_cards = [
            {"number": "4242424242424242", "exp_month": 12, "exp_year": 2025, "cvc": "123"},
            {"number": "4000000000000002", "exp_month": 11, "exp_year": 2025, "cvc": "456"},  # Declined card
            {"number": "4000000000009995", "exp_month": 10, "exp_year": 2025, "cvc": "789"}   # Insufficient funds
        ]
        
        self.amounts = [999, 1500, 2999, 4999, 9999]  # Amounts in cents
        self.currencies = ["usd", "eur", "gbp"]
        
        # API endpoints
        self.payment_endpoints = {
            "charge": "/api/payments/charge",
            "refund": "/api/payments/refund",
            "validate": "/api/payments/validate"
        }

    @task(4)
    def test_payment_charge(self):
        """Test payment charge with valid card"""
        card = self.test_cards[0]  # Use valid card
        amount = random.choice(self.amounts)
        currency = random.choice(self.currencies)
        
        charge_data = {
            "amount": amount,
            "currency": currency,
            "card": card,
            "description": f"Test charge ${amount/100}"
        }
        
        with self.client.post(
            self.payment_endpoints["charge"],
            json=charge_data,
            name="Payment Charge"
        ) as response:
            if response.status_code == 200:
                try:
                    result = response.json()
                    if result.get("status") == "succeeded":
                        print(f"Payment successful: ${amount/100} {currency}")
                    else:
                        print(f"Payment status: {result.get('status')}")
                except json.JSONDecodeError:
                    response.failure("Invalid JSON response")
            elif response.status_code == 400:
                print("Payment failed: Bad request")
            elif response.status_code == 402:
                print("Payment failed: Card declined")
            else:
                response.failure(f"Payment charge failed: {response.status_code}")

    @task(2)
    def test_payment_declined(self):
        """Test payment with declined card"""
        declined_card = self.test_cards[1]  # Declined card
        amount = random.choice(self.amounts)
        
        charge_data = {
            "amount": amount,
            "currency": "usd",
            "card": declined_card,
            "description": "Test declined payment"
        }
        
        with self.client.post(
            self.payment_endpoints["charge"],
            json=charge_data,
            name="Payment Declined"
        ) as response:
            if response.status_code == 402:
                print("Payment correctly declined")
            elif response.status_code == 200:
                try:
                    result = response.json()
                    if result.get("status") == "failed":
                        print("Payment failed as expected")
                    else:
                        response.failure("Declined card was accepted")
                except json.JSONDecodeError:
                    response.failure("Invalid JSON response")
            else:
                print(f"Declined payment returned: {response.status_code}")

    @task(2)
    def test_payment_refund(self):
        """Test payment refund"""
        # First create a charge, then refund it
        card = self.test_cards[0]
        amount = random.choice(self.amounts)
        
        # Create charge
        charge_data = {
            "amount": amount,
            "currency": "usd",
            "card": card,
            "description": "Test charge for refund"
        }
        
        with self.client.post(
            self.payment_endpoints["charge"],
            json=charge_data,
            name="Charge for Refund"
        ) as charge_response:
            if charge_response.status_code == 200:
                try:
                    charge_result = charge_response.json()
                    charge_id = charge_result.get("id")
                    
                    if charge_id:
                        # Process refund
                        refund_data = {
                            "charge_id": charge_id,
                            "amount": amount,
                            "reason": "requested_by_customer"
                        }
                        
                        with self.client.post(
                            self.payment_endpoints["refund"],
                            json=refund_data,
                            name="Payment Refund"
                        ) as refund_response:
                            if refund_response.status_code == 200:
                                print(f"Refund successful: ${amount/100}")
                            else:
                                response.failure(f"Refund failed: {refund_response.status_code}")
                    else:
                        print("No charge ID for refund")
                        
                except json.JSONDecodeError:
                    print("Invalid JSON in charge response")

    @task(2)
    def test_card_validation(self):
        """Test payment method validation"""
        card = random.choice(self.test_cards)
        
        validation_data = {
            "card": card,
            "validate_only": True
        }
        
        with self.client.post(
            self.payment_endpoints["validate"],
            json=validation_data,
            name="Card Validation"
        ) as response:
            if response.status_code == 200:
                try:
                    result = response.json()
                    is_valid = result.get("valid", False)
                    card_type = result.get("card_type", "unknown")
                    
                    print(f"Card validation: {is_valid}, type: {card_type}")
                    
                except json.JSONDecodeError:
                    response.failure("Invalid JSON response")
            else:
                response.failure(f"Card validation failed: {response.status_code}")

    @task(1)
    def test_invalid_payment_data(self):
        """Test payment with invalid data"""
        invalid_scenarios = [
            {"amount": -100, "currency": "usd", "description": "Negative amount"},
            {"amount": 1000, "currency": "xxx", "description": "Invalid currency"},
            {"amount": 0, "currency": "usd", "description": "Zero amount"}
        ]
        
        scenario = random.choice(invalid_scenarios)
        scenario["card"] = self.test_cards[0]
        
        with self.client.post(
            self.payment_endpoints["charge"],
            json=scenario,
            name="Invalid Payment Data"
        ) as response:
            if response.status_code == 400:
                print(f"Invalid data correctly rejected: {scenario['description']}")
            elif response.status_code == 200:
                response.failure(f"Invalid data accepted: {scenario['description']}")
            else:
                print(f"Invalid data returned: {response.status_code}")

Setup Instructions

  1. Replace payment endpoints with your actual payment API URLs
  2. Update test card numbers to match your payment provider's test cards
  3. Adjust currency codes based on your supported currencies
  4. Configure authentication headers if required by your payment API

What This Tests

  • Payment Processing: Tests successful payment charges
  • Decline Handling: Tests proper handling of declined cards
  • Refund Processing: Tests refund functionality
  • Card Validation: Tests payment method validation
  • Error Handling: Tests invalid payment data scenarios

Best Practices

  • Use test card numbers provided by your payment processor
  • Test different currencies and amounts
  • Validate both success and failure scenarios
  • Monitor payment processing times under load
  • Always test refund functionality

Common Issues

  • Test vs Live Keys: Ensure you're using test API keys, not live ones
  • Rate Limiting: Payment APIs often have strict rate limits
  • Currency Support: Verify which currencies your API supports
  • Webhook Delays: Payment confirmations may be asynchronous

Ready to run your test?
Launch your locust test at scale.