Create clear, comprehensive technical documentation for software and systems
Table of Contents:
Version Information:
Changelog (v2.1.0 - March 15, 2025):
/v1/charges endpoint (migrate to /v2/payments)Navigation Hierarchy:
Cross-References:
System Requirements:
Minimum Requirements:
Recommended Environment:
Browser Support (for embedded checkout UI):
Installation Instructions:
Step 1: Create Account
# Sign up at https://dashboard.paymentflow.com/signup
# Verify email address
# Complete business information
Step 2: Get API Keys
# Navigate to Settings → API Keys
# Copy your test keys:
# - Publishable Key (starts with pk_test_)
# - Secret Key (starts with sk_test_)
# Store Secret Key securely (never commit to git!)
Step 3: Install SDK
Node.js:
npm install @paymentflow/node
# or
yarn add @paymentflow/node
Python:
pip install paymentflow
PHP:
composer require paymentflow/paymentflow-php
Ruby:
gem install paymentflow
Step 4: Initialize Client
Node.js:
const PaymentFlow = require('@paymentflow/node');
const pf = new PaymentFlow('sk_test_YOUR_SECRET_KEY');
Python:
import paymentflow
paymentflow.api_key = 'sk_test_YOUR_SECRET_KEY'
Initial Configuration:
Set API Version (Optional):
const pf = new PaymentFlow('sk_test_YOUR_SECRET_KEY', {
apiVersion: '2025-03-15', // Pin to specific version
timeout: 80000, // 80 second timeout
maxRetries: 3 // Automatic retries on network failure
});
Quick Start Tutorial (5 Minutes):
Goal: Accept your first test payment
Step 1: Create Payment Intent
const paymentIntent = await pf.paymentIntents.create({
amount: 2000, // $20.00 in cents
currency: 'usd',
payment_method_types: ['card'],
description: 'Test Payment'
});
console.log(paymentIntent.client_secret);
// Output: pi_1Ab2cD3eF4gH5iJ6_secret_K7lM8nO9pQ0rS
Step 2: Use Client Secret in Frontend
<script src="https://js.paymentflow.com/v3"></script>
<script>
const pf = PaymentFlow('pk_test_YOUR_PUBLISHABLE_KEY');
const clientSecret = 'pi_1Ab2cD3eF4gH5iJ6_secret_K7lM8nO9pQ0rS';
pf.confirmCardPayment(clientSecret, {
payment_method: {
card: cardElement, // Assumes you've created card element
billing_details: {name: 'Test User'}
}
}).then(function(result) {
if (result.error) {
console.error(result.error.message);
} else {
console.log('Payment successful!', result.paymentIntent.id);
}
});
</script>
Step 3: Verify Payment
# Check your dashboard: https://dashboard.paymentflow.com/payments
# You'll see test payment with status: succeeded
Common Use Cases:
Architecture Overview:
[Your Application]
↓
[PaymentFlow SDK] → Handles requests, retries, authentication
↓
[PaymentFlow API] → Processes payments, stores data
↓
[Payment Networks] → Visa, Mastercard, ACH, etc.
↓
[Bank Accounts] → Customer → Your Account
Key Components:
Payment Intent: Represents customer's intention to pay. Tracks entire payment lifecycle from creation to confirmation.
Customer Object: Stores customer information for repeat purchases. Can attach payment methods, subscriptions.
Payment Method: Represents how customer pays (card, bank account, wallet). Can be reused for future charges.
Webhooks: Real-time notifications when payment events occur (successful charge, failed payment, dispute filed).
Key Terminology:
System Components:
PaymentFlow API: RESTful API accessed via HTTPS - Base URL: https://api.paymentflow.com - Format: JSON request/response - Authentication: Bearer token (API Secret Key) - Versioning: Date-based (2025-03-15)
Dashboard: Web interface for payment management - URL: https://dashboard.paymentflow.com - Features: Payment search, refund processing, analytics, settings - Mobile app available (iOS/Android)
SDKs: Official libraries for 8 languages - Handles authentication, retries, type safety - Open source on GitHub - Community SDKs for 15+ additional languages
Data Flow Diagram:
1. Customer clicks "Pay" on your website
↓
2. Your frontend creates Payment Intent (via your backend)
↓
3. PaymentFlow returns client_secret
↓
4. Frontend confirms payment with customer card details
↓
5. PaymentFlow processes via payment networks
↓
6. Payment succeeds or fails
↓
7. Webhook sent to your server (async notification)
↓
8. Your application fulfills order
Security Model:
API Keys:
Authentication:
POST /v2/payments HTTP/1.1
Host: api.paymentflow.com
Authorization: Bearer sk_test_YOUR_SECRET_KEY
Content-Type: application/json
Webhook Signatures:
Feature: Accepting One-Time Payments
Description: The PaymentFlow Payment Intents API allows you to accept one-time payments from customers using cards, bank transfers, or digital wallets with a single unified API.
Usage Instructions:
Step 1: Create Payment Intent (Backend)
const paymentIntent = await pf.paymentIntents.create({
amount: 2999, // $29.99 in cents
currency: 'usd',
payment_method_types: ['card', 'us_bank_account'],
metadata: {
order_id: 'ORD-12345',
customer_email: 'user@example.com'
}
});
Step 2: Return Client Secret to Frontend
res.json({clientSecret: paymentIntent.client_secret});
Step 3: Confirm Payment (Frontend)
const {error, paymentIntent} = await pf.confirmCardPayment(
clientSecret,
{payment_method: {card: cardElement}}
);
if (error) {
// Handle error (show message to customer)
displayError(error.message);
} else if (paymentIntent.status === 'succeeded') {
// Payment successful - show confirmation
showSuccessMessage();
}
Configuration Options:
Currency Support: 135+ currencies supported - USD, EUR, GBP, CAD, AUD, JPY, etc. - Automatic conversion at real-time rates - Settlement in your account currency
Payment Method Types:
card - Credit/debit cards (Visa, Mastercard, Amex, Discover)us_bank_account - ACH direct debitsepa_debit - European bank transfersapple_pay - Apple Pay (requires domain verification)google_pay - Google PayCapture Behavior:
Best Practices:
requires_payment_method, requires_confirmation, requires_action, processing, succeeded, canceledLimitations and Constraints:
Endpoint: Create Payment Intent
POST /v2/payment_intents
Description: Creates a new Payment Intent representing your customer's intention to pay. Track the payment lifecycle and confirm on the frontend.
Authentication: Requires secret API key
Request Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
amount | integer | Yes | Amount in smallest currency unit (e.g., cents). Must be ≥50 |
currency | string | Yes | Three-letter ISO currency code (lowercase): usd, eur, gbp |
payment_method_types | array | No | Array of payment method types. Default: ['card'] |
customer | string | No | ID of existing Customer object. Creates relationship |
description | string | No | Arbitrary string for your reference (max 1,000 chars) |
metadata | object | No | Set of key-value pairs (max 50 pairs) |
receipt_email | string | No | Email to send receipt (must be valid email format) |
statement_descriptor | string | No | Text on customer's credit card statement (max 22 chars) |
capture_method | string | No | automatic (default) or manual for delayed capture |
Request Example:
const paymentIntent = await pf.paymentIntents.create({
amount: 5999,
currency: 'usd',
payment_method_types: ['card', 'apple_pay'],
customer: 'cus_ABC123',
description: 'Premium Subscription - Annual',
metadata: {
subscription_id: 'sub_12345',
plan: 'premium_annual'
},
receipt_email: 'customer@example.com',
statement_descriptor: 'ACME CORP SUBSCRIPTION'
});
Response Format:
{
"id": "pi_1Ab2cD3eF4gH5iJ6",
"object": "payment_intent",
"amount": 5999,
"currency": "usd",
"status": "requires_payment_method",
"client_secret": "pi_1Ab2cD3eF4gH5iJ6_secret_K7lM8nO9pQ0rS",
"created": 1710523145,
"customer": "cus_ABC123",
"description": "Premium Subscription - Annual",
"metadata": {
"subscription_id": "sub_12345",
"plan": "premium_annual"
},
"payment_method_types": ["card", "apple_pay"],
"receipt_email": "customer@example.com"
}
Response Objects (Key Fields):
id (string): Unique identifier for this Payment Intentclient_secret (string): Secret used to confirm payment on frontend (pass to JavaScript)status (string): Current state - see Payment Intent Statuses belowamount (integer): Amount in smallest currency unitcurrency (string): Three-letter ISO codecreated (timestamp): Unix timestamp of creationAuthentication Methods:
Bearer Token (Recommended):
Authorization: Bearer sk_live_YOUR_SECRET_KEY
Basic Auth (Alternative):
Authorization: Basic BASE64(sk_live_YOUR_SECRET_KEY:)
Note: Secret key is username, password is empty
API Key Header (Deprecated):
X-API-Key: sk_live_YOUR_SECRET_KEY
Will be removed in v3.0
Rate Limits:
| Plan | Requests/Second | Burst Limit | Monthly Calls |
|---|---|---|---|
| Starter | 10/sec | 50 | 100,000 |
| Professional | 100/sec | 500 | 1,000,000 |
| Enterprise | 1,000/sec | 5,000 | Unlimited |
Rate Limit Headers (returned in every response):
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1710523200
Error Codes:
| Code | HTTP Status | Meaning | Resolution |
|---|---|---|---|
invalid_request_error | 400 | Malformed request | Check request parameters against documentation |
authentication_error | 401 | Invalid API key | Verify you're using correct secret key |
rate_limit_error | 429 | Too many requests | Implement exponential backoff retry |
api_error | 500 | PaymentFlow server error | Retry with exponential backoff |
payment_failed | 402 | Payment declined | Card declined by bank - inform customer |
insufficient_funds | 402 | Insufficient funds | Customer needs different payment method |
card_declined | 402 | Generic decline | Try different card |
Error Response Format:
{
"error": {
"type": "invalid_request_error",
"code": "parameter_missing",
"message": "Missing required parameter: amount",
"param": "amount",
"doc_url": "https://docs.paymentflow.com/errors/parameter_missing"
}
}
Complete Payment Flow (Node.js + Express):
const express = require('express');
const PaymentFlow = require('@paymentflow/node');
const app = express();
const pf = new PaymentFlow('sk_test_YOUR_SECRET_KEY');
// Endpoint to create payment intent
app.post('/create-payment-intent', async (req, res) => {
try {
const {amount, currency} = req.body;
// Create payment intent
const paymentIntent = await pf.paymentIntents.create({
amount,
currency,
automatic_payment_methods: {enabled: true},
metadata: {
user_id: req.user.id,
order_id: req.body.order_id
}
});
// Return client secret to frontend
res.json({
clientSecret: paymentIntent.client_secret
});
} catch (error) {
res.status(400).json({error: error.message});
}
});
// Webhook endpoint to handle payment confirmation
app.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
const signature = req.headers['pf-signature'];
const webhookSecret = 'whsec_YOUR_WEBHOOK_SECRET';
let event;
try {
// Verify webhook signature
event = pf.webhooks.constructEvent(
req.body,
signature,
webhookSecret
);
} catch (err) {
console.error('Webhook signature verification failed');
return res.status(400).send();
}
// Handle specific event types
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
console.log(`Payment ${paymentIntent.id} succeeded`);
// Fulfill order, send confirmation email, etc.
await fulfillOrder(paymentIntent.metadata.order_id);
break;
case 'payment_intent.payment_failed':
console.log('Payment failed:', event.data.object.last_payment_error);
// Notify customer, retry logic, etc.
break;
}
res.json({received: true});
});
app.listen(3000);
Python (Flask) Example:
from flask import Flask, request, jsonify
import paymentflow
app = Flask(__name__)
paymentflow.api_key = 'sk_test_YOUR_SECRET_KEY'
@app.route('/create-payment-intent', methods=['POST'])
def create_payment():
try:
data = request.get_json()
intent = paymentflow.PaymentIntent.create(
amount=data['amount'],
currency='usd',
automatic_payment_methods={'enabled': True},
metadata={
'user_id': str(current_user.id),
'order_id': data['order_id']
}
)
return jsonify({'clientSecret': intent.client_secret})
except paymentflow.error.PaymentFlowError as e:
return jsonify({'error': str(e)}), 400
@app.route('/webhook', methods=['POST'])
def webhook():
payload = request.get_data()
signature = request.headers.get('PF-Signature')
webhook_secret = 'whsec_YOUR_WEBHOOK_SECRET'
try:
event = paymentflow.Webhook.construct_event(
payload, signature, webhook_secret
)
except ValueError:
return 'Invalid payload', 400
except paymentflow.error.SignatureVerificationError:
return 'Invalid signature', 400
if event['type'] == 'payment_intent.succeeded':
payment_intent = event['data']['object']
# Fulfill the order
fulfill_order(payment_intent['metadata']['order_id'])
return jsonify({'success': True})
Integration Patterns:
Pattern 1: Checkout Page Integration
Pattern 2: Subscription Billing
Pattern 3: Marketplace with Split Payments
Testing Examples:
Test Card Numbers:
// Successful payment
const successCard = '4242424242424242';
// Payment requires authentication (3D Secure)
const sca_card = '4000002500003155';
// Declined card
const declinedCard = '4000000000000002';
// Insufficient funds
const insufficientCard = '4000000000009995';
// All test cards: Any future expiration, any 3-digit CVC
Test Webhooks Locally:
# Use PaymentFlow CLI to forward webhooks to localhost
pf listen --forward-to http://localhost:3000/webhook
# Trigger test webhook
pf trigger payment_intent.succeeded
Debugging Tips:
Enable Request Logging:
const pf = new PaymentFlow('sk_test_KEY', {
debug: true, // Logs all requests/responses
telemetry: false // Disable usage stats
});
Inspect API Request Details:
error.raw for complete API error responsepf.setAppInfo() to tag requests for easier filtering in dashboardCommon Issue 1: "Invalid API Key" Error
Symptoms: All API calls fail with 401 authentication error
Causes:
Solutions:
sk_Diagnostic Code:
console.log('API Key starts with:', pf.apiKey.substring(0, 7));
// Should output: sk_test or sk_live
Common Issue 2: Webhook Not Receiving Events
Symptoms: Payments succeed but webhook never called
Causes:
Solutions:
ngrok http 3000Testing Webhooks:
# Use PaymentFlow CLI
pf listen --forward-to https://your-domain.com/webhook
# Or use webhook.site for inspection
# Set webhook URL to: https://webhook.site/YOUR-UNIQUE-URL
Common Issue 3: Payment Declined with No Clear Reason
Symptoms: card_declined error but customer insists card works elsewhere
Causes:
Solutions:
error.decline_codeDecline Codes Reference:
insufficient_funds - "Your card has insufficient funds. Please try a different payment method."lost_card - "This card was reported lost. Please use a different card."generic_decline - "Your card was declined. Please contact your bank or try a different card."Error Message Reference:
| Error | User-Friendly Message | Action |
|---|---|---|
insufficient_funds | "Insufficient funds. Try different card?" | Offer alternative payment method |
authentication_required | "Your bank requires verification" | Trigger 3D Secure flow |
processing_error | "Payment processing failed. Try again?" | Retry immediately acceptable |
card_declined | "Card declined. Contact bank or try different card" | Suggest bank contact |
Performance Optimization:
Reduce API Calls:
SDK Best Practices:
// ❌ Bad: Creating new client each time
function charge() {
const pf = new PaymentFlow(key); // Creates new HTTP connection
return pf.paymentIntents.create(...);
}
// ✅ Good: Reuse client instance
const pf = new PaymentFlow(key); // Create once
function charge() {
return pf.paymentIntents.create(...);
}
Connection Pooling:
Support Resources:
Deployment Procedures:
Production Checklist:
Environment Variables:
# Production
PAYMENTFLOW_SECRET_KEY=sk_live_YOUR_LIVE_KEY
PAYMENTFLOW_WEBHOOK_SECRET=whsec_YOUR_WEBHOOK_SECRET
PAYMENTFLOW_PUBLIC_KEY=pk_live_YOUR_PUBLIC_KEY
# Never commit these to git!
# Use .env file and .gitignore
Backup and Recovery:
Data Retention: PaymentFlow retains all payment data indefinitely - Export via API or dashboard - Use /v2/reporting/exports endpoint for bulk exports - Format: CSV, JSON, or Excel
Disaster Recovery:
Monitoring and Logging:
What to Monitor:
Logging Best Practices:
// ❌ Don't log sensitive data
console.log('Payment for card:', fullCardNumber); // NEVER!
// ✅ Log only safe identifiers
console.log('Payment intent created:', paymentIntent.id);
console.log('Customer:', paymentIntent.customer);
Never Log: Full card numbers, CVV, API secret keys, webhook secrets
Dashboard Analytics:
Security Configuration:
API Key Rotation:
IP Allowlisting (Enterprise):
Webhook Security:
PCI Compliance:
Maintenance Tasks:
Weekly:
Monthly:
Quarterly:
Annually: