Skip to main content

🚀 AMAZING Digital Product System: Complete Implementation Guide

Status: ✨ PRODUCTION-READY
Version: 2.0
Date: October 21, 2025
Branch: rc-3

"One Click. Everything Works. Your customers get their digital product instantly with beautiful emails and complete fulfillment automation."


🎯 The Vision

Transform digital product selling from complex to AMAZING:

Before 😞

  • Manual file uploads
  • Complex configuration
  • No automation
  • Raw JSON editing
  • Customer support tickets for delivery

After

  • Drag-drop file → One click → DONE
  • Beautiful UI configuration
  • Complete automation
  • Instant fulfillment
  • Zero support friction

📦 What You Get

🎨 Frontend (React/TypeScript)

DigitalProductUploader Component (DigitalProductUploader.tsx)

  • Drag-and-drop file upload with beautiful UI
  • Intelligent file detection (PDF, Video, ZIP, Slides, etc.)
  • One-click product creation with smart defaults
  • Visual configuration for price, limits, expiration
  • Real-time upload progress
  • Success confirmation with activation checklist
  • Support for files up to 2GB

useDigitalProductUpload Hook (useDigitalProductUpload.ts)

  • Orchestrates entire upload flow
  • File upload → Product creation → Asset linking → Automation setup
  • Error handling and retry logic
  • Progress tracking
  • One hook to rule them all!

🔧 Backend (Java/Spring)

DigitalProductPaymentWebhookHandler (DigitalProductPaymentWebhookHandler.java)

  • Generic payment webhook: POST /v1/webhooks/payments/completed
  • Stripe webhook support: POST /v1/webhooks/stripe/charge-succeeded
  • PayPal webhook support: POST /v1/webhooks/paypal/payment-completed
  • Automatic fulfillment on payment
  • Email notifications sent instantly
  • Retry logic for failed fulfillments
  • Complete audit trail

Integration Points

  • Works with existing DigitalFulfillmentService
  • Leverages DigitalFulfillmentModule for async execution
  • Uses ContentTemplateAdvancedModule for beautiful emails
  • Integrates with Spring ACL for permissions
  • Supports all payment providers

🚀 Getting Started: 5-Minute Quick Start

Step 1: Upload Component Integration

// App.tsx
import { DigitalProductUploader } from "@components/DigitalProducts/DigitalProductUploader";

export function App() {
return (
<div>
<DigitalProductUploader />
</div>
);
}

Step 2: User Upload Experience

  1. User drags file or clicks to browse

    • Supports PDF, Video, Images, Office files, ZIP, Code
    • Auto-detects file type (🎥 video, 📕 PDF, etc.)
    • Shows file size (max 2GB)
  2. Configuration dialog opens automatically

    • Product name (pre-filled from filename)
    • Description (pre-filled with file type)
    • Pricing ($9.99 default, adjustable)
    • Download limits (unlimited by default)
    • Expiration (never by default)
    • Email notifications (configurable)
    • Automation settings (instant delivery enabled)
  3. Single click: "Create & Upload"

    • File uploaded and virus-scanned automatically
    • Product created in database
    • Digital asset linked to file
    • Fulfillment automation configured
    • Email template activated
    • Success confirmation with actions

Step 3: Configure Payment Webhook

# In your payment processor (Stripe, PayPal, etc):
# Set webhook URL to: https://yourdomain.com/v1/webhooks/payments/completed

Step 4: Customer Purchase Flow

  1. Customer visits storefront

  2. Clicks "Buy" on digital product

  3. Completes payment

  4. 🎉 Instant fulfillment:

    • DownloadAccess created
    • Secure token issued
    • Email sent with download link
    • Download available immediately
  5. Customer clicks link

  6. Downloads file with token validation

  7. Download tracked (count, timestamp)


🏗️ Architecture Overview

Data Flow

┌─────────────────────────────────────────────────────────────┐
│ DIGITAL PRODUCT SYSTEM │
└─────────────────────────────────────────────────────────────┘

USER UPLOAD
├─ 📤 DigitalProductUploader (React component)
│ ├─ Drag-drop file
│ ├─ Auto-detect type
│ └─ Open config dialog

├─ ⚙️ useDigitalProductUpload (hook)
│ ├─ Upload file → FileRecord (virus scanned)
│ ├─ Create Product → Product table
│ ├─ Link DigitalAsset → Product + File
│ ├─ Configure ProductDeliveryConfig → Automation
│ └─ Return success with all IDs

└─ ✅ Success confirmation

