Token导航 LogoToken导航TokenDH.com
前端设计敏感数据github未标认证来源可访问clear审计提醒

recur-portal循环门户

Agent Skill

recur-portal 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

635

周安装

27

GitHub Stars

公开资料未说明

下载量

222
CodexClaudeCursorGemini CLI

安装说明

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

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

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

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

命令行安装

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

skills.shnpx skills
npx skills add https://github.com/recur-tw/skills --skill recur-portal

简介

recur-portal 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中整理协作事项。

  • 适用于围绕仓库状态、代码变更或协作事项进行信息整理的任务。
  • 可查询仓库状态、Issue 详情、PR 内容和协作历史。
  • 安装命令:npx skills add https://github.com/recur-tw/skills --skill recur-portal。
  • 建议确认权限范围和维护状态,注意是否触发联网或文件操作。

SKILL.md

Recur Customer Portal Integration

You are helping implement Recur's Customer Portal, which allows subscribers to self-manage their subscriptions without contacting support.

What is Customer Portal?

Customer Portal is a hosted page where your customers can:

  • View active subscriptions and billing history
  • Update payment methods
  • Cancel or reactivate subscriptions
  • Switch between plans (upgrade/downgrade)

When to Use

ScenarioSolution
"Add account management page"Create portal session and redirect
"Let users update their card"Portal handles payment method updates
"Users need to cancel subscription"Portal provides self-service cancellation
"Show billing history"Portal displays invoices and payments

Quick Start: Create Portal Session

Portal sessions are created server-side (requires Secret Key).

Using Server SDK

import { Recur } from 'recur-tw/server'

const recur = new Recur(process.env.RECUR_SECRET_KEY!)

// Create portal session - identify customer by email, ID, or externalId
const session = await recur.portal.sessions.create({
  email: 'customer@example.com',  // or customer: 'cus_xxx' or externalId: 'user_123'
  returnUrl: 'https://yourapp.com/account',
})

// Redirect customer to portal
redirect(session.url)

Customer Identification

You can identify customers using one of these methods (in priority order):

// By Recur customer ID (highest priority)
await recur.portal.sessions.create({
  customer: 'cus_xxx',
  returnUrl: 'https://yourapp.com/account',
})

// By your system's user ID
await recur.portal.sessions.create({
  externalId: 'user_123',
  returnUrl: 'https://yourapp.com/account',
})

// By email (lowest priority)
await recur.portal.sessions.create({
  email: 'customer@example.com',
  returnUrl: 'https://yourapp.com/account',
})

Next.js Implementation

API Route (App Router)

// app/api/portal/route.ts
import { Recur } from 'recur-tw/server'
import { auth } from '@/lib/auth'  // Your auth solution
import { NextResponse } from 'next/server'

const recur = new Recur(process.env.RECUR_SECRET_KEY!)

export async function POST() {
  const session = await auth()

  if (!session?.user?.email) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  try {
    const portalSession = await recur.portal.sessions.create({
      email: session.user.email,
      returnUrl: `${process.env.NEXT_PUBLIC_APP_URL}/account`,
    })

    return NextResponse.json({ url: portalSession.url })
  } catch (error) {
    console.error('Portal session error:', error)
    return NextResponse.json(
      { error: 'Failed to create portal session' },
      { status: 500 }
    )
  }
}

Server Action

// app/actions/portal.ts
'use server'

import { Recur } from 'recur-tw/server'
import { auth } from '@/lib/auth'
import { redirect } from 'next/navigation'

const recur = new Recur(process.env.RECUR_SECRET_KEY!)

export async function openPortal() {
  const session = await auth()

  if (!session?.user?.email) {
    throw new Error('Unauthorized')
  }

  const portalSession = await recur.portal.sessions.create({
    email: session.user.email,
    returnUrl: `${process.env.NEXT_PUBLIC_APP_URL}/account`,
  })

  redirect(portalSession.url)
}

Portal Button Component

// components/portal-button.tsx
'use client'

import { useState } from 'react'

