Skip to main content

Mock Loki is now Stage! Read the announcement

Back to blog
Guides9 min read

A Complete Guide to Testing Payment Integrations

Payment integrations are notoriously difficult to test. Here's a comprehensive guide to testing Stripe, PayPal, and other payment providers without touching real money.

JP
James Park
VP of Product
October 28, 2024

Payment integrations are among the hardest things to test in software. Real money is on the line, edge cases abound, and third-party sandboxes are notoriously unreliable.

In this guide, we'll cover how to comprehensively test payment integrations using Stage.

The Testing Pyramid for Payments

Level 1: Happy Path Test that payments work when everything goes right: - Card is valid - Sufficient funds - No fraud flags - Network is fast

Level 2: Validation Errors Test input validation: - Invalid card numbers - Expired cards - Missing fields - Invalid amounts

Level 3: Processing Failures Test what happens when the payment processor fails: - Card declined - Insufficient funds - Fraud detected - Account frozen

Level 4: Network Failures Test network-level failures: - Timeouts - Connection refused - Partial responses - Rate limiting

Level 5: Edge Cases Test the weird stuff: - Currency conversion failures - Webhook delivery failures - Duplicate charges - Refund edge cases

Setting Up Stage for Payment Testing

1. Configure Your Dependencies

json
{
  "name": "stripe-api",
  "slug": "stripe",
  "baseUrl": "https://api.stripe.com",
  "auth": {
    "type": "bearer",
    "token": "sk_live_xxx"
  }
}

2. Create Test Scenarios

Scenario: Card Declined

json
{
  "name": "card-declined",
  "rules": [{
    "match": { "path": "/v1/charges", "method": "POST" },
    "action": {
      "type": "override",
      "status": 402,
      "body": {
        "error": {
          "type": "card_error",
          "code": "card_declined",
          "message": "Your card was declined."
        }
      }
    }
  }]
}

Scenario: Timeout

json
{
  "name": "payment-timeout",
  "rules": [{
    "match": { "path": "/v1/charges", "method": "POST" },
    "action": {
      "type": "latency",
      "ms": 35000
    }
  }]
}

3. Run Your Tests

typescript
describe('Payment Processing', () => {
  it('handles card declined gracefully', async () => {
    // Activate the scenario
    await stage.activateScenario('card-declined');

    const result = await processPayment({
      amount: 1000,
      currency: 'usd',
      source: 'tok_visa'
    });

    expect(result.status).toBe('failed');
    expect(result.error.code).toBe('card_declined');
    expect(ui.getByText('Payment failed')).toBeVisible();
  });
});

Common Patterns

Testing Idempotency Payment systems must handle duplicate requests. Test this by: 1. Send a payment request 2. Stage returns success 3. Send the same request again 4. Verify your system doesn't double-charge

Testing Webhook Failures 1. Process a payment 2. Stage simulates a webhook delivery failure 3. Verify your system retries or handles gracefully

Testing Partial Refunds 1. Create a charge 2. Request a partial refund 3. Stage returns success 4. Verify your system tracks the partial refund correctly

Best Practices

  1. Never test with real payment credentials: Always use Stage or test mode keys
  2. Test every error code: Payment processors have dozens of error codes—test them all
  3. Test timeout handling: What happens if the payment processor is slow?
  4. Test reconciliation: Does your system match payment records correctly?
  5. Test webhooks: Don't assume webhooks always arrive

Conclusion

Payment testing doesn't have to be painful. With Stage, you can simulate any payment scenario without touching real money or relying on flaky sandboxes.

Get started with Stage and ship payment features with confidence.

Share this article

Ready to transform your API testing?

Join thousands of developers who ship with confidence using Stage.