Service-Side SDK Integration
Complete guide to integrating monetize.software with your application using server-side SDK and webhooks. Perfect for adding monetization to established platforms without disrupting current user experience.
Complexity: Advanced
Perfect for: Existing applications, CRM systems, custom platforms
What We’ll Build
In this guide, you’ll implement:
- ✅ Server-side paywall integration with custom UI
- ✅ Webhook handling for real-time payment updates
- ✅ User synchronization between systems
- ✅ Custom checkout flow integration
- ✅ Subscription management in your existing interface
Setting up Server-Side Integration
Setting up Payment Infrastructure
Step 1: Create paywall
Create Server-Mode Paywall
Step 2: Create payment processor
Step 3: Connect payment processor to paywall
Step 4: Get API Credentials
- Go to “Settings” → “API Keys”
- Create or copy your API key
- Note your paywall ID from paywall settings
- Store securely in your environment variables:
# Environment variables
MONETIZE_API_KEY=your_api_key_here
MONETIZE_PAYWALL_ID=your_paywall_id_here
MONETIZE_API_URL=https://onlineapp.pro/api/v1
Step 5: Install Server SDK
Choose your preferred server technology:
Node.js/Express
Node.js/Express Setup:
// npm install axios
const axios = require('axios');
class MonetizeClient {
constructor(apiKey, paywallId) {
this.apiKey = apiKey;
this.paywallId = paywallId;
this.baseURL = 'https://onlineapp.pro/api/v1';
}
async makeRequest(endpoint, options = {}) {
const config = {
baseURL: this.baseURL,
headers: {
'x-api-key': this.apiKey,
'Content-Type': 'application/json',
...options.headers
},
...options
};
return axios(endpoint, config);
}
}
const monetize = new MonetizeClient(
process.env.MONETIZE_API_KEY,
process.env.MONETIZE_PAYWALL_ID
);
Building Custom Payment Flow
Step 4: Get Available Plans
Create endpoint to fetch pricing information:
// Node.js example - routes/pricing.js
app.get('/api/pricing', async (req, res) => {
try {
const response = await monetize.makeRequest(
`/paywall/${process.env.MONETIZE_PAYWALL_ID}/prices`
);
// Transform data for your frontend
const plans = response.data.map(plan => ({
id: plan.id,
name: plan.interval,
price: plan.unit_amount,
currency: plan.currency,
interval: plan.interval,
features: plan.metadata?.features || []
}));
res.json({ plans });
} catch (error) {
res.status(500).json({ error: 'Failed to fetch pricing' });
}
});
Step 5: Create Custom Checkout
Implement checkout creation with your custom UI:
// Create checkout session
app.post('/api/create-checkout', async (req, res) => {
const { email, priceId, planName, userMetadata } = req.body;
// Validate user in your system
const user = await User.findByEmail(email);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
try {
const checkoutData = {
email: email,
priceId: priceId,
successUrl: `${req.protocol}://${req.get('host')}/subscription/success`,
errorUrl: `${req.protocol}://${req.get('host')}/subscription/error`,
shopUrl: `${req.protocol}://${req.get('host')}/pricing`,
userMeta: {
userId: user.id,
planName: planName,
source: 'custom_checkout',
...userMetadata
}
};
const response = await monetize.makeRequest(
`/paywall/${process.env.MONETIZE_PAYWALL_ID}/start-checkout`,
{
method: 'POST',
data: checkoutData
}
);
res.json({
checkoutUrl: response.data.checkoutUrl,
});
} catch (error) {
console.error('Checkout creation failed:', error);
res.status(500).json({ error: 'Failed to create checkout' });
}
});
Step 6: Build Custom Pricing Page
Create your own pricing interface:
<!-- pricing.html -->
<div class="pricing-container">
<h1>Choose Your Plan</h1>
<div class="pricing-grid" id="pricing-grid">
<!-- Plans will be loaded dynamically -->
</div>
<!-- Custom checkout modal -->
<div id="checkout-modal" class="modal" style="display: none;">
<div class="modal-content">
<h3>Complete Your Purchase</h3>
<form id="checkout-form">
<input type="email" id="user-email" placeholder="Email" required>
<input type="text" id="company-name" placeholder="Company (optional)">
<button type="submit">Subscribe Now</button>
</form>
</div>
</div>
</div>
<script>
// Load pricing plans
async function loadPricing() {
const response = await fetch('/api/pricing');
const { plans } = await response.json();
const grid = document.getElementById('pricing-grid');
grid.innerHTML = plans.map(plan => `
<div class="pricing-card ${plan.recommended ? 'recommended' : ''}">
<h3>${plan.name}</h3>
<div class="price">$${plan.price}</div>
<div class="interval">per ${plan.interval}</div>
<button onclick="selectPlan('${plan.id}', '${plan.name}')">
Choose Plan
</button>
</div>
`).join('');
}
// Handle plan selection
function selectPlan(priceId, planName) {
selectedPlan = { priceId, planName };
document.getElementById('checkout-modal').style.display = 'block';
}
// Handle checkout form submission
document.getElementById('checkout-form').addEventListener('submit', async (e) => {
e.preventDefault();
const email = document.getElementById('user-email').value;
const company = document.getElementById('company-name').value;
try {
const response = await fetch('/api/create-checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: email,
priceId: selectedPlan.priceId,
planName: selectedPlan.planName,
userMetadata: { company: company }
})
});
const { checkoutUrl } = await response.json();
// Redirect to payment processor
window.location.href = checkoutUrl;
} catch (error) {
alert('Checkout failed. Please try again.');
}
});
// Load pricing on page load
loadPricing();
</script>
Webhook Integration
Step 7: Set up Webhook Endpoint
Create webhook handler to receive payment notifications:
// routes/webhooks.js
const express = require('express');
const crypto = require('crypto');
app.post('/webhook/monetize', express.raw({type: 'application/json'}), (req, res) => {
const signature = req.headers['x-signature'];
const body = req.body;
// Verify webhook signature (recommended for security)
if (!verifyWebhookSignature(body, signature)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(body);
console.log('Webhook received:', event.type);
// Handle different event types
switch (event.type) {
case 'payment.completed':
handlePaymentCompleted(event.data);
break;
case 'subscription.created':
handleSubscriptionCreated(event.data);
break;
case 'subscription.updated':
handleSubscriptionUpdated(event.data);
break;
case 'subscription.cancelled':
handleSubscriptionCancelled(event.data);
break;
case 'refund.created':
handleRefundCreated(event.data);
break;
default:
console.log('Unhandled event type:', event.type);
}
res.json({ received: true });
});
// Verify webhook signature
function verifyWebhookSignature(body, signature) {
const expectedSignature = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(body)
.digest('hex');
return signature === expectedSignature;
}
Step 8: Handle Payment Events
Implement handlers for different payment events:
// Handle completed payment
async function handlePaymentCompleted(data) {
const { payment, user, customer, price } = data;
try {
// Find user in your system
let localUser = await User.findByEmail(customer.email);
if (!localUser && user.id) {
localUser = await User.findById(user.id);
}
if (!localUser) {
// Create new user if doesn't exist
localUser = await User.create({
email: customer.email,
source: 'monetize_payment',
metadata: user.metadata
});
}
// Update payment/subscription status
if (price.interval === 'lifetime') {
// Handle lifetime access
await localUser.update({
lifetimeAccess: true,
purchaseDate: new Date(),
lastPaymentAmount: price.unit_amount
});
} else {
// Handle subscription or one-time payment
await localUser.update({
subscriptionStatus: 'active',
subscriptionPlan: price.interval,
subscriptionStartDate: new Date(),
lastPaymentDate: new Date(),
lastPaymentAmount: price.unit_amount
});
}
// Grant access to premium features
await grantPremiumAccess(localUser.id);
console.log(`Payment completed for user ${customer.email}`);
} catch (error) {
console.error('Error handling completed payment:', error);
}
}
// Handle subscription cancellation
async function handleSubscriptionCancelled(data) {
const { subscription, user, customer } = data;
try {
const localUser = await User.findByEmail(customer.email);
if (!localUser) return;
// Update subscription status
await localUser.update({
subscriptionStatus: 'cancelled',
cancellationDate: new Date(subscription.cancelled_at),
cancelAtPeriodEnd: subscription.cancel_at_period_end
});
// Revoke premium access (or set grace period if cancel_at_period_end is true)
if (!subscription.cancel_at_period_end) {
await revokePremiumAccess(localUser.id);
}
// Send cancellation email
await sendCancellationEmail(customer.email);
console.log(`Subscription cancelled for user ${customer.email}`);
} catch (error) {
console.error('Error handling cancellation:', error);
}
}
// Handle refund created
async function handleRefundCreated(data) {
const { refund, user, customer } = data;
try {
const localUser = await User.findByEmail(customer.email);
if (!localUser) return;
// Update refund status
await localUser.update({
refundStatus: 'processed',
refundDate: new Date(),
refundAmount: refund.amount,
refundReason: refund.reason
});
// Revoke access if needed
await revokePremiumAccess(localUser.id);
console.log(`Refund processed for user ${customer.email}`);
} catch (error) {
console.error('Error handling refund:', error);
}
}
Customer Portal Integration
Step 10: Customer Portal Access
Provide users with subscription management:
// Generate customer portal URL
app.post('/api/customer-portal', async (req, res) => {
const { userId } = req.body;
try {
const user = await User.findById(userId);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
const response = await monetize.makeRequest(
`/paywall/${process.env.MONETIZE_PAYWALL_ID}/customer-portal`,
{
method: 'POST',
data: {
email: user.email,
returnUrl: `${req.protocol}://${req.get('host')}/account/subscription`
}
}
);
res.json({ portalUrl: response.data.portalUrl });
} catch (error) {
res.status(500).json({ error: 'Failed to generate portal URL' });
}
});
Step 11: Subscription Management UI
Create subscription management interface in your app:
<!-- account/subscription.html -->
<div class="subscription-management">
<h2>Subscription Management</h2>
<div class="subscription-info" id="subscription-info">
<!-- Loaded dynamically -->
</div>
<div class="subscription-actions">
<button id="manage-subscription" class="btn btn-primary">
Manage Subscription
</button>
<button id="download-invoices" class="btn btn-secondary">
Download Invoices
</button>
</div>
</div>
<script>
// Load subscription info
async function loadSubscriptionInfo() {
const response = await fetch(`/api/user/${currentUserId}/subscription`);
const subscription = await response.json();
document.getElementById('subscription-info').innerHTML = `
<div class="subscription-card">
<h3>Current Plan: ${subscription.plan || 'Free'}</h3>
<p>Status: <span class="status ${subscription.isActive ? 'active' : 'inactive'}">
${subscription.isActive ? 'Active' : 'Inactive'}
</span></p>
${subscription.lastPayment ? `<p>Last payment: ${new Date(subscription.lastPayment).toLocaleDateString()}</p>` : ''}
<div class="features">
<h4>Available Features:</h4>
<ul>
${subscription.premiumFeatures.map(feature => `<li>${feature}</li>`).join('')}
</ul>
</div>
</div>
`;
}
// Handle subscription management
document.getElementById('manage-subscription').addEventListener('click', async () => {
const response = await fetch('/api/customer-portal', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId: currentUserId })
});
const { portalUrl } = await response.json();
window.open(portalUrl, '_blank');
});
loadSubscriptionInfo();
</script>
Congratulations! You’ve successfully integrated monetize.software with your existing service. Your users can now enjoy premium features while maintaining a seamless experience within your existing platform.