Claude Code Plugins

Community-maintained marketplace

Feedback

jvzoo-integration

@icemusike/nanoBananaAds
0
0

Expert JVZoo integration specialist for building backend systems that handle JVZoo purchases, create user accounts, manage software licenses, and process IPN notifications. Use when implementing JVZoo product licensing, account creation workflows, payment processing, refund handling, or connecting software products to JVZoo marketplace by product ID.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name jvzoo-integration
description Expert JVZoo integration specialist for building backend systems that handle JVZoo purchases, create user accounts, manage software licenses, and process IPN notifications. Use when implementing JVZoo product licensing, account creation workflows, payment processing, refund handling, or connecting software products to JVZoo marketplace by product ID.

JVZoo Integration Specialist

Expert guidance for building production-ready JVZoo integrations that handle purchases, license management, and account creation.

Overview

JVZoo integration involves three core components:

  1. IPN (Instant Payment Notification) - Real-time webhook from JVZoo when purchases occur
  2. License Management - Generate, validate, and track software licenses
  3. Account Creation - Automatically create user accounts from purchase data

IPN Setup & Security

IPN Endpoint Requirements

Your IPN endpoint must:

  • Accept POST requests from JVZoo servers
  • Respond within 10 seconds with HTTP 200
  • Validate authenticity using secret key
  • Handle idempotent processing (duplicates possible)
  • Log all requests for debugging

IPN Verification

Always verify IPN authenticity using SHA-1 hash:

Verification Hash = SHA1(secretKey + "|" + transactionId + "|" + amount + "|" + productId)

Compare this hash with the cverify parameter sent by JVZoo. Never process unverified IPNs.

Essential IPN Parameters

Transaction Info:

  • ctransaction - Transaction ID (use as idempotency key)
  • ctransaction_type - SALE, RFND (refund), CGBK (chargeback), INSTAL (recurring), CANCEL-REBILL
  • ctransreceipt - JVZoo receipt number
  • cproditem - Product ID (your product identifier)
  • cprodtype - STANDARD, RECURRING, etc.

Customer Info:

  • ccustemail - Customer email (primary identifier)
  • ccustname - Customer full name
  • ccustcc - Customer country code
  • ccuststate - Customer state/region

Financial Info:

  • ctransamount - Sale amount
  • ctransaffiliate - Affiliate commission
  • ctransvendor - Your earnings

Verification:

  • cverify - SHA-1 hash for verification

Account Creation Workflow

Step 1: Verify IPN

function verifyIPN(ipnData, secretKey) {
  const { ctransaction, ctransamount, cproditem, cverify } = ipnData;
  const hash = crypto
    .createHash('sha1')
    .update(`${secretKey}|${ctransaction}|${ctransamount}|${cproditem}`)
    .digest('hex')
    .toUpperCase();
  
  return hash === cverify.toUpperCase();
}

Step 2: Check for Duplicate Processing

async function isDuplicateTransaction(transactionId) {
  // Query database for existing transaction
  const existing = await db.transactions.findOne({ 
    jvzoo_transaction_id: transactionId 
  });
  return !!existing;
}

Step 3: Create or Update User Account

async function processNewSale(ipnData) {
  const { ccustemail, ccustname, cproditem, ctransaction } = ipnData;
  
  // Find or create user
  let user = await db.users.findOne({ email: ccustemail });
  
  if (!user) {
    user = await db.users.create({
      email: ccustemail,
      name: ccustname,
      created_via: 'jvzoo',
      jvzoo_transaction: ctransaction
    });
  }
  
  return user;
}

Step 4: Generate License Key

function generateLicenseKey(userId, productId, transactionId) {
  const timestamp = Date.now();
  const data = `${userId}:${productId}:${transactionId}:${timestamp}`;
  const hash = crypto
    .createHash('sha256')
    .update(data + process.env.LICENSE_SECRET)
    .digest('hex');
  
  // Format: XXXX-XXXX-XXXX-XXXX
  return hash.substring(0, 16).toUpperCase().match(/.{1,4}/g).join('-');
}

Step 5: Store License & Send Access

