Build Payments
That Scale Globally
The SnapPay API enables you to integrate secure, PCI DSS Level 1 compliant card payment processing into any application, in any language, anywhere in the world.
Authentication
SnapPay uses API keys to authenticate requests. You can view and manage your API keys in the SnapPay Dashboard. Every API request requires three credentials sent as JSON body parameters.
api_key required
Your secret API key. Found in your merchant dashboard under Developer → API Keys. Format: spay_live_xxxxxxxxxxxxxxxxxxxx for production, spay_test_xxxxxxxxxxxxxxxxxxxx for sandbox.
business_id required
Your unique business identifier (integer). A merchant account may have multiple business IDs for different stores, brands, or regions.
Base URL
All API endpoints are relative to the following base URLs depending on environment:
Production
Local / Development
Versioning
SnapPay API versioning is controlled via the URL prefix /v1. We guarantee backward compatibility within the same major version.
Payment Check
Verify the status of a card payment transaction by its developer-assigned transaction_id. Use this endpoint for server-to-server status polling, webhook verification fallback, or order fulfillment confirmation.
Request Body application/json
| Parameter | Type | Required | Description |
|---|---|---|---|
| api_key | string | required | Your secret SnapPay API key. |
| business_id | integer | required | Your registered business identifier. |
| transaction_id | string | required | Your application's unique transaction reference. |
Response Statuses
The status field in the response reflects the Stripe payment intent lifecycle:
success
Payment captured and confirmed. Returns full transaction details including card info, customer, amount, and date.
requires_action
3D Secure authentication is required. Redirect the customer to the authentication URL to proceed.
processing
Payment is being processed. Poll again in 2–5 seconds. This state is typically brief.
failed
Payment failed or was cancelled. The card was declined or the payment method is invalid. Prompt the customer to retry.
unknown
Unexpected internal state. Log the transaction_ref and contact SnapPay support.
Success Response (200 — succeeded)
{
"success": true,
"status": "success",
"message": "Payment confirmed",
"transaction_ref": "TXN-2026-XXXXXXXXXXXX",
"transaction_id": "your-transaction-id",
"amount": 4900,
"currency": "USD",
"card_last4": "4242",
"card_brand": "visa",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"product_name": "Pro Plan - Monthly",
"date_time": "22/03/2026 14:35:20",
"http_code": 200
}
{
"success": true,
"status": "requires_action",
"requires_action": true,
"transaction_ref": "TXN-2026-XXXXXXXXXXXX",
"transaction_id": "your-transaction-id",
"message": "Authentification 3D Secure requise",
"http_code": 200
}
{
"success": true,
"status": "processing",
"transaction_ref": "TXN-2026-XXXXXXXXXXXX",
"http_code": 202
}
{
"success": true,
"status": "failed",
"transaction_ref": "TXN-2026-XXXXXXXXXXXX",
"http_code": 400
}
{
"success": false,
"status": "error",
"message": "Validation failed",
"errors": {
"api_key": ["The api key field is required."],
"business_id": ["The business id field is required."]
}
}
Checkout SDK
The FinalPay SDK lets your customers pay via a secure hosted checkout popup — no PCI complexity on your side. Include the script, initialize with your credentials, and open the checkout in one call.
Step 1 — Include the SDK
<!-- Add before closing </body> tag --> <script src="https://api.usesnappay.com/finalpay-sdk.js"></script>
Step 2 — Initialize
Call FinalPay.initialize() once with your credentials before opening the checkout.
| Parameter | Type | Required | Description |
|---|---|---|---|
| apikey | string | required | Your SnapPay API key. Use spay_test_ prefix for sandbox, spay_live_ for production. |
| business_id | integer | required | Your registered business identifier. |
| mode | string | optional | TEST or LIVE. Defaults to LIVE. |
Step 3 — Open Checkout
Call FinalPay.openCheckout() to open the payment popup. Typically triggered on a button click.
| Parameter | Type | Required | Description |
|---|---|---|---|
| transaction_id | string | required | Your unique transaction reference. Must be unique per payment attempt. Example: 'TRX-' + Date.now() |
| amount | integer | required | Amount in the smallest currency unit (cents). Example: 8200 = $82.00 USD. |
| currency | string | required | 3-letter ISO currency code. Supported: USD, EUR, XOF, XAF, GHS, NGN, KES, ZAR. |
| description | string | optional | Product or service description shown in the checkout popup. |
| customer_name | string | optional | Customer's full name. Pre-fills the checkout form. |
| customer_phone | string | optional | Customer's phone number in E.164 format. Example: +15551234567 |
Step 4 — Handle Callbacks
Use FinalPay.onPaymentResponse() to handle the payment result and FinalPay.onError() for errors.
succeeded
Payment fully captured. data.reference, data.amount, data.currency, data.card_brand and data.card_last4 are available. → Fulfill the order.
failed / canceled
Payment was declined or cancelled. data.message contains the reason. → Prompt customer to retry with another card.
processing
Payment is being processed asynchronously. → Poll /payment/check with the transaction_id until status resolves.
requires_action
3D Secure authentication required. The SDK handles the redirect automatically. No action needed — wait for the final ACCEPTED or REFUSED callback.
Complete Example
<!DOCTYPE html> <html> <head> <script src="https://api.usesnappay.com/finalpay-sdk.js"></script> </head> <body> <button onclick="checkout()">Pay $82.00</button> <script> function checkout() { // 1. Initialize with your credentials FinalPay.initialize({ apikey: 'spay_live_xxxxxxxxxxxxxxxxxxxx', business_id: 451278963, mode: 'LIVE' // or 'TEST' for sandbox }); // 2. Open the checkout popup FinalPay.openCheckout({ transaction_id: 'TRX-' + Date.now() + '-' + Math.floor(Math.random() * 100000), amount: 8200, // 82.00 USD in cents currency: 'USD', description: 'Premium Training', customer_name: 'Jean Dupont', customer_phone:'+15551234567', }); // 3. Handle payment result FinalPay.onPaymentResponse(function(data) { if (data.status === 'ACCEPTED') { console.log('✓ Payment confirmed'); console.log('Ref:', data.reference); console.log('Amount:', data.amount, data.currency); if (data.payment_method === 'card') { console.log('Card:', data.card_brand, '****' + data.card_last4); } // Redirect to success page window.location.href = '/payment/success?ref=' + data.reference; } if (data.status === 'REFUSED') { console.error('✗ Payment refused:', data.message); alert('Payment failed: ' + data.message); } if (data.status === 'PENDING') { // Poll /payment/check until resolved pollPaymentStatus(data.transaction_id); } }); // 4. Handle errors (network, config, etc.) FinalPay.onError(function(err) { console.error('SDK Error:', err.message); }); } // Poll helper for PENDING status async function pollPaymentStatus(txId, retries = 5) { for (let i = 0; i < retries; i++) { await new Promise(r => setTimeout(r, 3000)); const res = await fetch('/api/payment/check', { /* your backend proxy */ }); const data = await res.json(); if (data.status !== 'processing') { console.log('Final status:', data.status); break; } } } </script> </body> </html>
import { useEffect } from 'react'; // Load SDK once in index.html or via useEffect export default function CheckoutButton({ product, customer }) { useEffect(() => { const script = document.createElement('script'); script.src = 'https://api.usesnappay.com/finalpay-sdk.js'; document.body.appendChild(script); return () => document.body.removeChild(script); }, []); const handleCheckout = () => { window.FinalPay.initialize({ apikey: process.env.REACT_APP_SNAPPAY_KEY, business_id: process.env.REACT_APP_BUSINESS_ID, mode: 'LIVE', }); window.FinalPay.openCheckout({ transaction_id: `TRX-${Date.now()}`, amount: product.priceInCents, currency: product.currency, description: product.name, customer_name: customer.name, }); window.FinalPay.onPaymentResponse((data) => { if (data.status === 'ACCEPTED') { window.location.href = `/success?ref=${data.reference}`; } else if (data.status === 'REFUSED') { alert(data.message); } }); window.FinalPay.onError((err) => console.error(err)); }; return ( <button onClick={handleCheckout}> Pay {product.currency} {(product.priceInCents / 100).toFixed(2)} </button> ); }
<!-- Vue 3 Composition API --> <template> <button @click="checkout">Pay {{ (amount / 100).toFixed(2) }} {{ currency }}</button> </template> <script setup> import { onMounted } from 'vue' const props = defineProps(['amount', 'currency', 'description']) onMounted(() => { const s = document.createElement('script') s.src = 'https://api.usesnappay.com/finalpay-sdk.js' document.body.appendChild(s) }) function checkout() { window.FinalPay.initialize({ apikey: import.meta.env.VITE_SNAPPAY_KEY, business_id: import.meta.env.VITE_BUSINESS_ID, mode: 'LIVE', }) window.FinalPay.openCheckout({ transaction_id: `TRX-${Date.now()}`, amount: props.amount, currency: props.currency, description: props.description, }) window.FinalPay.onPaymentResponse((data) => { if (data.status === 'ACCEPTED') window.location.href = `/success?ref=${data.reference}` else if (data.status === 'REFUSED') alert(data.message) }) window.FinalPay.onError((e) => console.error(e)) } </script>
Callback Data Fields
The data object received in onPaymentResponse() contains the following fields:
ACCEPTED, REFUSED, or PENDING
CARD-69C07...). Use for support inquiries.
openCheckout().
ACCEPTED.
ACCEPTED.
card.
Visa, Mastercard. Available when payment_method === 'card'.
payment_method === 'card'.
REFUSED.
Code Examples
Complete, production-ready code examples for the POST /payment/check endpoint in 10+ languages.
<?php // Install: composer require guzzlehttp/guzzle use GuzzleHttp\Client; $client = new Client(); try { $response = $client->post('https://api.usesnappay.com/payment/check', [ 'headers' => [ 'Content-Type' => 'application/json', 'Accept' => 'application/json', ], 'json' => [ 'api_key' => getenv('SNAPPAY_API_KEY'), 'business_id' => 42, 'transaction_id' => 'order_ABC123', ], ]); $data = json_decode($response->getBody(), true); switch ($data['status']) { case 'success': echo " Payment confirmed: " . $data['transaction_ref']; break; case 'requires_action': echo "3DS required for: " . $data['transaction_id']; break; case 'processing': echo " Still processing, retry in 3s"; break; case 'failed': echo "Payment failed"; break; } } catch (\Exception $e) { error_log('SnapPay error: ' . $e->getMessage()); }
// Install: npm install axios const axios = require('axios'); async function checkPayment(transactionId) { try { const { data } = await axios.post( 'https://api.usesnappay.com/payment/check', { api_key: process.env.SNAPPAY_API_KEY, business_id: 42, transaction_id: transactionId, }, { headers: { 'Content-Type': 'application/json' } } ); switch (data.status) { case 'success': console.log(` Paid by ${data.customer_name} — ${data.amount} ${data.currency}`); break; case 'requires_action': console.log('3D Secure required'); break; case 'processing': console.log(' Processing...'); setTimeout(() => checkPayment(transactionId), 3000); break; case 'failed': console.log('Payment failed'); break; } } catch (err) { console.error('SnapPay error:', err.response?.data ?? err.message); } } checkPayment('order_ABC123');
# Install: pip install requests import os import requests BASE_URL = "https://api.usesnappay.com" def check_payment(transaction_id: str) -> dict: payload = { "api_key": os.getenv("SNAPPAY_API_KEY"), "business_id": 42, "transaction_id": transaction_id, } response = requests.post( f"{BASE_URL}/payment/check", json=payload, timeout=10, ) response.raise_for_status() data = response.json() status = data.get("status") if status == "success": print(f" Confirmed — {data['customer_name']} paid {data['amount']} {data['currency']}") elif status == "requires_action": print("3DS authentication required") elif status == "processing": print(" Still processing...") elif status == "failed": print("Payment failed or cancelled") else: print(f"? Unknown status: {status}") return data if __name__ == "__main__": check_payment("order_ABC123")
// Browser — Vanilla JavaScript (fetch API) // Never expose your api_key in frontend code! async function checkPayment(transactionId) { const res = await fetch('https://api.usesnappay.com/payment/check', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ api_key: 'spay_test_your_key', business_id: 42, transaction_id: transactionId, }), }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); const handlers = { success: () => console.log('Payment confirmed ', data), requires_action: () => console.log('3DS required ', data), processing: () => setTimeout(() => checkPayment(transactionId), 3000), failed: () => console.log('Payment failed '), }; (handlers[data.status] ?? (() => console.warn('Unknown status', data)))(); } checkPayment('order_ABC123');
// Java 11+ — HttpClient (no external dependency) import java.net.URI; import java.net.http.*; import java.net.http.HttpResponse.BodyHandlers; public class SnapPayClient { private static final String BASE_URL = "https://api.usesnappay.com"; private static final HttpClient client = HttpClient.newHttpClient(); public static String checkPayment(String transactionId) throws Exception { String body = String.format(""" {"api_key":"%s","business_id":42,"transaction_id":"%s"} """, System.getenv("SNAPPAY_API_KEY"), transactionId); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(BASE_URL + "/payment/check")) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); System.out.println(response.body()); return response.body(); } public static void main(String[] args) throws Exception { checkPayment("order_ABC123"); } }
// Kotlin — OkHttp | build.gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import okhttp3.* import okhttp3.MediaType.Companion.toMediaType import okhttp3.RequestBody.Companion.toRequestBody import org.json.JSONObject val client = OkHttpClient() val JSON_MT = "application/json; charset=utf-8".toMediaType() fun checkPayment(transactionId: String): String { val payload = JSONObject().apply { put("api_key", System.getenv("SNAPPAY_API_KEY")) put("business_id", 42) put("transaction_id", transactionId) }.toString() val request = Request.Builder() .url("https://api.usesnappay.com/payment/check") .post(payload.toRequestBody(JSON_MT)) .addHeader("Accept", "application/json") .build() client.newCall(request).execute().use { response -> val body = response.body?.string() ?: "" when (JSONObject(body).getString("status")) { "success" -> println(" Payment confirmed") "requires_action" -> println("3DS required") "processing" -> println(" Processing...") "failed" -> println("Failed") } return body } } fun main() = checkPayment("order_ABC123")
// C# / .NET 6+ — System.Net.Http (built-in) using System.Net.Http.Json; using System.Text.Json; var http = new HttpClient { BaseAddress = new Uri("https://api.usesnappay.com") }; async Task CheckPaymentAsync(string transactionId) { var payload = new { api_key = Environment.GetEnvironmentVariable("SNAPPAY_API_KEY"), business_id = 42, transaction_id = transactionId, }; var response = await http.PostAsJsonAsync("/payment/check", payload); response.EnsureSuccessStatusCode(); var json = await response.Content.ReadFromJsonAsync<JsonDocument>(); var status = json!.RootElement.GetProperty("status").GetString(); switch (status) { case "success": Console.WriteLine(" Payment confirmed"); break; case "requires_action": Console.WriteLine("3DS required"); break; case "processing": Console.WriteLine(" Processing..."); break; case "failed": Console.WriteLine("Payment failed"); break; } } await CheckPaymentAsync("order_ABC123");
// Go — standard library only package main import ( "bytes"; "encoding/json"; "fmt"; "net/http"; "os" ) type CheckRequest struct { APIKey string `json:"api_key"`; BusinessID int `json:"business_id"`; TransactionID string `json:"transaction_id"` } type CheckResponse struct { Status string `json:"status"`; TxRef string `json:"transaction_ref"`; Amount int `json:"amount"` } func checkPayment(txID string) (*CheckResponse, error) { b, _ := json.Marshal(CheckRequest{os.Getenv("SNAPPAY_API_KEY"), 42, txID}) resp, err := http.Post("https://api.usesnappay.com/payment/check", "application/json", bytes.NewBuffer(b)) if err != nil { return nil, err } defer resp.Body.Close() var r CheckResponse json.NewDecoder(resp.Body).Decode(&r) return &r, nil } func main() { r, _ := checkPayment("order_ABC123") fmt.Printf("Status: %s | Ref: %s\n", r.Status, r.TxRef) }
# Ruby — net/http (standard library) require 'net/http' require 'json' require 'uri' def check_payment(transaction_id) uri = URI.parse('https://api.usesnappay.com/payment/check') http = Net::HTTP.new(uri.host, uri.port) req = Net::HTTP::Post.new(uri.request_uri) req['Content-Type'] = 'application/json' req.body = { api_key: ENV['SNAPPAY_API_KEY'], business_id: 42, transaction_id: transaction_id }.to_json data = JSON.parse(http.request(req).body) case data['status'] when 'success' then puts " Paid by #{data['customer_name']}" when 'requires_action' then puts '3DS required' when 'processing' then puts ' Processing...' when 'failed' then puts 'Payment failed' end end check_payment('order_ABC123')
// Swift 5.5+ — URLSession + async/await import Foundation struct PayCheckRequest: Codable { let apiKey: String; let businessId: Int; let transactionId: String enum CodingKeys: String, CodingKey { case apiKey = "api_key"; case businessId = "business_id"; case transactionId = "transaction_id" } } func checkPayment(transactionId: String) async throws { var req = URLRequest(url: URL(string: "https://api.usesnappay.com/payment/check")!) req.httpMethod = "POST" req.setValue("application/json", forHTTPHeaderField: "Content-Type") req.httpBody = try JSONEncoder().encode(PayCheckRequest( apiKey: ProcessInfo.processInfo.environment["SNAPPAY_API_KEY"] ?? "", businessId: 42, transactionId: transactionId )) let (data, _) = try await URLSession.shared.data(for: req) let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] switch json?["status"] as? String { case "success": print(" Payment confirmed") case "requires_action": print("3DS required") case "processing": print(" Processing...") case "failed": print("Payment failed") default: print("? Unknown status") } }
// Dart / Flutter — http package | pubspec.yaml: http: ^1.2.0 import 'dart:convert'; import 'package:http/http.dart' as http; Future<void> checkPayment(String transactionId) async { final response = await http.post( Uri.parse('https://api.usesnappay.com/payment/check'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'api_key': const String.fromEnvironment('SNAPPAY_API_KEY'), 'business_id': 42, 'transaction_id': transactionId, }), ); if (response.statusCode >= 400) throw Exception('HTTP ${response.statusCode}'); switch (jsonDecode(response.body)['status']) { case 'success': print(' Confirmed'); break; case 'requires_action': print('3DS required'); break; case 'processing': print(' Processing...'); break; case 'failed': print('Payment failed'); break; } } void main() => checkPayment('order_ABC123');
# cURL — Quick testing curl -X POST "https://api.usesnappay.com/payment/check" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "api_key": "spay_test_your_api_key_here", "business_id": 42, "transaction_id": "order_ABC123" }' # Pretty-print with jq curl -s -X POST "https://api.usesnappay.com/payment/check" \ -H "Content-Type: application/json" \ -d '{"api_key":"spay_test_your_api_key_here","business_id":42,"transaction_id":"order_ABC123"}' \ | jq .
Error Codes
All error responses follow a consistent JSON structure. The success field will be false, and message will describe the issue.
errors object in the response for field-level details. Ensure api_key (string), business_id (integer), and transaction_id (string) are all provided.
api_key does not match any registered merchant. Verify you are using the correct key from your dashboard and the right environment (test vs. live).
business_id is not associated with the provided api_key. Each API key belongs to a merchant — only business IDs owned by that merchant are valid.
transaction_id was found for this business. Confirm the transaction was created via SnapPay and that you are querying the correct environment (sandbox vs. production).
"failed". Prompt the user to try a different payment method and re-initiate the checkout flow.
transaction_ref from the response and contact SnapPay support at support@snappay.io with the reference.
X-RateLimit-Reset header for when your quota resets. The default limit is 300 requests/minute per API key.
Webhooks
Instead of polling /payment/check, configure your url_notification in your business settings to receive real-time POST notifications automatically on every payment status change.
Events
status = success
Payment fully captured. Safe to fulfill the order.
status = failed
Payment declined or cancelled by the customer.
status = requires_action
3D Secure challenge required. Redirect the customer.
payout.status = paid
Payout successfully sent to your bank account.
payout.status = failed
Payout failed. Contact support with the transaction ref.
payout.status = canceled
Payout was cancelled before processing.
Request Headers
sha256=HMAC_SHA256(payload, api_key) — verify this before processing.
payment.success
Payload Example
{
"event": "payment.success",
"transaction_ref": "CARD-69C0722161B3B-1774219809",
"transaction_id": "TRX-1774219787662-73987",
"status": "success",
"amount": 82.00,
"currency": "USD",
"customer_name": "Jean Dupont",
"customer_email": "jean@example.com",
"card_brand": "visa",
"card_last4": "4242",
"product_name": "Premium Training",
"business_id": "451278963",
"timestamp": "2026-03-22T22:50:13+00:00"
}
<?php // Récupérer le payload brut et la signature $payload = file_get_contents('php://input'); $signature = $_SERVER['HTTP_X_SNAPPAY_SIGNATURE'] ?? ''; $apiKey = 'your_api_key'; // votre clé API SnapPay // Calculer la signature attendue $expected = 'sha256=' . hash_hmac('sha256', $payload, $apiKey); // Comparer de manière sécurisée (timing-safe) if (!hash_equals($expected, $signature)) { http_response_code(401); exit('Invalid signature'); } // Signature valide — traiter le webhook $data = json_decode($payload, true); switch ($data['event']) { case 'payment.success': // Valider la commande break; case 'payment.failed': // Notifier le client break; case 'payment.requires_action': // Rediriger vers 3DS break; } http_response_code(200); echo json_encode(['received' => true]);
const crypto = require('crypto'); function verifyWebhook(rawBody, signature, apiKey) { const expected = 'sha256=' + crypto .createHmac('sha256', apiKey) .update(rawBody) .digest('hex'); // Timing-safe comparison return crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(signature) ); } // Express.js example app.post('/webhook/snappay', express.raw({type: 'application/json'}), (req, res) => { const signature = req.headers['x-snappay-signature']; const apiKey = 'your_api_key'; if (!verifyWebhook(req.body, signature, apiKey)) { return res.status(401).json({error: 'Invalid signature'}); } const data = JSON.parse(req.body); console.log(`Event: ${data.event} — Ref: ${data.transaction_ref}`); res.status(200).json({received: true}); });
import hmac import hashlib import json def verify_webhook(raw_body: bytes, signature: str, api_key: str) -> bool: expected = 'sha256=' + hmac.new( api_key.encode(), raw_body, hashlib.sha256 ).hexdigest() # Timing-safe comparison return hmac.compare_digest(expected, signature) # Flask example from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/webhook/snappay', methods=['POST']) def handle_webhook(): signature = request.headers.get('X-SnapPay-Signature', '') api_key = 'your_api_key' if not verify_webhook(request.data, signature, api_key): return jsonify({'error': 'Invalid signature'}), 401 data = request.get_json() print(f"Event: {data['event']} — Ref: {data['transaction_ref']}") return jsonify({'received': True}), 200
Retry Policy
failed in webhook logs. Contact support with your transaction_ref.
Rate Limiting
The SnapPay API enforces rate limits to protect platform stability. Rate limit information is returned in the response headers:
Changelog
Test Cards
Use the following card numbers in the sandbox environment (any future expiry date, any CVC). No real charges are made. All test transactions are isolated from production.
| Brand | Card Number | CVC | Expiry |
|---|---|---|---|
| Visa | 4242 4242 4242 4242 | Any 3 digits | Any future date |
| Visa (debit) | 4000 0566 5566 5556 | Any 3 digits | Any future date |
| Mastercard | 5555 5555 5555 4444 | Any 3 digits | Any future date |
| Mastercard (series 2) | 2223 0031 2200 3222 | Any 3 digits | Any future date |
| Mastercard (debit) | 5200 8282 8282 8210 | Any 3 digits | Any future date |
| Mastercard (prepaid) | 5105 1051 0510 5100 | Any 3 digits | Any future date |
| Amex | 3782 822463 10005 | Any 4 digits | Any future date |
| Amex | 3714 496353 98431 | Any 4 digits | Any future date |
| Discover | 6011 1111 1111 1117 | Any 3 digits | Any future date |
| Discover | 6011 0009 9013 9424 | Any 3 digits | Any future date |
| Discover (debit) | 6011 9811 1111 1113 | Any 3 digits | Any future date |
| Diners Club | 3056 9300 0902 0004 | Any 3 digits | Any future date |
| Diners Club (14-digit) | 3622 720627 1667 | Any 3 digits | Any future date |
| JCB | 3566 0020 2036 0505 | Any 3 digits | Any future date |
| UnionPay | 6200 0000 0000 0005 | Any 3 digits | Any future date |
| UnionPay (debit) | 6200 0000 0000 0047 | Any 3 digits | Any future date |
| UnionPay (19-digit) | 6205 5000 0000 0000 004 | Any 3 digits | Any future date |
Click any card number to copy it.
These cards trigger 3D Secure authentication flows. Use them to test your requires_action handling.
| Brand | Card Number | Behavior |
|---|---|---|
| Visa | 4000 0027 6000 3184 | Always requires 3DS authentication |
| Visa | 4000 0025 0000 3155 | 3DS required, authentication succeeds |
| Mastercard | 5200 8282 8282 8210 | 3DS optional, card enrolled |
| Visa | 4000 0082 6000 3178 | 3DS required, authentication fails |
Use these to simulate declined payments and test your error handling flows.
| Brand | Card Number | Decline Reason |
|---|---|---|
| Visa | 4000 0000 0000 0002 | generic_decline |
| Visa | 4000 0000 0000 9995 | insufficient_funds |
| Visa | 4000 0000 0000 9987 | lost_card |
| Visa | 4000 0000 0000 9979 | stolen_card |
| Visa | 4000 0000 0000 0069 | expired_card |
| Visa | 4000 0000 0000 0127 | incorrect_cvc |
| Visa | 4000 0000 0000 0119 | processing_error |
| Visa | 4242 4242 4242 4241 | incorrect_number |
Co-branded cards are issued under two networks simultaneously. The following simulate successful payments.
| Brand / Co-brand | Card Number | CVC |
|---|---|---|
| Cartes Bancaires / Visa | 4000 0025 0000 1001 | Any 3 digits |
| Cartes Bancaires / Mastercard | 5555 5525 0000 1001 | Any 3 digits |
| eftpos Australia / Visa | 4000 0503 6000 0001 | Any 3 digits |
| eftpos Australia / Mastercard | 5555 0503 6000 0080 | Any 3 digits |
| BCcard / DinaCard | 6555 9000 0060 4105 | Any 3 digits |
Use these PaymentMethod IDs directly in API calls instead of raw card numbers.
| Brand | PaymentMethod ID |
|---|---|
| Visa | pm_card_visa |
| Visa (debit) | pm_card_visa_debit |
| Mastercard | pm_card_mastercard |
| Mastercard (debit) | pm_card_mastercard_debit |
| Mastercard (prepaid) | pm_card_mastercard_prepaid |
| Amex | pm_card_amex |
| Discover | pm_card_discover |
| Diners Club | pm_card_diners |
| JCB | pm_card_jcb |
| UnionPay | pm_card_unionpay |
Legacy token IDs for older integrations. Most integrations should use PaymentMethods instead.
| Brand | Token |
|---|---|
| Visa | tok_visa |
| Visa (debit) | tok_visa_debit |
| Mastercard | tok_mastercard |
| Mastercard (debit) | tok_mastercard_debit |
| Mastercard (prepaid) | tok_mastercard_prepaid |
| Amex | tok_amex |
| Discover | tok_discover |
| Diners Club | tok_diners |
| JCB | tok_jcb |
| UnionPay | tok_unionpay |