Skip to main content

Overview

The API maintains two completely separate environments: test mode and live mode. Every API object includes a livemode boolean field indicating which environment it belongs to.
{
  "id": "pi_1abc123def456",
  "object": "payment_intent",
  "amount": 2000,
  "livemode": false,
  ...
}
  • livemode: true — the object was created with a live key and represents real money.
  • livemode: false — the object was created with a test key and is for development only.

Key prefixes and environments

Key prefixEnvironmentAccesses
sk_test_TestTest data only
pk_test_TestTest data only
sk_live_LiveLive data only
pk_live_LiveLive data only

Complete data isolation

Test and live data are isolated at the database level. This means:
  • A sk_test_ key cannot read, update, or delete objects created with a sk_live_ key.
  • A sk_live_ key cannot read, update, or delete objects created with a sk_test_ key.
  • Customer IDs, PaymentIntent IDs, and all other identifiers are unique per environment.
# This PaymentIntent was created in test mode
curl https://api.leanrails.com/v1/payment_intents/pi_test_abc123 \
  -u "sk_test_your_key:"
# => 200 OK

# Attempting to access it with a live key returns 404
curl https://api.leanrails.com/v1/payment_intents/pi_test_abc123 \
  -u "sk_live_your_key:"
# => 404 Not Found
{
  "error": {
    "type": "invalid_request_error",
    "code": "resource_missing",
    "message": "No PaymentIntent found with ID 'pi_test_abc123'.",
    "param": "id",
    "doc_url": "https://docs.leanrails.com/errors/resource_missing"
  }
}

Checking livemode in code

Always verify the livemode field when processing webhooks or debugging issues:
const fetch = require("node-fetch");

const API_KEY = process.env.API_KEY;
const credentials = Buffer.from(`${API_KEY}:`).toString("base64");

const response = await fetch(
  "https://api.leanrails.com/v1/payment_intents/pi_1abc123def456",
  {
    headers: { Authorization: `Basic ${credentials}` },
  }
);

const paymentIntent = await response.json();

if (paymentIntent.livemode) {
  console.log("This is a real payment - process fulfillment");
} else {
  console.log("This is a test payment - skip fulfillment");
}

Best practices

  • Use separate API keys per environment. Store sk_test_ in your development/staging config and sk_live_ in production.
  • Never use live keys in development. Test keys give you the same API behavior without moving real money.
  • Check livemode in webhook handlers. If you use the same webhook endpoint for both environments, always verify the livemode field before taking real-world actions.
  • Use test card numbers. In test mode, use payment methods like pm_card_visa and pm_card_declined to simulate different outcomes.

Test mode capabilities

Test mode supports all the same API endpoints and behaviors as live mode, including:
  • Creating and confirming PaymentIntents
  • Simulating successful and failed payments
  • Refunds and cancellations
  • Pagination, metadata, and expand
  • Webhooks (delivered to your configured test endpoint)