async function createLicense(userId, productId, ipnData) {
  const licenseKey = generateLicenseKey(
    userId, 
    productId, 
    ipnData.ctransaction
  );
  
  const license = await db.licenses.create({
    user_id: userId,
    product_id: productId,
    license_key: licenseKey,
    jvzoo_transaction: ipnData.ctransaction,
    jvzoo_receipt: ipnData.ctransreceipt,
    status: 'active',
    purchase_date: new Date(),
    transaction_type: ipnData.ctransaction_type
  });
  
  // Send email with license key and login details
  await sendWelcomeEmail(userId, licenseKey);
  
  return license;
}

Transaction Type Handling

SALE (New Purchase)

case 'SALE':
  await processNewSale(ipnData);
  await createLicense(user.id, ipnData.cproditem, ipnData);
  break;

RFND (Refund)

case 'RFND':
  await db.licenses.update(
    { jvzoo_transaction: ipnData.ctransaction },
    { status: 'refunded', refunded_at: new Date() }
  );
  await revokeAccess(user.id);
  break;

CGBK (Chargeback)

case 'CGBK':
  await db.licenses.update(
    { jvzoo_transaction: ipnData.ctransaction },
    { status: 'chargeback', chargeback_at: new Date() }
  );
  await revokeAccess(user.id);
  break;

INSTAL (Recurring Payment)

case 'INSTAL':
  await db.licenses.update(
    { jvzoo_transaction: ipnData.ctransaction },
    { 
      status: 'active',
      last_payment: new Date(),
      payment_count: { $inc: 1 }
    }
  );
  break;

CANCEL-REBILL (Subscription Cancelled)

case 'CANCEL-REBILL':
  await db.licenses.update(
    { jvzoo_transaction: ipnData.ctransaction },
    { 
      status: 'cancelled',
      cancelled_at: new Date()
    }
  );
  // Grace period before revoking access
  await scheduleAccessRevocation(user.id, 30); // 30 days
  break;

License Validation API

Build an API endpoint your software calls to validate licenses:

// POST /api/validate-license
async function validateLicense(req, res) {
  const { license_key, email } = req.body;
  
  const license = await db.licenses.findOne({
    license_key: license_key,
    status: 'active'
  });
  
  if (!license) {
    return res.json({ valid: false, error: 'Invalid license' });
  }
  
  const user = await db.users.findById(license.user_id);
  
  if (user.email !== email) {
    return res.json({ valid: false, error: 'Email mismatch' });
  }
  
  // Update last validation timestamp
  await db.licenses.update(
    { _id: license._id },
    { last_validated: new Date() }
  );
  
  return res.json({
    valid: true,
    license: {
      product_id: license.product_id,
      expiry_date: license.expiry_date,
      features: license.features
    }
  });
}

Database Schema Recommendations

Users Table

CREATE TABLE users (
  id UUID PRIMARY KEY,
  email VARCHAR(255) UNIQUE NOT NULL,
  name VARCHAR(255),
  created_via VARCHAR(50),
  jvzoo_transaction VARCHAR(255),
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

Licenses Table

CREATE TABLE licenses (
  id UUID PRIMARY KEY,
  user_id UUID REFERENCES users(id),
  product_id VARCHAR(100) NOT NULL,
  license_key VARCHAR(50) UNIQUE NOT NULL,
  jvzoo_transaction VARCHAR(255) UNIQUE NOT NULL,
  jvzoo_receipt VARCHAR(255),
  status VARCHAR(50) NOT NULL, -- active, refunded, chargeback, cancelled
  purchase_date TIMESTAMP NOT NULL,
  expiry_date TIMESTAMP,
  last_validated TIMESTAMP,
  transaction_type VARCHAR(50),
  refunded_at TIMESTAMP,
  chargeback_at TIMESTAMP,
  cancelled_at TIMESTAMP,
  payment_count INTEGER DEFAULT 1,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

Transactions Table (Audit Log)

CREATE TABLE jvzoo_transactions (
  id UUID PRIMARY KEY,
  jvzoo_transaction_id VARCHAR(255) UNIQUE NOT NULL,
  transaction_type VARCHAR(50) NOT NULL,
  product_id VARCHAR(100) NOT NULL,
  customer_email VARCHAR(255) NOT NULL,
  amount DECIMAL(10,2),
  raw_ipn_data JSONB,
  processed BOOLEAN DEFAULT FALSE,
  processed_at TIMESTAMP,
  created_at TIMESTAMP DEFAULT NOW()
);

Product ID Mapping

Map JVZoo product IDs to your internal product identifiers:

const PRODUCT_MAPPING = {
  '123456': {
    name: 'AI Ranker Pro - Basic',
    features: ['feature1', 'feature2'],
    internal_id: 'ai-ranker-basic'
  },
  '123457': {
    name: 'AI Ranker Pro - Premium',
    features: ['feature1', 'feature2', 'feature3', 'feature4'],
    internal_id: 'ai-ranker-premium'
  },
  '123458': {
    name: 'Callfluent AI - Starter',
    features: ['100-calls', 'basic-voices'],
    internal_id: 'callfluent-starter'
  }
};

function getProductConfig(jvzooProductId) {
  return PRODUCT_MAPPING[jvzooProductId] || null;
}

Security Best Practices

Environment Variables

Never hardcode credentials:

JVZOO_SECRET_KEY=your_secret_key_here
LICENSE_SECRET=separate_key_for_license_generation
DATABASE_URL=your_database_connection
SMTP_API_KEY=email_service_key

Rate Limiting

Protect your IPN endpoint from abuse:

const rateLimit = require('express-rate-limit');

const ipnLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // Max 100 requests per IP
  message: 'Too many requests from this IP'
});

