> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mypopup.shop/llms.txt
> Use this file to discover all available pages before exploring further.

# Payment Flow

> Dual payment methods: UPI Direct and Razorpay integration

## Overview

Popup Store supports **two payment methods**:

1. **UPI Direct** - Customer pays directly via UPI, uploads proof (2% discount)
2. **Razorpay** - Full payment gateway with cards, wallets, etc. (2-3% fee)

***

## Payment Method Comparison

| Feature          | UPI Direct                     | Razorpay                        |
| ---------------- | ------------------------------ | ------------------------------- |
| **Fee**          | 0% (2% discount to customer)   | 2-3% (paid by customer)         |
| **Verification** | Manual (seller verifies proof) | Automatic                       |
| **Settlement**   | Direct to seller UPI           | 2-3 business days               |
| **Supported**    | UPI only                       | Cards, UPI, Wallets, NetBanking |
| **Best For**     | High-trust, repeat customers   | New customers, international    |

***

## UPI Direct Flow

### 1. Customer Checkout

```mermaid theme={null}
sequenceDiagram
    participant Customer
    participant Web
    participant Supabase
    participant Seller

    Customer->>Web: Select "UPI Direct"
    Web->>Web: Calculate discount (2%)
    Web->>Customer: Show final amount
    Customer->>Web: Proceed
    Web->>Supabase: Create order (status: payment_pending)
    Supabase-->>Web: Order ID
    Web->>Customer: Show UPI QR Code + VPA
```

### 2. Payment Proof Upload

The customer pays via their UPI app, then uploads a screenshot:

```typescript theme={null}
// Upload payment proof to Supabase Storage
const { data, error } = await supabase.storage
  .from('payment-proofs')
  .upload(`${orderId}/${Date.now()}.jpg`, file);

// Update order with proof URL
await supabase
  .from('orders')
  .update({
    payment_proof_url: data.path,
    status: 'proof_uploaded'
  })
  .eq('id', orderId);
```

### 3. Seller Verification

Seller receives email with payment proof:

```
Subject: New Order #ORD-2024-001

Payment Method: UPI Direct
Amount: ₹2,598 (₹2,650 - 2% discount)

[View Payment Proof]
[Verify & Approve]
```

Seller clicks "Verify & Approve" in iOS app:

```swift theme={null}
func verifyPayment(orderId: String) async {
    try await supabase
        .from("orders")
        .update(["status": "paid"])
        .eq("id", orderId)
        .execute()

    // Send confirmation email to customer
    await sendOrderConfirmation(orderId)
}
```

***

## Razorpay Flow

### 1. Initialize Payment

```typescript theme={null}
import Razorpay from 'razorpay';

const razorpay = new Razorpay({
  key_id: process.env.RAZORPAY_KEY_ID,
  key_secret: process.env.RAZORPAY_KEY_SECRET
});

// Create Razorpay order
const order = await razorpay.orders.create({
  amount: finalAmount * 100, // Amount in paise
  currency: 'INR',
  receipt: `order_${orderId}`,
  notes: {
    storeId: store.id,
    orderId: orderId
  }
});
```

### 2. Frontend Checkout

```typescript theme={null}
const options = {
  key: process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID,
  amount: order.amount,
  currency: order.currency,
  name: store.business_name,
  description: `Order #${orderNumber}`,
  order_id: order.id,
  handler: async (response) => {
    // Payment successful
    await verifyPayment({
      razorpay_order_id: response.razorpay_order_id,
      razorpay_payment_id: response.razorpay_payment_id,
      razorpay_signature: response.razorpay_signature
    });
  },
  prefill: {
    name: buyerName,
    email: buyerEmail,
    contact: buyerPhone
  },
  theme: {
    color: store.brand_color
  }
};

const razorpayInstance = new window.Razorpay(options);
razorpayInstance.open();
```

### 3. Webhook Verification

Razorpay sends webhooks for payment events:

```typescript api/webhooks/razorpay/route.ts theme={null}
import crypto from 'crypto';

export async function POST(req: Request) {
  const body = await req.text();
  const signature = req.headers.get('x-razorpay-signature');

  // Verify webhook signature
  const expectedSignature = crypto
    .createHmac('sha256', process.env.RAZORPAY_WEBHOOK_SECRET)
    .update(body)
    .digest('hex');

  if (signature !== expectedSignature) {
    return Response.json({ error: 'Invalid signature' }, { status: 400 });
  }

  const event = JSON.parse(body);

  if (event.event === 'payment.captured') {
    // Update order status
    await supabase
      .from('orders')
      .update({
        status: 'paid',
        razorpay_payment_id: event.payload.payment.entity.id
      })
      .eq('razorpay_order_id', event.payload.payment.entity.order_id);

    // Send confirmation emails
    await sendOrderConfirmation(orderId);
  }

  return Response.json({ received: true });
}
```

***

## Fee Calculation

### UPI Direct (2% Discount)

```typescript theme={null}
const subtotal = 2650; // Cart total
const discount = subtotal * 0.02; // 2% discount
const finalAmount = subtotal - discount; // ₹2,598
```

### Razorpay (2-3% Fee)

```typescript theme={null}
const subtotal = 2650;
const checkoutFee = subtotal * 0.025; // 2.5% average
const finalAmount = subtotal + checkoutFee; // ₹2,716.25
```

<Info>
  The fee is **charged to the customer**, not deducted from the seller's payout.
</Info>

***

## Configuration

### Razorpay Setup

1. **Create Razorpay Account**: [razorpay.com](https://razorpay.com)
2. **Get API Keys**: Dashboard → Settings → API Keys
3. **Add to Environment**:

```bash .env.local theme={null}
NEXT_PUBLIC_RAZORPAY_KEY_ID=rzp_test_xxxxx
RAZORPAY_KEY_SECRET=xxxxx
RAZORPAY_WEBHOOK_SECRET=xxxxx
```

4. **Configure Webhook**:
   * URL: `https://mypopup.shop/api/webhooks/razorpay`
   * Events: `payment.captured`, `payment.failed`
   * Secret: (copy from Razorpay dashboard)

