Token导航 LogoToken导航TokenDH.com
前端设计需要联网github未标认证来源可访问clear审计提醒

subscription-integration订阅整合

Agent Skill

用于辅助前端页面、组件、样式和交互逻辑的开发与维护。它适合让 Agent 生成或审查 React、Next.js、Vue、Tailwind、CSS 等相关代码,整理组件结构,或定位布局和性能问题。使用时需要结合项目现有设计系统、路由和构建方式,避免只生成孤立片段;涉及页面改动时,应配合本地预览和构建检查确认视觉效果。

总安装

8,726

周安装

360

GitHub Stars

8

下载量

2,851
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:subscription-integration(订阅整合)
来源仓库:https://github.com/dodopayments/skills
仓库路径:skills/subscription-integration
安装命令:
npx skills add https://github.com/dodopayments/skills --skill subscription-integration
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/dodopayments/skills --skill subscription-integration

简介

用于辅助前端页面、组件、样式和交互逻辑的开发与维护,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。

  • 可结合来源仓库、安装命令和原始 README 继续核验具体用法。
  • 安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。
  • 当前顶部介绍:subscription-integration 用于辅助前端页面、组件、样式和交互逻辑的开发与维护。
  • 当前底部简介为空,暂无补充说明。

SKILL.md

Dodo Payments Subscription Integration

Reference: docs.dodopayments.com/developer-resources/subscription-integration-guide

Implement recurring billing with trials, plan changes, and usage-based pricing.


Quick Start

1. Create Subscription Product

In the dashboard (Products → Create Product):

  • Select "Subscription" type
  • Set billing interval (monthly, yearly, etc.)
  • Configure pricing

2. Create Checkout Session

import DodoPayments from 'dodopayments';

const client = new DodoPayments({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
});

const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'prod_monthly_plan', quantity: 1 }
  ],
  subscription_data: {
    trial_period_days: 14, // Optional trial
  },
  customer: {
    email: 'subscriber@example.com',
    name: 'Jane Doe',
  },
  return_url: 'https://yoursite.com/success',
});

// Redirect to session.checkout_url

3. Handle Webhook Events

// subscription.active - Grant access
// subscription.cancelled - Schedule access revocation
// subscription.renewed - Log renewal
// payment.succeeded - Track payments

Subscription Lifecycle

┌─────────────┐     ┌─────────┐     ┌────────┐
│   Created   │ ──▶ │  Trial  │ ──▶ │ Active │
└─────────────┘     └─────────┘     └────────┘
                                         │
                    ┌────────────────────┼────────────────────┐
                    ▼                    ▼                    ▼
              ┌──────────┐        ┌───────────┐        ┌───────────┐
              │ On Hold  │        │ Cancelled │        │  Renewed  │
              └──────────┘        └───────────┘        └───────────┘
                    │                    │
                    ▼                    ▼
              ┌──────────┐        ┌───────────┐
              │  Failed  │        │  Expired  │
              └──────────┘        └───────────┘

Webhook Events

EventWhenAction
subscription.activeSubscription startsGrant access
subscription.updatedAny field changesSync state
subscription.on_holdPayment failsNotify user, retry
subscription.renewedSuccessful renewalLog, send receipt
subscription.plan_changedUpgrade/downgradeUpdate entitlements
subscription.cancelledUser cancelsSchedule end of access
subscription.failedMandate creation failsNotify, retry options
subscription.expiredTerm endsRevoke access

Implementation Examples

Full Subscription Handler

// app/api/webhooks/subscription/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';

export async function POST(req: NextRequest) {
  const event = await req.json();
  const data = event.data;

  switch (event.type) {
    case 'subscription.active':
      await handleSubscriptionActive(data);
      break;
    case 'subscription.cancelled':
      await handleSubscriptionCancelled(data);
      break;
    case 'subscription.on_hold':
      await handleSubscriptionOnHold(data);
      break;
    case 'subscription.renewed':
      await handleSubscriptionRenewed(data);
      break;
    case 'subscription.plan_changed':
      await handlePlanChanged(data);
      break;
    case 'subscription.expired':
      await handleSubscriptionExpired(data);
      break;
  }

  return NextResponse.json({ received: true });
}

async function handleSubscriptionActive(data: any) {
  const {
    subscription_id,
    customer,
    product_id,
    next_billing_date,
    recurring_pre_tax_amount,
    payment_frequency_interval,
  } = data;

  // Create or update user subscription
  await prisma.subscription.upsert({
    where: { externalId: subscription_id },
    create: {
      externalId: subscription_id,
      userId: customer.customer_id,
      email: customer.email,
      productId: product_id,
      status: 'active',
      currentPeriodEnd: new Date(next_billing_date),
      amount: recurring_pre_tax_amount,
      interval: payment_frequency_interval,
    },
    update: {
      status: 'active',
      currentPeriodEnd: new Date(next_billing_date),
    },
  });

  // Grant access
  await prisma.user.update({
    where: { id: customer.customer_id },
    data: {
      subscriptionStatus: 'active',
      plan: product_id,
    },
  });

  // Send welcome email
  await sendWelcomeEmail(customer.email, product_id);
}

