mirror of
https://github.com/xtr-dev/payload-billing.git
synced 2025-12-10 02:43:24 +00:00
feat: implement advanced test provider with interactive UI and multiple scenarios
- Add comprehensive test provider with configurable payment outcomes (paid, failed, cancelled, expired, pending) - Support multiple payment methods (iDEAL, Credit Card, PayPal, Apple Pay, Bank Transfer) - Interactive test payment UI with responsive design and real-time processing simulation - Test mode indicators including warning banners, badges, and console warnings - React components for admin UI integration (TestModeWarningBanner, TestModeBadge, TestPaymentControls) - API endpoints for test payment processing and status polling - Configurable scenarios with custom delays and outcomes - Production safety mechanisms and clear test mode indicators - Complete documentation and usage examples Implements GitHub issue #20 for advanced test provider functionality. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -60,9 +60,130 @@ export const PaymentStatusBadge: React.FC<{ status: string }> = ({ status }) =>
|
||||
)
|
||||
}
|
||||
|
||||
// Test mode indicator components
|
||||
export const TestModeWarningBanner: React.FC<{ visible?: boolean }> = ({ visible = true }) => {
|
||||
if (!visible) return null
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
background: 'linear-gradient(90deg, #ff6b6b, #ffa726)',
|
||||
color: 'white',
|
||||
padding: '12px 20px',
|
||||
textAlign: 'center',
|
||||
fontWeight: 600,
|
||||
fontSize: '14px',
|
||||
marginBottom: '20px',
|
||||
borderRadius: '4px'
|
||||
}}>
|
||||
🧪 TEST MODE - Payment system is running in test mode for development
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const TestModeBadge: React.FC<{ visible?: boolean }> = ({ visible = true }) => {
|
||||
if (!visible) return null
|
||||
|
||||
return (
|
||||
<span style={{
|
||||
display: 'inline-block',
|
||||
background: '#6c757d',
|
||||
color: 'white',
|
||||
padding: '4px 8px',
|
||||
borderRadius: '4px',
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
textTransform: 'uppercase',
|
||||
marginLeft: '8px'
|
||||
}}>
|
||||
Test
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export const TestPaymentControls: React.FC<{
|
||||
paymentId?: string
|
||||
onScenarioSelect?: (scenario: string) => void
|
||||
onMethodSelect?: (method: string) => void
|
||||
}> = ({ paymentId, onScenarioSelect, onMethodSelect }) => {
|
||||
const [selectedScenario, setSelectedScenario] = React.useState('')
|
||||
const [selectedMethod, setSelectedMethod] = React.useState('')
|
||||
|
||||
const scenarios = [
|
||||
{ id: 'instant-success', name: 'Instant Success', description: 'Payment succeeds immediately' },
|
||||
{ id: 'delayed-success', name: 'Delayed Success', description: 'Payment succeeds after delay' },
|
||||
{ id: 'cancelled-payment', name: 'Cancelled Payment', description: 'User cancels payment' },
|
||||
{ id: 'declined-payment', name: 'Declined Payment', description: 'Payment declined' },
|
||||
{ id: 'expired-payment', name: 'Expired Payment', description: 'Payment expires' },
|
||||
{ id: 'pending-payment', name: 'Pending Payment', description: 'Payment stays pending' }
|
||||
]
|
||||
|
||||
const methods = [
|
||||
{ id: 'ideal', name: 'iDEAL', icon: '🏦' },
|
||||
{ id: 'creditcard', name: 'Credit Card', icon: '💳' },
|
||||
{ id: 'paypal', name: 'PayPal', icon: '🅿️' },
|
||||
{ id: 'applepay', name: 'Apple Pay', icon: '🍎' },
|
||||
{ id: 'banktransfer', name: 'Bank Transfer', icon: '🏛️' }
|
||||
]
|
||||
|
||||
return (
|
||||
<div style={{ border: '1px solid #e9ecef', borderRadius: '8px', padding: '16px', margin: '16px 0' }}>
|
||||
<h4 style={{ marginBottom: '12px', color: '#2c3e50' }}>🧪 Test Payment Controls</h4>
|
||||
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<label style={{ display: 'block', marginBottom: '8px', fontWeight: '600' }}>Payment Method:</label>
|
||||
<select
|
||||
value={selectedMethod}
|
||||
onChange={(e) => {
|
||||
setSelectedMethod(e.target.value)
|
||||
onMethodSelect?.(e.target.value)
|
||||
}}
|
||||
style={{ width: '100%', padding: '8px', borderRadius: '4px', border: '1px solid #ccc' }}
|
||||
>
|
||||
<option value="">Select payment method...</option>
|
||||
{methods.map(method => (
|
||||
<option key={method.id} value={method.id}>
|
||||
{method.icon} {method.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<label style={{ display: 'block', marginBottom: '8px', fontWeight: '600' }}>Test Scenario:</label>
|
||||
<select
|
||||
value={selectedScenario}
|
||||
onChange={(e) => {
|
||||
setSelectedScenario(e.target.value)
|
||||
onScenarioSelect?.(e.target.value)
|
||||
}}
|
||||
style={{ width: '100%', padding: '8px', borderRadius: '4px', border: '1px solid #ccc' }}
|
||||
>
|
||||
<option value="">Select test scenario...</option>
|
||||
{scenarios.map(scenario => (
|
||||
<option key={scenario.id} value={scenario.id}>
|
||||
{scenario.name} - {scenario.description}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{paymentId && (
|
||||
<div style={{ marginTop: '12px', padding: '8px', background: '#f8f9fa', borderRadius: '4px' }}>
|
||||
<small style={{ color: '#6c757d' }}>
|
||||
Payment ID: <code>{paymentId}</code>
|
||||
</small>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
BillingDashboardWidget,
|
||||
formatCurrency,
|
||||
getPaymentStatusColor,
|
||||
PaymentStatusBadge,
|
||||
TestModeWarningBanner,
|
||||
TestModeBadge,
|
||||
TestPaymentControls,
|
||||
}
|
||||
Reference in New Issue
Block a user