added gst and pan

This commit is contained in:
Mohammad Yaseen 2025-12-18 14:44:08 +05:30
parent a00d81a3fd
commit 83bd904299
22 changed files with 2077 additions and 462 deletions

66
GST_SETUP_INSTRUCTIONS.md Normal file
View File

@ -0,0 +1,66 @@
# GST Verification Setup Instructions
## Problem Identified ✅
The GST credentials you provided are **NOT in the `.env` file**. The environment variables are `undefined` when the app runs.
## Solution
You need to **add the following lines to your `.env` file**:
```env
# GST Verification (Setu)
GST_PROVIDER_URL=https://dg-sandbox.setu.co/api/verify/gst
GST_CLIENT_ID=292c6e76-dabf-49c4-8e48-90fba2916673
GST_CLIENT_SECRET=7IZMe9zvoBBuBukLiCP7n4KLwSOy11oP
GST_PRODUCT_INSTANCE_ID=69e23f7f-4f71-412e-aec6-b1da3fb77c6f
```
## Steps to Fix
1. **Open your `.env` file** (it's in the root of your project)
2. **Add the GST credentials** (copy the lines above)
3. **Restart your server**:
- Stop the current server (Ctrl+C in terminal)
- Run `npm run dev` again
4. **Test the endpoint** with this curl command:
### PowerShell (Windows):
```powershell
Invoke-WebRequest -Uri "http://localhost:3000/v1/gst/verify/27AAACM1234A1Z5" -Headers @{"x-api-key"="vf_test_369afc881da118c02ade27d323aaf3c186945edce6bcdf02"} -Method GET | Select-Object -ExpandProperty Content
```
### Bash/CMD (if you have curl.exe):
```bash
curl -X GET http://localhost:3000/v1/gst/verify/27AAACM1234A1Z5 -H "x-api-key: vf_test_369afc881da118c02ade27d323aaf3c186945edce6bcdf02"
```
## What I Fixed
1. ✅ **Applied the same fix that worked for PAN verification**:
- Created fresh axios instance to avoid global defaults pollution
- Clean credentials by trimming whitespace
- Added detailed debug logging
- Enhanced error handling for auth failures
2. ✅ **The code is ready** - it just needs the credentials in `.env`
## After Adding Credentials
Once you add the credentials to `.env` and restart, you should see:
**Debug logs in terminal:**
```
[GST Service] Sending request to provider: {
url: 'https://dg-sandbox.setu.co/api/verify/gst',
'x-client-id': '292c6e76-dabf-49c4-8e48-90fba2916673',
'x-client-secret': '****11oP',
'x-product-instance-id': '69e23f7f-4f71-412e-aec6-b1da3fb77c6f',
requestBody: { gstin: '27AAACM1234A1Z5' }
}
```
**Note**: If you still get authentication errors after adding to `.env`, it means the Setu sandbox credentials themselves are invalid or expired. You'll need to verify them with Setu.

View File

@ -27,6 +27,13 @@ npm install
PORT=3000
NODE_ENV=development
DATABASE_URL=postgres://india_api_2025:<PASSWORD>@localhost:5434/india-api-tech4biz
# Setu API Credentials for PAN Verification
PAN_PROVIDER_URL=https://dg-sandbox.setu.co/api/verify/pan
PAN_CLIENT_ID=your-client-id-here
PAN_CLIENT_SECRET=your-client-secret-here
PAN_PRODUCT_INSTANCE_ID=your-product-instance-id-here
# optional: JWT_SECRET, REDIS_URL, etc.
```
@ -52,16 +59,96 @@ verify-india-api/
└── README.md
```
## API Key Format
## Getting Your API Key
### Quick Method (For Testing)
Run the test API key creation script:
```bash
npm run create-test-key
```
This will create a test API key and display it in the console.
### Sign Up Method (For Production)
Create an account to get an API key:
```bash
curl -X POST http://localhost:3000/v1/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "your-email@example.com",
"password": "your-password-123",
"company_name": "Your Company"
}'
```
The response will include your `api_key` - save it immediately as it's only shown once!
### API Key Format
API keys start with `vf_live_` or `vf_test_`:
```
vf_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
vf_test_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
```
Authentication via header:
### Using API Key
Send it in the request header:
```
X-API-Key: vf_live_xxx
```
or
```
x-api-key: vf_live_xxx
```
**See `HOW_TO_GET_API_KEY.md` for detailed instructions.**
## PAN Verification (Setu Integration)
PAN verification is powered by Setu API. To use this feature:
1. Get your Setu API credentials:
- `PAN_PROVIDER_URL`: Setu API endpoint (default: https://dg-sandbox.setu.co/api/verify/pan)
- `PAN_CLIENT_ID`: Your Setu client ID (UUID format)
- `PAN_CLIENT_SECRET`: Your Setu client secret
- `PAN_PRODUCT_INSTANCE_ID`: Your Setu product instance ID (UUID format)
2. Add them to your `.env` file (see Setup section above)
3. Verify a PAN:
```bash
curl -X POST http://localhost:3000/v1/pan/verify \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"pan": "ABCDE1234A",
"reason": "KYC verification for account opening"
}'
```
**Response:**
```json
{
"success": true,
"data": {
"pan": "ABCDE1234A",
"full_name": "Kumar Gaurav Rathod",
"first_name": "Gaurav",
"middle_name": "Kumar",
"last_name": "Rathod",
"category": "Individual or Person",
"aadhaar_seeding_status": "LINKED",
"status": "VALID",
"setu_response_id": "0e370877-f860-4b5c-858b-42aebfea4879",
"setu_trace_id": "1-69437d63-75ac46a51036ab2233854e23",
"setu_message": "PAN is valid."
},
"meta": {
"request_id": "req_pan_1234567890",
"credits_used": 1,
"credits_remaining": 999,
"source": "setu-pan"
},
"timestamp": "2024-01-01T00:00:00.000Z"
}
```
## Response Format

201
SETU_PAN_INTEGRATION.md Normal file
View File

@ -0,0 +1,201 @@
# Setu PAN Verification Integration Guide
## Overview
This document explains how the Setu PAN verification API integration works and how it's implemented in this codebase.
## How Setu API Works
### 1. API Endpoint
- **URL**: `https://dg-sandbox.setu.co/api/verify/pan`
- **Method**: `POST`
- **Content-Type**: `application/json`
### 2. Authentication Headers
Setu API requires three custom headers for authentication:
- `x-client-id`: Your Setu client ID (UUID format)
- `x-client-secret`: Your Setu client secret
- `x-product-instance-id`: Your Setu product instance ID (UUID format)
### 3. Request Body
```json
{
"pan": "ABCDE1234A",
"consent": "Y",
"reason": "Reason for verifying PAN set by the developer"
}
```
**Fields:**
- `pan` (required): 10-character PAN number (format: 5 letters + 4 digits + 1 letter)
- `consent` (required): Must be "Y" to indicate user consent
- `reason` (required): Reason for verification
### 4. Response Format
```json
{
"id": "0e370877-f860-4b5c-858b-42aebfea4879",
"message": "PAN is valid.",
"data": {
"full_name": "Kumar Gaurav Rathod",
"first_name": "Gaurav",
"middle_name": "Kumar",
"last_name": "Rathod",
"category": "Individual or Person",
"aadhaar_seeding_status": "LINKED"
},
"traceId": "1-69437d63-75ac46a51036ab2233854e23"
}
```
## Implementation Details
### 1. Environment Variables
Add these to your `.env` file:
```env
PAN_PROVIDER_URL=https://dg-sandbox.setu.co/api/verify/pan
PAN_CLIENT_ID=292c6e76-dabf-49c4-8e48-90fba2916673
PAN_CLIENT_SECRET=7IZMe9zvoBBuBukLiCP7n4KLwSOy11oP
PAN_PRODUCT_INSTANCE_ID=439244ff-114e-41a8-ae74-a783f160622d
```
### 2. Service Layer (`src/services/panService.js`)
The service:
- Validates PAN format (10 characters: 5 letters, 4 digits, 1 letter)
- Checks Redis cache first to avoid duplicate API calls
- Calls Setu API with proper headers and request body
- Formats the response data
- Stores the result in PostgreSQL database
- Caches the result for 24 hours
### 3. Database Storage
All PAN verifications are stored in the `pan_verifications` table with the following fields:
- `pan`: The PAN number
- `full_name`, `first_name`, `middle_name`, `last_name`: Name components
- `category`: PAN category (Individual, Company, etc.)
- `aadhaar_seeding_status`: Aadhaar linking status
- `status`: VALID or INVALID
- `setu_response_id`: Setu's response ID
- `setu_trace_id`: Setu's trace ID for debugging
- `setu_message`: Message from Setu API
- `requested_by`: User ID who made the request
- `requested_at`: Timestamp of the request
### 4. API Endpoint
**POST** `/v1/pan/verify`
**Headers:**
```
Content-Type: application/json
X-API-Key: your-api-key-here
```
**Request Body:**
```json
{
"pan": "ABCDE1234A",
"reason": "KYC verification" // optional
}
```
**Response:**
```json
{
"success": true,
"data": {
"pan": "ABCDE1234A",
"full_name": "Kumar Gaurav Rathod",
"first_name": "Gaurav",
"middle_name": "Kumar",
"last_name": "Rathod",
"category": "Individual or Person",
"aadhaar_seeding_status": "LINKED",
"status": "VALID",
"setu_response_id": "0e370877-f860-4b5c-858b-42aebfea4879",
"setu_trace_id": "1-69437d63-75ac46a51036ab2233854e23",
"setu_message": "PAN is valid."
},
"meta": {
"request_id": "req_pan_1234567890",
"credits_used": 1,
"credits_remaining": 999,
"source": "setu-pan"
},
"timestamp": "2024-01-01T00:00:00.000Z"
}
```
## Getting Your API Key
Before testing, you need an API key. The easiest way is to run:
```bash
cd verify-india-api
npm run create-test-key
```
This will create and display a test API key. Copy it and use it in your requests.
**Alternative:** Sign up for a new account:
```bash
curl -X POST http://localhost:3000/v1/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "your-email@example.com",
"password": "your-password-123",
"company_name": "Your Company"
}'
```
The response includes your `api_key` - save it immediately!
**See `HOW_TO_GET_API_KEY.md` for detailed instructions.**
## Testing with Postman
1. **Set up the request:**
- Method: `POST`
- URL: `http://localhost:3000/v1/pan/verify`
- Headers:
- `Content-Type: application/json`
- `X-API-Key: your-api-key` (get it using the method above)
2. **Request Body (JSON):**
```json
{
"pan": "ABCDE1234A",
"reason": "Testing PAN verification"
}
```
3. **Expected Response:**
- Status: `200 OK`
- Body: JSON with PAN verification details
## Error Handling
The implementation handles various error scenarios:
- **Invalid PAN format**: Returns 400 with `INVALID_PAN_FORMAT` error
- **Missing credentials**: Returns 500 with `CONFIGURATION_ERROR`
- **Setu API errors**: Returns the status code from Setu with appropriate error message
- **Timeout errors**: Returns 504 with `PROVIDER_TIMEOUT`
- **Network errors**: Returns 500 with `VERIFICATION_FAILED`
## Key Features
1. **Caching**: Results are cached in Redis for 24 hours to reduce API calls
2. **Database Persistence**: All verifications are stored in PostgreSQL
3. **Error Handling**: Comprehensive error handling with proper status codes
4. **Validation**: PAN format validation before making API calls
5. **Logging**: API calls are logged for analytics
## Migration
After updating the code, run the database migration to update the `pan_verifications` table:
```bash
npm run migrate
```
This will add the new fields to store all Setu response data.

142
TEST_SETU_API_STEPS.md Normal file
View File

@ -0,0 +1,142 @@
# Step-by-Step Guide: Testing Setu PAN API
This guide will help you test the Setu PAN API with static/hardcoded values.
## Prerequisites
1. **Node.js installed** - Make sure you have Node.js installed on your system
2. **Dependencies installed** - All npm packages should be installed
## Step-by-Step Instructions
### Step 1: Navigate to the Project Directory
Open your terminal/command prompt and navigate to the project directory:
```bash
cd "C:\Users\front\Desktop\files 4\verify-india-api"
```
### Step 2: Verify Dependencies are Installed
Check if `node_modules` folder exists. If not, install dependencies:
```bash
npm install
```
This will install all required packages including `axios` which is needed for the API call.
### Step 3: Run the Test Script
You can run the test in two ways:
#### Option A: Using npm script (Recommended)
```bash
npm run test-setu-static
```
#### Option B: Direct node command
```bash
node scripts/test-setu-api-static.js
```
### Step 4: Check the Results
The script will:
- ✅ Show request details (URL, headers, body)
- ✅ Send the request to Setu API
- ✅ Display the response if successful
- ❌ Show error details if it fails
## Expected Output
### Success Case:
```
🧪 Testing Setu PAN API with Static Values
======================================================================
📋 Request Details:
URL: https://dg-sandbox.setu.co/api/verify/pan
Method: POST
...
🚀 Sending request to Setu API...
✅ SUCCESS! API is working correctly!
======================================================================
📊 Response Details:
Status Code: 200 OK
Response Time: 1234ms
...
✅ Test completed successfully!
```
### Error Case:
If there's an error, the script will show:
- Status code
- Error message
- Possible issues and solutions
## What the Script Tests
The script makes a POST request to Setu API with:
- **URL**: `https://dg-sandbox.setu.co/api/verify/pan`
- **Headers**:
- `Content-Type: application/json`
- `x-client-id: 292c6e76-dabf-49c4-8e48-90fba2916673`
- `x-client-secret: 7IZMe9zvoBBuBukLiCP7n4KLwSOy11oP`
- `x-product-instance-id: 439244ff-114e-41a8-ae74-a783f160622d`
- **Body**:
```json
{
"pan": "ABCDE1234A",
"consent": "Y",
"reason": "Testing"
}
```
## Troubleshooting
### Error: "Cannot find module 'axios'"
**Solution**: Run `npm install` to install dependencies
### Error: "Network Error" or "ECONNREFUSED"
**Solution**:
- Check your internet connection
- Verify the API URL is correct
- Check if there's a firewall blocking the request
### Error: "401 Unauthorized"
**Solution**:
- Check if the client-id and client-secret are correct
- Verify the credentials haven't expired
### Error: "403 Forbidden"
**Solution**:
- Check if the product-instance-id is correct
- Verify you have access to this product instance
### Error: "400 Bad Request"
**Solution**:
- Check if the PAN format is correct (should be 10 characters: 5 letters, 4 digits, 1 letter)
- Verify the request body structure
## Next Steps
Once the test passes:
1. ✅ Your Setu API credentials are working
2. ✅ You can use these credentials in your `.env` file
3. ✅ Your application can make PAN verification requests
## Notes
- This script uses **static/hardcoded values** - perfect for testing
- The credentials are embedded in the script for testing purposes
- For production, always use environment variables (`.env` file)
- The script has a 30-second timeout for the API request

23
check-schema.js Normal file
View File

@ -0,0 +1,23 @@
const { query, connectDB } = require('./src/database/connection');
async function checkSchema() {
await connectDB();
const result = await query(
`SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'pan_verifications'
ORDER BY ordinal_position`
);
console.log('Columns in pan_verifications table:');
result.rows.forEach(col => {
console.log(` - ${col.column_name}: ${col.data_type}`);
});
process.exit(0);
}
checkSchema().catch(err => {
console.error('Error:', err.message);
process.exit(1);
});

View File

@ -10,7 +10,12 @@
"migrate:down": "node src/database/migrate.js down",
"migrate:status": "node src/database/migrate.js status",
"migrate:pincodes": "node src/database/migrate-pincodes.js",
"create-test-key": "node scripts/create-test-api-key.js"
"create-test-key": "node scripts/create-test-api-key.js",
"create-live-key": "node scripts/create-live-api-key.js",
"check-pan-config": "node scripts/check-pan-config.js",
"test-pan-api": "node scripts/test-pan-api.js",
"test-setu-static": "node scripts/test-setu-api-static.js",
"test-localhost-pan": "node scripts/test-localhost-pan.js"
},
"keywords": [
"api",

View File

@ -0,0 +1,57 @@
/**
* Script to check if PAN API configuration is correct
* Usage: node scripts/check-pan-config.js
*/
require('dotenv').config();
console.log('\n🔍 Checking PAN API Configuration...\n');
console.log('=' .repeat(60));
const requiredVars = [
'PAN_PROVIDER_URL',
'PAN_CLIENT_ID',
'PAN_CLIENT_SECRET',
'PAN_PRODUCT_INSTANCE_ID'
];
let allSet = true;
requiredVars.forEach(varName => {
const value = process.env[varName];
if (value) {
// Mask sensitive values
if (varName === 'PAN_CLIENT_SECRET') {
const masked = value.length > 10
? value.substring(0, 4) + '***' + value.substring(value.length - 4)
: '***';
console.log(`${varName}: ${masked}`);
} else {
console.log(`${varName}: ${value}`);
}
} else {
console.log(`${varName}: NOT SET`);
allSet = false;
}
});
console.log('=' .repeat(60));
if (allSet) {
console.log('\n✅ All PAN API credentials are set!');
console.log('\n⚠ If you still get "Bad token" errors:');
console.log(' 1. Double-check the credentials are correct');
console.log(' 2. Make sure you restarted the server after adding .env variables');
console.log(' 3. Verify the credentials in Setu dashboard\n');
} else {
console.log('\n❌ Missing PAN API credentials!');
console.log('\n📝 Add these to your .env file:');
console.log(' PAN_PROVIDER_URL=https://dg-sandbox.setu.co/api/verify/pan');
console.log(' PAN_CLIENT_ID=your-client-id-here');
console.log(' PAN_CLIENT_SECRET=your-client-secret-here');
console.log(' PAN_PRODUCT_INSTANCE_ID=your-product-instance-id-here');
console.log('\n💡 After adding, restart your server!\n');
}
process.exit(allSet ? 0 : 1);

View File

@ -0,0 +1,68 @@
/**
* Helper script to create a live API key for local development
* Usage: node scripts/create-live-api-key.js
*/
require('dotenv').config();
const crypto = require('crypto');
const { query, connectDB } = require('../src/database/connection');
function generateApiKey(type = 'live') {
const prefix = type === 'test' ? 'vf_test_' : 'vf_live_';
return prefix + crypto.randomBytes(24).toString('hex');
}
async function createLiveApiKey() {
try {
await connectDB();
console.log('✅ Connected to database');
// Get a user to attach the key to. Prefer an existing user, otherwise create one.
let userRes = await query('SELECT id FROM users ORDER BY id LIMIT 1');
let userId;
if (userRes.rows.length === 0) {
console.log('No users found. Creating a default local user...');
const bcrypt = require('bcryptjs');
const passwordHash = await bcrypt.hash('localpass123', 10);
const userResult = await query(
`INSERT INTO users (email, password_hash, company_name, plan, monthly_quota, quota_reset_date, is_active)
VALUES ($1, $2, $3, $4, $5, DATE(NOW() + INTERVAL '1 month'), true)
RETURNING id`,
['local@example.com', passwordHash, 'Local Dev', 'dev', 100000]
);
userId = userResult.rows[0].id;
console.log('Created user id:', userId);
} else {
userId = userRes.rows[0].id;
console.log('Using existing user id:', userId);
}
// Deactivate any existing live keys for that user (optional)
await query('UPDATE api_keys SET is_active = false WHERE user_id = $1 AND key_prefix = $2', [userId, 'vf_live_']);
// Generate new live API key
const apiKey = generateApiKey('live');
const keyHash = crypto.createHash('sha256').update(apiKey).digest('hex');
await query(
`INSERT INTO api_keys (user_id, key_prefix, key_hash, key_hint, name, is_test_key, is_active)
VALUES ($1, $2, $3, $4, $5, false, true)`,
[userId, 'vf_live_', keyHash, apiKey.slice(-4), 'Local Live Key']
);
console.log('\n✅ Live API Key Created Successfully!\n');
console.log('Key:');
console.log(apiKey);
console.log('\nUse this in your request header:');
console.log('Header: x-api-key');
console.log('Value:', apiKey);
process.exit(0);
} catch (error) {
console.error('❌ Error creating live API key:', error.message);
console.error(error);
process.exit(1);
}
}
createLiveApiKey();

View File

@ -0,0 +1,296 @@
/**
* Test script to verify PAN API through localhost
* This script tests the Setu API integration through your local API server
* Usage: node scripts/test-localhost-pan.js
*/
require('dotenv').config();
const axios = require('axios');
const crypto = require('crypto');
const { query, connectDB } = require('../src/database/connection');
// Static Setu API credentials (from your curl command)
const SETU_CREDENTIALS = {
PAN_PROVIDER_URL: 'https://dg-sandbox.setu.co/api/verify/pan',
PAN_CLIENT_ID: '292c6e76-dabf-49c4-8e48-90fba2916673',
PAN_CLIENT_SECRET: '7IZMe9zvoBBuBukLiCP7n4KLwSOy11oP',
PAN_PRODUCT_INSTANCE_ID: '439244ff-114e-41a8-ae74-a783f160622d'
};
const LOCALHOST_URL = process.env.LOCALHOST_URL || 'http://localhost:3000';
const TEST_PAN = 'ABCDE1234A';
// Function to generate API key
function generateApiKey(type = 'test') {
const prefix = type === 'test' ? 'vf_test_' : 'vf_live_';
return prefix + crypto.randomBytes(24).toString('hex');
}
// Function to create or get test API key
async function getOrCreateTestApiKey() {
try {
await connectDB();
// Check if test user exists
let testUser = await query(
'SELECT * FROM users WHERE email = $1',
['test@example.com']
);
let userId;
if (testUser.rows.length === 0) {
// Create test user
const bcrypt = require('bcryptjs');
const passwordHash = await bcrypt.hash('testpassword123', 10);
const userResult = await query(
`INSERT INTO users (email, password_hash, company_name, plan, monthly_quota, quota_reset_date, is_active)
VALUES ($1, $2, $3, $4, $5, DATE(NOW() + INTERVAL '1 month'), true)
RETURNING id, email, plan`,
['test@example.com', passwordHash, 'Test Company', 'free', 10000]
);
userId = userResult.rows[0].id;
} else {
userId = testUser.rows[0].id;
}
// Check for existing active test API key
const existingKeys = await query(
`SELECT ak.* FROM api_keys ak
WHERE ak.user_id = $1 AND ak.is_test_key = true AND ak.is_active = true
ORDER BY ak.created_at DESC
LIMIT 1`,
[userId]
);
if (existingKeys.rows.length > 0) {
// We can't retrieve the original key, so we'll need to create a new one
// But first, let's try to use the existing one by checking if we can authenticate
// For now, let's create a new one to be safe
console.log(' Found existing test API key, creating a new one...');
// Deactivate old keys
await query(
'UPDATE api_keys SET is_active = false WHERE user_id = $1 AND is_test_key = true',
[userId]
);
}
// Generate new API key
const apiKey = generateApiKey('test');
const keyHash = crypto.createHash('sha256').update(apiKey).digest('hex');
await query(
`INSERT INTO api_keys (user_id, key_prefix, key_hash, key_hint, name, is_test_key, is_active)
VALUES ($1, $2, $3, $4, $5, true, true)`,
[userId, 'vf_test_', keyHash, apiKey.slice(-4), 'Test Key']
);
return apiKey;
} catch (error) {
console.error(' ❌ Error creating API key:', error.message);
throw error;
}
}
// Function to check if server is running
async function checkServerRunning() {
try {
const response = await axios.get(`${LOCALHOST_URL}/health`, {
timeout: 3000
});
return response.status === 200;
} catch (error) {
return false;
}
}
// Function to update .env file with Setu credentials
async function updateEnvFile() {
const fs = require('fs');
const path = require('path');
const envPath = path.join(__dirname, '..', '.env');
try {
let envContent = '';
// Read existing .env if it exists
if (fs.existsSync(envPath)) {
envContent = fs.readFileSync(envPath, 'utf8');
}
// Update or add Setu credentials
const updates = {
'PAN_PROVIDER_URL': SETU_CREDENTIALS.PAN_PROVIDER_URL,
'PAN_CLIENT_ID': SETU_CREDENTIALS.PAN_CLIENT_ID,
'PAN_CLIENT_SECRET': SETU_CREDENTIALS.PAN_CLIENT_SECRET,
'PAN_PRODUCT_INSTANCE_ID': SETU_CREDENTIALS.PAN_PRODUCT_INSTANCE_ID
};
let updated = false;
for (const [key, value] of Object.entries(updates)) {
const regex = new RegExp(`^${key}=.*$`, 'm');
if (regex.test(envContent)) {
envContent = envContent.replace(regex, `${key}=${value}`);
updated = true;
} else {
// Add new line if file doesn't end with newline
if (envContent && !envContent.endsWith('\n')) {
envContent += '\n';
}
envContent += `${key}=${value}\n`;
updated = true;
}
}
if (updated) {
fs.writeFileSync(envPath, envContent, 'utf8');
console.log(' ✅ Updated .env file with Setu credentials');
return true;
}
return false;
} catch (error) {
console.log(' ⚠️ Could not update .env file:', error.message);
console.log(' Please manually add these to your .env file:');
console.log(` PAN_PROVIDER_URL=${SETU_CREDENTIALS.PAN_PROVIDER_URL}`);
console.log(` PAN_CLIENT_ID=${SETU_CREDENTIALS.PAN_CLIENT_ID}`);
console.log(` PAN_CLIENT_SECRET=${SETU_CREDENTIALS.PAN_CLIENT_SECRET}`);
console.log(` PAN_PRODUCT_INSTANCE_ID=${SETU_CREDENTIALS.PAN_PRODUCT_INSTANCE_ID}`);
return false;
}
}
// Main test function
async function testLocalhostPAN() {
console.log('\n🧪 Testing PAN API through Localhost\n');
console.log('='.repeat(70));
// Step 1: Check server is running
console.log('\n📡 Step 1: Checking if server is running...');
const serverRunning = await checkServerRunning();
if (!serverRunning) {
console.log(' ❌ Server is not running!');
console.log('\n Please start the server first:');
console.log(' npm start');
console.log(' or');
console.log(' npm run dev');
process.exit(1);
}
console.log(' ✅ Server is running on', LOCALHOST_URL);
// Step 2: Update .env file with Setu credentials
console.log('\n⚙ Step 2: Setting up Setu API credentials...');
const envUpdated = await updateEnvFile();
if (envUpdated) {
console.log(' ⚠️ Note: If your server was already running, restart it to load new credentials');
console.log(' Restart command: Press Ctrl+C and run "npm start" again');
}
// Step 3: Create/get API key
console.log('\n🔑 Step 3: Creating test API key...');
let apiKey;
try {
apiKey = await getOrCreateTestApiKey();
console.log(' ✅ Test API key created:', apiKey);
} catch (error) {
console.log(' ❌ Failed to create API key:', error.message);
console.log(' Make sure your database is running and migrations are applied');
process.exit(1);
}
// Step 4: Test the API endpoint
console.log('\n🚀 Step 4: Testing PAN verification endpoint...');
console.log(` URL: ${LOCALHOST_URL}/v1/pan/verify`);
console.log(` PAN: ${TEST_PAN}`);
try {
const startTime = Date.now();
const response = await axios.post(
`${LOCALHOST_URL}/v1/pan/verify`,
{
pan: TEST_PAN,
consent: 'Y',
reason: 'Testing'
},
{
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey
},
timeout: 30000
}
);
const endTime = Date.now();
const duration = endTime - startTime;
console.log('\n ✅ SUCCESS! API is working correctly!\n');
console.log('='.repeat(70));
console.log('\n📊 Response Details:');
console.log(` Status Code: ${response.status} ${response.statusText}`);
console.log(` Response Time: ${duration}ms`);
console.log(`\n📦 Response Body:`);
console.log(JSON.stringify(response.data, null, 2));
if (response.data.success && response.data.data) {
console.log('\n✅ PAN Verification Successful!');
if (response.data.data.full_name) {
console.log(` PAN Holder: ${response.data.data.full_name}`);
}
if (response.data.data.status) {
console.log(` Status: ${response.data.data.status}`);
}
}
console.log('\n' + '='.repeat(70));
console.log('\n✅ All tests passed! Your localhost API is working correctly!\n');
} catch (error) {
console.log('\n ❌ ERROR! API request failed!\n');
console.log('='.repeat(70));
if (error.response) {
console.log('\n📊 Error Response:');
console.log(` Status Code: ${error.response.status} ${error.response.statusText}`);
console.log(`\n📦 Error Body:`);
console.log(JSON.stringify(error.response.data, null, 2));
console.log('\n⚠ Possible Issues:');
if (error.response.status === 401) {
console.log(' - Invalid API key (try running this script again to create a new key)');
} else if (error.response.status === 500) {
console.log(' - Server error (check server logs)');
console.log(' - Make sure Setu credentials are in .env file');
console.log(' - Restart the server after updating .env');
} else if (error.response.status === 400) {
console.log(' - Bad request (check PAN format)');
}
} else if (error.request) {
console.log('\n⚠ No response received from server');
console.log(` Error: ${error.message}`);
console.log('\n Possible Issues:');
console.log(' - Server is not running');
console.log(' - Wrong URL (check LOCALHOST_URL)');
console.log(' - Network connectivity problem');
} else {
console.log(`\n⚠️ Request setup error: ${error.message}`);
}
console.log('\n' + '='.repeat(70));
console.log('\n❌ Test failed!\n');
process.exit(1);
}
}
// Run the test
testLocalhostPAN().catch(error => {
console.error('\n❌ Unexpected error:', error.message);
console.error(error);
process.exit(1);
});

103
scripts/test-pan-api.js Normal file
View File

@ -0,0 +1,103 @@
/**
* Test script to verify PAN API is working correctly
* Usage: node scripts/test-pan-api.js
*/
require('dotenv').config();
const axios = require('axios');
async function testPanAPI() {
console.log('\n🧪 Testing PAN API Configuration...\n');
console.log('=' .repeat(60));
// Check environment variables
console.log('\n1. Checking Environment Variables:');
const requiredVars = {
'PAN_PROVIDER_URL': process.env.PAN_PROVIDER_URL,
'PAN_CLIENT_ID': process.env.PAN_CLIENT_ID,
'PAN_CLIENT_SECRET': process.env.PAN_CLIENT_SECRET,
'PAN_PRODUCT_INSTANCE_ID': process.env.PAN_PRODUCT_INSTANCE_ID
};
let allSet = true;
for (const [key, value] of Object.entries(requiredVars)) {
if (value) {
const masked = key === 'PAN_CLIENT_SECRET' && value.length > 10
? value.substring(0, 4) + '***' + value.substring(value.length - 4)
: value;
console.log(`${key}: ${masked}`);
} else {
console.log(`${key}: NOT SET`);
allSet = false;
}
}
if (!allSet) {
console.log('\n❌ Missing environment variables! Please check your .env file.\n');
process.exit(1);
}
// Test Setu API directly
console.log('\n2. Testing Setu API Directly:');
try {
const setuResponse = await axios.post(
process.env.PAN_PROVIDER_URL,
{
pan: 'ABCDE1234A',
consent: 'Y',
reason: 'Testing PAN verification'
},
{
headers: {
'Content-Type': 'application/json',
'x-client-id': process.env.PAN_CLIENT_ID,
'x-client-secret': process.env.PAN_CLIENT_SECRET,
'x-product-instance-id': process.env.PAN_PRODUCT_INSTANCE_ID
},
timeout: 30000
}
);
if (setuResponse.status === 200 && setuResponse.data) {
console.log(' ✅ Setu API is working!');
console.log(` Response: ${setuResponse.data.message || 'Success'}`);
} else {
console.log(' ⚠️ Setu API returned unexpected response');
console.log(` Status: ${setuResponse.status}`);
}
} catch (error) {
console.log(' ❌ Setu API test failed!');
if (error.response) {
console.log(` Status: ${error.response.status}`);
console.log(` Error: ${error.response.data?.message || error.response.data?.error || 'Unknown error'}`);
} else {
console.log(` Error: ${error.message}`);
}
console.log('\n⚠ Your Setu credentials may be incorrect or the API is down.\n');
process.exit(1);
}
// Test local API (if server is running)
console.log('\n3. Testing Local API (make sure server is running on port 3000):');
try {
// First, get a test API key
console.log(' You need an API key to test the local endpoint.');
console.log(' Run: npm run create-test-key');
console.log(' Then test with:');
console.log(' curl -X POST http://localhost:3000/v1/pan/verify \\');
console.log(' -H "Content-Type: application/json" \\');
console.log(' -H "x-api-key: YOUR_API_KEY" \\');
console.log(' -d \'{"pan": "ABCDE1234A", "reason": "Testing"}\'');
} catch (error) {
console.log(' ⚠️ Could not test local API:', error.message);
}
console.log('\n' + '=' .repeat(60));
console.log('\n✅ Configuration check complete!\n');
}
testPanAPI().catch(error => {
console.error('\n❌ Test failed:', error.message);
process.exit(1);
});

View File

@ -0,0 +1,126 @@
/**
* Static test script to verify Setu PAN API is working
* This script uses hardcoded values from the curl command
* Usage: node scripts/test-setu-api-static.js
*/
const axios = require('axios');
// Static values from the curl command
const SETU_API_URL = 'https://dg-sandbox.setu.co/api/verify/pan';
const CLIENT_ID = '292c6e76-dabf-49c4-8e48-90fba2916673';
const CLIENT_SECRET = '7IZMe9zvoBBuBukLiCP7n4KLwSOy11oP';
const PRODUCT_INSTANCE_ID = '439244ff-114e-41a8-ae74-a783f160622d';
const REQUEST_BODY = {
pan: 'ABCDE1234A',
consent: 'Y',
reason: 'Testing'
};
async function testSetuAPI() {
console.log('\n🧪 Testing Setu PAN API with Static Values\n');
console.log('='.repeat(70));
console.log('\n📋 Request Details:');
console.log(` URL: ${SETU_API_URL}`);
console.log(` Method: POST`);
console.log(` Headers:`);
console.log(` - Content-Type: application/json`);
console.log(` - x-client-id: ${CLIENT_ID}`);
console.log(` - x-client-secret: ${CLIENT_SECRET.substring(0, 8)}...`);
console.log(` - x-product-instance-id: ${PRODUCT_INSTANCE_ID}`);
console.log(` Body:`, JSON.stringify(REQUEST_BODY, null, 2));
console.log('\n🚀 Sending request to Setu API...\n');
try {
const startTime = Date.now();
const response = await axios.post(
SETU_API_URL,
REQUEST_BODY,
{
headers: {
'Content-Type': 'application/json',
'x-client-id': CLIENT_ID,
'x-client-secret': CLIENT_SECRET,
'x-product-instance-id': PRODUCT_INSTANCE_ID
},
timeout: 30000 // 30 seconds
}
);
const endTime = Date.now();
const duration = endTime - startTime;
console.log('✅ SUCCESS! API is working correctly!\n');
console.log('='.repeat(70));
console.log('\n📊 Response Details:');
console.log(` Status Code: ${response.status} ${response.statusText}`);
console.log(` Response Time: ${duration}ms`);
console.log(` Response Headers:`, JSON.stringify(response.headers, null, 2));
console.log(`\n📦 Response Body:`);
console.log(JSON.stringify(response.data, null, 2));
// Check if response has expected structure
if (response.data && response.data.data) {
console.log('\n✅ Response structure is valid!');
if (response.data.data.full_name) {
console.log(` PAN Holder Name: ${response.data.data.full_name}`);
}
if (response.data.message) {
console.log(` Message: ${response.data.message}`);
}
}
console.log('\n' + '='.repeat(70));
console.log('\n✅ Test completed successfully!\n');
} catch (error) {
console.log('❌ ERROR! API request failed!\n');
console.log('='.repeat(70));
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log('\n📊 Error Response Details:');
console.log(` Status Code: ${error.response.status} ${error.response.statusText}`);
console.log(` Response Headers:`, JSON.stringify(error.response.headers, null, 2));
console.log(`\n📦 Error Response Body:`);
console.log(JSON.stringify(error.response.data, null, 2));
console.log('\n⚠ Possible Issues:');
if (error.response.status === 401) {
console.log(' - Invalid credentials (client-id or client-secret)');
} else if (error.response.status === 403) {
console.log(' - Access forbidden (check product-instance-id)');
} else if (error.response.status === 400) {
console.log(' - Bad request (check PAN format or request body)');
} else if (error.response.status === 404) {
console.log(' - Endpoint not found (check API URL)');
} else if (error.response.status >= 500) {
console.log(' - Server error (Setu API might be down)');
}
} else if (error.request) {
// The request was made but no response was received
console.log('\n⚠ No response received from server');
console.log(` Error: ${error.message}`);
console.log('\n Possible Issues:');
console.log(' - Network connectivity problem');
console.log(' - API endpoint is unreachable');
console.log(' - Request timeout (took longer than 30 seconds)');
} else {
// Something happened in setting up the request that triggered an Error
console.log(`\n⚠️ Request setup error: ${error.message}`);
}
console.log('\n' + '='.repeat(70));
console.log('\n❌ Test failed!\n');
process.exit(1);
}
}
// Run the test
testSetuAPI();

