토스페이먼츠 결제 위젯 연동하기

April·2023년 7월 30일
2

React🚀

목록 보기
43/43
post-thumbnail
post-custom-banner

들어가며,

우리 회사 서비스는 현재 국내에서 서비스하고 있는데 글로벌 런칭 준비중이라 그에 맞춰 결제 서비스도 해외결제가 가능하도록 바꾸는 스프린트가 진행됐다💪

해외결제는 토스페이먼츠를 붙히면 됐고 거의 다 완성되어갈 즈음..
국내결제도 토스페이먼츠로 변경해야한다는 것을 알았다..😳😳(지금은 아임포트..)

구현했던 컴포넌트를 공통으로 사용하고자 코드를 수정하고..
아임포트에서 점진적으로 토스페이먼츠로 전환하는 코드를 추가하고..
그 과정에서 해외결제에 페이팔 + 해외카드를 추가하는 과정이.. 생각보다 순탄하지는 않았던..🫠🫠(이 과정에서 토스페이먼츠에서 운영하는 디코에서 많은 도움을 받았다!!👍👍)

어찌저찌 QA까지 진행했으나.. 가장 중요한 계약이 완료되지 않아.. 결국 완성된 코드는.. PR만 올라가있는 상태다..😕
(계약 시작한건 5월말인데.. 8월초인 지금도.. 이 코드를 질질 끌고 가고 있..😮‍💨😮‍💨)

일단, 여기까지 진행한 내용을 또.. 기록해보자!!!!!🔥🔥


토스페이먼츠 결제 위젯 연동하기

1. SDK 설치

yarn add @tosspayments/payment-widget-sdk
# 또는
npm i @tosspayments/payment-widget-sdk

2. 결제 위젯 띄우기

  • SDK 패키지에서 loadPaymentWidgetPaymentWidget인스턴스를 반환하기
  • loadPaymentWidgetclientKeycustomerKey를 파라미터는 받는데
    • clientKey는 위젯을 렌더링하는 상점을 식별하고
    • customerKey로 결제 고객을 식별한다
      • 비회원 결제라면 @tosspayments/payment-widget-sdk에서 ANONYMOUS를 불러와서 사용할 수 있는데, 우리 서비스는 로그인 없이는 결제할 수 없기에 별도로 지정하진 않았다
  • 렌더링 할 위치에 <div id="payment-widget" />를 추가하기
  • loadPaymentWidget()을 호출해서 인스턴스를 생성하고
  • renderPaymentMethods()로 결제 위젯을 렌더링하고 useRef를 사용해서 인스턴스를 저장하기
    • 우리 서비스는 결제 위젯을 국내/해외 결제에 공통으로 사용되는데, 해외결제도 구현해야 했기에 currencycountry를 추가했다
      • 로그인 유저에 따라 해당하는 값을 서버에서 받아온다
import React, { useEffect, useRef } from "react"
import { loadPaymentWidget, PaymentWidgetInstance } from "@tosspayments/payment-widget-sdk"
import useTranslation from 'next-translate/useTranslation';
 

const TossPayments = () => {
  const { t } = useTranslation();
  const paymentWidgetRef = useRef<PaymentWidgetInstance | null>(null);
  const paymentMethodsWidgetRef = useRef<ReturnType<
    PaymentWidgetInstance['renderPaymentMethods']
  > | null>(null);
  
  useEffect(() => {
    const TOSS_CLIENT_KEY = isDevelopment()
            ? process.env.NEXT_PUBLIC_TOSS_CLIENT_KEY_DEV
            : process.env.NEXT_PUBLIC_TOSS_CLIENT_KEY

    (async () => {
      const paymentWidget = await loadPaymentWidget(
        TOSS_CLIENT_KEY,
        String(userData?.getUser.userId),
      );

      const paymentMethodsWidget = paymentWidget.renderPaymentMethods(
        '#payment-widget',
        {
          value: paymentInfo.price,
          currency: listCoin?.listInappCoin[0].currency, // 'USD' 해외결제는 필수,
          country: listCoin?.listInappCoin[0].countryCode, //'US' 해외결제는 필수,
        },
      );

      paymentWidgetRef.current = paymentWidget; // 결제 위젯 인스턴스 저장
      paymentMethodsWidgetRef.current = paymentMethodsWidget; // 결제 방법을 가져오거나 커스텀 결제 수단을 추가할 때 사용

    })();
  }, []);


  return (
    <section css={container}>
        <div id="payment-widget" />
        <button type='button' onClick={onRequestPayment}>{`${t('payment_screen_description_price_monetary_unit', {
            value: paymentInfo?.price ?? 0,
          })} ${t('payment_screen_button_pay')}`}</button>
    </section>
  );
};