async function handleSubscriptionCancelled(data: any) {
  const { subscription_id, customer, cancelled_at, cancel_at_next_billing_date } = data;

  await prisma.subscription.update({
    where: { externalId: subscription_id },
    data: {
      status: 'cancelled',
      cancelledAt: new Date(cancelled_at),
      // Keep access until end of billing period if cancel_at_next_billing_date
      accessEndsAt: cancel_at_next_billing_date
        ? new Date(data.next_billing_date)
        : new Date(),
    },
  });

  // Send cancellation email
  await sendCancellationEmail(customer.email, cancel_at_next_billing_date);
}

async function handleSubscriptionOnHold(data: any) {
  const { subscription_id, customer } = data;

  await prisma.subscription.update({
    where: { externalId: subscription_id },
    data: { status: 'on_hold' },
  });

  // Notify user about payment issue
  await sendPaymentFailedEmail(customer.email);
}

async function handleSubscriptionRenewed(data: any) {
  const { subscription_id, next_billing_date } = data;

  await prisma.subscription.update({
    where: { externalId: subscription_id },
    data: {
      status: 'active',
      currentPeriodEnd: new Date(next_billing_date),
    },
  });
}

async function handlePlanChanged(data: any) {
  const { subscription_id, product_id, recurring_pre_tax_amount } = data;

  await prisma.subscription.update({
    where: { externalId: subscription_id },
    data: {
      productId: product_id,
      amount: recurring_pre_tax_amount,
    },
  });

  // Update user entitlements based on new plan
  await updateUserEntitlements(subscription_id, product_id);
}

async function handleSubscriptionExpired(data: any) {
  const { subscription_id, customer } = data;

  await prisma.subscription.update({
    where: { externalId: subscription_id },
    data: { status: 'expired' },
  });

  // Revoke access
  await prisma.user.update({
    where: { id: customer.customer_id },
    data: {
      subscriptionStatus: 'expired',
      plan: null,
    },
  });
}

Subscription with Trial

const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'prod_pro_monthly', quantity: 1 }
  ],
  subscription_data: {
    trial_period_days: 14,
  },
  customer: {
    email: 'user@example.com',
    name: 'John Doe',
  },
  return_url: 'https://yoursite.com/welcome',
});

Customer Portal for Self-Service

Allow customers to manage their subscription:

// Create portal session
const portal = await client.customers.createPortalSession({
  customer_id: 'cust_xxxxx',
  return_url: 'https://yoursite.com/account',
});

// Redirect to portal.url

Portal features:

  • View subscription details
  • Update payment method
  • Cancel subscription
  • View billing history

On-Demand (Usage-Based) Subscriptions

For metered/usage-based billing:

Create Subscription with Mandate

const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'prod_usage_based', quantity: 1 }
  ],
  customer: { email: 'user@example.com' },
  return_url: 'https://yoursite.com/success',
});

Charge for Usage

// When usage occurs, create a charge
const charge = await client.subscriptions.charge({
  subscription_id: 'sub_xxxxx',
  amount: 1500, // $15.00 in cents
  description: 'API calls for January 2025',
});

Track Usage Events

// payment.succeeded - Charge succeeded
// payment.failed - Charge failed, implement retry logic

Subscriptions with Credit Entitlements

Attach credit entitlements to subscription products to grant credits each billing cycle:

Setup

  1. Create a credit entitlement (Dashboard → Products → Credits)
  2. Create/edit a subscription product
  3. In Entitlements section, click Attach next to Credits
  4. Configure: credits per cycle, trial credits, proration, low balance threshold

Checkout with Credits

// Product has credit entitlement attached (e.g., 10,000 AI tokens/month)
const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'prod_pro_with_credits', quantity: 1 }
  ],
  subscription_data: {
    trial_period_days: 14, // Trial credits can differ from regular amount
  },
  customer: { email: 'user@example.com' },
  return_url: 'https://yoursite.com/success',
});

Credit Lifecycle per Cycle

Each billing cycle:

  1. New credits issuedcredit.added webhook fires
  2. Usage deducts credits — Automatically via meters or manually via API
  3. Cycle ends — Unused credits expire or roll over based on settings
  4. Overage handled — Forgiven, billed, or carried as deficit

Handle Credit Webhooks in Subscription Context

