
โ Toss Payments SDK๋?
Toss Payments SDK๋ ์น์ฌ์ดํธ๋ ์ฑ์ ์จ๋ผ์ธ ๊ฒฐ์ ๊ธฐ๋ฅ์ ๋ถ์ผ ์ ์๋๋ก ํ ์คํ์ด๋จผ์ธ ๊ฐ ์ ๊ณตํ๋ ๊ฐ๋ฐ ๋๊ตฌ
Toss ์ฑ ์์ฒด๋ฅผ ์ฐ๋ํ๋ ๋๊ตฌ๋ผ๊ธฐ๋ณด๋ค, ์นด๋·๊ณ์ข์ด์ฒด·๊ฐํธ๊ฒฐ์ ๋ฑ์ ์ฒ๋ฆฌํ๋ PG(Payment Gateway) ์๋น์ค๋ฅผ ์ฐ๋ํ๋ SDK
์ด๋ค ๊ธฐ๋ฅ์ ์ ๊ณตํ๋?
Toss Payments SDK๋ฅผ ์ด์ฉํ๋ฉด ์ง์ ๊ฒฐ์ UI์ ์นด๋์ฌ๋ณ ์ธ์ฆ ์ ์ฐจ๋ฅผ ๋ชจ๋ ๋ง๋ค์ง ์์๋ ๋๋ค.
- ์ ์ฉ·์ฒดํฌ์นด๋
- ํ ์คํ์ด, ๋ค์ด๋ฒํ์ด, ์นด์นด์คํ์ด ๋ฑ์ ๊ฐํธ๊ฒฐ์
- ๊ณ์ข์ด์ฒด
- ๊ฐ์๊ณ์ข
- ํด๋ํฐ ๊ฒฐ์
- ์๋๊ฒฐ์
- ํด์ธ๊ฒฐ์
โ Installation
npm i @tosspayments/tosspayments-sdk
https://docs.tosspayments.com/sdk/v2/js
ํ ์คํ์ด๋จผ์ธ JavaScript SDK | ํ ์คํ์ด๋จผ์ธ ๊ฐ๋ฐ์์ผํฐ
ํ ์คํ์ด๋จผ์ธ JavaScript SDK๋ฅผ ์ถ๊ฐํ๊ณ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์์๋ด ๋๋ค.
docs.tosspayments.com
โ Usage
Payments Widgets
import { Form, type MetaFunction } from "react-router";
import type { Route } from "./+types/promote-page";
import { PageHero } from "~/common/components/page-hero";
import SelectPair from "~/common/components/select-pair";
import { Calendar } from "~/common/components/ui/calendar";
import { Label } from "~/common/components/ui/label";
import { useEffect, useRef, useState } from "react";
import type { DateRange } from "react-day-picker";
import { DateTime } from "luxon";
import { Button } from "~/common/components/ui/button";
import {
loadTossPayments,
type TossPaymentsWidgets,
} from "@tosspayments/tosspayments-sdk";
export function loader({ request }: Route.LoaderArgs) {
return { requestUrl: request.url };
}
export function action({ request }: Route.ActionArgs) {
return { requestMethod: request.method };
}
export const meta: MetaFunction = () => {
return [
{ title: "Promote Product | wemake" },
{ name: "description", content: "Promote your product" },
];
};
const clientKey = "test_gck_docs_Ovk5rk1EwkEbP0W43n07xlzm";
const customerKey = "hQXCTRFORCizlkzGCjBSD"; // userId
export default function PromotePage() {
const [promotionPeriod, setPromotionPeriod] = useState<
DateRange | undefined
>();
const totalDays =
promotionPeriod?.from && promotionPeriod.to
? DateTime.fromJSDate(promotionPeriod.to).diff(
DateTime.fromJSDate(promotionPeriod.from),
"days",
).days
: 0;
const widgets = useRef<TossPaymentsWidgets | null>(null);
useEffect(() => {
const initToss = async () => {
const toss = await loadTossPayments(clientKey);
widgets.current = toss.widgets({ customerKey });
await widgets.current.setAmount({
value: 0,
currency: "KRW",
});
await widgets.current.renderPaymentMethods({
selector: "#toss-payment-methods",
});
await widgets.current.renderAgreement({
selector: "#toss-payment-agreement",
});
};
initToss();
}, [clientKey, customerKey]);
useEffect(() => {
if (widgets.current) {
widgets.current.setAmount({
value: totalDays * 20000,
currency: "KRW",
});
}
}, [promotionPeriod]);
return (
<div>
<PageHero
title="Promote Your Product"
subtitle="Boost your product's visibility"
/>
<div className="grid grid-cols-6 gap-10">
<Form className="col-span-3 mx-auto flex flex-col items-center gap-10">
<SelectPair
label="Select a product"
description="Select the product you want to promote."
name="product"
placeholder="Select a product"
options={[
{
label: "AI Dark Mode Maker",
value: "ai-dark-mode-maker-1",
},
{
label: "AI Dark Mode Maker",
value: "ai-dark-mode-maker-2",
},
{
label: "AI Dark Mode Maker",
value: "ai-dark-mode-maker-3",
},
]}
/>
<div className="flex w-full flex-col items-center gap-2">
<Label className="flex flex-col gap-1">
Select a range of dates for promotion
<small className="text-muted-foreground">
Minimum duration is 3 days.
</small>
</Label>
<Calendar
className="min-w-sm"
mode="range"
selected={promotionPeriod}
onSelect={setPromotionPeriod}
min={3}
disabled={[{ before: new Date() }]}
/>
</div>
</Form>
<aside className="col-span-3 flex flex-col items-center px-20">
<div id="toss-payment-methods" className="w-full" />
<div id="toss-payment-agreement" className="w-full" />
<Button disabled={totalDays === 0} className="w-full">
Checkout (
{(totalDays * 20000).toLocaleString("ko-KR", {
style: "currency",
currency: "KRW",
})}
)
</Button>
</aside>
</div>
</div>
);
}
Request Payment
const handleSubmit = async (e: React.SubmitEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const product = formData.get("product");
if (!product || !promotionPeriod?.from || !promotionPeriod.to) return;
await widgets.current?.requestPayment({
orderId: crypto.randomUUID(),
orderName: `wemake promotion for ${product}`,
customerEmail: "email@naver.com",
customerName: "somename",
customerMobilePhone: "01012345678",
metadata: {
product,
promotionFrom: DateTime.fromJSDate(promotionPeriod.from).toISO(),
promotionTo: DateTime.fromJSDate(promotionPeriod.to).toISO(),
},
successUrl: `${window.location.href}/success`,
failUrl: `${window.location.href}/fail`,
});
};
Confirm Payment
import z from "zod";
import type { Route } from "./+types/promote-success-page";
// paymentType=NORMAL&orderId=ad00d236-4320-466e-ba48-33d8a327c400&paymentKey=tgen_202607221317267l4k2&amount=60000
const paramsSchema = z.object({
paymentType: z.string(),
orderId: z.uuid(),
paymentKey: z.string(),
amount: z.coerce.number(),
});
const TOSS_SECRET_KEY = "test_gsk_docs_OaPz8L5KdmQXkzRz3y47BMw6";
export const loader = async ({ request }: Route.LoaderArgs) => {
const url = new URL(request.url);
const { success, data } = paramsSchema.safeParse(
Object.fromEntries(url.searchParams),
);
if (!success) {
return new Response(null, { status: 404 });
}
const encryptedSecretKey = `Basic ${Buffer.from(TOSS_SECRET_KEY + ":").toString("base64")}`;
const response = await fetch(
`https://api.tosspayments.com/v1/payments/confirm`,
{
method: "POST",
headers: {
Authorization: encryptedSecretKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
orderId: data.orderId,
paymentKey: data.paymentKey,
amount: data.amount,
}),
},
);
const responseData = await response.json();
return Response.json({ responseData });
};


