Claude-skill-registry jvzoo-integration
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.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/jvzoo-integration" ~/.claude/skills/majiayu000-claude-skill-registry-jvzoo-integration && rm -rf "$T"
skills/data/jvzoo-integration/SKILL.mdJVZoo 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:
- IPN (Instant Payment Notification) - Real-time webhook from JVZoo when purchases occur
- License Management - Generate, validate, and track software licenses
- 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:
- Transaction ID (use as idempotency key)ctransaction
- SALE, RFND (refund), CGBK (chargeback), INSTAL (recurring), CANCEL-REBILLctransaction_type
- JVZoo receipt numberctransreceipt
- Product ID (your product identifier)cproditem
- STANDARD, RECURRING, etc.cprodtype
Customer Info:
- Customer email (primary identifier)ccustemail
- Customer full nameccustname
- Customer country codeccustcc
- Customer state/regionccuststate
Financial Info:
- Sale amountctransamount
- Affiliate commissionctransaffiliate
- Your earningsctransvendor
Verification:
- SHA-1 hash for verificationcverify
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.