PAYMENT EVENT
├─ 💳 Payment webhook received
│ └─ POST /v1/webhooks/payments/completed
│ or /v1/webhooks/stripe/charge-succeeded
│ or /v1/webhooks/paypal/payment-completed

├─ 🎯 DigitalProductPaymentWebhookHandler
│ ├─ Fetch SalesOrder by ID
│ ├─ Find digital product LineItems
│ ├─ For each digital product:
│ │ ├─ Create OrderFulfillmentTask
│ │ ├─ Execute async fulfillment:
│ │ │ ├─ Call completeFulfillmentTask()
│ │ │ ├─ DigitalFulfillmentModule executes
│ │ │ ├─ DownloadAccess created with token
│ │ │ └─ ACL permission granted
│ │ └─ Send email notification
│ └─ Return success with counts

└─ 📧 Customer receives:
├─ Instant download link
├─ Token valid for specified period
├─ Secure, trackable access
└─ Download counter

DOWNLOAD
├─ 🔗 Customer clicks download link
├─ ✅ Token validated
├─ ✅ Access not expired
├─ ✅ Download limit not exceeded
├─ 📥 File served from storage
├─ 📊 Download tracked (count, timestamp)
└─ 🎉 Customer has their product

Component Stack

Frontend Layer
├─ DigitalProductUploader.tsx (648 lines)
│ ├─ Drag-drop zone
│ ├─ Configuration dialog
│ ├─ Progress tracking
│ └─ Success display
└─ useDigitalProductUpload.ts (150 lines)
├─ Orchestrates upload flow
├─ File validation
├─ Product creation
├─ Asset linking
└─ Automation setup

Backend Layer (Java)
├─ DigitalProductPaymentWebhookHandler (400 lines)
│ ├─ Generic webhook endpoint
│ ├─ Stripe integration
│ ├─ PayPal integration
│ └─ Async fulfillment execution

├─ DigitalFulfillmentService (existing)
│ ├─ Asset creation
│ ├─ Download link generation
│ ├─ Fulfillment completion
│ └─ Access revocation

├─ DigitalFulfillmentModule (ExecModule)
│ └─ Workflow execution for fulfillment

├─ ContentTemplateAdvancedModule (enhanced)
│ ├─ Beautiful email templates
│ ├─ Handlebars support
│ ├─ 13+ built-in filters
│ └─ Variable interpolation

└─ Models (ThorAPI-generated)
├─ Product (type="download")
├─ FileRecord (virus scanned)
├─ DigitalAsset (file→product link)
├─ DownloadAccess (ACL permission)
├─ OrderFulfillmentTask (lifecycle)
└─ ProductDeliveryConfig (automation rules)

Storage Layer
├─ FileRecord storage (S3, local, etc.)
├─ Database (PostgreSQL/JPA)
└─ Token cache (optional Redis)

📊 Supported File Types

TypeExamplesUse Case
📕 PDFEbooks, guides, whitepapersDigital products
🎥 VideoMP4, MOVCourses, tutorials
📊 SlidesPPT, PPTXPresentations
📦 ZIPArchives, bundlesMultiple files
📈 SpreadsheetXLS, XLSXData templates
📄 DocumentsDOCX, DOCGuides, templates
🖼️ ImagesJPG, PNGCollections, art
📝 TextTXT, CSVContent, data

🔐 Security Features

File Security

  • Virus scanning on upload (automatic)
  • File type validation
  • Size limits (2GB max)
  • SHA-256 checksums

Download Security

  • Token-based access (UUID, encrypted)
  • Expiration (configurable per product)
  • Usage limits (downloads counted)
  • Soft-delete on refund

Access Control

  • Spring ACL enforced
  • Principal-level permissions
  • RBAC integration
  • Audit trail logged

Data Encryption

  • Sensitive fields encrypted at rest
  • HTTPS required
  • Token encryption in storage
  • Metadata secured

🎯 API Reference

Frontend Hooks

useDigitalProductUpload()

const {
uploadDigitalProduct, // async (request) => DigitalProductUploadResult
loading, // boolean
error, // string | null
success, // boolean
createdProduct, // DigitalProductUploadResult | null
reset, // () => void
} = useDigitalProductUpload();

Usage:

await uploadDigitalProduct({
file: selectedFile, // File object
name: "My Course", // string
description: "Learn React", // string
price: 29.99, // number
salePrice: 19.99, // number
maxDownloads: -1, // -1 for unlimited
expiresAfterDays: 365, // -1 for never
notifyOnExpiry: true, // boolean
deliveryType: "instant_digital", // string
autoFulfill: true, // boolean
notificationTemplate: "digital-product-delivery", // string
requiresApproval: false, // boolean
});

