๐Ÿ’ต ๋ชจ๋ฐ”์ผ ์›น ๊ฒฐ์ œ ๊ธฐ๋Šฅ ( by iamport )

๋ฐ•์ƒ์€ยท2022๋…„ 9์›” 14์ผ
0

๐Ÿงบ bleshop ๐Ÿงบ

๋ชฉ๋ก ๋ณด๊ธฐ
9/10

iamport - ๋ชจ๋ฐ”์ผ ์›น ๊ฒฐ์ œ๋ฅผ ์ฐธ๊ณ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ตฌํ˜„ ๋™๊ธฐ

๊ฐœ๋ฐœ ๋ชจ๋“œ์ผ ๋•Œ๋Š” ๋ชจ๋ฐ”์ผ๋กœ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ํž˜๋“ค์–ด์„œ ๋ฐ์Šคํฌํƒ‘ ์›น๋ธŒ๋ผ์šฐ์ €๋งŒ์„ ์ด์šฉํ•ด์„œ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๋„˜์–ด๊ฐ”์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ AWS-EC2์— ๋ฐฐํฌํ•˜๊ณ  ๋ชจ๋ฐ”์ผ๋กœ ๋“ค์–ด๊ฐ€์„œ ๊ฒฐ์ œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด๋ณด๋‹ˆ ์ด์ƒํ•˜๊ฒŒ๋„ ๊ฒฐ์ œ ๋กœ์ง์€ ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋Š”๋ฐ ๊ฒฐ์ œ ์ •๋ณด๊ฐ€ DB์— ๊ธฐ๋ก๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ iamport ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐพ์•„๋ณด๋ฉด์„œ ๋ชจ๋ฐ”์ผ ์›น ๊ฒฐ์ œ ๊ธฐ๋Šฅ์„ ๊ฐœ๋ฐœํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ ์›์ธ

์นด์นด์˜คํŽ˜์ด ๊ฒฐ์ œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ชจ๋ฐ”์ผ ์›น๊ณผ ๋ฐ์Šคํฌํƒ‘ ์›น์€ ๊ฒฐ์ œ ๋ฐฉ์‹์ด ์กฐ๊ธˆ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.
๋ฐ์Šคํฌํƒ‘ ์›น์˜ ๊ฒฝ์šฐ์—๋Š” QR์ฝ”๋“œ๋‚˜ ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ/์ƒ๋…„์›”์ผ์„ ์ด์šฉํ•ด์„œ ์นด์นด์˜คํ†ก์œผ๋กœ ๊ฒฐ์ œํ•˜๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.
์ด๋ ‡๊ฒŒ ๊ฒฐ์ œํ•˜๊ฒŒ ๋˜๋ฉด ์›น๋ธŒ๋ผ์šฐ์ €๋Š” ๊ฐ€๋งŒํžˆ ์žˆ๊ณ  ๋‹ค๋ฅธ ๊ณณ์—์„œ ๊ฒฐ์ œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ฒฐ๊ณผ๋งŒ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
๊ทธ ๊ฒฐ์ œ ๊ฒฐ๊ณผ๋ฅผ IMP.request_pay(data, callback)์—์„œ callback์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋ฐ›์•„์„œ ๊ฒฐ์ œ ๊ธฐ๋ก์„ DB์— ๋‚จ๊ธฐ๋„๋ก ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ( ์˜ˆ์‹œ์—์„œ rsp๊ฐ€ ๊ฒฐ์ œ ๊ฒฐ๊ณผ ๋ฐ์ดํ„ฐ )

  • ์˜ˆ์‹œ
const onPayment = useCallback(
  (pg: "kakaopay" | "tosspay") => () => {
    if (!process.env.NEXT_PUBLIC_IAMPORT_CODE)
      return toast.error("iamport์˜ ๊ฐ€๋งน์  ์‹๋ณ„์ฝ”๋“œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.");
    if (!paymentData) {
      toast.error(
        "๊ฒฐ์ œํ•  ์ƒํ’ˆ์˜ ์ •๋ณด๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฉ”์ธ ํ™”๋ฉด์œผ๋กœ ์ด๋™๋ฉ๋‹ˆ๋‹ค."
      );
      return router.push("/");
    }

    // iamport๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ€๋งน์  ์‹๋ณ„์ฝ”๋“œ ๋“ฑ๋ก
    window.IMP.init(process.env.NEXT_PUBLIC_IAMPORT_CODE);
    
    const callback = async (rsp) => {
      if (
        !rsp.buyer_name ||
        !rsp.buyer_addr ||
        !rsp.paid_amount ||
        !rsp.buyer_email ||
        !rsp.buyer_tel ||
        !rsp.pg_provider
      )
        return toast.warning("๊ฒฐ์ œ์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค.");

      if (rsp.success) {
        try {
          // >>> ๊ฒฐ์ œ ์™„๋ฃŒ DB ์ €์žฅ
          await apiService.orderService.apiCreateOrder({
            singleData: rsp.custom_data.singleData,
            orderData: {
              name: rsp.buyer_name,
              address: rsp.buyer_addr,
              residence: rsp.custom_data.residence,
              message: rsp.custom_data.message,
              amount: rsp.paid_amount,
              email: rsp.buyer_email,
              phone: rsp.buyer_tel,
              provider: rsp.pg_provider,
            },
          });

          toast.success(
            "๊ฒฐ์ œ๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. 2์ดˆ๋’ค์— ๊ฒฐ์ œ๋‚ด์—ญ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.",
            { autoClose: 2000 }
          );

          setTimeout(() => router.push("/information/order"), 2000);
        } catch (error) {
          console.error("error >> ", error);

          if (error instanceof AxiosError) {
            toast.error(error.response?.data.message);
          } else {
            toast.error("์•Œ ์ˆ˜ ์—†๋Š” ๋ฌธ์ œ๋กœ ์ธํ•ด ๊ฒฐ์ œ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.");
          }
        }
      } else {
        toast.error("๊ฒฐ์ œ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. " + rsp.error_msg, {
          autoClose: 2000,
        });
      }
    };

    window.IMP.request_pay({ ...paymentData, pg, pay_method: "card" }, callback);
  },
  [router, paymentData]
);