app.post('/ipn', ipnLimiter, handleIPN);

Input Validation

Always validate and sanitize IPN data:

function sanitizeIPNData(data) {
  return {
    ctransaction: String(data.ctransaction || '').trim(),
    ccustemail: String(data.ccustemail || '').toLowerCase().trim(),
    ccustname: String(data.ccustname || '').trim(),
    cproditem: String(data.cproditem || '').trim(),
    ctransamount: parseFloat(data.ctransamount || 0),
    // ... sanitize all fields
  };
}

Error Handling

Always return 200 OK to prevent retries, but log errors:

app.post('/ipn', async (req, res) => {
  try {
    // Always acknowledge receipt
    res.status(200).send('OK');
    
    // Process asynchronously
    await processIPN(req.body);
  } catch (error) {
    // Log error but don't tell JVZoo about it
    console.error('IPN Processing Error:', error);
    await logError(error, req.body);
  }
});

Testing Your Integration

Test Mode

JVZoo provides test IPNs. Look for test indicators:

function isTestTransaction(ipnData) {
  return ipnData.ctransaction.toLowerCase().includes('test') ||
         ipnData.ccustemail.toLowerCase().includes('test');
}

Manual IPN Testing

Send test IPN to your endpoint:

curl -X POST https://yoursite.com/ipn \
  -d "ctransaction=TEST123456" \
  -d "ccustemail=test@example.com" \
  -d "ccustname=Test User" \
  -d "cproditem=123456" \
  -d "ctransamount=97.00" \
  -d "ctransaction_type=SALE" \
  -d "cverify=YOUR_CALCULATED_HASH"

Validation Checklist

  • IPN verification working correctly
  • Duplicate transactions prevented
  • User accounts created successfully
  • License keys generated and stored
  • Welcome emails sent
  • Refunds revoke access
  • Chargebacks logged
  • Recurring payments extend access
  • All transactions logged for audit
  • Error handling returns 200 OK

Common Implementation Frameworks

Node.js + Express

See references/nodejs-implementation.md for complete Express.js example with middleware, error handling, and database integration.

Python + FastAPI

See references/python-implementation.md for FastAPI implementation with async handling and Pydantic validation.

Next.js API Routes

See references/nextjs-implementation.md for serverless implementation using Next.js API routes with Supabase.

Troubleshooting

IPN not being received:

  • Check JVZoo IPN URL configuration in product settings
  • Verify endpoint is publicly accessible (not localhost)
  • Check firewall/server logs
  • Ensure endpoint responds within 10 seconds

Hash verification failing:

  • Verify secret key matches JVZoo dashboard
  • Check parameter order in hash calculation
  • Ensure string concatenation uses pipes (|)
  • Convert hash to uppercase for comparison

Duplicate accounts created:

  • Implement idempotency check using ctransaction
  • Use database constraints on transaction ID
  • Log all IPN requests to audit table

License validation failing:

  • Check license status in database
  • Verify email matches purchase email
  • Ensure license hasn't been refunded/charged back
  • Check for expiry dates on time-limited licenses

Quick Start Template

For fastest implementation, use scripts/setup_ipn_endpoint.js to generate boilerplate IPN handler with your framework of choice (Express, FastAPI, Next.js).

See references/deployment-guide.md for production deployment considerations including SSL, monitoring, logging, and backup strategies.