# UPIDrop — REST API & Webhook Reference

Complete, simple, step-by-step developer reference for the UPIDrop Zero-Fee UPI Payment Gateway API.

---

## 1. Quick Overview

- **Base URL:** `https://upidrop-pg.upidrop-payment-gateway.workers.dev`
- **Hosted Checkout URL:** `https://upidrop.roori.in/pay.html`
- **Authentication:** Pass your Merchant Public Key in the `X-API-Key` HTTP header.
- **Content Type:** All requests must include `Content-Type: application/json`.
- **Zero Commission (0%):** UPIDrop does not take a percentage or per-transaction fee. Customer payments transfer directly to your merchant UPI VPA.

---

## 2. Authentication & Keys

Every merchant gets two keys in their UPIDrop Dashboard:
1. **Public Key (`pk_live_...`)**: Safe to use in client-side code, JS SDK, mobile apps, or frontend buttons.
2. **Secret Key (`sk_live_...`)**: Keep strictly private on your backend server.
3. **Webhook Secret (`whsec_...`)**: Used to verify the HMAC-SHA256 signature of incoming webhooks.

---

## 3. Order Lifecycle & How Payments Work

```
1. CREATE ORDER
   Your backend or frontend creates an order.
   POST /api/v1/orders
   ↓
2. PRESENT PAYMENT TO CUSTOMER
   Customer scans the UPI QR code or taps a 1-click app intent (GPay, PhonePe, Paytm, CRED).
   Hosted checkout URL: https://upidrop.roori.in/pay.html?orderId={id}&token={token}
   ↓
3. SMARTTRACK RECONCILIATION
   Option A (Automated): SmartTrack detects bank SMS/S2S callback with matching Dynamic Paisa amount.
   Option B (Manual UTR): Customer enters 12-digit UTR from their UPI app receipt.
   ↓
4. VERIFICATION & SETTLEMENT
   Status changes to 'paid'.
   UPIDrop fires a signed HMAC webhook to your server (`order.paid`).
   Customer is redirected to your thank-you page.
```

---

## 4. API Endpoints

### 4.1 Create an Order

Create a new payment order. This returns an `orderId`, an order security `token`, and an intelligent `upiDeepLink` with Dynamic Paisa offset for zero-friction auto-matching.

**Request:**
- **Method:** `POST`
- **Path:** `/api/v1/orders`
- **Headers:**
  - `X-API-Key: pk_live_YOUR_PUBLIC_KEY`
  - `Content-Type: application/json`

**Request Body:**
```json
{
  "amount": 499.00,
  "currency": "INR",
  "customer_name": "Aarav Sharma",
  "customer_email": "aarav@example.com",
  "customer_phone": "9876543210",
  "item_description": "Course Access / Order #1042",
  "redirect_url": "https://yoursite.com/thank-you",
  "metadata": {
    "cart_id": "cart_9812",
    "customer_id": "cust_55"
  }
}
```

**Response (`201 Created`):**
```json
{
  "success": true,
  "orderId": "ORD_01JK8Z9Y9G2A",
  "token": "tok_sec_9918274a7b...",
  "amount": 499.14,
  "baseAmount": 499.00,
  "paisaOffset": 0.14,
  "upiDeepLink": "upi://pay?pa=merchant@okaxis&pn=Merchant+Store&am=499.14&cu=INR&tn=ORD_01JK8Z9Y9G2A",
  "qrPayload": "upi://pay?pa=merchant@okaxis&pn=Merchant+Store&am=499.14&cu=INR&tn=ORD_01JK8Z9Y9G2A",
  "checkoutUrl": "https://upidrop.roori.in/pay.html?orderId=ORD_01JK8Z9Y9G2A&token=tok_sec_9918274a7b...",
  "expiresAt": "2026-09-09T18:45:00.000Z"
}
```

---

### 4.2 Submit 12-Digit UTR (Customer or S2S)

Submit the 12-digit UPI Transaction Reference (UTR) number after paying in a UPI app.

**Request:**
- **Method:** `POST`
- **Path:** `/api/v1/orders/:orderId/utr`
- **Headers:** `Content-Type: application/json`

**Request Body:**
```json
{
  "utr": "423871928374",
  "token": "tok_sec_9918274a7b..."
}
```

**Response (`200 OK`):**
```json
{
  "success": true,
  "status": "pending_verification",
  "message": "UTR recorded. SmartTrack verification in progress."
}
```

---

### 4.3 Check Order Status (Public Polling)

Poll the public status of an order without exposing sensitive keys. Perfect for frontend checkout pages and mobile apps.

**Request:**
- **Method:** `GET`
- **Path:** `/api/v1/orders/:orderId/public`

