Skip to main content

Overview

The Payments API integrates with Razorpay to handle premium template purchases. It provides endpoints for creating payment orders and verifying payment signatures.
All payment endpoints require authentication. Include your Clerk JWT token in the Authorization header.

Create Payment Order

Create a Razorpay order for purchasing a premium template.
POST /api/v1/razorpay/create-order

Headers

Authorization
string
required
Bearer token from Clerk authentication
Content-Type
string
required
Must be application/json

Request Body

templateName
string
required
The name/title of the template to purchaseExample: "Modern Portfolio", "Developer Showcase"
currency
enum
required
Currency for the transaction
  • INR - Indian Rupee
  • USD - US Dollar

Response

status
boolean
true if order created successfully, false otherwise
message
string
Human-readable status message
data
object
Razorpay order details (only present on success)

Example Request

curl -X POST https://api.gitfolio.in/api/v1/razorpay/create-order \
  -H "Authorization: Bearer your_clerk_token" \
  -H "Content-Type: application/json" \
  -d '{
    "templateName": "Modern Portfolio",
    "currency": "INR"
  }'

Success Response

Status: 201 Created
{
  "status": true,
  "message": "Order Created.",
  "data": {
    "order_id": "order_MQRTx7y3zK9XYZ",
    "amount": 49900,
    "currency": "INR",
    "receipt": "receipt_abc123"
  }
}
Amount is in smallest currency unit:
  • INR: 49900 paise = ₹499
  • USD: 999 cents = $9.99

Error Responses

Unauthorized

Status: 401 Unauthorized
{
  "status": false,
  "message": "Unauthorized"
}

Invalid Parameters

Status: 400 Bad Request
{
  "status": false,
  "message": "Invalid Parameters"
}

Template Not Found

Status: 404 Not Found
{
  "status": false,
  "message": "No template found."
}

Free Template

If you try to create an order for a free template: Status: 200 OK
{
  "status": false,
  "message": "Order Failed , Free Template!"
}

Order Creation Failed

Status: 200 OK
{
  "status": false,
  "message": "Order Failed"
}

Server Error

Status: 500 Internal Server Error
{
  "status": false,
  "message": "Internal Server Error"
}

Verify Payment

Verify the payment signature after user completes payment on Razorpay.
POST /api/v1/razorpay/verify-payment

Headers

Authorization
string
required
Bearer token from Clerk authentication
Content-Type
string
required
Must be application/json

Request Body

orderId
string
required
The Razorpay order ID from the create-order response
rzp_payment_id
string
required
The Razorpay payment ID returned after successful payment
rzp_signature
string
required
The payment signature generated by Razorpay

Response

status
boolean
true if payment verified successfully, false otherwise
message
string
Human-readable status message

Example Request

curl -X POST https://api.gitfolio.in/api/v1/razorpay/verify-payment \
  -H "Authorization: Bearer your_clerk_token" \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "order_MQRTx7y3zK9XYZ",
    "rzp_payment_id": "pay_MQRTxAbCdEfGhI",
    "rzp_signature": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
  }'

Success Response

Status: 200 OK
{
  "status": true,
  "message": "Payment Verified."
}
After successful verification:
  • The template is added to the user’s purchased templates
  • The payment record is updated with SUCCESS status
  • The user can now use the premium template

Error Responses

Unauthorized

Status: 401 Unauthorized
{
  "status": false,
  "message": "Unauthorized"
}

Invalid Parameters

Status: 400 Bad Request
{
  "status": false,
  "message": "Invalid Parameters"
}

Verification Failed

Status: 403 Forbidden
{
  "status": false,
  "message": "Payment not Verified."
}
This indicates:
  • Invalid signature
  • Payment ID doesn’t match order
  • Tampered payment data
  • Payment was not successful

Server Error

Status: 500 Internal Server Error
{
  "status": false,
  "message": "Internal Server Error"
}

Payment Flow

The complete payment flow involves multiple steps:

Step 1: Create Order

Create a Razorpay order on your server:
const orderResponse = await fetch(
  'https://api.gitfolio.in/api/v1/razorpay/create-order',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${clerkToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      templateName: 'Modern Portfolio',
      currency: 'INR'
    })
  }
);

const orderData = await orderResponse.json();

if (!orderData.status) {
  // Handle error
  console.error(orderData.message);
  return;
}

const { order_id, amount, currency } = orderData.data;

Step 2: Initialize Razorpay Checkout

Use the Razorpay client SDK to collect payment:
const options = {
  key: 'YOUR_RAZORPAY_KEY_ID', // Your Razorpay key
  amount: amount,
  currency: currency,
  name: 'GitFolio',
  description: 'Premium Template Purchase',
  order_id: order_id,
  handler: function(response) {
    // Payment successful - verify on server
    verifyPayment({
      orderId: order_id,
      rzp_payment_id: response.razorpay_payment_id,
      rzp_signature: response.razorpay_signature
    });
  },
  prefill: {
    name: userFullName,
    email: userEmail
  },
  theme: {
    color: '#3B82F6'
  }
};

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

Step 3: Verify Payment

