@vegaci_shared/vsplit-payment-gateway
v1.0.5
Published
A B2B payment gateway integration package for Stripe with split payment capabilities
Maintainers
Readme
VSplit Payment Gateway
A comprehensive B2B payment gateway integration package for Stripe with advanced split payment capabilities.
Features
- 🎯 Simple Integration: Minimal setup with just configuration
- 💳 Single Card Payments: Traditional one-card payment processing
- 🔄 Customer Split Payments: Let customers split their payment across multiple cards
- ⏱️ Timeout Management: Automatic cancellation and refunds for incomplete payments
- 💰 Optional Merchant Splits: Revenue sharing between merchant, platform, drivers, etc.
- 🔒 Secure: Follows Stripe security best practices
- 📱 React Ready: Built-in React hooks and components
- 🎨 Customizable: Theming and styling options
- 📊 Real-time Status: Payment verification and status tracking
- 🪝 Webhooks: Event-driven architecture for notifications
- 📱 TypeScript: Fully typed for better development experience
🚀 Live Demo
Experience VSplit in action with our interactive demo application:
Local Demo Setup
Clone & Setup:
git clone https://github.com/ModyG/vsplit-payment-gateway.git cd vsplit-payment-gateway npm run demoAdd your Stripe keys to
demo/vsplit-demo/.env.localVisit:
http://localhost:3000
Demo Scenarios
- Customer Split Payment - Customer chooses to split total across multiple cards
- Single Card Payment - Traditional one-card payment processing
- Merchant Revenue Sharing - Optional splits for merchant/platform/drivers
- Timeout & Auto-Refund - Automatic refunds if split payments incomplete
Installation
npm install @vsplit/payment-gateway
# or
yarn add @vsplit/payment-gatewayQuick Start
1. Basic Setup (Vanilla JS/TS)
import VSplitPaymentGateway from '@vsplit/payment-gateway';
// Initialize the gateway
const gateway = new VSplitPaymentGateway({
stripePublishableKey: 'pk_test_...',
apiEndpoint: 'https://your-backend.com/api',
apiKey: 'your-api-key', // optional
currency: 'usd',
defaultTimeout: 600, // 10 minutes
});
// Single payment
const paymentResult = await gateway.initializePayment({
amount: 5000, // $50.00 in cents
currency: 'usd',
orderId: 'order_123',
customer: {
email: '[email protected]',
name: 'John Doe',
},
});
// Process the payment
const result = await gateway.processPayment(paymentResult.paymentId, {
// Stripe payment method or elements
});2. React Integration
import React from 'react';
import {
VSplitProvider,
useVSplit,
PaymentForm,
} from '@vsplit/payment-gateway/react';
// Wrap your app
function App() {
return (
<VSplitProvider
config={{
stripePublishableKey: 'pk_test_...',
apiEndpoint: 'https://your-backend.com/api',
}}
>
<PaymentPage />
</VSplitProvider>
);
}
// Payment component
function PaymentPage() {
const { initializePayment, status, loading, error } = useVSplit();
const handlePayment = async () => {
await initializePayment({
amount: 5000,
currency: 'usd',
orderId: 'order_123',
});
};
return (
<div>
<button
onClick={handlePayment}
disabled={loading}
>
Start Payment
</button>
<PaymentForm
onSuccess={(result) => console.log('Payment successful:', result)}
onError={(error) => console.error('Payment failed:', error)}
/>
</div>
);
}3. Customer Split Payments
Customer wants to pay $100 but split across 3 cards:
// Customer chooses to split payment
const splitSession = await gateway.initializeSplitPayment({
totalAmount: 10000, // $100.00
numberOfCards: 3,
cardAmounts: [4000, 3500, 2500], // $40 + $35 + $25 = $100
currency: 'usd',
orderId: 'order_123',
customer: {
email: '[email protected]',
name: 'John Doe',
},
timeout: 600, // 10 minutes to complete all payments
});
// Process each card payment
for (let i = 0; i < splitSession.cardPayments.length; i++) {
const result = await gateway.processSplitPayment(
splitSession.sessionId,
i,
paymentMethod // Customer's card data for this split
);
if (!result.success) {
console.log(`Card ${i + 1} failed:`, result.error);
// Customer can retry or all payments will be refunded after timeout
}
}4. Merchant Revenue Splitting (Optional)
After customer pays, split revenue between recipients:
// Initialize payment with automatic merchant splits
const paymentResult = await gateway.initializePaymentWithMerchantSplits({
amount: 10000, // $100.00 customer payment
currency: 'usd',
orderId: 'order_123',
customer: {
email: '[email protected]',
name: 'John Doe',
},
// Optional: Automatically split revenue after successful payment
merchantSplits: [
{ amount: 8500, label: 'Merchant Revenue', recipient: 'merchant_123' }, // $85
{ amount: 1000, label: 'Platform Fee', recipient: 'platform_123' }, // $10
{ amount: 500, label: 'Processing Fee', recipient: 'processor_123' }, // $5
],
});
// Process customer's payment first
const result = await gateway.processPayment(paymentResult.paymentId, paymentMethod);
// If customer payment succeeds, merchant splits are processed automatically
if (result.success) {
console.log('Customer paid $100, revenue split between 3 recipients');
}
### 5. React Customer Split Payment Component
```tsx
import { CustomerSplitPayment } from '@vsplit/payment-gateway/react';
function CustomerSplitPaymentPage() {
const [paymentAmount, setPaymentAmount] = useState(10000); // $100
const [numberOfCards, setNumberOfCards] = useState(2);
const handleSplitAmountChange = (cardIndex: number, amount: number) => {
// Update split amounts ensuring they sum to total
};
return (
<div>
<h2>Split Your Payment</h2>
<p>Total: ${(paymentAmount / 100).toFixed(2)}</p>
<label>
How many cards would you like to use?
<select value={numberOfCards} onChange={(e) => setNumberOfCards(+e.target.value)}>
<option value={1}>1 Card (Full Payment)</option>
<option value={2}>2 Cards</option>
<option value={3}>3 Cards</option>
<option value={4}>4 Cards</option>
</select>
</label>
{numberOfCards > 1 ? (
<CustomerSplitPayment
totalAmount={paymentAmount}
numberOfCards={numberOfCards}
onStepComplete={(stepIndex, result) => {
console.log(`Card ${stepIndex + 1} payment completed:`, result);
}}
onAllComplete={(session) => {
console.log('All card payments completed:', session);
}}
onTimeout={(session) => {
console.log('Payment timeout - refunding all successful payments');
}}
onError={(error) => {
console.error('Split payment error:', error);
}}
/>
) : (
<SinglePayment amount={paymentAmount} />
)}
</div>
);
}6. React Merchant Split Component (Optional)
import { MerchantSplitPayment } from '@vsplit/payment-gateway/react';
function MerchantSplitPaymentPage() {
const merchantSplits = [
{ amount: 8500, label: 'Your Revenue', recipient: 'merchant_123' },
{ amount: 1000, label: 'Platform Fee', recipient: 'platform_123' },
{ amount: 500, label: 'Payment Processing', recipient: 'stripe_123' },
];
return (
<MerchantSplitPayment
amount={10000} // $100 total customer payment
merchantSplits={merchantSplits}
onPaymentComplete={(result) => {
console.log('Customer payment completed:', result);
}}
onSplitsComplete={(splits) => {
console.log('Revenue splits completed:', splits);
}}
onError={(error) => {
console.error('Payment or split error:', error);
}}
/>
);
}Configuration
VSplitConfig
interface VSplitConfig {
/** Stripe publishable key */
stripePublishableKey: string;
/** API endpoint for your backend integration */
apiEndpoint: string;
/** Optional API key for authentication */
apiKey?: string;
/** Environment (sandbox/production) */
environment?: 'sandbox' | 'production';
/** Default currency (ISO 4217) */
currency?: string;
/** Default timeout for split payments in seconds */
defaultTimeout?: number;
/** Custom styling options */
theme?: PaymentTheme;
}PaymentTheme
interface PaymentTheme {
/** Primary color */
primaryColor?: string;
/** Border radius */
borderRadius?: string;
/** Font family */
fontFamily?: string;
/** Custom CSS classes */
customClasses?: {
container?: string;
button?: string;
input?: string;
error?: string;
success?: string;
};
}Event Handling
// Listen to payment events
gateway.on('payment:success', (result) => {
console.log('Payment succeeded:', result);
});
gateway.on('payment:failed', (result) => {
console.log('Payment failed:', result);
});
gateway.on('split:completed', (session) => {
console.log('All split payments completed:', session);
});
gateway.on('split:timeout', (session) => {
console.log('Split payment timed out:', session);
// Automatic refunds are handled internally
});
gateway.on('error', (error) => {
console.error('Gateway error:', error);
});Backend Integration
Required API Endpoints
Your backend needs to implement these endpoints:
POST /payment/initialize
{
amount: number;
currency: string;
orderId: string;
customer?: CustomerInfo;
metadata?: Record<string, string>;
}POST /payment/split/initialize
{
splits: Array<{ amount: number; label?: string }>;
timeout?: number;
}POST /payment/refund
{
paymentIntentId: string;
amount?: number;
reason?: string;
}Example Backend (Node.js/Express)
import Stripe from 'stripe';
import express from 'express';
const stripe = new Stripe('sk_test_...');
const app = express();
app.post('/api/payment/initialize', async (req, res) => {
try {
const { amount, currency, orderId } = req.body;
const paymentIntent = await stripe.paymentIntents.create({
amount,
currency,
metadata: { orderId },
automatic_payment_methods: { enabled: true },
});
res.json({
success: true,
data: {
id: paymentIntent.id,
clientSecret: paymentIntent.client_secret,
status: paymentIntent.status,
amount: paymentIntent.amount,
currency: paymentIntent.currency,
created: paymentIntent.created,
},
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message,
});
}
});
app.post('/api/payment/split/initialize', async (req, res) => {
try {
const { splits, timeout } = req.body;
const paymentIntents = await Promise.all(
splits.map((split) =>
stripe.paymentIntents.create({
amount: split.amount,
currency: 'usd',
automatic_payment_methods: { enabled: true },
})
)
);
const session = {
sessionId: `split_${Date.now()}`,
paymentIntents: paymentIntents.map((pi) => ({
id: pi.id,
clientSecret: pi.client_secret,
status: pi.status,
amount: pi.amount,
currency: pi.currency,
created: pi.created,
})),
status: 'pending',
totalAmount: splits.reduce((sum, split) => sum + split.amount, 0),
currency: 'usd',
expiresAt: Date.now() + timeout * 1000,
completedPayments: 0,
failedPayments: 0,
};
res.json({
success: true,
data: session,
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message,
});
}
});Webhook Handling
import { WebhookEvent } from '@vsplit/payment-gateway';
app.post('/webhooks/vsplit', (req, res) => {
const event: WebhookEvent = req.body;
switch (event.type) {
case 'payment.succeeded':
// Handle successful payment
break;
case 'split_payment.completed':
// Handle completed split payment
break;
case 'split_payment.timeout':
// Handle split payment timeout
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
res.json({ received: true });
});Styling
CSS Classes
The package includes built-in CSS classes that you can customize:
/* Import the base styles */
@import '@vsplit/payment-gateway/dist/styles.css';
/* Customize as needed */
.vsplit-container {
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.vsplit-button {
background: linear-gradient(45deg, #007bff, #0056b3);
}Custom Theme
const gateway = new VSplitPaymentGateway({
// ... other config
theme: {
primaryColor: '#007bff',
borderRadius: '8px',
fontFamily: 'Inter, sans-serif',
customClasses: {
container: 'my-custom-container',
button: 'my-custom-button',
},
},
});Error Handling
try {
const result = await gateway.processPayment(paymentId, paymentMethod);
if (!result.success) {
// Handle payment failure
console.error('Payment failed:', result.error);
}
} catch (error) {
// Handle unexpected errors
console.error('Unexpected error:', error);
}TypeScript Support
The package is fully typed. Import types as needed:
import {
VSplitConfig,
PaymentResult,
PaymentSessionConfig,
SplitPaymentConfig,
PaymentStatus,
WebhookEvent,
} from '@vsplit/payment-gateway';Testing
// Use Stripe test keys for development
const gateway = new VSplitPaymentGateway({
stripePublishableKey: 'pk_test_...',
apiEndpoint: 'http://localhost:3000/api',
environment: 'sandbox',
});
// Test card numbers (Stripe test cards)
// 4242424242424242 - Visa
// 4000000000000002 - Card declined
// 4000000000009995 - Insufficient fundsSecurity Best Practices
- API Keys: Never expose secret keys in frontend code
- HTTPS: Always use HTTPS in production
- Validation: Validate all inputs on your backend
- Webhooks: Verify webhook signatures
- PCI Compliance: This package handles sensitive data securely through Stripe
Support
- 📧 Email: [email protected]
- 📚 Documentation: https://docs.vsplit.com
- 🐛 Issues: https://github.com/ModyG/vsplit/issues
License
MIT License - see LICENSE file for details.
