chargebee-webhooks

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Chargebee Webhooks

Chargebee Webhooks

When to Use This Skill

何时使用该技能

  • Setting up Chargebee webhook handlers
  • Debugging Basic Auth verification failures
  • Understanding Chargebee event types and payloads
  • Processing subscription billing events
  • 设置Chargebee webhook处理器
  • 调试Basic Auth验证失败问题
  • 了解Chargebee事件类型和负载结构
  • 处理订阅计费事件

Essential Code

核心代码示例

Chargebee uses Basic Authentication for webhook verification. Here's how to implement it:
Chargebee 使用Basic Authentication进行webhook验证,以下是具体实现方式:

Express.js

Express.js

javascript
// Verify Chargebee webhook with Basic Auth
// NOTE: Chargebee uses Basic Auth (not HMAC signatures), so raw body access
// is not required. Use express.json() for automatic JSON parsing:
app.post('/webhooks/chargebee', express.json(), (req, res) => {
  // Extract Basic Auth credentials
  const auth = req.headers.authorization;
  if (!auth || !auth.startsWith('Basic ')) {
    return res.status(401).send('Unauthorized');
  }

  // Decode and verify credentials
  const encoded = auth.substring(6);
  const decoded = Buffer.from(encoded, 'base64').toString('utf-8');
  const [username, password] = decoded.split(':');

  const expectedUsername = process.env.CHARGEBEE_WEBHOOK_USERNAME;
  const expectedPassword = process.env.CHARGEBEE_WEBHOOK_PASSWORD;

  if (username !== expectedUsername || password !== expectedPassword) {
    return res.status(401).send('Invalid credentials');
  }

  // Access the parsed JSON directly
  const event = req.body;
  console.log(`Received ${event.event_type} event:`, event.id);

  // Handle specific event types
  switch (event.event_type) {
    case 'subscription_created':
    case 'subscription_changed':
    case 'subscription_cancelled':
      // Process subscription events
      break;
    case 'payment_succeeded':
    case 'payment_failed':
      // Process payment events
      break;
  }

  res.status(200).send('OK');
});

// Note: If you later need raw body access (e.g., for HMAC signature
// verification with other providers), use express.raw():
// app.post('/webhooks/other', express.raw({ type: 'application/json' }), (req, res) => {
//   const rawBody = req.body.toString();
//   // ... verify signature using rawBody ...
// });
javascript
// Verify Chargebee webhook with Basic Auth
// NOTE: Chargebee uses Basic Auth (not HMAC signatures), so raw body access
// is not required. Use express.json() for automatic JSON parsing:
app.post('/webhooks/chargebee', express.json(), (req, res) => {
  // Extract Basic Auth credentials
  const auth = req.headers.authorization;
  if (!auth || !auth.startsWith('Basic ')) {
    return res.status(401).send('Unauthorized');
  }

  // Decode and verify credentials
  const encoded = auth.substring(6);
  const decoded = Buffer.from(encoded, 'base64').toString('utf-8');
  const [username, password] = decoded.split(':');

  const expectedUsername = process.env.CHARGEBEE_WEBHOOK_USERNAME;
  const expectedPassword = process.env.CHARGEBEE_WEBHOOK_PASSWORD;

  if (username !== expectedUsername || password !== expectedPassword) {
    return res.status(401).send('Invalid credentials');
  }

  // Access the parsed JSON directly
  const event = req.body;
  console.log(`Received ${event.event_type} event:`, event.id);

  // Handle specific event types
  switch (event.event_type) {
    case 'subscription_created':
    case 'subscription_changed':
    case 'subscription_cancelled':
      // Process subscription events
      break;
    case 'payment_succeeded':
    case 'payment_failed':
      // Process payment events
      break;
  }

  res.status(200).send('OK');
});

// Note: If you later need raw body access (e.g., for HMAC signature
// verification with other providers), use express.raw():
// app.post('/webhooks/other', express.raw({ type: 'application/json' }), (req, res) => {
//   const rawBody = req.body.toString();
//   // ... verify signature using rawBody ...
// });

Next.js (App Router)

Next.js (App Router)

typescript
// app/webhooks/chargebee/route.ts
import { NextRequest } from 'next/server';

