Payment Processing
Handle recurring billing, payment methods, invoicing, and payment failures using the Connect API
Implement comprehensive payment processing for telecommunications services including payment collection, stored payment methods, billing management, and payment failure handling. This use case covers the complete payment lifecycle from initial collection to ongoing billing.
Prerequisites
Before you begin, ensure you have:
- Order Management: Understanding of order creation and pricing flows
- Customer Management: Active customers with subscription services
- Payment Gateway Integration: Access to payment processors (cards, bank transfers, digital wallets)
- Billing System: Understanding of billing cycles and pricing models
- Compliance: PCI DSS compliance for handling payment data
Overview
Payment processing encompasses:
- Payment session creation for secure payment collection
- Payment profile management for stored payment methods
- Payment processing and transaction handling
- Payment failure management and retry logic
- Billing and invoice management
- Promotional pricing and discount handling
Step-by-Step Implementation
Step 1: Create Payment Sessions for Orders
Create secure payment sessions to collect payment for orders:
# Create payment session for order
curl -X POST "https://apiv2.example.com/api/v2/payment-sessions" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"orderId": "123e4567-e89b-12d3-a456-426614174000",
"paymentProvider": "STRIPE",
"hosted": true,
"returnUrl": "https://yourstore.com/payment/success",
"cancelUrl": "https://yourstore.com/payment/cancel"
}'
# Check payment session status
curl -X GET "https://apiv2.example.com/api/v2/payment-sessions/{paymentSessionId}" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json"// Create payment session for order
const createOrderPaymentSession = async (orderId, paymentOptions = {}) => {
const sessionData = {
orderId: orderId,
paymentProvider: paymentOptions.paymentProvider || 'STRIPE',
hosted: true,
returnUrl: paymentOptions.returnUrl || `${window.location.origin}/payment/success`,
cancelUrl: paymentOptions.cancelUrl || `${window.location.origin}/payment/cancel`,
metadata: {
source: 'customer_portal',
timestamp: new Date().toISOString(),
},
};
const response = await fetch('https://apiv2.example.com/api/v2/payment-sessions', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_ACCESS_TOKEN',
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify(sessionData),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Payment session creation failed: ${error.message}`);
}
return await response.json();
};
// Monitor payment session status
const checkPaymentSessionStatus = async (paymentSessionId) => {
const response = await fetch(
`https://apiv2.example.com/api/v2/payment-sessions/${paymentSessionId}`,
{
method: 'GET',
headers: {
Authorization: 'Bearer YOUR_ACCESS_TOKEN',
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
},
);
const session = await response.json();
return {
status: session.status,
paymentSessionId: session.paymentSessionId,
orderId: session.orderId,
hostedUrl: session.hostedUrl,
updatedAt: session.updatedAt,
};
};
// Look up the customer attached to a session's order
const getOrderCustomer = async (orderId) => {
const response = await fetch(`https://apiv2.example.com/api/v2/orders/${orderId}`, {
method: 'GET',
headers: {
Authorization: 'Bearer YOUR_ACCESS_TOKEN',
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
});
const order = await response.json();
return order.customer;
};
// Payment session component
const PaymentSessionHandler = ({ orderId, onSuccess, onFailure }) => {
const [paymentSession, setPaymentSession] = useState(null);
const [loading, setLoading] = useState(false);
const initiatePayment = async () => {
setLoading(true);
try {
const session = await createOrderPaymentSession(orderId, {
returnUrl: `${window.location.origin}/orders/${orderId}/success`,
cancelUrl: `${window.location.origin}/orders/${orderId}/cancel`,
});
setPaymentSession(session);
// Redirect to payment URL
window.location.href = session.hostedUrl;
} catch (error) {
console.error('Payment session creation failed:', error);
onFailure(error);
} finally {
setLoading(false);
}
};
return (
<div className="payment-session">
<button onClick={initiatePayment} disabled={loading}>
{loading ? 'Creating Payment Session...' : 'Pay Now'}
</button>
</div>
);
};Step 2: Manage Payment Profiles
Set up stored payment methods for recurring billing:
# Create payment profile session
curl -X POST "https://apiv2.example.com/api/v2/payment-profiles/sessions" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"returnUrl": "https://yourstore.com/billing/payment-methods",
"paymentMethods": ["card", "bank_account"]
}'
# Get payment profiles
curl -X GET "https://apiv2.example.com/api/v2/payment-profiles" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json"// Create payment profile session for storing payment methods
const createPaymentProfileSession = async (returnUrl, customerContext) => {
const sessionData = {
returnUrl: returnUrl,
paymentMethods: ['card', 'bank_account'],
metadata: {
customerId: customerContext.customerId,
source: 'billing_setup',
},
};
const response = await fetch('https://apiv2.example.com/api/v2/payment-profiles/sessions', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_ACCESS_TOKEN',
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify(sessionData),
});
return await response.json();
};
// Get stored payment profiles
const getPaymentProfiles = async () => {
const response = await fetch('https://apiv2.example.com/api/v2/payment-profiles', {
method: 'GET',
headers: {
Authorization: 'Bearer YOUR_ACCESS_TOKEN',
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
});
return await response.json();
};
// Get specific payment profile
const getPaymentProfile = async (paymentProfileId) => {
const response = await fetch(
`https://apiv2.example.com/api/v2/payment-profiles/${paymentProfileId}`,
{
method: 'GET',
headers: {
Authorization: 'Bearer YOUR_ACCESS_TOKEN',
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
},
);
return await response.json();
};Step 3: Process Payments
Handle payment processing and transaction management:
# List payments
curl -X GET "https://apiv2.example.com/api/v2/payments?customerId={customerId}&limit=50" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json"
# Get payment details
curl -X GET "https://apiv2.example.com/api/v2/payments/{paymentId}" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json"// List payments with filtering
const getPayments = async (filters = {}) => {
const params = new URLSearchParams();
if (filters.customerId) params.append('customerId', filters.customerId);
if (filters.status) params.append('status', filters.status);
if (filters.dateFrom) params.append('dateFrom', filters.dateFrom);
if (filters.dateTo) params.append('dateTo', filters.dateTo);
if (filters.limit) params.append('limit', filters.limit);
if (filters.offset) params.append('offset', filters.offset);
const response = await fetch(`https://apiv2.example.com/api/v2/payments?${params}`, {
method: 'GET',
headers: {
Authorization: 'Bearer YOUR_ACCESS_TOKEN',
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
});
return await response.json();
};
// Get specific payment details
const getPaymentDetails = async (paymentId) => {
const response = await fetch(`https://apiv2.example.com/api/v2/payments/${paymentId}`, {
method: 'GET',
headers: {
Authorization: 'Bearer YOUR_ACCESS_TOKEN',
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
});
return await response.json();
};
// Payment management component
const PaymentManager = ({ customerId }) => {
const [payments, setPayments] = useState([]);
const [paymentProfiles, setPaymentProfiles] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
Promise.all([getPayments({ customerId, limit: 50 }), getPaymentProfiles()]).then(
([paymentsData, profilesData]) => {
setPayments(paymentsData.items || []);
setPaymentProfiles(profilesData.items || []);
setLoading(false);
},
);
}, [customerId]);
const handleAddPaymentMethod = async () => {
try {
const session = await createPaymentProfileSession(
`${window.location.origin}/billing/payment-methods`,
{ customerId },
);
window.location.href = session.hostedUrl;
} catch (error) {
console.error('Failed to create payment profile session:', error);
}
};
if (loading) return <div>Loading payment information...</div>;
return (
<div className="payment-manager">
<h3>Payment Methods</h3>
<div className="payment-profiles">
{paymentProfiles.map((profile) => (
<div key={profile.paymentProfileId} className="payment-profile">
<span>
{profile.brand} •••• {profile.lastFour}
</span>
<span>
Expires {profile.expiryMonth}/{profile.expiryYear}
</span>
</div>
))}
<button onClick={handleAddPaymentMethod}>Add Payment Method</button>
</div>
<h3>Payment History</h3>
<div className="payment-history">
{payments.map((payment) => (
<div key={payment.paymentId} className="payment-item">
<span>{payment.description}</span>
<span>
{payment.amount} {payment.currency}
</span>
<span className={`status ${payment.status}`}>{payment.status}</span>
<span>{new Date(payment.createdAt).toLocaleDateString()}</span>
</div>
))}
</div>
</div>
);
};Handling Payment Failures and Retries
Implement robust payment failure handling with retry logic:
// Payment failure handler with exponential backoff
class PaymentRetryHandler {
constructor(maxRetries = 3) {
this.maxRetries = maxRetries;
this.retryDelays = [24, 72, 168]; // Hours: 1 day, 3 days, 1 week
}
async handlePaymentFailure(paymentSessionId, failureReason) {
console.log(`Payment failed for session ${paymentSessionId}: ${failureReason}`);
// Get payment session details
const session = await checkPaymentSessionStatus(paymentSessionId);
const customer = await getOrderCustomer(session.orderId);
// Categorize failure type
const failureCategory = this.categorizeFailure(failureReason);
switch (failureCategory) {
case 'insufficient_funds':
await this.scheduleRetry(paymentSessionId, 24); // Retry in 24 hours
await this.notifyCustomer(customer.customerId, 'insufficient_funds');
break;
case 'expired_card':
await this.requestPaymentMethodUpdate(customer.customerId);
break;
case 'fraud_suspected':
await this.escalateToFraud(session);
break;
case 'technical_error':
await this.scheduleRetry(paymentSessionId, 1); // Retry in 1 hour
break;
default:
await this.escalateToSupport(session);
}
}
categorizeFailure(reason) {
const failureMap = {
insufficient_funds: 'insufficient_funds',
card_declined: 'insufficient_funds',
expired_card: 'expired_card',
invalid_cvc: 'expired_card',
fraud_suspected: 'fraud_suspected',
processing_error: 'technical_error',
network_error: 'technical_error',
};
return failureMap[reason] || 'unknown';
}
async scheduleRetry(paymentSessionId, delayHours) {
// In production, this would schedule a background job
setTimeout(
async () => {
try {
// Create new payment session with same order
const originalSession = await checkPaymentSessionStatus(paymentSessionId);
const customer = await getOrderCustomer(originalSession.orderId);
const newSession = await createOrderPaymentSession(originalSession.orderId);
// Notify customer of retry attempt
await this.notifyCustomerRetry(customer.customerId, newSession.hostedUrl);
} catch (error) {
console.error('Payment retry failed:', error);
}
},
delayHours * 60 * 60 * 1000,
);
}
async notifyCustomer(customerId, failureType) {
// Implement customer notification logic
console.log(`Notifying customer ${customerId} about ${failureType}`);
}
async requestPaymentMethodUpdate(customerId) {
// Create payment profile session for updating payment method
const session = await createPaymentProfileSession(
`${process.env.BASE_URL}/billing/update-payment`,
{ customerId },
);
// Send email with update link
await this.notifyCustomer(customerId, 'payment_method_update_required');
}
}
Promotional Pricing and Discounts
Handle promotional codes and discount applications:
// Get promotion by promo code
const getPromotionByCode = async (promoCode) => {
const response = await fetch(
`https://apiv2.example.com/api/v2/discounts/promotions/promo-code/${promoCode}`,
{
method: 'GET',
headers: {
Authorization: 'Bearer YOUR_ACCESS_TOKEN',
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
},
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Invalid promo code: ${error.message}`);
}
return await response.json();
};
// Apply promo code to order pricing
const applyPromoCodeToOrder = async (orderId, promoCode) => {
try {
// Validate promo code first
const promotion = await getPromotionByCode(promoCode);
// Calculate order price with promo code
const pricingResponse = await fetch(
`https://apiv2.example.com/api/v2/orders/${orderId}/calculate-price`,
{
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_ACCESS_TOKEN',
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
promoCode: promoCode,
}),
},
);
const pricing = await pricingResponse.json();
return {
originalTotal: pricing.subtotal,
discountAmount: pricing.discountAmount,
finalTotal: pricing.total,
promotion: promotion,
};
} catch (error) {
console.error('Failed to apply promo code:', error);
throw error;
}
};
// Promo code component
const PromoCodeInput = ({ orderId, onApplied, onError }) => {
const [promoCode, setPromoCode] = useState('');
const [loading, setLoading] = useState(false);
const [applied, setApplied] = useState(null);
const handleApply = async () => {
if (!promoCode.trim()) return;
setLoading(true);
try {
const result = await applyPromoCodeToOrder(orderId, promoCode);
setApplied(result);
onApplied(result);
} catch (error) {
onError(error.message);
} finally {
setLoading(false);
}
};
return (
<div className="promo-code-input">
<input
type="text"
placeholder="Enter promo code"
value={promoCode}
onChange={(e) => setPromoCode(e.target.value.toUpperCase())}
disabled={loading || applied}
/>
<button onClick={handleApply} disabled={loading || applied}>
{loading ? 'Applying...' : 'Apply'}
</button>
{applied && (
<div className="promo-applied">
<p>✓ {applied.promotion.name} applied</p>
<p>
Discount: -{applied.discountAmount} {applied.promotion.currency}
</p>
</div>
)}
</div>
);
};
Next Steps
After implementing payment processing:
Order fulfillment
Handle service provisioning after successful payment
Customer self-service
Enable customers to manage their payment methods
Best Practices
Security
- Never store raw payment card data - use tokenized payment profiles
- Implement PCI DSS compliance for card processing
- Use HTTPS for all payment-related communications
- Validate all payment webhooks and callbacks
User Experience
- Provide clear payment status updates to customers
- Implement user-friendly error messages for payment failures
- Offer multiple payment methods when possible
- Save successful payment methods for future use
Reliability
- Implement robust retry logic for failed payments
- Handle payment processor downtime gracefully
- Monitor payment success rates and failure patterns
- Set up alerts for payment processing issues
Common Questions
Q: How do I handle different currencies? A: The API supports multiple currencies. Specify the currency in payment sessions and ensure your payment processor supports the target currency.
Q: Can I process refunds through the API? A: Refunds are typically handled through your payment processor’s dashboard or API, then reflected in the Connect API payment records.
Q: How do I implement recurring billing? A: Use stored payment profiles with scheduled payment sessions. The billing system can automatically create payment sessions for recurring charges.