Backend Webhooks

POST /v1/webhooks/payments/completed

Request:

{
"event": "payment.completed",
"orderId": "550e8400-e29b-41d4-a716-446655440000",
"paymentId": "pay_abc123",
"amount": 99.99,
"currency": "USD",
"paymentMethod": "stripe_card",
"timestamp": 1634829600
}

Response:

{
"status": "success",
"fulfillmentTasks": 2,
"emailsSent": 2,
"message": "✨ 2 digital products fulfilled and notification emails sent"
}

POST /v1/webhooks/stripe/charge-succeeded

Stripe sends:

{
"type": "charge.succeeded",
"data": {
"object": {
"id": "ch_1234567890",
"amount": 9999,
"metadata": {
"order_id": "550e8400-e29b-41d4-a716-446655440000"
},
"created": 1634829600
}
}
}

POST /v1/webhooks/paypal/payment-completed

PayPal sends:

{
"type": "PAYMENT.CAPTURE.COMPLETED",
"resource": {
"id": "5O190127588888416",
"amount": {
"value": "99.99",
"currency_code": "USD"
},
"supplementary_data": {
"related_ids": {
"order_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
}
}

Existing REST Endpoints

Create Digital Asset

POST /v1/Product/{productId}/createDigitalAsset
Content-Type: application/json

{
"fileId": "uuid",
"deliveryMethod": "direct_download",
"accessModel": "perpetual",
"maxDownloads": -1,
"expiresAfterDays": -1,
"notifyCustomerOnExpiry": false
}
POST /v1/Product/{productId}/generateDownloadLink
Content-Type: application/json

{
"principalId": "uuid",
"validityMinutes": 60
}

Response:
{
"downloadAccess": {...},
"fileRecord": {...},
"downloadUrl": "https://...",
"urlExpiresAt": "2025-10-21T13:00:00Z"
}

Download File

GET /v1/DownloadAccess/{accessId}/file?token={token}

🧪 Testing

Unit Tests

@SpringBootTest
class DigitalProductUploadTest {

@Test
void testEndToEndUpload() {
// Arrange
File file = new File("course.mp4");

// Act
DigitalProductUploadResult result = service.uploadDigitalProduct(file);

// Assert
assertThat(result.product().getName()).isNotEmpty();
assertThat(result.digitalAsset().getFileId()).isNotNull();
assertThat(result.deliveryConfig().isAutoFulfill()).isTrue();
}
}

Webhook Tests

@Test
void testPaymentWebhookFulfillment() {
// Arrange
PaymentCompletedEvent event = new PaymentCompletedEvent(
orderId, paymentId, 99.99, "USD", "stripe", now);

// Act
ResponseEntity<PaymentWebhookResponse> response =
handler.handlePaymentCompleted(event, signature);

// Assert
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().fulfillmentTasks()).isGreaterThan(0);
}

🚀 Deployment

Prerequisites

  • ✅ Java 17+
  • ✅ Spring Boot 3.4.6+
  • ✅ PostgreSQL 12+
  • ✅ File storage (S3, local, etc.)
  • ✅ React 18+
  • ✅ Content templates configured

Build

# Build backend
mvn clean install -DskipTests

# Build frontend
cd web
npm install && npm run build

Configuration

# .env
DATABASE_URL=jdbc:postgresql://localhost:5432/valkyr
STORAGE_BUCKET=s3://my-bucket
JWT_SECRET=your-secret-key
WEBHOOK_SECRET=your-webhook-secret

# Email
MAIL_FROM=noreply@yourdomain.com
MAIL_API_KEY=your-sendgrid-key

Deploy

# Docker
docker build -f Dockerfile.jdk21-tests -t valkyrai:latest .
docker run -p 8080:8080 valkyrai:latest

# OR Kubernetes
kubectl apply -f k8s/deployment.yaml

📈 Monitoring

Webhook Health

-- Check webhook execution rates
SELECT
DATE_TRUNC('hour', created_at) as hour,
COUNT(*) as webhook_calls,
SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) as successful,
SUM(CASE WHEN status = 'error' THEN 1 ELSE 0 END) as failed
FROM webhook_logs
WHERE endpoint = '/v1/webhooks/payments/completed'
GROUP BY hour
ORDER BY hour DESC
LIMIT 24;

Fulfillment Status