export async function POST(req: NextRequest) {
  // Extract Basic Auth credentials
  const auth = req.headers.get('authorization');
  if (!auth || !auth.startsWith('Basic ')) {
    return new Response('Unauthorized', { status: 401 });
  }

  // Decode and verify credentials
  const encoded = auth.substring(6);
  const decoded = Buffer.from(encoded, 'base64').toString('utf-8');
  const [username, password] = decoded.split(':');

  const expectedUsername = process.env.CHARGEBEE_WEBHOOK_USERNAME;
  const expectedPassword = process.env.CHARGEBEE_WEBHOOK_PASSWORD;

  if (username !== expectedUsername || password !== expectedPassword) {
    return new Response('Invalid credentials', { status: 401 });
  }

  // Process the webhook
  const event = await req.json();
  console.log(`Received ${event.event_type} event:`, event.id);

  return new Response('OK', { status: 200 });
}
typescript
// app/webhooks/chargebee/route.ts
import { NextRequest } from 'next/server';

export async function POST(req: NextRequest) {
  // Extract Basic Auth credentials
  const auth = req.headers.get('authorization');
  if (!auth || !auth.startsWith('Basic ')) {
    return new Response('Unauthorized', { status: 401 });
  }

  // Decode and verify credentials
  const encoded = auth.substring(6);
  const decoded = Buffer.from(encoded, 'base64').toString('utf-8');
  const [username, password] = decoded.split(':');

  const expectedUsername = process.env.CHARGEBEE_WEBHOOK_USERNAME;
  const expectedPassword = process.env.CHARGEBEE_WEBHOOK_PASSWORD;

  if (username !== expectedUsername || password !== expectedPassword) {
    return new Response('Invalid credentials', { status: 401 });
  }

  // Process the webhook
  const event = await req.json();
  console.log(`Received ${event.event_type} event:`, event.id);

  return new Response('OK', { status: 200 });
}

FastAPI

FastAPI

python
undefined
python
undefined

main.py

main.py

from fastapi import FastAPI, Header, HTTPException, Depends from typing import Optional import base64 import os
app = FastAPI()
def verify_chargebee_auth(authorization: Optional[str] = Header(None)): """Verify Chargebee webhook Basic Auth""" if not authorization or not authorization.startswith("Basic "): raise HTTPException(status_code=401, detail="Unauthorized")
# Decode credentials
encoded = authorization[6:]
decoded = base64.b64decode(encoded).decode('utf-8')

# Split username:password (handle colons in password)
if ':' not in decoded:
    raise HTTPException(status_code=401, detail="Invalid authorization format")

colon_index = decoded.index(':')
username = decoded[:colon_index]
password = decoded[colon_index + 1:]

expected_username = os.getenv("CHARGEBEE_WEBHOOK_USERNAME")
expected_password = os.getenv("CHARGEBEE_WEBHOOK_PASSWORD")

if username != expected_username or password != expected_password:
    raise HTTPException(status_code=401, detail="Invalid credentials")

return True
@app.post("/webhooks/chargebee") async def handle_chargebee_webhook( event: dict, auth_valid: bool = Depends(verify_chargebee_auth) ): """Handle Chargebee webhook events""" event_type = event.get("event_type") print(f"Received {event_type} event: {event.get('id')}")
# Process event based on type
if event_type in ["subscription_created", "subscription_changed", "subscription_cancelled"]:
    # Handle subscription events
    pass
elif event_type in ["payment_succeeded", "payment_failed"]:
    # Handle payment events
    pass

return {"status": "OK"}
undefined
from fastapi import FastAPI, Header, HTTPException, Depends from typing import Optional import base64 import os
app = FastAPI()
def verify_chargebee_auth(authorization: Optional[str] = Header(None)): """Verify Chargebee webhook Basic Auth""" if not authorization or not authorization.startswith("Basic "): raise HTTPException(status_code=401, detail="Unauthorized")
# Decode credentials
encoded = authorization[6:]
decoded = base64.b64decode(encoded).decode('utf-8')

# Split username:password (handle colons in password)
if ':' not in decoded:
    raise HTTPException(status_code=401, detail="Invalid authorization format")

colon_index = decoded.index(':')
username = decoded[:colon_index]
password = decoded[colon_index + 1:]

expected_username = os.getenv("CHARGEBEE_WEBHOOK_USERNAME")
expected_password = os.getenv("CHARGEBEE_WEBHOOK_PASSWORD")

if username != expected_username or password != expected_password:
    raise HTTPException(status_code=401, detail="Invalid credentials")

return True
@app.post("/webhooks/chargebee") async def handle_chargebee_webhook( event: dict, auth_valid: bool = Depends(verify_chargebee_auth) ): """Handle Chargebee webhook events""" event_type = event.get("event_type") print(f"Received {event_type} event: {event.get('id')}")
# Process event based on type
if event_type in ["subscription_created", "subscription_changed", "subscription_cancelled"]:
    # Handle subscription events
    pass