**Response (`200 OK`):**
```json
{
  "success": true,
  "order": {
    "id": "ORD_01JK8Z9Y9G2A",
    "amount": 499.14,
    "status": "paid",
    "utr": "423871928374",
    "item_description": "Course Access / Order #1042",
    "created_at": "2026-09-09T18:30:00.000Z",
    "expires_at": "2026-09-09T18:45:00.000Z"
  }
}
```

#### Order Statuses:
- `pending`: Order created; waiting for payment.
- `pending_verification`: UTR submitted by user; awaiting bank reconciliation.
- `payment_detected`: Bank inbound notification matched; final verification occurring.
- `paid`: Payment verified and confirmed. Goods/services should be fulfilled.
- `late_success`: Payment arrived after original 15-minute expiry, successfully matched and honored.
- `expired`: 15-minute checkout window lapsed without payment.
- `rejected`: UTR already used or invalid.

---

### 4.4 Inbound Credit Callback (SmartTrack S2S Bridge)

Used by automated bank notification bridges (Paytm Business, PhonePe Business, or Android SMS Bridge) to deliver incoming UPI credit notifications to UPIDrop.

**Request:**
- **Method:** `POST`
- **Path:** `/api/v1/inbound-credit`
- **Headers:**
  - `X-API-Key: pk_live_YOUR_PUBLIC_KEY`
  - `Content-Type: application/json`

**Request Body:**
```json
{
  "amount": 499.14,
  "utr": "423871928374",
  "sender_vpa": "customer@oksbi",
  "timestamp": "2026-09-09T18:32:10.000Z",
  "source": "android_sms_bridge"
}
```

**Response (`200 OK`):**
```json
{
  "success": true,
  "matched": true,
  "orderId": "ORD_01JK8Z9Y9G2A",
  "confidenceScore": 98
}
```

---

## 5. Webhook Notifications

When an order reaches `paid` or `late_success`, UPIDrop immediately sends an HTTP `POST` request to the **Webhook URL** specified in your Dashboard Settings.

### 5.1 Webhook Headers
- `Content-Type: application/json`
- `X-UPIDrop-Signature: <HMAC-SHA256 hex string>`
- `User-Agent: UPIDrop-Webhook-Dispatcher/2.0`

### 5.2 Webhook Payload
```json
{
  "event": "order.paid",
  "timestamp": 1788950000,
  "order": {
    "id": "ORD_01JK8Z9Y9G2A",
    "amount": 499.14,
    "base_amount": 499.00,
    "paisa_offset": 0.14,
    "currency": "INR",
    "status": "paid",
    "utr": "423871928374",
    "customer_name": "Aarav Sharma",
    "customer_email": "aarav@example.com",
    "customer_phone": "9876543210",
    "auto_matched_via": "smarttrack_sms",
    "metadata": {
      "cart_id": "cart_9812",
      "customer_id": "cust_55"
    }
  }
}
```

### 5.3 Signature Verification (Node.js Example)

Always verify the signature using your `whsec_...` key in constant time to prevent tampering:

```javascript
import crypto from 'crypto';

export function verifyWebhook(rawBodyString, signatureHeader, webhookSecret) {
  const hmac = crypto.createHmac('sha256', webhookSecret);
  hmac.update(rawBodyString);
  const expectedSignature = hmac.digest('hex');

  const trusted = Buffer.from(expectedSignature, 'utf8');
  const untrusted = Buffer.from(signatureHeader || '', 'utf8');

  if (trusted.length !== untrusted.length) {
    return false;
  }

  return crypto.timingSafeEqual(trusted, untrusted);
}
```

---

## 6. Error Codes Reference

| HTTP Code | Error Message | Description | Solution |
| :--- | :--- | :--- | :--- |
| `401` | `Invalid API key` | Provided key was missing, invalid, or belongs to another merchant. | Verify `X-API-Key` matches `pk_live_...` in Dashboard. |
| `403` | `KYC required to accept payments` | Merchant KYC has not been completed or verified. | Complete KYC in Dashboard Settings. |
| `404` | `Order not found` | The requested `orderId` does not exist. | Check order ID formatting (`ORD_...`). |
| `409` | `Duplicate UTR detected` | This 12-digit UTR has already been claimed on another order. | Prevent customer from submitting identical bank references. |
| `422` | `Order is expired` | Attempting to pay an order whose 15-minute window has elapsed. | Create a new order. |

---

## 7. Developer Support

- **Dashboard:** [https://upidrop.roori.in](https://upidrop.roori.in)
- **Integration Guides:**
  - [JavaScript SDK Guide](/docs/javascript-sdk.md)
  - [WooCommerce Guide](/docs/woocommerce.md)
  - [Shopify Guide](/docs/shopify.md)
  - [Wix Guide](/docs/wix.md)
  - [AI Coding Tools Guide](/docs/ai-coding-tools.md)
- **Email:** [rohitmore3192@gmail.com](mailto:rohitmore3192@gmail.com)
