recur-skills 0.0.4 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +5 -3
- package/README.md +23 -2
- package/package.json +2 -2
- package/skills/recur-checkout/SKILL.md +4 -0
- package/skills/recur-entitlements/SKILL.md +4 -0
- package/skills/recur-help/SKILL.md +11 -0
- package/skills/recur-portal/SKILL.md +324 -0
- package/skills/recur-quickstart/SKILL.md +4 -0
- package/skills/recur-webhooks/SKILL.md +4 -0
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Claude Code skills for integrating Recur - Taiwan's subscription payment platform",
|
|
9
|
-
"version": "0.0.
|
|
9
|
+
"version": "0.0.7"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
@@ -18,14 +18,16 @@
|
|
|
18
18
|
"./skills/recur-quickstart",
|
|
19
19
|
"./skills/recur-checkout",
|
|
20
20
|
"./skills/recur-webhooks",
|
|
21
|
-
"./skills/recur-entitlements"
|
|
21
|
+
"./skills/recur-entitlements",
|
|
22
|
+
"./skills/recur-portal"
|
|
22
23
|
],
|
|
23
24
|
"commands": [
|
|
24
25
|
"./skills/recur-help/SKILL.md",
|
|
25
26
|
"./skills/recur-quickstart/SKILL.md",
|
|
26
27
|
"./skills/recur-checkout/SKILL.md",
|
|
27
28
|
"./skills/recur-webhooks/SKILL.md",
|
|
28
|
-
"./skills/recur-entitlements/SKILL.md"
|
|
29
|
+
"./skills/recur-entitlements/SKILL.md",
|
|
30
|
+
"./skills/recur-portal/SKILL.md"
|
|
29
31
|
]
|
|
30
32
|
}
|
|
31
33
|
]
|
package/README.md
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
# Recur Skills
|
|
1
|
+
# Recur Skills
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Skills to help developers integrate [Recur](https://recur.tw) - Taiwan's subscription payment platform.
|
|
4
|
+
|
|
5
|
+
Supports Claude Code, Cursor, Codex, GitHub Copilot, Gemini CLI, Antigravity, and other AI coding agents.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
9
|
+
### npx skills add (Recommended)
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx skills add recur-tw/skills
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Claude Code Plugin
|
|
16
|
+
|
|
7
17
|
```bash
|
|
8
18
|
/plugin marketplace add recur-tw/skills
|
|
9
19
|
/plugin install recur-skills@recur-skills
|
|
@@ -71,6 +81,17 @@ Implement access control and permission checking.
|
|
|
71
81
|
- Paywall components
|
|
72
82
|
- Server-side verification
|
|
73
83
|
|
|
84
|
+
### recur-portal
|
|
85
|
+
|
|
86
|
+
Implement Customer Portal for subscription self-service.
|
|
87
|
+
|
|
88
|
+
**Triggers:** "customer portal", "帳戶管理", "訂閱管理", "更新付款方式", "self-service"
|
|
89
|
+
|
|
90
|
+
- Create portal sessions
|
|
91
|
+
- Portal button component
|
|
92
|
+
- Next.js API routes and server actions
|
|
93
|
+
- Account management pages
|
|
94
|
+
|
|
74
95
|
## Usage
|
|
75
96
|
|
|
76
97
|
Once installed, Claude will automatically use these skills when you're working on Recur integration tasks.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "recur-skills",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "Claude Code skills for Recur - Taiwan's subscription payment platform",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"recur",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"lint": "eslint src/",
|
|
47
47
|
"typecheck": "tsc --noEmit",
|
|
48
48
|
"prepublishOnly": "pnpm build",
|
|
49
|
-
"version": "node scripts/sync-version.js && git add .claude-plugin/marketplace.json"
|
|
49
|
+
"version": "node scripts/sync-version.js && git add .claude-plugin/marketplace.json skills/*/SKILL.md"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"commander": "^12.1.0",
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: recur-checkout
|
|
3
3
|
description: Implement Recur checkout flows including embedded, modal, and redirect modes. Use when adding payment buttons, checkout forms, subscription purchase flows, or when user mentions "checkout", "結帳", "付款按鈕", "embedded checkout".
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: recur
|
|
7
|
+
version: "0.0.7"
|
|
4
8
|
---
|
|
5
9
|
|
|
6
10
|
# Recur Checkout Integration
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: recur-entitlements
|
|
3
3
|
description: Implement access control and permission checking with Recur entitlements API. Use when building paywalls, checking subscription status, gating premium features, or when user mentions "paywall", "權限檢查", "entitlements", "access control", "premium features".
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: recur
|
|
7
|
+
version: "0.0.7"
|
|
4
8
|
---
|
|
5
9
|
|
|
6
10
|
# Recur Entitlements & Access Control
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: recur-help
|
|
3
3
|
description: List all available Recur skills and how to use them. Use when user asks "what can Recur do", "Recur skills", "Recur 有什麼功能", "help with Recur", "如何使用 Recur skills".
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: recur
|
|
7
|
+
version: "0.0.7"
|
|
4
8
|
---
|
|
5
9
|
|
|
6
10
|
# Recur Skills 使用指南
|
|
@@ -33,6 +37,12 @@ description: List all available Recur skills and how to use them. Use when user
|
|
|
33
37
|
- 說:「檢查付費權限」「paywall」「權限檢查」「entitlements」
|
|
34
38
|
- 或輸入:`/recur-entitlements`
|
|
35
39
|
|
|
40
|
+
### 5. recur-portal
|
|
41
|
+
**用途**:實作客戶自助入口(管理訂閱、更新付款方式)
|
|
42
|
+
**觸發方式**:
|
|
43
|
+
- 說:「加上帳戶管理」「customer portal」「訂閱管理」「更新付款方式」
|
|
44
|
+
- 或輸入:`/recur-portal`
|
|
45
|
+
|
|
36
46
|
## 回覆格式
|
|
37
47
|
|
|
38
48
|
用繁體中文回覆,格式如下:
|
|
@@ -48,6 +58,7 @@ description: List all available Recur skills and how to use them. Use when user
|
|
|
48
58
|
| **checkout** | 結帳流程 | 說「加上結帳按鈕」 |
|
|
49
59
|
| **webhooks** | 付款通知 | 說「設定 webhook」 |
|
|
50
60
|
| **entitlements** | 權限檢查 | 說「檢查付費權限」 |
|
|
61
|
+
| **portal** | 客戶自助入口 | 說「加上帳戶管理」 |
|
|
51
62
|
|
|
52
63
|
💡 **建議從 quickstart 開始**,它會引導你完成 SDK 安裝和基本設定。
|
|
53
64
|
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: recur-portal
|
|
3
|
+
description: Implement Customer Portal for subscription self-service. Use when building account pages, letting customers manage subscriptions, update payment methods, view billing history, or when user mentions "customer portal", "帳戶管理", "訂閱管理", "更新付款方式", "self-service".
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: recur
|
|
7
|
+
version: "0.0.7"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Recur Customer Portal Integration
|
|
11
|
+
|
|
12
|
+
You are helping implement Recur's Customer Portal, which allows subscribers to self-manage their subscriptions without contacting support.
|
|
13
|
+
|
|
14
|
+
## What is Customer Portal?
|
|
15
|
+
|
|
16
|
+
Customer Portal is a hosted page where your customers can:
|
|
17
|
+
- View active subscriptions and billing history
|
|
18
|
+
- Update payment methods
|
|
19
|
+
- Cancel or reactivate subscriptions
|
|
20
|
+
- Switch between plans (upgrade/downgrade)
|
|
21
|
+
|
|
22
|
+
## When to Use
|
|
23
|
+
|
|
24
|
+
| Scenario | Solution |
|
|
25
|
+
|----------|----------|
|
|
26
|
+
| "Add account management page" | Create portal session and redirect |
|
|
27
|
+
| "Let users update their card" | Portal handles payment method updates |
|
|
28
|
+
| "Users need to cancel subscription" | Portal provides self-service cancellation |
|
|
29
|
+
| "Show billing history" | Portal displays invoices and payments |
|
|
30
|
+
|
|
31
|
+
## Quick Start: Create Portal Session
|
|
32
|
+
|
|
33
|
+
Portal sessions are created server-side (requires Secret Key).
|
|
34
|
+
|
|
35
|
+
### Using Server SDK
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { Recur } from 'recur-tw/server'
|
|
39
|
+
|
|
40
|
+
const recur = new Recur(process.env.RECUR_SECRET_KEY!)
|
|
41
|
+
|
|
42
|
+
// Create portal session - identify customer by email, ID, or externalId
|
|
43
|
+
const session = await recur.portal.sessions.create({
|
|
44
|
+
email: 'customer@example.com', // or customer: 'cus_xxx' or externalId: 'user_123'
|
|
45
|
+
returnUrl: 'https://yourapp.com/account',
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// Redirect customer to portal
|
|
49
|
+
redirect(session.url)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Customer Identification
|
|
53
|
+
|
|
54
|
+
You can identify customers using one of these methods (in priority order):
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// By Recur customer ID (highest priority)
|
|
58
|
+
await recur.portal.sessions.create({
|
|
59
|
+
customer: 'cus_xxx',
|
|
60
|
+
returnUrl: 'https://yourapp.com/account',
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// By your system's user ID
|
|
64
|
+
await recur.portal.sessions.create({
|
|
65
|
+
externalId: 'user_123',
|
|
66
|
+
returnUrl: 'https://yourapp.com/account',
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// By email (lowest priority)
|
|
70
|
+
await recur.portal.sessions.create({
|
|
71
|
+
email: 'customer@example.com',
|
|
72
|
+
returnUrl: 'https://yourapp.com/account',
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Next.js Implementation
|
|
77
|
+
|
|
78
|
+
### API Route (App Router)
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// app/api/portal/route.ts
|
|
82
|
+
import { Recur } from 'recur-tw/server'
|
|
83
|
+
import { auth } from '@/lib/auth' // Your auth solution
|
|
84
|
+
import { NextResponse } from 'next/server'
|
|
85
|
+
|
|
86
|
+
const recur = new Recur(process.env.RECUR_SECRET_KEY!)
|
|
87
|
+
|
|
88
|
+
export async function POST() {
|
|
89
|
+
const session = await auth()
|
|
90
|
+
|
|
91
|
+
if (!session?.user?.email) {
|
|
92
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const portalSession = await recur.portal.sessions.create({
|
|
97
|
+
email: session.user.email,
|
|
98
|
+
returnUrl: `${process.env.NEXT_PUBLIC_APP_URL}/account`,
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
return NextResponse.json({ url: portalSession.url })
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error('Portal session error:', error)
|
|
104
|
+
return NextResponse.json(
|
|
105
|
+
{ error: 'Failed to create portal session' },
|
|
106
|
+
{ status: 500 }
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Server Action
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
// app/actions/portal.ts
|
|
116
|
+
'use server'
|
|
117
|
+
|
|
118
|
+
import { Recur } from 'recur-tw/server'
|
|
119
|
+
import { auth } from '@/lib/auth'
|
|
120
|
+
import { redirect } from 'next/navigation'
|
|
121
|
+
|
|
122
|
+
const recur = new Recur(process.env.RECUR_SECRET_KEY!)
|
|
123
|
+
|
|
124
|
+
export async function openPortal() {
|
|
125
|
+
const session = await auth()
|
|
126
|
+
|
|
127
|
+
if (!session?.user?.email) {
|
|
128
|
+
throw new Error('Unauthorized')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const portalSession = await recur.portal.sessions.create({
|
|
132
|
+
email: session.user.email,
|
|
133
|
+
returnUrl: `${process.env.NEXT_PUBLIC_APP_URL}/account`,
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
redirect(portalSession.url)
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Portal Button Component
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
// components/portal-button.tsx
|
|
144
|
+
'use client'
|
|
145
|
+
|
|
146
|
+
import { useState } from 'react'
|
|
147
|
+
|
|
148
|
+
export function PortalButton() {
|
|
149
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
150
|
+
|
|
151
|
+
const handleClick = async () => {
|
|
152
|
+
setIsLoading(true)
|
|
153
|
+
try {
|
|
154
|
+
const response = await fetch('/api/portal', { method: 'POST' })
|
|
155
|
+
const { url, error } = await response.json()
|
|
156
|
+
|
|
157
|
+
if (error) throw new Error(error)
|
|
158
|
+
|
|
159
|
+
window.location.href = url
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error('Failed to open portal:', error)
|
|
162
|
+
alert('無法開啟帳戶管理頁面,請稍後再試')
|
|
163
|
+
} finally {
|
|
164
|
+
setIsLoading(false)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<button onClick={handleClick} disabled={isLoading}>
|
|
170
|
+
{isLoading ? '載入中...' : '管理訂閱'}
|
|
171
|
+
</button>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Using Server Action with Button
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
// components/portal-button-action.tsx
|
|
180
|
+
'use client'
|
|
181
|
+
|
|
182
|
+
import { openPortal } from '@/app/actions/portal'
|
|
183
|
+
|
|
184
|
+
export function PortalButton() {
|
|
185
|
+
return (
|
|
186
|
+
<form action={openPortal}>
|
|
187
|
+
<button type="submit">管理訂閱</button>
|
|
188
|
+
</form>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Portal Session Response
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
interface PortalSession {
|
|
197
|
+
id: string // Session ID (e.g., 'portal_sess_xxx')
|
|
198
|
+
object: 'portal.session'
|
|
199
|
+
url: string // URL to redirect customer to
|
|
200
|
+
customer: string // Customer ID
|
|
201
|
+
returnUrl: string // URL to return after portal exit
|
|
202
|
+
status: 'active' | 'expired'
|
|
203
|
+
expiresAt: string // ISO 8601 (sessions last 1 hour)
|
|
204
|
+
accessedAt: string | null
|
|
205
|
+
createdAt: string
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Using REST API Directly
|
|
210
|
+
|
|
211
|
+
If not using the SDK:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
const response = await fetch('https://api.recur.tw/v1/portal/sessions', {
|
|
215
|
+
method: 'POST',
|
|
216
|
+
headers: {
|
|
217
|
+
'Authorization': `Bearer ${process.env.RECUR_SECRET_KEY}`,
|
|
218
|
+
'Content-Type': 'application/json',
|
|
219
|
+
},
|
|
220
|
+
body: JSON.stringify({
|
|
221
|
+
email: 'customer@example.com',
|
|
222
|
+
return_url: 'https://yourapp.com/account',
|
|
223
|
+
}),
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
const { url } = await response.json()
|
|
227
|
+
// Redirect to url
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Common Patterns
|
|
231
|
+
|
|
232
|
+
### Account Page with Portal Link
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
// app/account/page.tsx
|
|
236
|
+
import { auth } from '@/lib/auth'
|
|
237
|
+
import { PortalButton } from '@/components/portal-button'
|
|
238
|
+
|
|
239
|
+
export default async function AccountPage() {
|
|
240
|
+
const session = await auth()
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<div>
|
|
244
|
+
<h1>帳戶設定</h1>
|
|
245
|
+
<p>Email: {session?.user?.email}</p>
|
|
246
|
+
|
|
247
|
+
<section>
|
|
248
|
+
<h2>訂閱管理</h2>
|
|
249
|
+
<p>管理您的訂閱、更新付款方式、查看帳單記錄</p>
|
|
250
|
+
<PortalButton />
|
|
251
|
+
</section>
|
|
252
|
+
</div>
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Conditional Portal Access
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
// Only show portal button if user has subscription
|
|
261
|
+
function SubscriptionSection({ hasSubscription }: { hasSubscription: boolean }) {
|
|
262
|
+
if (!hasSubscription) {
|
|
263
|
+
return (
|
|
264
|
+
<div>
|
|
265
|
+
<p>您目前沒有訂閱</p>
|
|
266
|
+
<a href="/pricing">查看方案</a>
|
|
267
|
+
</div>
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return (
|
|
272
|
+
<div>
|
|
273
|
+
<p>您目前的訂閱:Pro 方案</p>
|
|
274
|
+
<PortalButton />
|
|
275
|
+
</div>
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Portal Configuration
|
|
281
|
+
|
|
282
|
+
Configure portal behavior in Recur Dashboard → Settings → Customer Portal:
|
|
283
|
+
|
|
284
|
+
- **Default Return URL**: Where to redirect after leaving portal
|
|
285
|
+
- **Allowed Actions**: Enable/disable cancel, update payment, switch plan
|
|
286
|
+
- **Branding**: Custom logo and colors
|
|
287
|
+
|
|
288
|
+
## Security Notes
|
|
289
|
+
|
|
290
|
+
1. **Server-side only**: Portal sessions require Secret Key (sk_xxx)
|
|
291
|
+
2. **Short-lived**: Sessions expire in 1 hour
|
|
292
|
+
3. **One-time use**: Each session URL should only be used once
|
|
293
|
+
4. **Verify user**: Always authenticate the user before creating a portal session
|
|
294
|
+
|
|
295
|
+
## Error Handling
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
try {
|
|
299
|
+
const session = await recur.portal.sessions.create({
|
|
300
|
+
email: userEmail,
|
|
301
|
+
returnUrl: returnUrl,
|
|
302
|
+
})
|
|
303
|
+
redirect(session.url)
|
|
304
|
+
} catch (error) {
|
|
305
|
+
if (error.code === 'customer_not_found') {
|
|
306
|
+
// Customer doesn't exist in Recur
|
|
307
|
+
// Maybe they haven't subscribed yet
|
|
308
|
+
redirect('/pricing')
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (error.code === 'missing_return_url') {
|
|
312
|
+
// returnUrl is required
|
|
313
|
+
console.error('Missing return URL')
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
throw error
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Related Skills
|
|
321
|
+
|
|
322
|
+
- `/recur-quickstart` - Initial SDK setup
|
|
323
|
+
- `/recur-checkout` - Implement purchase flows
|
|
324
|
+
- `/recur-entitlements` - Check subscription access
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: recur-quickstart
|
|
3
3
|
description: Quick setup guide for Recur payment integration. Use when starting a new Recur integration, setting up API keys, installing the SDK, or when user mentions "integrate Recur", "setup Recur", "Recur 串接", "金流設定".
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: recur
|
|
7
|
+
version: "0.0.7"
|
|
4
8
|
---
|
|
5
9
|
|
|
6
10
|
# Recur Quickstart
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: recur-webhooks
|
|
3
3
|
description: Set up and handle Recur webhook events for payment notifications. Use when implementing webhook handlers, verifying signatures, handling subscription events, or when user mentions "webhook", "付款通知", "訂閱事件", "payment notification".
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: recur
|
|
7
|
+
version: "0.0.7"
|
|
4
8
|
---
|
|
5
9
|
|
|
6
10
|
# Recur Webhook Integration
|