elif event_type in ["payment_succeeded", "payment_failed"]:
    # Handle payment events
    pass

return {"status": "OK"}
undefined

Common Event Types

常见事件类型

⚠️ WARNING: Verify Event Names!
The event type names below are examples and MUST be verified against the Chargebee API documentation for your specific Chargebee configuration. Event names can vary significantly between API versions and configurations.
Special attention required for:
  • Payment events (shown as
    payment_succeeded
    and
    payment_failed
    below)
  • Invoice events (shown as
    invoice_generated
    below)
  • Any custom events specific to your Chargebee setup
Always check your Chargebee Webhook settings for the exact event names your account uses.
EventTriggered WhenCommon Use Cases
subscription_created
New subscription is createdProvision access, send welcome email
subscription_changed
Subscription is modifiedUpdate user permissions, sync changes
subscription_cancelled
Subscription is cancelledRevoke access, trigger retention flow
subscription_reactivated
Cancelled subscription is reactivatedRestore access, send notification
payment_succeeded
Payment is successfully processedUpdate payment status, send receipt
payment_failed
Payment attempt failsRetry payment, notify customer
invoice_generated
Invoice is createdSend invoice to customer
customer_created
New customer is createdCreate user account, sync data
⚠️ 警告:请验证事件名称!
以下事件类型仅为示例,**必须对照你的Chargebee配置查看Chargebee API文档**进行验证。不同API版本和配置下的事件名称可能存在显著差异。
需要特别注意以下类型:
  • 支付事件(如下方的
    payment_succeeded
    payment_failed
  • 发票事件(如下方的
    invoice_generated
  • 你的Chargebee账户中配置的任何自定义事件
请务必在Chargebee Webhook设置中确认你的账户使用的具体事件名称。
事件触发时机常见应用场景
subscription_created
创建新订阅时开通用户权限、发送欢迎邮件
subscription_changed
修改订阅时更新用户权限、同步变更信息
subscription_cancelled
取消订阅时收回用户权限、触发客户留存流程
subscription_reactivated
重新激活已取消的订阅时恢复用户权限、发送通知邮件
payment_succeeded
支付成功时更新支付状态、发送收据
payment_failed
支付尝试失败时重试支付、通知客户
invoice_generated
创建发票时向客户发送发票
customer_created
创建新客户时创建用户账号、同步客户数据

Environment Variables

环境变量

bash
undefined
bash
undefined

Chargebee webhook Basic Auth credentials

Chargebee webhook Basic Auth credentials

CHARGEBEE_WEBHOOK_USERNAME=your_webhook_username CHARGEBEE_WEBHOOK_PASSWORD=your_webhook_password
undefined
CHARGEBEE_WEBHOOK_USERNAME=your_webhook_username CHARGEBEE_WEBHOOK_PASSWORD=your_webhook_password
undefined

Local Development

本地开发测试

For local webhook testing, use Hookdeck CLI:
bash
brew install hookdeck/hookdeck/hookdeck
hookdeck listen 3000 --path /webhooks/chargebee
No account required. Provides local tunnel + web UI for inspecting requests.
本地测试webhook可使用Hookdeck CLI:
bash
brew install hookdeck/hookdeck/hookdeck
hookdeck listen 3000 --path /webhooks/chargebee
无需注册账户,提供本地隧道和Web界面用于检查请求。

Reference Materials

参考资料

  • Overview - What Chargebee webhooks are, common event types
  • Setup - Configure webhooks in Chargebee dashboard
  • Verification - Basic Auth verification details and gotchas
  • 概述 - Chargebee webhook是什么,常见事件类型
  • 设置指南 - 在Chargebee控制台配置webhook
  • 验证细节 - Basic Auth验证的详细说明和注意事项

Examples

示例项目

  • Express Example - Complete Express.js implementation with tests
  • Next.js Example - Next.js App Router implementation with tests
  • FastAPI Example - Python FastAPI implementation with tests
  • Express示例 - 完整的Express.js实现及测试用例
  • Next.js示例 - Next.js App Router实现及测试用例
  • FastAPI示例 - Python FastAPI实现及测试用例

Recommended: webhook-handler-patterns

推荐搭配:webhook-handler-patterns

We recommend installing the webhook-handler-patterns skill alongside this one for handler sequence, idempotency, error handling, and retry logic. Key references (open on GitHub):
我们建议同时安装webhook-handler-patterns技能,以获取处理器流程设计、幂等性处理、错误处理和重试逻辑的最佳实践。关键参考内容(可在GitHub查看):

Related Skills

相关技能