🚀 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
-
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)
-
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)
-
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
-
Customer visits storefront
-
Clicks "Buy" on digital product
-
Completes payment
-
🎉 Instant fulfillment:
- DownloadAccess created
- Secure token issued
- Email sent with download link
- Download available immediately
-
Customer clicks link
-
Downloads file with token validation
-
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
| Type | Examples | Use Case |
|---|---|---|
| Ebooks, guides, whitepapers | Digital products | |
| 🎥 Video | MP4, MOV | Courses, tutorials |
| 📊 Slides | PPT, PPTX | Presentations |
| 📦 ZIP | Archives, bundles | Multiple files |
| 📈 Spreadsheet | XLS, XLSX | Data templates |
| 📄 Documents | DOCX, DOC | Guides, templates |
| 🖼️ Images | JPG, PNG | Collections, art |
| 📝 Text | TXT, CSV | Content, 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
}
Generate Download Link
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
- Creator uploads
React-Mastery.pdf(5MB) - Fills: Name="React Mastery", Price=$29.99
- Clicks "Create & Upload"
- System creates:
- Product (name="React Mastery", price=$29.99)
- FileRecord (scanned, stored)
- DigitalAsset (unlimited downloads, never expires)
- ProductDeliveryConfig (instant_digital, auto-fulfill)
- Customer buys → Payment webhook fires
- System fulfills:
- DownloadAccess created
- Email sent with link
- Link valid 30 days
- Customer clicks link → PDF downloads
Example 2: Video Course Sale
- Instructor uploads
Typescript-Course.mp4(500MB) - Fills: Name="TypeScript Course", Price=$99
- Clicks "Create & Upload"
- System handles:
- Large video file uploaded and scanned
- Product created as "download" type
- DigitalAsset configured for streaming
- Auto-fulfillment enabled
- 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
- Creator uploads
Marketing-Bundle.zip(1.2GB) - Contains: PDF, Videos, Templates, Code
- Fills: Name="Marketing Bundle", Price=$149
- Clicks "Create & Upload"
- System creates single product for entire bundle
- 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:
- Verify webhook URL in payment processor settings
- Check firewall allows inbound traffic
- Review webhook logs:
SELECT * FROM webhook_logs ORDER BY created_at DESC - Test with:
curl -X POST http://localhost:8080/v1/webhooks/payments/completed -d '...'
Issue: Email not sending
Solution:
- Verify email template exists in ContentData
- Check MAIL_API_KEY environment variable
- Verify sender email configured
- Review email logs for errors
Issue: Download token expired
Solution:
- Regenerate new token:
POST /v1/Product/{productId}/generateDownloadLink - Send customer new download link
- Extend expiration in ProductDeliveryConfig
Issue: Large file upload timing out
Solution:
- Increase timeout:
server.servlet.session.timeout=1800s - Use multipart upload for files > 100MB
- 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
📚 Related Documentation
- 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.