stripe
Use to accept payments — Checkout, Payment Intents, subscriptions — and to securely handle webhooks with signature verification.
Works with
---
name: stripe
description: Use to accept payments — Checkout, Payment Intents, subscriptions — and to securely handle webhooks with signature verification.
license: MIT
---
# Stripe — Skillship
> Accept payments online. The fastest path is **Stripe Checkout** (a hosted payment page); the durable
> source of truth for "did they actually pay?" is a **webhook** you verify with a signature.
## 🧭 When to use this skill
- Use when: you need one-time payments, subscriptions, or a checkout flow.
- Use when: you must react to payment events (fulfill orders, grant access).
- Don't use for: storing raw card numbers yourself (let Stripe handle PCI scope).
## ⚡ Quickstart
### 1. Install
```bash
npm install stripe @stripe/stripe-js
```
### 2. Configure env (Dashboard → Developers → API keys)
```bash
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_... # safe for the browser
STRIPE_SECRET_KEY=sk_test_... # SERVER ONLY — never expose
STRIPE_WEBHOOK_SECRET=whsec_... # from `stripe listen` or Dashboard
```
### 3. Server client
```ts
// lib/stripe.ts
import "server-only";
import Stripe from "stripe";
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
```
## 🧩 Common recipes
### Recipe: Create a Checkout Session (Next.js Route Handler)
```ts
// app/api/checkout/route.ts
import { stripe } from "@/lib/stripe";
import { NextResponse } from "next/server";
export async function POST(req: Request) {
const origin = req.headers.get("origin")!;
const session = await stripe.checkout.sessions.create({
mode: "payment", // "subscription" for recurring, "setup" to save a card
line_items: [{ price: process.env.PRICE_ID!, quantity: 1 }], // never trust client-sent prices
success_url: `${origin}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${origin}/?canceled=true`,
automatic_tax: { enabled: true },
});
return NextResponse.redirect(session.url!, 303);
}
```
> **Always** set the amount/price on the **server** from a trusted price ID — never from the request body.
### Recipe: Verify a webhook (this is the important one)
```ts
// app/api/webhooks/stripe/route.ts
import { stripe } from "@/lib/stripe";
export async function POST(req: Request) {
const body = await req.text(); // RAW body — do not JSON.parse first
const sig = req.headers.get("stripe-signature")!;
let event;
try {
event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
} catch (err) {
return new Response(`Webhook Error: ${(err as Error).message}`, { status: 400 });
}
// Return 2xx FAST; do heavy work async / idempotently
switch (event.type) {
case "checkout.session.completed":
// fulfill the order — key off event.id to dedupe
break;
case "payment_intent.succeeded":
break;
default:
break;
}
return new Response(null, { status: 200 });
}
```
### Recipe: Test webhooks locally
```bash
stripe login
stripe listen --forward-to localhost:3000/api/webhooks/stripe
# copy the printed whsec_... into STRIPE_WEBHOOK_SECRET
stripe trigger payment_intent.succeeded
```
### Recipe: Test cards
| Scenario | Card |
|---|---|
| Success | `4242 4242 4242 4242` |
| Requires 3DS auth | `4000 0025 0000 3155` |
| Declined | `4000 0000 0000 9995` |
## 🚀 Ship to production
- [ ] Switch from `sk_test_`/`pk_test_` to **live** keys, set in the host secret store.
- [ ] Webhook endpoint is public **HTTPS** and registered in the Dashboard for the events you need.
- [ ] Signature verification uses the **raw** request body (disable body parsing / don't `JSON.parse` first).
- [ ] Handler returns `2xx` quickly; fulfillment runs async and is **idempotent** (dedupe by `event.id`).
- [ ] Don't assume event ordering; refetch objects via the API when needed.
- [ ] Exempt the webhook route from CSRF protection.
- [ ] Fulfill **only after** payment success events — never on the client redirect alone.
## 🔐 Security & secrets
- `STRIPE_SECRET_KEY` and `STRIPE_WEBHOOK_SECRET` are server-only. Never bundle them to the client.
- Verify every webhook signature — otherwise an attacker can POST fake "payment succeeded" events.
- Stripe includes a timestamp in the signature (default 5-min tolerance) to block replay attacks; keep server clocks synced (NTP). Never set tolerance to `0`.
- Roll the signing secret periodically or if compromised.
## 🐛 Common errors & fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| `No signatures found matching the expected signature` | Body was parsed/modified before verify | Use the raw body (`await req.text()`), not parsed JSON |
| Webhook shows as failed / timeout | Doing heavy work before responding | Return `200` first, process async |
| Duplicate fulfillment | Stripe retried the event | Dedupe by `event.id`; make handlers idempotent |
| Order fulfilled but no payment | Fulfilling on client redirect | Fulfill on `checkout.session.completed` webhook |
| `Invalid API Key provided` | Test vs live key mismatch | Match key mode to environment |
## 📚 Sources
- https://docs.stripe.com/checkout/quickstart
- https://docs.stripe.com/payments/accept-a-payment
- https://docs.stripe.com/webhooksMore API Design skills
lark-event
larksuite/cli
Lark/Feishu real-time event listening / subscribing / consuming: stream events as NDJSON via `lark-cli event consume <EventKey>` (covers IM messages/reactions/chat changes, Approval status changes, Task updates, VC meeting started/joined/ended, Minutes generated, Whiteboard updated, etc.). Use for Lark bots, real-time message processing, long-running subscribers, streaming webhook/push handlers. Supports `--max-events` / `--timeout` bounded runs and a stderr ready-marker contract — designed for AI agents running as subprocesses.
lark-contact
larksuite/cli
飞书 / Lark 通讯录:按姓名 / 邮箱解析成 open_id,或按 open_id 反查姓名 / 部门 / 邮箱 / 联系方式 / 个人状态 / 签名,以及按关键词搜索当前用户可见的机器人 / 智能体(agent)。当用户提到一个名字要下一步发消息 / 排日程,或拿到 open_id 想查具体信息时使用。不负责部门树遍历、按部门列员工、组织架构图,这类需求走原生 OpenAPI。
lark-openapi-explorer
larksuite/cli
飞书/Lark 原生 OpenAPI 探索:从官方文档库中挖掘未经 CLI 封装的原生 OpenAPI 接口。当用户的需求无法被现有 lark-* skill 或 lark-cli 已注册命令满足,需要查找并调用原生飞书 OpenAPI 时使用。

