npx skills add https://github.com/recur-tw/skills --skill recur-webhooksSKILL.md
Recur Webhook Integration
You are helping implement Recur webhooks to receive real-time payment and subscription events.
Webhook Events
Core Events (Most Common)
| Event | When Fired |
|---|---|
checkout.completed | Payment successful, subscription/order created |
subscription.activated | Subscription is now active |
subscription.cancelled | Subscription was cancelled |
subscription.renewed | Recurring payment successful |
subscription.past_due | Payment failed, subscription at risk |
order.paid | One-time purchase completed |
refund.created | Refund initiated |
All Supported Events
type WebhookEventType =
// Checkout
| 'checkout.created'
| 'checkout.completed'
// Orders
| 'order.paid'
| 'order.payment_failed'
// Subscription Lifecycle
| 'subscription.created'
| 'subscription.activated'
| 'subscription.cancelled'
| 'subscription.expired'
| 'subscription.trial_ending'
// Subscription Changes
| 'subscription.upgraded'
| 'subscription.downgraded'
| 'subscription.renewed'
| 'subscription.past_due'
// Scheduled Changes
| 'subscription.schedule_created'
| 'subscription.schedule_executed'
| 'subscription.schedule_cancelled'
// Invoices
| 'invoice.created'
| 'invoice.paid'
| 'invoice.payment_failed'
// Customer
| 'customer.created'
| 'customer.updated'
// Product
| 'product.created'
| 'product.updated'
// Refunds
| 'refund.created'
| 'refund.succeeded'
| 'refund.failed'
Webhook Handler Implementation
Next.js App Router
// app/api/webhooks/recur/route.ts
import { NextRequest, NextResponse } from 'next/server'
import crypto from 'crypto'
const WEBHOOK_SECRET = process.env.RECUR_WEBHOOK_SECRET!
function verifySignature(payload: string, signature: string): boolean {
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payload)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
)
}
export async function POST(request: NextRequest) {
const payload = await request.text()
const signature = request.headers.get('x-recur-signature')
// Verify signature
if (!signature || !verifySignature(payload, signature)) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 })
}
const event = JSON.parse(payload)
// Handle events
switch (event.type) {
case 'checkout.completed':
await handleCheckoutCompleted(event.data)
break
case 'subscription.activated':
await handleSubscriptionActivated(event.data)
break
case 'subscription.cancelled':
await handleSubscriptionCancelled(event.data)
break
case 'subscription.renewed':
await handleSubscriptionRenewed(event.data)
break
case 'subscription.past_due':
await handleSubscriptionPastDue(event.data)
break
case 'refund.created':
await handleRefundCreated(event.data)
break
default:
console.log(`Unhandled event type: ${event.type}`)
}
return NextResponse.json({ received: true })
}
// Event handlers
async function handleCheckoutCompleted(data: any) {
const { customerId, subscriptionId, orderId, productId, amount } = data
// Update your database
// Grant access to the user
// Send confirmation email
}
async function handleSubscriptionActivated(data: any) {
const { subscriptionId, customerId, productId, status } = data
// Update user's subscription status in your database
// Enable premium features
}
async function handleSubscriptionCancelled(data: any) {
const { subscriptionId, customerId, cancelledAt, accessUntil } = data
// Mark subscription as cancelled
// User still has access until accessUntil date
// Send cancellation confirmation email
}
async function handleSubscriptionRenewed(data: any) {
const { subscriptionId, customerId, amount, nextBillingDate } = data
// Update billing records
// Extend access period
}
async function handleSubscriptionPastDue(data: any) {
const { subscriptionId, customerId, failureReason } = data
// Notify user of payment failure
// Consider sending dunning emails
// May want to restrict access after grace period
}
async function handleRefundCreated(data: any) {
const { refundId, orderId, amount, reason } = data
// Update order status
// Adjust user credits/access
// Send refund notification
}
Express.js
import express from 'express'
import crypto from 'crypto'
const app = express()
// Important: Use raw body for signature verification
app.post(
'/api/webhooks/recur',
express.raw({ type: 'application/json' }),
(req, res) => {
const payload = req.body.toString()
const signature = req.headers['x-recur-signature'] as string
// Verify signature
const expected = crypto
.createHmac('sha256', process.env.RECUR_WEBHOOK_SECRET!)
.update(payload)
.digest('h
...
Repository
recur-tw/skillsParent repository
Repository Stats
Stars0
Forks0