Monetize.software can send you notifications any time an event happens in your app and monitor state changes for your subscribers.
With webhooks integrated, you can:
- Keep your backend synchronized with real-time subscription and purchase data
- Set up automated workflows that respond to subscription events
- Send timely communications to subscribers about billing issues or engage with them during unsubscription
Supported events
Event | Description |
payment.completed | Successfully completed payment (one-time or first subscription payment) |
subscription.created | New subscription creation |
subscription.updated | Subscription update (plan change, status change, etc.) |
subscription.cancelled | Subscription cancellation |
refund.created | Refund creation |
Webhook Structure
HTTP Headers
Every webhook request includes these headers:
Content-Type: application/json User-Agent: Monetize-Webhooks/1.0 X-Monetize-Signature: sha256=<signature> (if secret key is configured)
Payload Structure
Each webhook notification has this JSON structure:
{ "id": "evt_1234567890_abcdef123", // Unique event identifier "type": "payment.completed", // Event type "created_at": "2024-01-15T12:30:45.000Z", // Event creation time in ISO 8601 format "data": { "price": { "id": "cus_customer123", // Internal price id "unit_amount": 2000, // Amount in cents "interval": "year" // Plan - week, month, year, lifetime }, "customer": { "id": "cus_customer123", "email": "user@example.com" }, "paywall": { "id": "123" }, "acquiring": { "id": "acq_456", "provider": "stripe" }, } }
Payment-Specific Fields
Additional fields for payment events:
"payment": { "id": "pi_payment123", "status": "succeeded", }
Subscription-Specific Fields
Additional fields for subscription events:
"subscription": { "id": "sub_subscription123", "status": "active", "current_period_start": "2024-01-15T12:30:45.000Z", "current_period_end": "2024-02-15T12:30:45.000Z" }
Refund-Specific Fields
Additional fields for refund events:
"refund": { "id": "re_refund123", "reason": "requested_by_customer", "amount": 2999 }
Authentication Verification
Webhook Signature
If you have configured a secret key, each webhook will contain an
X-Monetize-Signature
header with an HMAC SHA256 signature of the request body.Signature Verification Examples
Python
import hmac import hashlib def verify_webhook_signature(payload, signature, secret): expected_signature = hmac.new( secret.encode('utf-8'), payload.encode('utf-8'), hashlib.sha256 ).hexdigest() # Remove "sha256=" prefix from header signature = signature.replace('sha256=', '') return hmac.compare_digest(expected_signature, signature) # Usage is_valid = verify_webhook_signature( request.body, request.headers.get('X-Monetize-Signature'), 'your_webhook_secret' )
Node.js
const crypto = require('crypto'); function verifyWebhookSignature(payload, signature, secret) { const expectedSignature = crypto .createHmac('sha256', secret) .update(payload) .digest('hex'); // Remove "sha256=" prefix from header signature = signature.replace('sha256=', ''); return crypto.timingSafeEqual( Buffer.from(expectedSignature), Buffer.from(signature) ); } // Usage const isValid = verifyWebhookSignature( req.body, req.headers['x-monetize-signature'], 'your_webhook_secret' );
PHP
function verifyWebhookSignature($payload, $signature, $secret) { $expectedSignature = hash_hmac('sha256', $payload, $secret); // Remove "sha256=" prefix from header $signature = str_replace('sha256=', '', $signature); return hash_equals($expectedSignature, $signature); } // Usage $isValid = verifyWebhookSignature( file_get_contents('php://input'), $_SERVER['HTTP_X_MONETIZE_SIGNATURE'], 'your_webhook_secret' );
Webhook Processing
Endpoint Requirements
- HTTPS: Webhooks are only sent to HTTPS URLs
- Response Status: Your endpoint should return HTTP status 200-299 for successful processing
- Timeout: Response timeout is 10 seconds
- Idempotency: Be prepared to receive duplicate events
Processing Example
const express = require('express'); const app = express(); app.use(express.json()); app.post('/webhooks/monetize', async (req, res) => { try { // Verify signature const signature = req.headers['x-monetize-signature']; if (signature && !verifyWebhookSignature( JSON.stringify(req.body), signature, 'your_secret' )) { return res.status(401).json({ error: 'Invalid signature' }); } // Parse data const event = req.body; // Process event switch (event.type) { case 'payment.completed': await handlePaymentCompleted(event.data); break; case 'subscription.created': await handleSubscriptionCreated(event.data); break; // ... other events } res.status(200).json({ status: 'success' }); } catch (err) { console.error(`Webhook error: ${err}`); res.status(500).json({ error: 'Internal error' }); } }); async function handlePaymentCompleted(data) { // Your payment processing logic console.log(`Payment completed: ${data.id}`); // e.g., activate user access } async function handleSubscriptionCreated(data) { // Your subscription creation logic console.log(`Subscription created: ${data.subscription.id}`); // e.g., grant premium features access }
Retry Mechanism
The system automatically retries webhook delivery on failure:
- Number of attempts: 5 attempts
- Intervals: 5, 10, 20, 40, and 80 minutes
- Retry conditions: HTTP status 5xx or connection timeout
Logging
All webhook delivery attempts are logged, including:
- Send time
- HTTP response status
- Response body (first 1000 characters)
- Error messages
- Attempt count
Logs are available in the control panel under Settings → Webhooks → select specific webhook.
FAQ
What to do if webhooks aren't being received?
- Check that the URL is accessible and returns status 200
- Ensure HTTPS is being used
- Check webhook logs in the control panel
- Verify the webhook is active and subscribed to needed events
Can I set up multiple webhooks?
Yes, you can create multiple webhooks with different URLs and events.
How to update existing webhook settings?
In Settings → Webhooks section, click the edit icon next to the desired webhook.
What to do if webhooks arrive duplicated?
This is normal behavior during retry attempts. Implement idempotent processing using the event
id
field for deduplication.Security
- Always verify the signature when using a secret key
- Use HTTPS for all webhook URLs
- Don't log full webhook data in plain text
- Restrict access to webhook endpoints
- Validate data before processing
Limitations
- Maximum payload size: 1 MB
- Timeout: 10 seconds
- Maximum webhooks per account: 10
- Log retention: 30 days
Support
If you encounter webhook issues, contact technical support providing:
- Webhook ID
- Event time
- Expected behavior
- Actual behavior
- Your side logs (if available)