๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ - Toss Payments SDK

2026. 7. 22. 11:58ยท๐Ÿ“‚ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

Toss Payments SDK

 

 

โœ… 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 });
};

 

Toss Docs

 

Toss Docs

 

๊ฒฐ์ œ๊ฐ€ ์ง„ํ–‰์ด ๋˜์—ˆ์ง€๋งŒ 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
'๐Ÿ“‚ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ - Vercel React Router Preset
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ - React Email
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ - Cloudflared
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ - React Router v7 - useFetcher
j2yonghwa
j2yonghwa
Trying to be a fullstack developer ๐Ÿš€
  • j2yonghwa
    j2yonghwa
    j2yonghwa
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (179)
      • โฐ Daily WakaTime (1)
      • ๐Ÿ–๏ธ ๋…ธ๋งˆ๋“œ์ฝ”๋” (2)
      • ๐Ÿบ Dev Setup (4)
      • ๐Ÿ”ญ Tech Info (1)
      • ๐Ÿ” Security (1)
      • ๐Ÿšซ Error (1)
      • ๐Ÿค– AI (4)
      • ๐Ÿ“‚ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (29)
      • ♣๏ธ Next.js 14 (10)
      • ♠๏ธ Next.js 12 (20)
      • ๐Ÿ›ธ React Native (12)
      • ๐Ÿฆ‹ TypeScript (2)
      • ๐Ÿ Python (2)
      • ๐ŸŒŠ TailwindCSS (4)
      • ๐Ÿงฉ SQL (32)
      • ๐Ÿ’Ž Prisma (5)
      • ๐ŸŒฑ MongoDB (4)
      • ๐ŸŽฏ Redis (1)
      • ๐Ÿงฌ GraphQL (2)
      • ๐Ÿ”ฅ Firebase (7)
      • ๐Ÿ’ธ Third-Party Services (5)
      • ๐Ÿ•ธ๏ธ Web (1)
      • ๐Ÿ† ์ฝ”๋”ฉํ…Œ์ŠคํŠธ (23)
      • ๐Ÿ“™ ๋ชจ๋”ฅ๋‹ค (5)
      • ๐Ÿ“— ์ฝ”ํ…Œ ํ•ฉ๊ฒฉ์ž ๋˜๊ธฐ -JS- (0)
      • ๐Ÿ“˜ ํด๋ฆฐ์ฝ”๋“œ (0)
      • ๐Ÿฏ ๊ฟ€ํŒ ๐Ÿ (1)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

    • ๊นƒํ—™
  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    Next.js
    ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
    PostgreSQL
    React Native
    API
    mongoDB
    Prisma
    MySQL
    Python
    react router
    third-party services
    SQL
    0๋ ˆ๋ฒจ
    ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ
    dev setup
    next.js 14
    Firebase
    ์ฝ”๋”ฉํ…Œ์ŠคํŠธ ์ž…๋ฌธ
    database
    next.js 12
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.3
j2yonghwa
๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ - Toss Payments SDK
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”