# Jivadaya Central Payment Gateway
## Official Developer Integration & Connection Guide

**Gateway Base URL:** `https://payment.jivadaya.org`  
**API Version:** 2.0 (HMAC Signed Server-to-Server)  
**Supported Gateways:** CCAvenue & Razorpay  

---

## 1. Overview & Architecture

The Jivadaya Central Payment Gateway allows any website or application (such as `pay.jivadaya.org`, `donate.jivadaya.org`, `vec.jivadaya.org`, or external partner sites) to accept payments via CCAvenue and Razorpay through a single endpoint.

No payment gateway API keys or merchant credentials are required on your client server. All encryption, merchant keys, and payment capture are handled centrally by `payment.jivadaya.org`.

### Secure Payment Flow

```text
  Client Site                      Central Hub                   CCAvenue / Razorpay
(Your Website)               (payment.jivadaya.org)               (Payment Gateway)
      │                                │                                  │
  1.  │── POST /api/payment/initiate ─►│                                  │
      │   (amount, order_id,           │                                  │
      │    callback_url, webhook_url)  │── 2. Encrypt & Redirect ────────►│
      │                                │                                  │
      │                                │                                  │ 3. User Pays
      │                                │                                  │
      │                                │◄── 4. Server-to-Server Callback ─│
      │                                │       (Encrypted Result)         │
      │                                │                                  │
      │◄── 5. Signed Webhook (S2S) ────│       [Updates Hub DB]           │
      │   (X-Jivadaya-Signature HMAC)  │                                  │
      │                                │                                  │
      │    6. User Browser Redirect ───│                                  │
      │◄── (callback_url?order_id=...)─│                                  │
```

> [!CAUTION]
> **Crucial Security Rule:**
> Never trust the browser redirect URL (`callback_url?status=Success`) to mark an order as paid or deliver services. The redirect is purely for displaying a confirmation page to the user. Always rely exclusively on the **HMAC-signed server-to-server webhook** to update your database.

---

## 2. Initiation Endpoint

```http
POST https://payment.jivadaya.org/api/payment/initiate
Content-Type: application/x-www-form-urlencoded (or application/json)
```

### Request Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `amount` | Number | **Yes** | Amount in INR (e.g. `200`, `1100.00`). |
| `billing_name` | String | **Yes** | Full name of donor / student / payer. |
| `billing_tel` | String | Recommended | 10-digit mobile number. |
| `billing_email` | String | Recommended | Email address for payment receipt. |
| `order_id` | String | Recommended | Unique order / reference ID generated by your system. |
| `pg` | String | Optional | Gateway choice: `ccavenue` (default) or `razorpay`. |
| `callback_url` | URL | **Yes** | Where donor is redirected after payment (e.g. `https://yoursite.com/status`). |
| `webhook_url` | URL | **Yes** | Your backend endpoint that receives the signed payment result. |
| `campaign_slug` | String | Optional | Campaign identifier (e.g. `general`, `vec26-registration`). |
| `target_table_id` | String | Optional | Target table identifier (e.g. `students`, `sales_serp`, `jd_donations`). |
| `need_80g` | String | Optional | `Yes` or `No` for 80G tax exemption receipts. |
| `pan_number` | String | Optional | Donor PAN number (required if `need_80g` is `Yes`). |

---

## 3. How to Connect (Integration Methods)

### Method A — Plain HTML Form (Simplest)

```html
<form action="https://payment.jivadaya.org/api/payment/initiate" method="POST">
  <!-- Required Details -->
  <input type="number" name="amount" value="200" required>
  <input type="text" name="billing_name" placeholder="Full Name" required>
  <input type="tel" name="billing_tel" placeholder="Mobile Number" required>
  <input type="email" name="billing_email" placeholder="Email Address">

  <!-- Integration Settings -->
  <input type="hidden" name="order_id" value="VEC26-ABC12345">
  <input type="hidden" name="pg" value="ccavenue">
  <input type="hidden" name="callback_url" value="https://yoursite.com/status">
  <input type="hidden" name="webhook_url" value="https://yoursite.com/api/webhook/payment">

  <button type="submit">Pay Now</button>
</form>
```

### Method B — JavaScript SDK

Load the connector script on your webpage:
```html
<script src="https://payment.jivadaya.org/payment-connector.js"></script>
```

Trigger payment programmatically:
```javascript
JivadayaPayment.initiate({
  amount: "500",
  billing_name: "Aarav Patel",
  billing_tel: "9876543210",
  billing_email: "aarav@example.com",
  order_id: "ORD_" + Date.now(),
  pg: "ccavenue", // or "razorpay"
  callback_url: "https://yoursite.com/status",
  webhook_url: "https://yoursite.com/api/webhook/payment"
});
```

### Method C — Dynamic Form Submission (React / Next.js)

```typescript
function initiatePayment(paymentData: { amount: string; name: string; mobile: string; orderId: string }) {
  const form = document.createElement('form');
  form.method = 'POST';
  form.action = 'https://payment.jivadaya.org/api/payment/initiate';

  const fields = {
    amount: paymentData.amount,
    billing_name: paymentData.name,
    billing_tel: paymentData.mobile,
    order_id: paymentData.orderId,
    pg: 'ccavenue',
    callback_url: `${window.location.origin}/status`,
    webhook_url: `${window.location.origin}/api/webhook/payment`,
  };

  Object.entries(fields).forEach(([key, val]) => {
    const input = document.createElement('input');
    input.type = 'hidden';
    input.name = key;
    input.value = val;
    form.appendChild(input);
  });

  document.body.appendChild(form);
  form.submit();
}
```

---

## 4. Webhook Implementation & Security (Required)