๊ฒฐ์ ๊ฐ ์งํ์ด ๋์์ง๋ง success route๋ก ์ด๋ํ์ฌ ์ ๋ณด๋ฅผ ์ ์กํ์ง ๋ชปํ ์ํฉ์ ๋๋นํ์ฌ ํ ์ค ๊ด๋ฆฌ์ ํจ๋์์ api๋ฅผ ํธ์ถํ๋๋กwebhook์ ์ค์ ํด๋์์ผ ํ๋ค.
https://docs.tosspayments.com/guides/v2/payment-widget/integration?frontend=react
์ฐ๋ํ๊ธฐ | ํ ์คํ์ด๋จผ์ธ ๊ฐ๋ฐ์์ผํฐ
ํ ์คํ์ด๋จผ์ธ ์ ๊ฐํธํ ๊ฒฐ์ ์ฐ๋ ๊ณผ์ ์ ํ๋์ ๋ณผ ์ ์์ต๋๋ค. ๊ฐ ๋จ๊ณ๋ณ ์ค๋ช ๊ณผ ํจ๊ป ๋ฌ๋ผ์ง๋ UI์ ์ฝ๋๋ฅผ ํ์ธํด๋ณด์ธ์.
docs.tosspayments.com
'๐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ - Vercel React Router Preset (0) | 2026.07.23 |
|---|---|
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ - React Email (0) | 2026.07.21 |
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ - Cloudflared (0) | 2026.07.21 |
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ - React Router v7 - useFetcher (0) | 2026.06.26 |
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ - Supabase SSR Package, Auth (0) | 2026.06.10 |