case 'credit.added':
  // Credits issued with subscription renewal
  await syncCreditBalance(data.customer_id, data.credit_entitlement_id, data.balance_after);
  break;
case 'credit.balance_low':
  // Notify customer or suggest upgrade
  await sendLowBalanceAlert(data.customer_id, data.credit_entitlement_name, data.available_balance);
  break;
case 'credit.deducted':
  // Track consumption for analytics
  await logCreditUsage(data.customer_id, data.amount);
  break;

Plan Changes with Credits

When customers upgrade/downgrade, credit proration can be enabled:

  • Proration enabled: Remaining credits are prorated based on time left in cycle
  • Proration disabled: Credits continue as-is until next cycle

Plan Changes

Upgrade/Downgrade Flow

// Get available plans
const plans = await client.products.list({
  type: 'subscription',
});

// Change plan
await client.subscriptions.update({
  subscription_id: 'sub_xxxxx',
  product_id: 'prod_new_plan',
  proration_behavior: 'create_prorations', // or 'none'
});

Handling subscription.plan_changed

async function handlePlanChanged(data: any) {
  const { subscription_id, product_id, customer } = data;

  // Map product to features/limits
  const planFeatures = getPlanFeatures(product_id);

  await prisma.user.update({
    where: { externalId: customer.customer_id },
    data: {
      plan: product_id,
      features: planFeatures,
      apiLimit: planFeatures.apiLimit,
      storageLimit: planFeatures.storageLimit,
    },
  });
}

Access Control Pattern

Middleware Example (Next.js)

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export async function middleware(request: NextRequest) {
  // Check subscription status
  const session = await getSession(request);

  if (!session?.user) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  const subscription = await getSubscription(session.user.id);

  // Check if accessing premium feature
  if (request.nextUrl.pathname.startsWith('/dashboard/pro')) {
    if (!subscription || subscription.status !== 'active') {
      return NextResponse.redirect(new URL('/pricing', request.url));
    }

    // Check if plan includes this feature
    if (!subscription.features.includes('pro')) {
      return NextResponse.redirect(new URL('/upgrade', request.url));
    }
  }

  return NextResponse.next();
}

React Hook for Subscription State

// hooks/useSubscription.ts
import useSWR from 'swr';

export function useSubscription() {
  const { data, error, mutate } = useSWR('/api/subscription', fetcher);

  return {
    subscription: data,
    isLoading: !error && !data,
    isError: error,
    isActive: data?.status === 'active',
    isPro: data?.plan?.includes('pro'),
    refresh: mutate,
  };
}

// Usage in component
function PremiumFeature() {
  const { isActive, isPro } = useSubscription();

  if (!isActive) {
    return <UpgradePrompt />;
  }

  if (!isPro) {
    return <ProUpgradePrompt />;
  }

  return <ActualFeature />;
}

Common Patterns

Grace Period for Failed Payments

async function handleSubscriptionOnHold(data: any) {
  const gracePeriodDays = 7;

  await prisma.subscription.update({
    where: { externalId: data.subscription_id },
    data: {
      status: 'on_hold',
      gracePeriodEnds: new Date(Date.now() + gracePeriodDays * 24 * 60 * 60 * 1000),
    },
  });

  // Schedule job to revoke access after grace period
  await scheduleAccessRevocation(data.subscription_id, gracePeriodDays);
}

Prorated Upgrades

When upgrading mid-cycle:

// Dodo handles proration automatically
// Customer pays difference for remaining days
await client.subscriptions.update({
  subscription_id: 'sub_xxxxx',
  product_id: 'prod_higher_plan',
  proration_behavior: 'create_prorations',
});

Cancellation with End-of-Period Access

// subscription.cancelled event includes:
// - cancel_at_next_billing_date: boolean
// - next_billing_date: string (when access should end)

if (data.cancel_at_next_billing_date) {
  // Keep access until next_billing_date
  await scheduleAccessRevocation(
    data.subscription_id,
    new Date(data.next_billing_date)
  );
}

Testing

Test Scenarios

  1. New subscription → subscription.active
  2. Renewal success → subscription.renewed + payment.succeeded
  3. Renewal failure → subscription.on_hold + payment.failed
  4. Plan upgrade → subscription.plan_changed
  5. Cancellation → subscription.cancelled
  6. Expiration → subscription.expired

Test in Dashboard

Use test mode and trigger events manually from the webhook settings.


Resources

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Claude Code

29.75%
按下载量换算848

Antigravity

20.93%
按下载量换算597

OpenCode

15.1%
按下载量换算431

Gemini CLI

12.39%
按下载量换算353

Cursor

7.83%
按下载量换算223

Codex

2.97%
按下载量换算85

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

可疑

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。

来源信息

继续浏览同类 Skills