-- Check fulfillment success rate
SELECT
status,
COUNT(*) as count,
ROUND(100.0 * COUNT(*) / SUM(COUNT(*)) OVER (), 2) as percentage
FROM order_fulfillment_task
WHERE fulfillment_type = 'digital_delivery'
AND created_at > NOW() - INTERVAL '7 days'
GROUP BY status;

Email Delivery

-- Check email notification rates
SELECT
template_id,
COUNT(*) as sent,
SUM(CASE WHEN opened = true THEN 1 ELSE 0 END) as opened,
SUM(CASE WHEN clicked = true THEN 1 ELSE 0 END) as clicked,
ROUND(100.0 * SUM(CASE WHEN opened THEN 1 ELSE 0 END) / COUNT(*), 2) as open_rate
FROM email_delivery_logs
WHERE created_at > NOW() - INTERVAL '7 days'
GROUP BY template_id;

🎓 Examples

Example 1: E-Book Sale

  1. Creator uploads React-Mastery.pdf (5MB)
  2. Fills: Name="React Mastery", Price=$29.99
  3. Clicks "Create & Upload"
  4. System creates:
    • Product (name="React Mastery", price=$29.99)
    • FileRecord (scanned, stored)
    • DigitalAsset (unlimited downloads, never expires)
    • ProductDeliveryConfig (instant_digital, auto-fulfill)
  5. Customer buys → Payment webhook fires
  6. System fulfills:
    • DownloadAccess created
    • Email sent with link
    • Link valid 30 days
  7. Customer clicks link → PDF downloads

Example 2: Video Course Sale

  1. Instructor uploads Typescript-Course.mp4 (500MB)
  2. Fills: Name="TypeScript Course", Price=$99
  3. Clicks "Create & Upload"
  4. System handles:
    • Large video file uploaded and scanned
    • Product created as "download" type
    • DigitalAsset configured for streaming
    • Auto-fulfillment enabled
  5. On first purchase:
    • 🎉 Instant email with download link
    • Customer gets secure token
    • Access tracking enabled
    • Can download up to 5 times in 90 days

Example 3: Bundle Sale

  1. Creator uploads Marketing-Bundle.zip (1.2GB)
  2. Contains: PDF, Videos, Templates, Code
  3. Fills: Name="Marketing Bundle", Price=$149
  4. Clicks "Create & Upload"
  5. System creates single product for entire bundle
  6. On purchase:
    • One email with one link
    • Customer downloads entire ZIP
    • All bundle contents included
    • Usage tracked at bundle level

🛠️ Troubleshooting

Issue: Webhook not being called

Solution:

  1. Verify webhook URL in payment processor settings
  2. Check firewall allows inbound traffic
  3. Review webhook logs: SELECT * FROM webhook_logs ORDER BY created_at DESC
  4. Test with: curl -X POST http://localhost:8080/v1/webhooks/payments/completed -d '...'

Issue: Email not sending

Solution:

  1. Verify email template exists in ContentData
  2. Check MAIL_API_KEY environment variable
  3. Verify sender email configured
  4. Review email logs for errors

Issue: Download token expired

Solution:

  1. Regenerate new token: POST /v1/Product/{productId}/generateDownloadLink
  2. Send customer new download link
  3. Extend expiration in ProductDeliveryConfig

Issue: Large file upload timing out

Solution:

  1. Increase timeout: server.servlet.session.timeout=1800s
  2. Use multipart upload for files > 100MB
  3. Consider chunked upload strategy

🚀 Future Enhancements

  • Advanced: Multi-tier pricing (basic, premium, enterprise)
  • Advanced: Subscription access model
  • Advanced: A/B testing for pricing
  • Advanced: Regional delivery optimization
  • Advanced: Customer usage analytics
  • Advanced: License key generation
  • Advanced: White-label reseller support
  • Advanced: API-based third-party integrations

  • CMS Workflow Integration: .valoride/ADVANCED_CMS_WORKFLOW_INTEGRATION.md
  • System Patterns: .valoride/systemPatterns.md
  • ThorAPI Reference: thorapi/README.md
  • Digital Product Review: .valoride/DIGITAL_PRODUCT_REVIEW.md

🎉 Summary

You now have an AMAZING digital product system that:

One-click uploads - Drag-drop any file
Beautiful UI - No JSON editing
Instant fulfillment - On payment completion
Email notifications - Professional templates
Secure downloads - Token-based access
Complete tracking - Analytics and audit trail
Multi-provider - Stripe, PayPal, etc.
Production-ready - Tested and documented

Get started now and make your customers smile! 💜


Made with ❤️ to transform digital product selling from complex to absolutely AMAZING.

Built on ValkyrAI - The most developer-friendly workflow engine.