3. 결제하는 함수 구현하기

  • ref에 저장했던 결제 위젯 인스턴스에서 결제를 요청하는 requestPayment()로 결제 함수 구현하기
  • 결제 성공하면 URL의 네가지 파라미터가 전달되는데 쿼리를 추가했다
    • http://{ORIGIN}/complete/tosspayments?paymentType=NORMAL&orderId=RUxTyT8ajvSX2xDXKnybC&amount=664.98
      • paymentKey: 결제의 키값.
      • orderId: 상점에서 발급한 주문 건 ID. requestPayment()에 담아 보낸 값.
      • amount: 실제로 결제된 금액
      • paymentType: 결제 유형. NORMAL | BRANDPAY
  • 결제 실패시에도 쿼리에 파라미터가 전달된다
    • https://{ORIGIN}/complete/tosspayments?code={ERROR_CODE}&message={ERROR_MESSAGE}&orderId={ORDER_ID}
    • 에러 코드는 여기서 확인할 수 있다

  const onRequestPayment = async () => {
    // 결제 성공하면 필요한 쿼리를 추가할 수 있다
    const successQuery = `price=${paymentInfo?.price}&coin=${paymentInfo?.amount}&method=${selectedPaymentMethod}`;
    const successUrl = `${window.location.origin}/payment/complete/toss_payments?${successQuery}`
    const failUrl = `${window.location.origin}/payment/complete/toss_payments`

    await paymentWidgetRef.current?.requestPayment({
      orderId: paymentInfo?.orderId,
      orderName: paymentInfo?.name,
      successUrl,
      failUrl,
      customerEmail: userData?.getUser.email,
      customerName: userData?.getUser.name,
      products: [
        {
          name: paymentInfo?.name,
          quantity: 1,
          unitAmount: paymentInfo?.price,
          currency: listCoin?.listInappCoin[0].currency,
          description: userData?.getUser.name,
        },
      ],
    });
  };

4. 커스텀 결제수단 추가하기

  • 결제 위젯에 추가로 결제 경로를 설정할 수 있다

  • 상점 관리자에서 UI를 추가한다

  • 설정한 key값 기준으로 결제 코드를 추가한다

    • 해외결제를 지원할 때 페이팔 외에 다국어 결제창을 띄우기 위한 코드를 추가했다

      paymentMethodsWidgetRef.current?.on('customRequest', paymentMethodKey => {
          if (paymentMethodKey === 'card') { // 상점관리자에서 설정한 key값
            // 커스텀 결제수단으로 결제하는 코드
          }
      })
  • 추가했다가.. 1~2개월 뒤에 페이팔에서 해외카드도 지원될 예정이라고 들어서 우선 페이팔 먼저 오픈하기로 했다!




구현은 끝났고.. QA도 마쳤는데.. 토스페이먼츠와 계약이 아직 진행중이라.. 실제 결제가 진행되는 것은 조금 더 이후가 될 것 같다..🫠🫠





profile
🚀 내가 보려고 쓰는 기술블로그
post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 7월 30일

유익한 글이었습니다.

답글 달기