View File

@ -3,11 +3,19 @@ const panBankSchema = `
CREATE TABLE IF NOT EXISTS pan_verifications (
id SERIAL PRIMARY KEY,
pan VARCHAR(10) NOT NULL,
name VARCHAR(255),
full_name VARCHAR(255),
first_name VARCHAR(255),
middle_name VARCHAR(255),
last_name VARCHAR(255),
category VARCHAR(100),
aadhaar_seeding_status VARCHAR(50),
status VARCHAR(50),
pan_type VARCHAR(50),
name_match BOOLEAN,
name_match_score INTEGER,
setu_response_id VARCHAR(255),
setu_trace_id VARCHAR(255),
setu_message TEXT,
requested_by INTEGER REFERENCES users(id),
requested_at TIMESTAMP DEFAULT NOW()
);

View File

@ -14,6 +14,7 @@ const ifscRoutes = require('./routes/ifsc');
const pincodeRoutes = require('./routes/pincode');
const gstRoutes = require('./routes/gst');
const panRoutes = require('./routes/pan');
console.log('✅ PAN routes loaded:', typeof panRoutes);
const bankRoutes = require('./routes/bank');
const userRoutes = require('./routes/user');
@ -23,7 +24,31 @@ const app = express();
app.use(helmet());
app.use(cors());
app.use(express.json());
// JSON body parser with error handling
app.use(express.json({
limit: '10mb',
verify: (req, res, buf) => {
try {
JSON.parse(buf);
} catch (e) {
console.error('JSON parse error:', e.message);
res.status(400).json({
success: false,
error: { code: 'INVALID_JSON', message: 'Invalid JSON in request body' }
});
}
}
}));
// Debug middleware to log all requests
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.originalUrl}`);
console.log('Headers:', req.headers);
console.log('Body:', req.body);
next();
});
app.use(morgan('combined'));
app.get('/health', (req, res) => {
@ -49,7 +74,18 @@ app.use('/v1/user', userRoutes);
app.use('/v1/ifsc', ifscRoutes);
app.use('/v1/pincode', pincodeRoutes);
app.use('/v1/gst', gstRoutes);
// Test route to verify routing works (must be BEFORE panRoutes)
app.get('/v1/pan/test', (req, res) => {
console.log('PAN test route hit!');
res.json({ message: 'PAN route is working', timestamp: new Date().toISOString() });
});
// Log all registered routes for debugging
console.log('Registering PAN routes at /v1/pan');
app.use('/v1/pan', panRoutes);
console.log('PAN routes registered');
app.use('/v1/bank', bankRoutes);
app.use('*', (req, res) => {
@ -68,8 +104,19 @@ async function startServer() {
try {
await connectDB();
console.log('✅ PostgreSQL connected');
// // Try to connect Redis, but don't fail if it's not available
// try {
// const { connectRedis, isDummyCache } = require('./cache/redis');
// await connectRedis();
// console.log('✅ Redis connected', isDummyCache() ? 'using dummy cache' : 'using real cache');
// if (isDummyCache()) {
// console.log('📦 Using in-memory cache (Redis not available)');
// } else {
// console.log('✅ Redis connected');
// }
// } catch (redisError) {
// console.log('📦 Using in-memory cache (Redis connection failed)');
// }
app.listen(PORT, () => {
console.log(`✅ Server running on port ${PORT}`);

View File

@ -1,6 +1,5 @@
const crypto = require('crypto');
const { query } = require('../database/connection');
const { cacheGet, cacheSet } = require('../cache/redis');
async function authenticateApiKey(req, res, next) {
try {
@ -13,17 +12,17 @@ async function authenticateApiKey(req, res, next) {
});
}
if (!apiKey.startsWith('vf_live_') && !apiKey.startsWith('vf_test_')) {
const prefix = process.env.API_KEY_PREFIX || 'vf_live_';
const testPrefix = 'vf_test_'; // Keep test prefix constant or add to env if needed
if (!apiKey.startsWith(prefix) && !apiKey.startsWith(testPrefix)) {
return res.status(401).json({
success: false,
error: { code: 'INVALID_API_KEY_FORMAT', message: 'Invalid API key format' }
});
}
const cacheKey = `apikey:${apiKey}`;
let keyData = await cacheGet(cacheKey);
if (!keyData) {
// Hash the API key and query database directly
const keyHash = crypto.createHash('sha256').update(apiKey).digest('hex');
const result = await query(
@ -41,9 +40,7 @@ async function authenticateApiKey(req, res, next) {
});
}
keyData = result.rows[0];
await cacheSet(cacheKey, keyData, 300);
}
const keyData = result.rows[0];
if (!keyData.user_active) {
return res.status(403).json({

View File

@ -8,13 +8,24 @@ const { logApiCall } = require('../services/analytics');
router.use(authenticateApiKey);
router.use(rateLimit);
router.get('/verify/:gstin', async (req, res, next) => {
// POST /v1/gst/verify { gstin }
router.post('/verify', async (req, res, next) => {
const startTime = Date.now();
let success = false;
try {
const { gstin } = req.params;
const gstinRegex = /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/;
const { gstin } = req.body;
// Relaxed regex to allow any alphanumeric char at 14th position (instead of strict Z)
const gstinRegex = /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[A-Z0-9]{1}[0-9A-Z]{1}$/;
if (!gstin) {
return res.status(400).json({
success: false,
error: { code: 'MISSING_GSTIN', message: 'GSTIN is required' }
});
}
if (!gstinRegex.test(gstin.toUpperCase())) {
return res.status(400).json({
@ -41,7 +52,63 @@ router.get('/verify/:gstin', async (req, res, next) => {
request_id: `req_gst_${Date.now()}`,
credits_used: 1,
credits_remaining: req.user.remaining - 1,
source: 'gstn'
source: 'setu-gst'
}
});
} catch (error) {
next(error);
} finally {
await logApiCall({
userId: req.user.id,
apiKeyId: req.user.apiKeyId,
endpoint: '/v1/gst/verify',
method: 'POST',
params: { gstin: req.body.gstin },
status: success ? 200 : 500,
duration: Date.now() - startTime,
success,
isTestKey: req.user.isTestKey
});
}
});
// GET /v1/gst/verify/:gstin
router.get('/verify/:gstin', async (req, res, next) => {
const startTime = Date.now();
let success = false;
try {
const { gstin } = req.params;
// Relaxed regex to allow any alphanumeric char at 14th position (instead of strict Z)
const gstinRegex = /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[A-Z0-9]{1}[0-9A-Z]{1}$/;
if (!gstinRegex.test(gstin.toUpperCase())) {
return res.status(400).json({
success: false,
error: { code: 'INVALID_GSTIN', message: 'Invalid GSTIN format' }
});
}
const result = await verifyGSTIN(gstin.toUpperCase());
if (!result.success) {
return res.status(result.statusCode || 404).json({
success: false,
error: { code: result.errorCode, message: result.message }
});
}
success = true;
res.json({
success: true,
data: result.data,
meta: {
request_id: `req_gst_${Date.now()}`,
credits_used: 1,
credits_remaining: req.user.remaining - 1,
source: 'setu-gst'
}
});

View File

@ -5,67 +5,94 @@ const { rateLimit } = require('../middleware/rateLimit');
const { verifyPAN } = require('../services/panService');
const { logApiCall } = require('../services/analytics');
// Health check endpoint (no auth required)
router.get('/health', (req, res) => {
res.json({
success: true,
service: 'PAN Verification',
provider: 'Setu',
timestamp: new Date().toISOString()
});
});
// Apply authentication and rate limiting middleware to all other routes
router.use(authenticateApiKey);
router.use(rateLimit);
/**
* PAN Verification Routes
* POST /v1/pan/verify - Verify a PAN number using Setu API
*/
/**
* @route POST /v1/pan/verify
* @desc Verify PAN number
* @body {string} pan - PAN number to verify (required)
* @body {string} reason - Reason for verification (optional)
* @returns {Object} Verification result
*/
router.post('/verify', async (req, res, next) => {
const startTime = Date.now();
let success = false;
try {
const { pan, name, dob } = req.body;
const panRegex = /^[A-Z]{5}[0-9]{4}[A-Z]{1}$/;
const { pan, reason } = req.body;
// Validate PAN is provided
if (!pan) {
return res.status(400).json({
success: false,
error: { code: 'MISSING_PAN', message: 'PAN is required' }
error: {
code: 'MISSING_PAN',
message: 'PAN number is required. Please provide a valid PAN in the request body.'
}
});
}
if (!panRegex.test(pan.toUpperCase())) {
return res.status(400).json({
success: false,
error: { code: 'INVALID_PAN', message: 'Invalid PAN format' }
});
}
const result = await verifyPAN(pan.toUpperCase(), name, dob);
// Call verification service
const result = await verifyPAN(pan, reason, req.user?.id);
// Return appropriate response based on result
if (!result.success) {
return res.status(result.statusCode || 404).json({
// If result has Setu-formatted data, return it directly
if (result.data && result.data.verification) {
return res.status(result.statusCode || 500).json(result.data);
}
// Otherwise return standard error format
return res.status(result.statusCode || 500).json({
success: false,
error: { code: result.errorCode, message: result.message }
error: {
code: result.errorCode || 'VERIFICATION_ERROR',
message: result.message || 'PAN verification failed'
}
});
}
success = true;
res.json({
success: true,
data: result.data,
meta: {
request_id: `req_pan_${Date.now()}`,
credits_used: 1,
credits_remaining: req.user.remaining - 1
}
});
// Return Setu-formatted response directly
return res.status(200).json(result.data);
} catch (error) {
console.error('[PAN Route] Unexpected error:', error);
next(error);
} finally {
// Log API call
await logApiCall({
userId: req.user.id,
apiKeyId: req.user.apiKeyId,
endpoint: '/v1/pan/verify',
method: 'POST',
params: { pan: req.body.pan },
params: { pan: req.body.pan, reason: req.body.reason },
status: success ? 200 : 500,
duration: Date.now() - startTime,
success,
isTestKey: req.user.isTestKey
isTestKey: req.user.isTestKey,
errorMessage: success ? null : 'PAN verification failed'
});
}
});
module.exports = router;

View File

@ -1,7 +1,17 @@
const axios = require('axios');
axios.defaults.headers.common['Authorization'] = `Bearer ${process.env.BANK_PROVIDER_KEY}`;
axios.defaults.headers.post['Content-Type'] = 'application/json';
// Create a dedicated axios instance for bank service to avoid polluting global axios
// which was causing authentication issues with PAN/GST services
const bankAxiosInstance = axios.create({
headers: {
common: {
'Authorization': `Bearer ${process.env.BANK_PROVIDER_KEY || ''}`
},
post: {
'Content-Type': 'application/json'
}
}
});
const { cacheGet, cacheSet } = require('../cache/redis');
const { query } = require('../database/connection');
@ -38,23 +48,6 @@ async function verifyBankAccount(accountNumber, ifsc, name = null) {
};
const response = { data: mockResponseData };
// Commenting out the actual external API call for now
// const response = await axios.post(
// process.env.BANK_PROVIDER_URL,
// {
// account_number: accountNumber,
// ifsc: ifsc.toUpperCase(),
// name: name
// },
// {
// headers: {
// 'Authorization': `Bearer ${process.env.BANK_PROVIDER_KEY}`,
// 'Content-Type': 'application/json'
// },
// timeout: 30000
// }
// );
if (!response.data || response.data.status_code !== 200) {
return { success: false, statusCode: 404, errorCode: 'ACCOUNT_NOT_FOUND', message: 'Account not found' };
}

View File

@ -1,101 +1,5 @@
// const axios = require('axios');
// const { cacheGet, cacheSet } = require('../cache/redis');
// const STATE_NAMES = {
// '01': 'Jammu & Kashmir', '02': 'Himachal Pradesh', '03': 'Punjab',
// '04': 'Chandigarh', '05': 'Uttarakhand', '06': 'Haryana', '07': 'Delhi',
// '08': 'Rajasthan', '09': 'Uttar Pradesh', '10': 'Bihar', '11': 'Sikkim',
// '12': 'Arunachal Pradesh', '13': 'Nagaland', '14': 'Manipur', '15': 'Mizoram',
// '16': 'Tripura', '17': 'Meghalaya', '18': 'Assam', '19': 'West Bengal',
// '20': 'Jharkhand', '21': 'Odisha', '22': 'Chhattisgarh', '23': 'Madhya Pradesh',
// '24': 'Gujarat', '26': 'Dadra & Nagar Haveli', '27': 'Maharashtra',
// '29': 'Karnataka', '30': 'Goa', '31': 'Lakshadweep', '32': 'Kerala',
// '33': 'Tamil Nadu', '34': 'Puducherry', '35': 'Andaman & Nicobar',
// '36': 'Telangana', '37': 'Andhra Pradesh', '38': 'Ladakh'
// };
// async function verifyGSTIN(gstin) {
// try {
// const cacheKey = `gst:${gstin}`;
// const cached = await cacheGet(cacheKey);
// if (cached) return { success: true, data: cached };
// const response = await axios.post(
// process.env.GST_PROVIDER_URL,
// { id_number: gstin },
// {
// headers: {
// 'Authorization': `Bearer ${process.env.GST_PROVIDER_KEY}`,
// 'Content-Type': 'application/json'
// },
// timeout: 30000
// }
// );
// if (!response.data || response.data.status_code !== 200) {
// return { success: false, statusCode: 404, errorCode: 'GSTIN_NOT_FOUND', message: 'GSTIN not found' };
// }
// const d = response.data.data;
// const data = {
// gstin,
// legal_name: d.legal_name || d.lgnm,
// trade_name: d.trade_name || d.tradeNam,
// status: d.status || d.sts,
// registration_date: d.registration_date || d.rgdt,
// last_updated: d.last_update || d.lstupdt,
// business_type: d.business_type || d.ctb,
// constitution: d.constitution || d.ctj,
// state: d.state || STATE_NAMES[gstin.substring(0, 2)],
// state_code: gstin.substring(0, 2),
// pan: gstin.substring(2, 12),
// address: {
// building: d.address?.bno || d.pradr?.addr?.bno || '',
// floor: d.address?.flno || d.pradr?.addr?.flno || '',
// street: d.address?.st || d.pradr?.addr?.st || '',
// locality: d.address?.loc || d.pradr?.addr?.loc || '',
// city: d.address?.city || d.pradr?.addr?.city || '',
// district: d.address?.dst || d.pradr?.addr?.dst || '',
// state: d.address?.stcd || d.pradr?.addr?.stcd || '',
// pincode: d.address?.pncd || d.pradr?.addr?.pncd || ''
// },
// nature_of_business: d.nature_of_business || d.nba || [],
// filing_status: {
// gstr1: d.filing_status?.gstr1 || 'Unknown',
// gstr3b: d.filing_status?.gstr3b || 'Unknown',
// last_filed_date: d.filing_status?.last_filed || null
// }
// };
// await cacheSet(cacheKey, data, 86400);
// return { success: true, data };
// } catch (error) {
// // Surface provider details when available to avoid generic 500s
// if (error.code === 'ECONNABORTED') {
// return { success: false, statusCode: 504, errorCode: 'PROVIDER_TIMEOUT', message: 'Service timeout' };
// }
// const providerStatus = error.response?.status || error.response?.data?.status_code;
// const providerMessage = error.response?.data?.message || error.message;
// if (providerStatus) {
// return {
// success: false,
// statusCode: providerStatus,
// errorCode: error.response?.data?.error_code || 'PROVIDER_ERROR',
// message: providerMessage || 'Provider error'
// };
// }
// return { success: false, statusCode: 500, errorCode: 'VERIFICATION_FAILED', message: 'Verification failed' };
// }
// }
// module.exports = { verifyGSTIN };
const axios = require('axios');
const { cacheGet, cacheSet } = require('../cache/redis');
const { query } = require('../database/connection');
const STATE_NAMES = {
'01': 'Jammu & Kashmir', '02': 'Himachal Pradesh', '03': 'Punjab',
@ -111,81 +15,269 @@ const STATE_NAMES = {
};
async function verifyGSTIN(gstin) {
// Create a fresh axios instance to avoid global axios.defaults pollution from other services
const axiosInstance = axios.create();
// Defensively remove globally set headers just in case
delete axiosInstance.defaults.headers.common['Authorization'];
try {
const cacheKey = `gst:${gstin}`;
const cached = await cacheGet(cacheKey);
if (cached) return { success: true, data: cached };
// Look up GSTIN in the local database seeded from gst.csv
const result = await query(
'SELECT * FROM gst_registrations WHERE gstin = $1',
[gstin]
);
// Get and clean Setu API credentials
const providerUrl = process.env.GST_PROVIDER_URL || 'https://dg-sandbox.setu.co/api/verify/gst';
const clientId = process.env.GST_CLIENT_ID;
const clientSecret = process.env.GST_CLIENT_SECRET;
const productInstanceId = process.env.GST_PRODUCT_INSTANCE_ID;
if (!result.rows.length) {
return { success: false, statusCode: 404, errorCode: 'GSTIN_NOT_FOUND', message: 'GSTIN not found' };
// Validate environment variables
if (!clientId || !clientSecret || !productInstanceId) {
console.error('GST API credentials missing. Check environment variables: GST_CLIENT_ID, GST_CLIENT_SECRET, GST_PRODUCT_INSTANCE_ID');
return {
success: false,
statusCode: 500,
errorCode: 'CONFIGURATION_ERROR',
message: 'Server configuration error: Missing GST provider credentials'
};
}
const d = result.rows[0];
// Clean credentials (trim whitespace)
const cleanClientId = clientId.trim();
const cleanClientSecret = clientSecret.trim();
const cleanProductInstanceId = productInstanceId.trim();
const cleanUrl = providerUrl.trim();
// nature_of_business in CSV is stored as a single string like "Manufacturing|Services"
const natureOfBusinessArray = d.nature_of_business
? String(d.nature_of_business).split('|').map((v) => v.trim()).filter(Boolean)
: [];
// Debug: log outgoing provider request (mask sensitive parts)
try {
const maskedSecret = cleanClientSecret ? ('****' + cleanClientSecret.slice(-4)) : null;
console.log('[GST Service] Sending request to provider:', {
url: cleanUrl,
'x-client-id': cleanClientId,
'x-client-secret': maskedSecret,
'x-product-instance-id': cleanProductInstanceId,
requestBody: { gstin }
});
} catch (logErr) {
console.error('[GST Service] Failed to log provider request:', logErr.message);
}
const data = {
gstin,
legal_name: d.legal_name,
trade_name: d.trade_name,
status: d.status,
registration_date: d.registration_date,
last_updated: d.last_updated,
business_type: d.business_type,
constitution: d.constitution,
state: d.state || STATE_NAMES[gstin.substring(0, 2)],
const response = await axiosInstance.post(
cleanUrl,
{ gstin },
{
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'x-client-id': cleanClientId,
'x-client-secret': cleanClientSecret,
'x-product-instance-id': cleanProductInstanceId
},
timeout: 30000
}
);
if (!response || response.status !== 200 || !response.data) {
return {
success: false,
statusCode: response?.status || 502,
errorCode: 'GST_PROVIDER_ERROR',
message: 'GST provider returned an unexpected response'
};
}
const d = response.data.data || {};
const setuVerification = response.data.verification || 'SUCCESS';
const setuMessage = response.data.message || '';
const traceId = response.data.traceId || null;
const requestId = response.data.requestId || null;
// Check if we got valid company data, otherwise handle as invalid/empty
const company = d.company || {};
const address = d.address?.principle || {};
const gstData = d.gst || {};
const jurisdiction = d.jurisdiction || {};
const mappedData = {
gstin: gstData.id || gstin,
legal_name: company.name || '',
trade_name: company.tradeName || '',
status: company.status || 'Inactive',
registration_date: gstData.registrationDate || '',
last_updated: gstData.lastUpdatedDate || '',
business_type: company.type || '',
constitution: company.constitutionOfBusiness || '',
state: address.stateCode || company.state || STATE_NAMES[gstin.substring(0, 2)] || '',
state_code: gstin.substring(0, 2),
pan: gstin.substring(2, 12),
address: {
building: d.address_building || '',
floor: d.address_floor || '',
street: d.address_street || '',
locality: d.address_locality || '',
city: d.address_city || '',
district: d.address_district || '',
state: d.address_state_code || '',
pincode: d.address_pincode || ''
building: address.buildingName || '',
building_number: address.buildingNumber || '',
floor: address.floorNo || '',
street: address.street || '',
locality: address.location || '',
city: address.city || '',
district: address.district || '',
state: address.stateCode || '',
pincode: address.pinCode || ''
},
nature_of_business: natureOfBusinessArray,
filing_status: {
gstr1: d.filing_status_gstr1 || 'Unknown',
gstr3b: d.filing_status_gstr3b || 'Unknown',
last_filed_date: d.filing_last_filed_date || null
nature_of_business: company.natureOfBusinessActivity ? [company.natureOfBusinessActivity] : [],
jurisdiction: {
centre: jurisdiction.centre || '',
state: jurisdiction.state || ''
}
};
await cacheSet(cacheKey, data, 86400);
return { success: true, data };
const responseData = {
verification: setuVerification,
message: setuMessage,
requestId: requestId,
traceId: traceId,
data: Object.keys(d).length > 0 ? mappedData : {}
};
await cacheSet(cacheKey, responseData, 86400);
return { success: true, data: responseData };
} catch (error) {
// Surface provider details when available to avoid generic 500s
// Handle timeout errors
if (error.code === 'ECONNABORTED') {
return { success: false, statusCode: 504, errorCode: 'PROVIDER_TIMEOUT', message: 'Service timeout' };
return {
success: false,
statusCode: 504,
errorCode: 'PROVIDER_TIMEOUT',
message: 'GST verification service timeout'
};
}
const providerStatus = error.response?.status || error.response?.data?.status_code;
const providerMessage = error.response?.data?.message || error.message;
// Handle Setu API errors
if (error.response) {
const providerStatus = error.response.status;
// Handle Provider Authentication Errors specifically
if (providerStatus === 401 || providerStatus === 403) {
// Log provider auth failure details for debugging (mask any obvious secrets)
try {
const providerData = error.response.data;
const providerHeaders = error.response.headers || {};
const maskedHeaders = { ...providerHeaders };
if (maskedHeaders['x-client-secret']) {
maskedHeaders['x-client-secret'] = '****' + String(maskedHeaders['x-client-secret']).slice(-4);
}
console.error('[GST Service] Provider Authentication Failed', {
status: providerStatus,
data: providerData,
headers: maskedHeaders
});
} catch (logErr) {
console.error('[GST Service] Failed to log provider auth error:', logErr.message);
}
// Surface provider message when available to aid debugging (do not expose secrets)
const providerMsg = error.response.data?.message || error.response.data || 'Upstream provider authentication failed';
return {
success: false,
statusCode: 502,
errorCode: 'GST_PROVIDER_AUTH_ERROR',
message: typeof providerMsg === 'string' ? providerMsg : 'Upstream provider authentication failed. Check provider logs.'
};
}
// For other provider errors
const errorData = error.response.data || {};
const providerMessage = errorData.message || error.message;
if (providerStatus) {
return {
success: false,
statusCode: providerStatus,
errorCode: error.response?.data?.error_code || 'PROVIDER_ERROR',
errorCode: errorData.error_code || 'PROVIDER_ERROR',
message: providerMessage || 'Provider error'
};
}
return { success: false, statusCode: 500, errorCode: 'VERIFICATION_FAILED', message: 'Verification failed' };
// Generic error
console.error('GST verification error:', error.message);
return {
success: false,
statusCode: 500,
errorCode: 'VERIFICATION_FAILED',
message: 'GST verification failed'
};
}
}
module.exports = { verifyGSTIN };
// const axios = require('axios');
// async function verifyGstSetu(gstin) {
// const baseUrl = process.env.SETU_BASE_URL;
// const clientId = process.env.SETU_CLIENT_ID;
// const clientSecret = process.env.SETU_CLIENT_SECRET;
// const productInstanceId = process.env.SETU_GST_INSTANCE_ID;
// // Validate environment variables
// if (!baseUrl || !clientId || !clientSecret || !productInstanceId) {
// throw new Error('Setu GST API configuration is incomplete. Check environment variables.');
// }
// try {
// console.log(`🌐 Attempting Setu GST verification for GSTIN: ${gstin}`);
// const response = await axios.post(
// `${baseUrl}/api/verify/gst`,
// {
// gstin: gstin,
// consent: 'Y',
// reason: 'Vendor verification'
// },
// {
// headers: {
// 'Content-Type': 'application/json',
// 'x-client-id': clientId,
// 'x-client-secret': clientSecret,
// 'x-product-instance-id': productInstanceId
// },
// timeout: 30000 // 30 second timeout
// }
// );
// // Check if Setu returned a successful verification (case-insensitive)
// const verification = response.data?.verification?.toLowerCase();
// if (response.data && (verification === 'success' || response.data.status === 'success')) {
// console.log(`✅ Setu GST verification successful for GSTIN: ${gstin}`);
// return {
// success: true,
// source: 'SETU_API',
// data: response.data.data
// };
// }
// // If verification field is not SUCCESS, treat as failure
// throw new Error(response.data?.message || 'GST verification failed via Setu');
// } catch (error) {
// // Re-throw with more context
// if (error.response) {
// // Setu returned an error response
// const statusCode = error.response.status;
// const errorMessage = error.response.data?.message || error.response.data?.error || 'Unknown Setu API error';
// console.warn(`⚠️ Setu GST API failed for GSTIN ${gstin}: Setu API Error (${statusCode}): ${errorMessage}`);
// throw new Error(`Setu API Error (${statusCode}): ${errorMessage}`);
// } else if (error.code === 'ECONNABORTED') {
// console.warn(`⚠️ Setu GST API timeout for GSTIN ${gstin}`);
// throw new Error('Setu API timeout');
// } else {
// console.warn(`⚠️ Setu GST API failed for GSTIN ${gstin}: ${error.message}`);
// throw error;
// }
// }
// }
// module.exports = { verifyGstSetu };

View File

@ -1,140 +1,223 @@
const axios = require('axios');
const { cacheGet, cacheSet } = require('../cache/redis');
const { query } = require('../database/connection');
const PAN_TYPES = {
'P': 'Individual',
'C': 'Company',
'H': 'HUF',
'A': 'AOP',
'B': 'BOI',
'G': 'Government',
'J': 'Artificial Juridical Person',
'L': 'Local Authority',
'F': 'Firm/Partnership',
'T': 'Trust'
// Create a fresh axios instance to avoid global axios.defaults pollution from other services
const axiosInstance = axios.create();
async function verifyPAN(pan, reason = null, userId = null) {
try {
// Validate PAN format (10 characters: 5 letters, 4 digits, 1 letter)
const panRegex = /^[A-Z]{5}[0-9]{4}[A-Z]{1}$/;
const panUpper = pan.toUpperCase();
if (!panRegex.test(panUpper)) {
return {
success: false,
statusCode: 400,
errorCode: 'INVALID_PAN_FORMAT',
message: 'Invalid PAN format. PAN must be 10 characters: 5 letters, 4 digits, 1 letter.'
};
}
// Get Setu API credentials from environment variables
const providerUrl = process.env.PAN_PROVIDER_URL || 'https://dg-sandbox.setu.co/api/verify/pan';
const clientId = process.env.PAN_CLIENT_ID;
const clientSecret = process.env.PAN_CLIENT_SECRET;
const productInstanceId = process.env.PAN_PRODUCT_INSTANCE_ID;
// Validate environment variables
if (!clientId || !clientSecret || !productInstanceId) {
console.error('PAN API credentials missing. Check environment variables: PAN_CLIENT_ID, PAN_CLIENT_SECRET, PAN_PRODUCT_INSTANCE_ID');
return {
success: false,
statusCode: 500,
errorCode: 'CONFIGURATION_ERROR',
message: 'Server configuration error: Missing PAN provider credentials'
};
}
// Clean credentials (just trim whitespace)
const cleanClientId = clientId ? clientId.trim() : '';
const cleanClientSecret = clientSecret ? clientSecret.trim() : '';
const cleanProductInstanceId = productInstanceId ? productInstanceId.trim() : '';
const cleanUrl = providerUrl ? providerUrl.trim() : '';
// Prepare request body for Setu API
const requestBody = {
pan: panUpper,
consent: 'Y',
reason: reason || 'Identity verification for KYC compliance'
};
async function verifyPAN(pan, name = null, dob = null) {
// Call Setu API
// Debug: log outgoing provider request (mask sensitive parts)
try {
const cacheKey = `pan:${pan}`;
const cached = await cacheGet(cacheKey);
if (cached) {
if (name) {
cached.name_match = cached.name === name.toUpperCase();
cached.name_match_score = cached.name_match ? 100 : 0;
const maskedSecret = cleanClientSecret ? ('****' + cleanClientSecret.slice(-4)) : null;
console.log('[PAN Service] Sending request to provider:', {
url: cleanUrl,
'x-client-id': cleanClientId,
'x-client-secret': maskedSecret,
'x-product-instance-id': cleanProductInstanceId,
requestBody
});
} catch (logErr) {
console.error('[PAN Service] Failed to log provider request:', logErr.message);
}
return { success: true, data: cached };
const response = await axiosInstance.post(
cleanUrl,
requestBody,
{
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'x-client-id': cleanClientId,
'x-client-secret': cleanClientSecret,
'x-product-instance-id': cleanProductInstanceId
},
timeout: 30000 // 30 second timeout
}
);
// Check if response is successful
if (!response || response.status !== 200 || !response.data) {
return {
success: false,
statusCode: response?.status || 502,
errorCode: 'PAN_PROVIDER_ERROR',
message: 'PAN provider returned an unexpected response'
};
}
// Extract data from Setu response
const setuData = response.data.data || {};
const setuMessage = response.data.message || '';
const setuId = response.data.id || null;
const setuVerification = response.data.verification || 'SUCCESS';
const traceId = response.data.traceId || null;
const parts = name.trim().split(" ");
const firstName = parts[0] || "DUMMY";
const lastName = parts.slice(1).join(" ") || "NAME";
const mockResponseData = {
status_code: 200,
// Format response to match Setu's structure
const responseData = {
verification: setuVerification,
message: setuMessage,
data: {
name: name || "DUMMY NAME",
status: "ACTIVE",
type: PAN_TYPES[pan[3]] || 'Individual',
full_name: name || "DUMMY NAME",
last_name: lastName,
first_name: firstName,
}
};
const response = { data: mockResponseData };
// Commenting out the actual external API call for now
// const response = await axios.post(
// process.env.PAN_PROVIDER_URL,
// {
// id_number: pan,
// name: name,
// dob: dob
// },
// {
// headers: {
// 'Authorization': `Bearer ${process.env.PAN_PROVIDER_KEY}`,
// 'Content-Type': 'application/json'
// },
// timeout: 30000
// }
// );
if (!response.data || response.data.status_code !== 200) {
return { success: false, statusCode: 404, errorCode: 'PAN_NOT_FOUND', message: 'PAN not found' };
}
const d = response.data.data;
const panType = PAN_TYPES[pan[3]] || 'Unknown';
const data = {
pan,
name: d.name || d.full_name || '',
status: d.status || 'Valid',
type: d.type || panType,
name_match: name ? (d.name || d.full_name || '').toUpperCase() === name.toUpperCase() : null,
name_match_score: name ? (d.name || d.full_name || '').toUpperCase() === name.toUpperCase() ? 100 : 0 : null,
last_name: d.last_name || d.surname || '',
first_name: d.first_name || '',
middle_name: d.middle_name || '',
title: d.title || ''
full_name: setuData.full_name || null,
first_name: setuData.first_name || null,
middle_name: setuData.middle_name || null,
last_name: setuData.last_name || null,
category: setuData.category || null,
aadhaar_seeding_status: setuData.aadhaar_seeding_status || null
},
id: setuId,
traceId: traceId
};
await cacheSet(cacheKey, data, 86400);
// Persist PAN verification result to Postgres (best-effort, non-blocking)
try {
await query(
// Store in database (non-blocking, best-effort)
query(
`INSERT INTO pan_verifications (
pan,
name,
full_name,
first_name,
middle_name,
last_name,
category,
aadhaar_seeding_status,
status,
pan_type,
name_match,
name_match_score,
setu_response_id,
setu_trace_id,
setu_message,
requested_by
) VALUES ($1, $2, $3, $4, $5, $6, $7)`,
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`,
[
data.pan,
data.name,
data.status,
data.type,
data.name_match,
data.name_match_score,
null, // requested_by (user id) - can be wired from route later if needed
pan.toUpperCase(),
setuData.full_name,
setuData.first_name,
setuData.middle_name,
setuData.last_name,
setuData.category,
setuData.aadhaar_seeding_status,
setuVerification === 'SUCCESS' ? 'VALID' : 'INVALID',
setuId,
traceId,
setuMessage,
userId
]
);
} catch (dbError) {
console.error('Failed to store PAN verification in database:', dbError.message || dbError);
}
).catch(dbError => {
console.error('Failed to store PAN verification in database:', dbError.message);
});
return { success: true, data };
return { success: true, data: responseData };
} catch (error) {
// Surface provider details when available to avoid generic 500s
// Handle timeout errors
if (error.code === 'ECONNABORTED') {
return { success: false, statusCode: 504, errorCode: 'PROVIDER_TIMEOUT', message: 'Service timeout' };
return {
success: false,
statusCode: 504,
errorCode: 'PROVIDER_TIMEOUT',
message: 'PAN verification service timeout'
};
}
const providerStatus = error.response?.status || error.response?.data?.status_code;
const providerMessage = error.response?.data?.message || error.message;
// Handle Setu API errors
if (error.response) {
const providerStatus = error.response.status;
// Handle Provider Authentication Errors specifically
if (providerStatus === 401 || providerStatus === 403) {
// Log provider auth failure details for debugging (mask any obvious secrets)
try {
const providerData = error.response.data;
const providerHeaders = error.response.headers || {};
const maskedHeaders = { ...providerHeaders };
if (maskedHeaders['x-client-secret']) {
maskedHeaders['x-client-secret'] = '****' + String(maskedHeaders['x-client-secret']).slice(-4);
}
console.error('[PAN Service] Provider Authentication Failed', {
status: providerStatus,
data: providerData,
headers: maskedHeaders
});
} catch (logErr) {
console.error('[PAN Service] Failed to log provider auth error:', logErr.message);
}
// Surface provider message when available to aid debugging (do not expose secrets)
const providerMsg = error.response.data?.message || error.response.data || 'Upstream provider authentication failed';
return {
success: false,
statusCode: 502,
errorCode: 'PAN_PROVIDER_AUTH_ERROR',
message: typeof providerMsg === 'string' ? providerMsg : 'Upstream provider authentication failed. Check provider logs.'
};
}
// For other provider errors, return Setu-formatted error response if available
const errorData = error.response.data || {};
if (providerStatus) {
return {
success: false,
statusCode: providerStatus,
errorCode: error.response?.data?.error_code || 'PROVIDER_ERROR',
message: providerMessage || 'Provider error'
data: {
verification: errorData.verification || 'failed',
message: errorData.message || 'PAN verification failed',
id: errorData.id || null,
traceId: errorData.traceId || null
}
};
}
return { success: false, statusCode: 500, errorCode: 'VERIFICATION_FAILED', message: 'Verification failed' };
// Generic error
console.error('PAN verification error:', error.message);
return {
success: false,
statusCode: 500,
errorCode: 'VERIFICATION_FAILED',
message: 'PAN verification failed'
};
}
}
module.exports = { verifyPAN };

58
test-direct-setu.js Normal file
View File

@ -0,0 +1,58 @@
// Test script - run from project root: node test-direct-setu.js
require('dotenv').config();
const axios = require('axios');
async function testDirect() {
console.log('\n=== Testing Direct Setu API Call ===\n');
const clientId = process.env.PAN_CLIENT_ID;
const clientSecret = process.env.PAN_CLIENT_SECRET;
const productId = process.env.PAN_PRODUCT_INSTANCE_ID;
const url = process.env.PAN_PROVIDER_URL || 'https://dg-sandbox.setu.co/api/verify/pan';
console.log('Loaded from .env:');
console.log(' clientId:', clientId);
console.log(' clientSecret:', clientSecret ? '****' + clientSecret.slice(-4) : 'MISSING');
console.log(' productId:', productId);
console.log(' url:', url);
console.log('');
console.log('Testing with axios...');
try {
const response = await axios.post(
url,
{
pan: 'ABCDE1234A',
consent: 'Y',
reason: 'Testing PAN verification'
},
{
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'x-client-id': clientId,
'x-client-secret': clientSecret,
'x-product-instance-id': productId
},
timeout: 30000
}
);
console.log('✅ SUCCESS!');
console.log('Status:', response.status);
console.log('Data:', JSON.stringify(response.data, null, 2));
} catch (error) {
console.log('❌ FAILED!');
if (error.response) {
console.log('Status:', error.response.status);
console.log('Data:', JSON.stringify(error.response.data, null, 2));
console.log('Headers:', JSON.stringify(error.response.headers, null, 2));
} else {
console.log('Error:', error.message);
}
}
}
testDirect();

69
test-gst-credentials.js Normal file
View File

@ -0,0 +1,69 @@
const axios = require('axios');
// Test with EXACT same approach as gstService.js
async function testWithAxiosInstance() {
// Create a fresh axios instance (same as in gstService.js)
const axiosInstance = axios.create();
const providerUrl = 'https://dg-sandbox.setu.co/api/verify/gst';
const clientId = '292c6e76-dabf-49c4-8e48-90fba2916673';
const clientSecret = '7IZMe9zvoBBuBukLiCP7n4KLwSOy11oP';
const productInstanceId = '69e23f7f-4f71-412e-aec6-b1da3fb77c6f';
// Clean credentials (trim whitespace) - EXACT same as gstService.js
const cleanClientId = clientId.trim();
const cleanClientSecret = clientSecret.trim();
const cleanProductInstanceId = productInstanceId.trim();
const cleanUrl = providerUrl.trim();
const gstin = '29AABCT1332L1ZV';
console.log('Testing with axios instance (EXACT same as gstService.js)...\n');
console.log('Cleaned credentials:');
console.log(' URL:', cleanUrl);
console.log(' Client ID:', cleanClientId);
console.log(' Client Secret:', '****' + cleanClientSecret.slice(-4));
console.log(' Product Instance ID:', cleanProductInstanceId);
console.log(' GSTIN:', gstin);
console.log('\n---\n');
try {
const response = await axiosInstance.post(
cleanUrl,
{ gstin },
{
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'x-client-id': cleanClientId,
'x-client-secret': cleanClientSecret,
'x-product-instance-id': cleanProductInstanceId
},
timeout: 30000
}
);
console.log('✅ SUCCESS!');
console.log('Status:', response.status);
console.log('Data:', JSON.stringify(response.data, null, 2));
} catch (error) {
if (error.response) {
console.log('❌ FAILED');
console.log('Status:', error.response.status);
console.log('Error:', JSON.stringify(error.response.data, null, 2));
console.log('\n---\n');
// Log the actual request that was sent
console.log('Request config:');
console.log(' URL:', error.config.url);
console.log(' Method:', error.config.method);
console.log(' Headers:', JSON.stringify(error.config.headers, null, 2));
console.log(' Data:', error.config.data);
} else {
console.log('❌ ERROR:', error.message);
}
}
}
testWithAxiosInstance();