Copy and paste this code into your new Base44 subscription database app
1. Create a new Base44 app for the subscription database
2. Create the Subscription entity (copy schema below)
3. Create all 6 backend functions (copy code below)
4. Set SUBSCRIPTION_API_KEY secret in the external app
5. Publish the external app and get the function URLs
6. Configure SUBSCRIPTION_API_URL and SUBSCRIPTION_API_KEY in this main app
{
"name": "Subscription",
"type": "object",
"properties": {
"user_email": {
"type": "string",
"description": "Email of the subscriber"
},
"password": {
"type": "string",
"description": "Hashed password for authentication"
},
"subscription_type": {
"type": "string",
"description": "Type of subscription"
},
"start_date": {
"type": "string",
"format": "date-time",
"description": "When the subscription starts"
},
"end_date": {
"type": "string",
"format": "date-time",
"description": "When the subscription expires"
},
"payment_status": {
"type": "string",
"enum": ["pending", "completed", "failed"],
"default": "pending",
"description": "Status of the payment"
},
"amount_paid": {
"type": "number",
"description": "Amount paid for this subscription"
},
"is_active": {
"type": "boolean",
"default": true,
"description": "Whether this subscription is currently active"
},
"stripe_subscription_id": {
"type": "string",
"description": "Stripe subscription ID (if applicable)"
},
"reset_token": {
"type": "string",
"description": "Password reset token"
},
"reset_token_expires": {
"type": "string",
"format": "date-time",
"description": "When the reset token expires"
}
},
"required": ["user_email", "password", "subscription_type", "start_date", "end_date"]
}Create this in entities/Subscription.json
import { createClientFromRequest } from 'npm:@base44/sdk@0.8.6';
Deno.serve(async (req) => {
try {
const base44 = createClientFromRequest(req);
const payload = await req.json();
const apiKey = req.headers.get('x-api-key');
const expectedKey = Deno.env.get('SUBSCRIPTION_API_KEY');
if (!apiKey || apiKey !== expectedKey) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
const { email, password } = payload;
// Find subscription by email
const subscriptions = await base44.asServiceRole.entities.Subscription.filter({
user_email: email,
is_active: true
});
if (subscriptions.length === 0) {
return Response.json({ valid: false, message: 'No subscription found' });
}
const activeSub = subscriptions.find(sub => new Date(sub.end_date) > new Date());
if (!activeSub) {
return Response.json({ valid: false, message: 'Subscription expired' });
}
// Verify password using Web Crypto API
const encoder = new TextEncoder();
const data = encoder.encode(password);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
if (activeSub.password !== hashHex) {
return Response.json({ valid: false, message: 'Invalid password' });
}
return Response.json({
valid: true,
subscription: activeSub
});
} catch (error) {
console.error('Validation error:', error);
return Response.json({ error: error.message }, { status: 500 });
}
});Create this in functions/validateSubscription.js
import { createClientFromRequest } from 'npm:@base44/sdk@0.8.6';
Deno.serve(async (req) => {
try {
const base44 = createClientFromRequest(req);
const apiKey = req.headers.get('x-api-key');
const expectedKey = Deno.env.get('SUBSCRIPTION_API_KEY');
if (!apiKey || apiKey !== expectedKey) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
const subscriptions = await base44.asServiceRole.entities.Subscription.list('-created_date');
return Response.json({ subscriptions });
} catch (error) {
console.error('List error:', error);
return Response.json({ error: error.message }, { status: 500 });
}
});Create this in functions/listSubscriptions.js
import { createClientFromRequest } from 'npm:@base44/sdk@0.8.6';
Deno.serve(async (req) => {
try {
const base44 = createClientFromRequest(req);
const apiKey = req.headers.get('x-api-key');
const expectedKey = Deno.env.get('SUBSCRIPTION_API_KEY');
if (!apiKey || apiKey !== expectedKey) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
const payload = await req.json();
const { user_email, password, subscription_type, start_date, end_date, payment_status, amount_paid, is_active } = payload;
// Hash password using Web Crypto API
const encoder = new TextEncoder();
const data = encoder.encode(password);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashedPassword = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
const subscription = await base44.asServiceRole.entities.Subscription.create({
user_email,
password: hashedPassword,
subscription_type,
start_date,
end_date,
payment_status: payment_status || 'completed',
amount_paid,
is_active: is_active !== undefined ? is_active : true
});
return Response.json({ subscription });
} catch (error) {
console.error('Create error:', error);
return Response.json({ error: 'Failed to create subscription' }, { status: 500 });
}
});Create this in functions/createSubscription.js
import { createClientFromRequest } from 'npm:@base44/sdk@0.8.6';
Deno.serve(async (req) => {
try {
const base44 = createClientFromRequest(req);
const apiKey = req.headers.get('x-api-key');
const expectedKey = Deno.env.get('SUBSCRIPTION_API_KEY');
if (!apiKey || apiKey !== expectedKey) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
const payload = await req.json();
const { id, updates } = payload;
const subscription = await base44.asServiceRole.entities.Subscription.update(id, updates);
return Response.json({ subscription });
} catch (error) {
console.error('Update error:', error);
return Response.json({ error: error.message }, { status: 500 });
}
});Create this in functions/updateSubscription.js
import { createClientFromRequest } from 'npm:@base44/sdk@0.8.6';
Deno.serve(async (req) => {
try {
const base44 = createClientFromRequest(req);
const apiKey = req.headers.get('x-api-key');
const expectedKey = Deno.env.get('SUBSCRIPTION_API_KEY');
if (!apiKey || apiKey !== expectedKey) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
const { user_email } = await req.json();
// Find subscription
const subscriptions = await base44.asServiceRole.entities.Subscription.filter({
user_email,
is_active: true
});
if (subscriptions.length === 0) {
// Return success to prevent email enumeration
return Response.json({ success: true, message: 'If account exists, reset email sent' });
}
const subscription = subscriptions[0];
// Generate reset token (valid for 1 hour)
const resetToken = crypto.randomUUID() + '-' + Date.now();
const expiresAt = new Date(Date.now() + 60 * 60 * 1000).toISOString();
await base44.asServiceRole.entities.Subscription.update(subscription.id, {
reset_token: resetToken,
reset_token_expires: expiresAt
});
// TODO: Send email with reset link (integrate with email service)
// Reset URL should be: https://your-main-app.com/password-reset-confirm?token={resetToken}
return Response.json({
success: true,
message: 'Reset email sent'
});
} catch (error) {
console.error('Reset password error:', error);
return Response.json({ error: 'Failed to process request' }, { status: 500 });
}
});Create this in functions/resetPassword.js
import { createClientFromRequest } from 'npm:@base44/sdk@0.8.6';
Deno.serve(async (req) => {
try {
const base44 = createClientFromRequest(req);
const apiKey = req.headers.get('x-api-key');
const expectedKey = Deno.env.get('SUBSCRIPTION_API_KEY');
if (!apiKey || apiKey !== expectedKey) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
const { reset_token, new_password } = await req.json();
if (!reset_token || !new_password || new_password.length < 8) {
return Response.json({ error: 'Invalid request' }, { status: 400 });
}
// Find subscription with this reset token
const subscriptions = await base44.asServiceRole.entities.Subscription.filter({
reset_token,
is_active: true
});
if (subscriptions.length === 0) {
return Response.json({ error: 'Invalid or expired reset token' }, { status: 400 });
}
const subscription = subscriptions[0];
// Check if token expired
if (new Date(subscription.reset_token_expires) < new Date()) {
return Response.json({ error: 'Reset token expired' }, { status: 400 });
}
// Hash new password
const encoder = new TextEncoder();
const data = encoder.encode(new_password);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashedPassword = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
// Update password and clear reset token
await base44.asServiceRole.entities.Subscription.update(subscription.id, {
password: hashedPassword,
reset_token: null,
reset_token_expires: null
});
return Response.json({
success: true,
message: 'Password reset successfully'
});
} catch (error) {
console.error('Confirm reset error:', error);
return Response.json({ error: 'Failed to reset password' }, { status: 500 });
}
});Create this in functions/confirmPasswordReset.js
External App:
Main App (this app):