ํ•˜์ง€๋งŒ ๋ชจ๋ฐ”์ผ ์›น์œผ๋กœ ์ ‘๊ทผํ•ด์„œ ๊ฒฐ์ œํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ํœด๋Œ€ํฐ์— ์นด์นด์˜คํ†ก์ด ์„ค์น˜๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์–ด๋–ค ์นด์นด์˜คํ†ก ์œ ์ €๊ฐ€ ๊ฒฐ์ œ๋ฅผ ์›ํ•˜๋Š”์ง€๋ฅผ ํŒ๋‹จํ•  ํ•„์š” ์—†์ด ์นด์นด์˜คํ†ก์œผ๋กœ ๋ฐ”๋กœ ๋“ค์–ด๊ฐ€์„œ ๋กœ๊ทธ์ธํ•œ ์œ ์ €์—๊ฒŒ ๊ฒฐ์ œ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
๊ทธ๋ ‡๊ฒŒ ๋˜๋ฉด ๋ธŒ๋ผ์šฐ์ €์—์„œ ์นด์นด์˜คํ†ก์œผ๋กœ ํ™”๋ฉด ์ „ํ™˜์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋ฉฐ ๊ฒฐ์ œ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ๋งˆ์น˜๋ฉด ๊ฒฐ์ œํ•  ๋•Œ ์ž…๋ ฅํ–ˆ๋˜ m_redirect_url์„ ์ด์šฉํ•ด์„œ ํ•ด๋‹น url ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ๋ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์œ„ ๋ฐ์Šคํฌํƒ‘ ์›น์—์„œ ๊ฒฐ์ œ๋ฅผ ์ง„ํ–‰ํ–ˆ์„ ๋•Œ์™€ ๋‹ค๋ฅด๊ฒŒ callback์ด ์‹คํ–‰๋˜์ง€ ์•Š์•„ DB์— ๊ฒฐ์ œ ์ •๋ณด๊ฐ€ ๋“ฑ๋ก๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋˜ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
( ๋‹ค๋ฅธ ๊ฒฐ์ œ ๋ฐฉ์‹์„ ์„ ํƒํ•ด์„œ ๋‹ค๋ฅธ ์›น์‚ฌ์ดํŠธ๋‚˜ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ๋˜์–ด์„œ ๊ฒฐ์ œ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค. )

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

์œ„์˜ ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•ด์„œ iamport์—์„œ ํŠน์ • ๊ฒฐ์ œ์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ป๋Š” ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ด์ค๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์ธ ํ๋ฆ„์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.
1. m_redirect_url์—์„œ ๊ฒฐ์ œ ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•  url๋ฅผ ์ง€์ •
2. iamport์—์„œ m_redirect_url๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ํ•  ๋•Œ imp_uid์™€ merchant_uid ๊ฐ’์„ query string์œผ๋กœ ์ฒจ๋ถ€
3. ํ•ด๋‹น url์—์„œ REST API KEY์™€ REST API SECRET KEY๋ฅผ ์ด์šฉํ•ด์„œ access_token์„ ์–ป์Œ
4. access_token๊ณผ imp_uid์„ ์ด์šฉํ•ด์„œ ํŠน์ • ๊ฒฐ์ œ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์–ป์Œ
5. ์–ป์€ ์ •๋ณด๋ฅผ ์ด์šฉํ•ด์„œ DB์— ๊ฒฐ์ œ ์ •๋ณด๋ฅผ ๊ธฐ๋กํ•จ
6. ๊ฒฐ์ œ ์ •๋ณด ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ํ•จ

๊ณต์‹ ํŽ˜์ด์ง€์— ์œ„์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์„ค๋ช…์ด ๋งค์šฐ ์ž˜๋˜์–ด ์žˆ์–ด์„œ ๊ตณ์ด ์ฝ”๋“œ ์˜ˆ์‹œ๋ฅผ ์ž‘์„ฑํ•˜์ง€๋Š” ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค.