export function PortalButton() {
  const [isLoading, setIsLoading] = useState(false)

  const handleClick = async () => {
    setIsLoading(true)
    try {
      const response = await fetch('/api/portal', { method: 'POST' })
      const { url, error } = await response.json()

      if (error) throw new Error(error)

      window.location.href = url
    } catch (error) {
      console.error('Failed to open portal:', error)
      alert('無法開啟帳戶管理頁面,請稍後再試')
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <button onClick={handleClick} disabled={isLoading}>
      {isLoading ? '載入中...' : '管理訂閱'}
    </button>
  )
}

Using Server Action with Button

// components/portal-button-action.tsx
'use client'

import { openPortal } from '@/app/actions/portal'

export function PortalButton() {
  return (
    <form action={openPortal}>
      <button type="submit">管理訂閱</button>
    </form>
  )
}

Portal Session Response

interface PortalSession {
  id: string              // Session ID (e.g., 'portal_sess_xxx')
  object: 'portal.session'
  url: string             // URL to redirect customer to
  customer: string        // Customer ID
  returnUrl: string       // URL to return after portal exit
  status: 'active' | 'expired'
  expiresAt: string       // ISO 8601 (sessions last 1 hour)
  accessedAt: string | null
  createdAt: string
}

Using REST API Directly

If not using the SDK:

const response = await fetch('https://api.recur.tw/v1/portal/sessions', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.RECUR_SECRET_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    email: 'customer@example.com',
    return_url: 'https://yourapp.com/account',
  }),
})

const { url } = await response.json()
// Redirect to url

Common Patterns

Account Page with Portal Link

// app/account/page.tsx
import { auth } from '@/lib/auth'
import { PortalButton } from '@/components/portal-button'

export default async function AccountPage() {
  const session = await auth()

  return (
    <div>
      <h1>帳戶設定</h1>
      <p>Email: {session?.user?.email}</p>

      <section>
        <h2>訂閱管理</h2>
        <p>管理您的訂閱、更新付款方式、查看帳單記錄</p>
        <PortalButton />
      </section>
    </div>
  )
}

Conditional Portal Access

// Only show portal button if user has subscription
function SubscriptionSection({ hasSubscription }: { hasSubscription: boolean }) {
  if (!hasSubscription) {
    return (
      <div>
        <p>您目前沒有訂閱</p>
        <a href="/pricing">查看方案</a>
      </div>
    )
  }

  return (
    <div>
      <p>您目前的訂閱:Pro 方案</p>
      <PortalButton />
    </div>
  )
}

Portal Configuration

Configure portal behavior in Recur Dashboard → Settings → Customer Portal:

  • Default Return URL: Where to redirect after leaving portal
  • Allowed Actions: Enable/disable cancel, update payment, switch plan
  • Branding: Custom logo and colors

Security Notes

  1. Server-side only: Portal sessions require Secret Key (sk_xxx)
  2. Short-lived: Sessions expire in 1 hour
  3. One-time use: Each session URL should only be used once
  4. Verify user: Always authenticate the user before creating a portal session

Error Handling

try {
  const session = await recur.portal.sessions.create({
    email: userEmail,
    returnUrl: returnUrl,
  })
  redirect(session.url)
} catch (error) {
  if (error.code === 'customer_not_found') {
    // Customer doesn't exist in Recur
    // Maybe they haven't subscribed yet
    redirect('/pricing')
  }

  if (error.code === 'missing_return_url') {
    // returnUrl is required
    console.error('Missing return URL')
  }

  throw error
}

Related Skills

  • /recur-quickstart - Initial SDK setup
  • /recur-checkout - Implement purchase flows
  • /recur-entitlements - Check subscription access

适合场景

01

用户想查找某类 Agent Skill 时

02

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

03

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

04

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

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

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

能力 4

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

能力 5

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

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

平台分布

Claude Code

27.1%
按下载量换算60

Gemini CLI

23.71%
按下载量换算53

Antigravity

16.47%
按下载量换算37

Codex

11.86%
按下载量换算26

OpenCode

8.01%
按下载量换算18

Cursor

3.42%
按下载量换算8

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

可疑

权限和风险

敏感数据

该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

安装前确认

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

来源信息

继续浏览同类 Skills