payment-integration
npx skills add https://github.com/kyuhyi/bsd_claude_skills --skill payment-integration
Agent 安装分布
Skill 文档
ð³ Next.js ê²°ì ìì¤í íµí© ì¤í¬
ì´ ì¤í¬ì BSD ë°ì´ë¸ì½ë© ìê°ìë¤ì´ Next.jsì Node.js를 ì¬ì©íì¬ ìì íê³ ì 문ì ì¸ ê²°ì ìì¤í ì 구ì¶í ì ìëë¡ ëìµëë¤.
ð ì´ ì¤í¬ì´ íë ì¼
- íë«í¼ ì ëµ: ë¹ì¦ëì¤ ëª¨ë¸(ì¼íì±/구ë )ì ë§ë ìµì ì PGì¬(Toss, Stripe) ì ì
- Next.js íµí©: í´ë¼ì´ì¸í¸ ì¬ì´ë ê²°ì ìì² ë° ìë² ì¬ì´ë ì¹ì¸ ë¡ì§ 구í
- ì¹í (Webhook) ì°ë: ê²°ì ìë£, ì·¨ì ë± ì´ë²¤í¸ë¥¼ ë¹ëê¸°ë¡ ì²ë¦¬íì¬ DB ë기í
- ë³´ì ë° ê²ì¦: ë°ì´í° ìë³ì¡° ë°©ì§ ë° ìë² ì¬ì´ë ê¸ì¡ ê²ì¦ ë¡ì§ 구í
- 구ë ê´ë¦¬: Stripe Billing ë±ì íì©í ìë ê²°ì ë° ë©¤ë²ì ì°ë
ð¯ ì¸ì ì´ ì¤í¬ì ì¬ì©íëì?
- “Next.js ì¼í몰ì í ì¤íì´ë¨¼ì¸ ì ì ¯(Payment Widget)ì ë£ê³ ì¶ì´ì”
- “Stripe를 ì¬ì©í´ ê¸ë¡ë² 구ë ìë¹ì¤ë¥¼ ë§ë¤ê³ ì¶ì´ì”
- “ê²°ì ê° ìë£ëë©´ ìëì¼ë¡ ì림í¡ì ë³´ë´ê³ ìê° ê¶íì ì´ì´ì£¼ê³ ì¶ì´ì”
ð ï¸ ì¶ì² 기ì ì¤í
1. Payment Gateways
- Toss Payments: êµë´ ê²°ì ìµê°ì, ìì ¯ 기ë°ì ê°í¸í ì°ë
- Stripe: ê¸ë¡ë² ë° êµ¬ë ê²°ì íì¤
2. Implementation
- Next.js App Router: API Route Handlers를 íì©í ê²°ì ì¹ì¸
- Server Actions: ë³´ìì´ ê°íë ë°ì´í° ì²ë¦¬
ð» 구í ìì (Next.js + Toss Payments)
1. Server Side (app/api/payment/confirm/route.ts)
import { NextResponse } from "next/server";
export async function POST(req: Request) {
const { paymentKey, orderId, amount } = await req.json();
// 1. ìë² ì¬ì´ë ê¸ì¡ ê²ì¦ ë¡ì§ ì¶ê° íì
// const product = await db.product.findUnique({ where: { orderId } });
// if (product.price !== amount) throw new Error('Invalid amount');
const secretKey = process.env.TOSS_SECRET_KEY;
const basicToken = Buffer.from(`${secretKey}:`).toString("base64");
const response = await fetch(
"https://api.tosspayments.com/v1/payments/confirm",
{
method: "POST",
headers: {
Authorization: `Basic ${basicToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ paymentKey, orderId, amount }),
}
);
const result = await response.json();
if (response.ok) {
// DB ì
ë°ì´í¸ ë± íì²ë¦¬ ìí
return NextResponse.json(result);
} else {
return NextResponse.json(result, { status: response.status });
}
}
2. Client Side (components/Checkout.tsx)
"use client";
import { loadTossPayments } from "@tosspayments/payment-sdk";
export default function Checkout() {
const handlePayment = async () => {
const tossPayments = await loadTossPayments(
process.env.NEXT_PUBLIC_TOSS_CLIENT_KEY!
);
await tossPayments.requestPayment("ì¹´ë", {
amount: 50000,
orderId: "ORDER_123",
orderName: "Next.js ë§ì¤í°í´ëì¤",
successUrl: `${window.location.origin}/success`,
failUrl: `${window.location.origin}/fail`,
});
};
return <button onClick={handlePayment}>50,000ì ê²°ì í기</button>;
}
â ï¸ ë³´ì ì²´í¬ë¦¬ì¤í¸
- ê¸ì¡ ê²ì¦: ë°ëì ìë² ì¬ì´ëìì ì¤ì DBì ê°ê²©ê³¼ ë¹êµ ê²ì¦
- API Key ê´ë¦¬: Secret Keyë ì ë í´ë¼ì´ì¸í¸ì ë
¸ì¶íì§ ë§ê³
.envë¡ ê´ë¦¬ - 멱ë±ì±(Idempotency): ì¤ë³µ ê²°ì ë°©ì§ë¥¼ ìí
Idempotency-Keyíì©
ð¬ ìì ëí
ì¬ì©ì: “í ì¤íì´ë¨¼ì¸ ë¡ ì 기 ê²°ì 기ë¥ì Next.jsì ë£ê³ ì¶ì´”
Claude: “Next.js App Routerì í ì¤íì´ë¨¼ì¸ ë¹ë§ API를 ì¬ì©íì¬ ìì í ì 기 ê²°ì ìì¤í ì 구ì¶í´ëë¦¬ê² ìµëë¤. ì¹´ë ë±ë¡ë¶í° ìì½ ê²°ì ë¡ì§, ê·¸ë¦¬ê³ ê²°ì ê²°ê³¼ ì¹í ì²ë¦¬ê¹ì§ í¬í¨ë ì½ë를 ìì±í´ ë릴ê²ì…”
ð¯ íµì¬ ì 리
ì´ ì¤í¬ì ì¬ì©íë©´: â ìì í ê²°ì ìí¤í ì²: ìë² ì¬ì´ë ê²ì¦ì´ í¬í¨ë ê²¬ê³ í ìì¤í â ìµì ì°ë ë°©ì: Toss UI Widget, Stripe Checkout ë± ìµì í¸ë ë ë°ì â ë¹ì¦ëì¤ íì¥ì±: ì 기 구ë , ë¶ë¶ ì·¨ì, ìì¤í¬ë¡ ë± ë³µì¡í ê¸°ë¥ ì§ì â ìì ì ì¸ ì´ì: ì¹í ì²ë¦¬ë¥¼ íµí ë°ì´í° ë¶ì¼ì¹ 문ì í´ê²°
BSD íìì´ë¼ë©´: ì´ì ë¨ìí ì¹ì¬ì´í¸ë¥¼ ëì´, ì¤ì ììµì´ ë°ìíë ë¹ì¦ëì¤ íë«í¼ì ì§ì ê°ë°í ì ììµëë¤! ð³