Payment Integration
This guide covers integrating Stripe payments into your Twigz application.
Setup
1. Install Stripe SDK
npm install @stripe/stripe-js
2. Initialize Stripe
import { loadStripe } from '@stripe/stripe-js';
const stripePromise = loadStripe('pk_test_your_publishable_key');
Payment Flow
1. Create Payment Intent
// Create payment intent on backend
const paymentIntent = await convex.mutation(api.customers.payments.createPaymentIntent, {
amount: 5000, // Amount in cents (50.00 AED)
currency: "AED",
customerId: "user_123",
metadata: {
orderId: "order_456",
storeId: "store_789"
}
});
2. Confirm Payment
// Confirm payment on frontend
const stripe = await stripePromise;
const { error } = await stripe.confirmPayment({
clientSecret: paymentIntent.clientSecret,
elements,
confirmParams: {
return_url: "https://yourapp.com/checkout/success"
}
});
3. Handle Payment Status
// Check payment status
const status = await convex.query(api.customers.payments.getPaymentIntentStatus, {
paymentIntentId: paymentIntent.id
});
if (status === "succeeded") {
// Payment successful, proceed with order
} else if (status === "requires_payment_method") {
// Payment failed, show error
}
Payment Methods
Card Payments
// Use Stripe Elements for card input
const elements = stripe.elements({
clientSecret: paymentIntent.clientSecret
});
const cardElement = elements.create('card');
cardElement.mount('#card-element');
Apple Pay / Google Pay
// Enable Apple Pay and Google Pay
const paymentRequest = stripe.paymentRequest({
country: 'AE',
currency: 'aed',
total: {
label: 'Total',
amount: 5000,
},
});
const prButton = elements.create('paymentRequestButton', {
paymentRequest,
});
Webhook Handling
Stripe Webhook Endpoint
// Handle Stripe webhooks
app.post('/stripe/webhook', express.raw({type: 'application/json'}), async (req, res) => {
const sig = req.headers['stripe-signature'];
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
try {
const event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
switch (event.type) {
case 'payment_intent.succeeded':
// Handle successful payment
break;
case 'payment_intent.payment_failed':
// Handle failed payment
break;
}
res.json({received: true});
} catch (err) {
console.error('Webhook error:', err.message);
res.status(400).send(`Webhook Error: ${err.message}`);
}
});
Marketplace Fees
// Calculate platform commission
const commission = await convex.query(api.customers.checkoutHelpers.calculatePlatformCommission, {
amount: 5000,
categoryId: "food",
storeId: "store_123"
});
console.log(`Platform commission: ${commission.commissionAmount} AED`);
console.log(`Store receives: ${commission.storeAmount} AED`);
Error Handling
Common Payment Errors
const handlePaymentError = (error) => {
switch (error.code) {
case 'card_declined':
return 'Your card was declined. Please try a different payment method.';
case 'insufficient_funds':
return 'Insufficient funds. Please try a different card.';
case 'expired_card':
return 'Your card has expired. Please update your payment method.';
default:
return 'Payment failed. Please try again.';
}
};
Testing
Test Cards
// Use Stripe test cards
const testCards = {
success: '4242424242424242',
declined: '4000000000000002',
insufficient_funds: '4000000000009995',
expired: '4000000000000069'
};
Test Webhooks
# Install Stripe CLI
stripe listen --forward-to localhost:3000/stripe/webhook
Best Practices
- Always validate payment amounts on the backend
- Use webhooks for reliable payment status updates
- Handle errors gracefully with user-friendly messages
- Test thoroughly with Stripe test cards
- Secure your keys - never expose secret keys in frontend code
Always use Stripe’s test mode during development and switch to live mode only when ready for production.