sg-delivery-customer/payment_integration_guide.md

4.2 KiB

Frontend Payment SDK & API Integration Guide

This guide details how to integrate your client-side application (Web/React/React Native) with the backend Payment & Wallet module using Stripe Elements and Razorpay Checkout SDKs.


2. Razorpay Integration (Web & Mobile)

Razorpay uses Orders API where the backend pre-creates a transaction order, and the frontend opens a secure Checkout Widget overlay.

SDK Installation

<!-- For Web: include checkout script in index.html -->
<script src="https://checkout.razorpay.com/v1/checkout.js"></script>
# For Mobile (React Native)
npm install react-native-razorpay

Complete Razorpay Checkout Flow (Web)

declare const Razorpay: any;

export const RazorpayCheckout = ({ orderId, totalAmount, userProfile }) => {
  const handlePayment = async () => {
    // Step 1: Initialize Payment Session on backend to get Razorpay Order ID
    const sessionResponse = await fetch('/payments/create-session', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ orderId, paymentMethod: 'UPI' }), // OR 'CARD'
    });

    const { gatewayOrderId, provider } = await sessionResponse.json();

    // Step 2: Set up checkout configuration options
    const options = {
      key: process.env.REACT_APP_RAZORPAY_KEY_ID, // Enter public key
      amount: totalAmount * 100, // Amount in paise
      currency: 'INR',
      name: 'Delivery Platform',
      description: `Payment for Order #${orderId}`,
      order_id: gatewayOrderId, // The Order ID fetched from backend
      prefill: {
        name: userProfile.name,
        email: userProfile.email,
        contact: userProfile.phone,
      },
      theme: { color: '#3399cc' },
      // Step 3: Signature validation callback handler
      handler: async (response: {
        razorpay_payment_id: string;
        razorpay_order_id: string;
        razorpay_signature: string;
      }) => {
        // Step 4: Verify signature cryptographically on the backend
        const verifyResponse = await fetch('/payments/process', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            gatewayOrderId: response.razorpay_order_id,
            gatewayPaymentId: response.razorpay_payment_id,
            gatewaySignature: response.razorpay_signature,
          }),
        });

        const confirmation = await verifyResponse.json();
        if (confirmation.success) {
          alert('Order Placed and Driver Dispatched successfully!');
        }
      },
      modal: {
        ondismiss: function () {
          console.log('Payment checkout dialog closed by customer.');
        },
      },
    };

    const rzp = new Razorpay(options);
    rzp.open();
  };

  return <button onClick={handlePayment}>Pay via Razorpay</button>;
};

3. Wallet Top-Up Flow (Backend-Only Deduction)

Wallet checkouts and top-ups run completely inside backend transactions without client-side redirects.

export const WalletPayment = ({ orderId, walletBalance, totalAmount }) => {
  const handleWalletPay = async () => {
    if (walletBalance < totalAmount) {
      alert('Insufficient wallet balance. Please top up.');
      return;
    }

    // Initialize session for WALLET (processed fully by backend transaction)
    const response = await fetch('/payments/create-session', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ orderId, paymentMethod: 'WALLET' }),
    });

    const result = await response.json();

    // For wallet, session creation directly attempts to deduct balance
    const verifyResponse = await fetch('/payments/process', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        gatewayOrderId: result.paymentReference, // Virtual reference for wallet
        gatewayPaymentId: `wallet_txn_${Date.now()}`,
      }),
    });

    const confirmation = await verifyResponse.json();
    if (confirmation.success) {
      alert('Balance deducted and order confirmed!');
    }
  };

  return <button onClick={handleWalletPay}>Deduct Wallet Balance</button>;
};