Skip to main content

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

// 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

  1. Always validate payment amounts on the backend
  2. Use webhooks for reliable payment status updates
  3. Handle errors gracefully with user-friendly messages
  4. Test thoroughly with Stripe test cards
  5. 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.