### UPI Direct Setup

Seller configures UPI VPA in iOS app:

```swift theme={null}
struct PaymentSettingsView: View {
    @State private var upiId: String = ""

    var body: some View {
        Form {
            Section("UPI Direct") {
                TextField("UPI ID", text: $upiId)
                    .textInputAutocapitalization(.never)
                    .keyboardType(.emailAddress)

                Text("Example: seller@paytm")
                    .font(.caption)
                    .foregroundColor(.secondary)
            }

            Button("Save") {
                saveUpiSettings()
            }
        }
    }
}
```

***

## Security

### Payment Verification

**Razorpay Signature Verification:**

```typescript theme={null}
function verifyRazorpaySignature(
  orderId: string,
  paymentId: string,
  signature: string
): boolean {
  const body = `${orderId}|${paymentId}`;

  const expectedSignature = crypto
    .createHmac('sha256', process.env.RAZORPAY_KEY_SECRET)
    .update(body)
    .digest('hex');

  return signature === expectedSignature;
}
```

### UPI Proof Validation

Manual verification checklist for sellers:

* [ ] Amount matches order total
* [ ] UPI transaction ID is visible
* [ ] Timestamp is recent (within 1 hour of order)
* [ ] Recipient name/UPI ID matches store's UPI

<Warning>
  **Never** auto-approve UPI payments without manual verification. Fraudulent screenshots are common!
</Warning>

***

## Order Status Lifecycle

```mermaid theme={null}
stateDiagram-v2
    [*] --> payment_pending
    payment_pending --> proof_uploaded: UPI proof uploaded
    payment_pending --> paid: Razorpay webhook
    proof_uploaded --> paid: Seller verifies
    proof_uploaded --> cancelled: Invalid proof
    paid --> processing: Seller confirms
    processing --> shipped: Package dispatched
    shipped --> delivered: Customer receives
    delivered --> [*]
```

### Status Definitions

| Status            | Description          | Next Action      |
| ----------------- | -------------------- | ---------------- |
| `payment_pending` | Waiting for payment  | Customer pays    |
| `proof_uploaded`  | UPI proof submitted  | Seller verifies  |
| `paid`            | Payment confirmed    | Seller processes |
| `processing`      | Order being prepared | Seller ships     |
| `shipped`         | Package dispatched   | Delivery         |
| `delivered`       | Customer received    | Complete         |
| `cancelled`       | Order cancelled      | Refund (if paid) |

***

## Email Notifications

### Order Confirmation (Customer)

Sent when payment is confirmed:

```typescript theme={null}
await resend.emails.send({
  from: 'orders@mypopup.shop',
  to: buyerEmail,
  subject: `Order Confirmation - ${store.business_name}`,
  react: OrderConfirmationEmail({
    orderNumber,
    items,
    totalAmount,
    shippingAddress,
    paymentMethod
  })
});
```

### New Order Alert (Seller)

Sent immediately when order is placed:

```typescript theme={null}
await resend.emails.send({
  from: 'alerts@mypopup.shop',
  to: store.owner_email,
  subject: `New Order #${orderNumber}`,
  react: NewOrderEmail({
    orderNumber,
    customerName: buyerName,
    items,
    totalAmount,
    paymentMethod,
    paymentProofUrl // if UPI Direct
  })
});
```

***

## Testing

### Razorpay Test Mode

Use test credentials for development:

```bash theme={null}
# Test Key ID
rzp_test_xxxxxxxxxxxxxx

# Test Cards
4111 1111 1111 1111 (Visa - Success)
5555 5555 5555 4444 (Mastercard - Success)
4000 0000 0000 0002 (Declined)

# Test UPI
success@razorpay (Success)
failure@razorpay (Failed)
```

### UPI Direct Testing

Create a test order flow:

1. Use a dummy UPI screenshot
2. Upload to dev environment
3. Verify seller can see proof in iOS app
4. Approve/reject and check status updates

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Razorpay payment not capturing">
    Check:

    * API keys are correct (test vs live)
    * Webhook is configured with correct URL
    * Webhook secret matches environment variable
    * Order amount is in paise (multiply by 100)
  </Accordion>

  <Accordion title="UPI proof upload failing">
    Common issues:

    * File size > 5MB (compress image)
    * Storage bucket permissions (check RLS policies)
    * Invalid file type (only .jpg, .png, .webp)
    * Network timeout (increase timeout limit)
  </Accordion>

  <Accordion title="Payment verified but order still pending">
    This is a webhook delay. Check:

    * Webhook logs in Razorpay dashboard
    * Your webhook endpoint is reachable
    * Signature verification is passing
    * Database update query is running
  </Accordion>
</AccordionGroup>

***

<CardGroup cols={2}>
  <Card title="Razorpay Integration" icon="credit-card" href="/integrations/razorpay">
    Complete Razorpay setup guide
  </Card>

  <Card title="Order Fulfillment" icon="box" href="/seller/order-fulfillment">
    How sellers process and ship orders
  </Card>
</CardGroup>