After user completes payment, verify the signature:
async function verifyPayment(paymentData) {
  const response = await fetch(
    'https://api.gitfolio.in/api/v1/razorpay/verify-payment',
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${clerkToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(paymentData)
    }
  );

  const result = await response.json();

  if (result.status) {
    // Payment verified successfully
    console.log('Payment successful!');
    // Redirect to dashboard or template selection
    window.location.href = '/dashboard/templates';
  } else {
    // Verification failed
    console.error('Payment verification failed');
    alert('Payment verification failed. Please contact support.');
  }
}

Complete Integration Example

class TemplatePayment {
  constructor(clerkToken, razorpayKeyId) {
    this.clerkToken = clerkToken;
    this.razorpayKeyId = razorpayKeyId;
  }

  async purchaseTemplate(templateName, currency, userInfo) {
    try {
      // Step 1: Create order
      const order = await this.createOrder(templateName, currency);
      
      if (!order.status) {
        throw new Error(order.message);
      }

      // Step 2: Open Razorpay checkout
      return new Promise((resolve, reject) => {
        const options = {
          key: this.razorpayKeyId,
          amount: order.data.amount,
          currency: order.data.currency,
          name: 'GitFolio',
          description: `Purchase ${templateName}`,
          order_id: order.data.order_id,
          handler: async (response) => {
            // Step 3: Verify payment
            const verified = await this.verifyPayment({
              orderId: order.data.order_id,
              rzp_payment_id: response.razorpay_payment_id,
              rzp_signature: response.razorpay_signature
            });
            
            if (verified.status) {
              resolve(verified);
            } else {
              reject(new Error('Payment verification failed'));
            }
          },
          prefill: {
            name: userInfo.name,
            email: userInfo.email
          },
          theme: {
            color: '#3B82F6'
          },
          modal: {
            ondismiss: () => {
              reject(new Error('Payment cancelled by user'));
            }
          }
        };

        const rzp = new Razorpay(options);
        rzp.open();
      });
    } catch (error) {
      console.error('Payment failed:', error);
      throw error;
    }
  }

  async createOrder(templateName, currency) {
    const response = await fetch(
      'https://api.gitfolio.in/api/v1/razorpay/create-order',
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${this.clerkToken}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ templateName, currency })
      }
    );
    return await response.json();
  }

  async verifyPayment(paymentData) {
    const response = await fetch(
      'https://api.gitfolio.in/api/v1/razorpay/verify-payment',
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${this.clerkToken}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(paymentData)
      }
    );
    return await response.json();
  }
}

// Usage
const payment = new TemplatePayment(userClerkToken, 'rzp_live_xxxxx');

try {
  await payment.purchaseTemplate(
    'Modern Portfolio',
    'INR',
    { name: 'John Doe', email: '[email protected]' }
  );
  console.log('Template purchased successfully!');
} catch (error) {
  console.error('Purchase failed:', error.message);
}

Template Pricing

Templates have different pricing based on category:
  • Free Templates: No payment required, category = "Free"
  • Premium Templates: Require payment, have both INR and USD pricing
Pricing is stored in the Template model:
{
  id: string,
  title: string,
  category: string,  // "Free" or "Premium"
  USDpricing: number, // Price in USD (e.g., 9.99)
  INRpricing: number  // Price in INR (e.g., 499)
}
The API automatically:
  • Rejects payment creation for free templates
  • Selects the correct pricing based on currency
  • Converts to smallest unit (paise/cents) for Razorpay

Payment Records

All payments are tracked in the database with the following statuses:
status
enum
Payment status in the database
  • PENDING - Order created, payment not yet completed
  • SUCCESS - Payment verified successfully
  • FAILED - Payment failed or verification failed
You can retrieve payment history using the Dashboard API:
GET /api/v1/dashboard/user/payments

Security Best Practices

Always verify payment signatures on the server:
  • Client sends payment response to server
  • Server verifies signature using Razorpay secret
  • Only grant access after server verification
The API automatically checks:
  • User is authenticated
  • Order belongs to the authenticated user
  • Payment ID matches the order
For production, implement Razorpay webhooks to:
  • Get payment confirmation asynchronously
  • Handle failed payments
  • Process refunds
  • Update subscription status
  • Keep Razorpay secret key on server only
  • Use key ID on client-side only
  • Don’t log sensitive payment data

Testing

Test Mode

Razorpay provides test mode for development:
  1. Use test API keys (starts with rzp_test_)
  2. Use test card numbers for payments
  3. No real money is charged

Test Cards

Razorpay test card numbers: Successful Payment:
  • Card: 4111 1111 1111 1111
  • CVV: Any 3 digits
  • Expiry: Any future date
Failed Payment:
  • Card: 4000 0000 0000 0002
  • CVV: Any 3 digits
  • Expiry: Any future date
See Razorpay test cards for more.

Currency Support

Currently supported currencies:
CurrencyCodeSmallest Unit
Indian RupeeINRPaise (1/100)
US DollarUSDCents (1/100)
To add more currencies:
  1. Add pricing columns to Template model
  2. Update create-order logic
  3. Configure Razorpay account for multi-currency

Build docs developers (and LLMs) love