๋‹ค๋งŒ ํ† ํฐ๊ณผ ๊ฒฐ์ œ์ •๋ณด๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ํƒ€์ž…๊ณผ api ์š”์ฒญ ํ•จ์ˆ˜๋“ค์„ ์ฒจ๋ถ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์œผ๋กœ custom_data๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” string ํƒ€์ž…์ด๋ฏ€๋กœ JSON.parse()๋ฅผ ์ด์šฉํ•ด์„œ ํŒŒ์‹ฑํ•˜๊ณ  ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ํ† ํฐ๊ณผ ๊ฒฐ์ œ ์ •๋ณด๋ฅผ ์–ป๋Š” ํ•จ์ˆ˜
import axios from "axios";

// type
import type {
  // IamportGetTokenBody,
  IamportGetTokenResponse,
  IamportGetPaymentDataBody,
  IamportGetPaymentDataResponse,
} from "@src/types";

const iamportInstance = axios.create({
  baseURL: "https://api.iamport.kr",
  timeout: 10000,
});

/**
 * 2022/09/14 - iamport ์—‘์„ธ์Šค ํ† ํฐ ๋ฐœ๊ธ‰ ๋ฐ›๊ธฐ - by 1-blue
 * @returns ์—‘์„ธ์Šค ํ† ํฐ
 */
const apiGetToken = () =>
  iamportInstance.post<IamportGetTokenResponse>(
    `/users/getToken`,
    {
      imp_key: process.env.IAMPORT_REST_API_KEY,
      imp_secret: process.env.IAMPORT_REST_API_SECRET,
    },
    {
      headers: {
        "Content-Type": "application/json",
      },
    }
  );

/**
 * 2022/09/14 - iamport ์—‘์„ธ์Šค ํ† ํฐ์„ ์ด์šฉํ•ด ๊ฒฐ์ œ ์ •๋ณด ์กฐํšŒ - by 1-blue
 * @returns ๊ฒฐ์ œ ์ •๋ณด
 */
const apiGetPaymentData = ({
  imp_uid,
  access_token,
}: IamportGetPaymentDataBody) =>
  iamportInstance.get<IamportGetPaymentDataResponse>(`/payments/${imp_uid}`, {
    headers: { Authorization: access_token },
  });

/**
 * 2022/09/14 - iamport ๊ด€๋ จ api ์š”์ฒญ ๊ฐ์ฒด - by 1-blue
 */
const iamportService = {
  apiGetToken,
  apiGetPaymentData,
};

export default iamportService;
  • type ( ์œ„์—์„œ importํ•œ ํƒ€์ž… )
type RequestPaymentData = {
  amount: number;
  apply_num: string;
  bank_code: string | null;
  bank_name: string | null;
  buyer_addr: string;
  buyer_email: string;
  buyer_name: string;
  buyer_postcode: string;
  buyer_tel: string;
  cancel_amount: number;
  cancel_history: [];
  cancel_reason: string | null;
  cancel_receipt_urls: [];
  cancelled_at: number;
  card_code: string | null;
  card_name: string | null;
  card_number: string | null;
  card_quota: number;
  card_type: string | null;
  cash_receipt_issued: false;
  channel: string;
  currency: string;
  custom_data: string;
  customer_uid: string | null;
  customer_uid_usage: string | null;
  emb_pg_provider: string | null;
  escrow: false;
  fail_reason: string | null;
  failed_at: number;
  imp_uid: string;
  merchant_uid: string;
  name: string;
  paid_at: number;
  pay_method: string;
  pg_id: string;
  pg_provider: string;
  pg_tid: string;
  receipt_url: string;
  started_at: number;
  status: string;
  user_agent: string;
  vbank_code: string | null;
  vbank_date: number;
  vbank_holder: string | null;
  vbank_issued_at: number;
  vbank_name: string | null;
  vbank_num: string | null;
};

/**
 * 2022/09/14 - iamport ์—‘์„ธ์Šค ํ† ํฐ ์š”์ฒญ ์†ก์‹  ํƒ€์ž… - by 1-blue
 */
export type IamportGetTokenBody = {};
/**
 * 2022/09/14 - iamport ์—‘์„ธ์Šค ํ† ํฐ ์š”์ฒญ ์ˆ˜์‹  ํƒ€์ž… - by 1-blue
 */
export type IamportGetTokenResponse = { response: { access_token: string } };
/**
 * 2022/09/14 - iamport ๊ฒฐ์ œ ์ •๋ณด ์š”์ฒญ ์†ก์‹  ํƒ€์ž… - by 1-blue
 */
export type IamportGetPaymentDataBody = {
  imp_uid: string;
  access_token: string;
};
/**
 * 2022/09/14 - iamport ๊ฒฐ์ œ ์ •๋ณด ์š”์ฒญ ์ˆ˜์‹  ํƒ€์ž… - by 1-blue
 */
export type IamportGetPaymentDataResponse = { response: RequestPaymentData };

0๊ฐœ์˜ ๋Œ“๊ธ€