Upon transaction completion, `payment.jivadaya.org` sends a direct server-to-server POST request to your `webhook_url` with the `X-Jivadaya-Signature` header.

### Webhook HTTP Headers
```http
POST /api/webhook/payment
Content-Type: application/json
X-Jivadaya-Signature: sha256=5f8d9b... (HMAC-SHA256 hex)
```

### Webhook JSON Payload
```json
{
  "status": "Success",
  "order_status": "Success",
  "order_id": "VEC26-ABC12345",
  "payment_id": "312009845612",
  "tracking_id": "312009845612",
  "amount": "200.00",
  "gateway": "CCAvenue",
  "timestamp": "2026-09-20T10:00:00.000Z"
}
```

### Status Values
- `Success` — Payment verified and captured.
- `Failure` — Transaction declined by bank/gateway.
- `Aborted` — Payment cancelled by user.

---

### Verifying Signatures

#### Node.js / Express Implementation

> ⚠️ **Important:** You must use `express.raw({ type: 'application/json' })` for your webhook route **before** `express.json()` so that the raw payload buffer is preserved for HMAC verification.

```javascript
import express from 'express';
import crypto from 'crypto';

const app = express();
const WEBHOOK_SECRET = process.env.JIVADAYA_WEBHOOK_SECRET;

// 1. Webhook route with raw body buffer
app.post('/api/webhook/payment', express.raw({ type: 'application/json' }), async (req, res) => {
  const rawBody = req.body;
  const sigHeader = req.headers['x-jivadaya-signature'] || '';

  // Step 1: Verify HMAC signature
  if (!sigHeader.startsWith('sha256=')) {
    return res.status(401).json({ error: 'Missing or malformed signature' });
  }

  const receivedSig = sigHeader.slice(7);
  const expectedSig = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(rawBody)
    .digest('hex');

  const a = Buffer.from(receivedSig, 'hex');
  const b = Buffer.from(expectedSig, 'hex');
  const isValid = a.length === b.length && crypto.timingSafeEqual(a, b);

  if (!isValid) {
    console.error('❌ Webhook rejected: Signature mismatch');
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Step 2: Parse validated payload
  const payload = JSON.parse(rawBody.toString('utf8'));

  // Step 3: Handle test pings
  if (payload._test) {
    return res.status(200).json({ received: true, test: true });
  }

  // Step 4: Update your database safely
  if (payload.status === 'Success') {
    // e.g. await db.orders.update({ where: { orderId: payload.order_id }, data: { status: 'PAID' } });
    console.log(`✅ Order ${payload.order_id} marked as PAID`);
  } else {
    console.log(`❌ Order ${payload.order_id} marked as ${payload.status}`);
  }

  // Step 5: Always respond with 200 OK
  return res.status(200).json({ received: true });
});
```

#### PHP Implementation

```php
<?php
$secret = getenv('JIVADAYA_WEBHOOK_SECRET');
$rawBody = file_get_contents('php://input');
$sigHeader = $_SERVER['HTTP_X_JIVADAYA_SIGNATURE'] ?? '';

if (strpos($sigHeader, 'sha256=') !== 0) {
    http_response_code(401);
    echo json_encode(["error" => "Missing or invalid signature header"]);
    exit;
}

$receivedSig = substr($sigHeader, 7);
$expectedSig = hash_hmac('sha256', $rawBody, $secret);

if (!hash_equals($expectedSig, $receivedSig)) {
    http_response_code(401);
    echo json_encode(["error" => "Signature mismatch"]);
    exit;
}

$data = json_decode($rawBody, true);

if (!empty($data['_test'])) {
    http_response_code(200);
    echo json_encode(["received" => true, "test" => true]);
    exit;
}

if ($data['status'] === 'Success') {
    // Update order status in your database
}

http_response_code(200);
echo json_encode(["received" => true]);
?>
```

---

## 5. Direct Status Verification API (Fallback Query)

If your user lands back on `callback_url` before the webhook arrives, your server can query the Central Hub directly:

```http
GET https://payment.jivadaya.org/api/payment/status/:order_id
Accept: application/json
```

### Response (200 OK)
```json
{
  "order_id": "VEC26-ABC12345",
  "status": "Success",
  "order_status": "Success",
  "amount": "200.00",
  "payment_id": "312009845612",
  "tracking_id": "312009845612",
  "payment_mode": "Net Banking",
  "billing_name": "Aarav Patel"
}
```

If the order does not exist or has not been processed yet, the API returns:
```json
{ "status": "NOT_FOUND", "error": "Order not found" }
```

---

## 6. Client Onboarding & Admin Portal

You can register any new domain and generate a webhook secret instantly using the visual admin dashboard:

👉 **`https://payment.jivadaya.org/admin.html`**

1. Enter your `ADMIN_API_KEY` (saved automatically in browser storage).
2. Go to **"Connect New Website"**, input the domain (e.g. `mytemple.org`), and click **"Generate Secret & Register"**.
3. Copy the generated secret and send the **pre-filled handover message** to the developer.
4. Test the live integration using **"Test Live Webhook"** to ensure HTTP 200 OK.

---

## 7. Go-Live Checklist

- [ ] Domain registered in `ph_clients` via `https://payment.jivadaya.org/admin.html`.
- [ ] `JIVADAYA_WEBHOOK_SECRET` stored in your client server's `.env` file.
- [ ] `webhook_url` endpoint implemented with raw body buffer and HMAC verification.
- [ ] Tested live test ping from `admin.html` and verified it returns `HTTP 200`.
- [ ] Form submission generates a unique `order_id` for each checkout.
- [ ] Performed an end-to-end ₹1 test transaction with CCAvenue/Razorpay.
