토스 결제 모듈 구현하기 - 2(Nest.js)

jonghyun.log·2022년 6월 8일
2

결제

목록 보기
3/3
post-thumbnail

실제 코드로 결제 토스 api 사용하기

이전 글에서 발급받은 api키를 가지고 실제로 코드로 api를 사용해 보도록 하겠습니다.

먼저, 토스 깃허브에 올라온 예시코드를 한번 보면

토스 예시 코드

router.get("/success", function (req, res) {
  got
    .post("https://api.tosspayments.com/v1/payments/" + req.query.paymentKey, {
      headers: {
        Authorization:
          "Basic " + Buffer.from(secretKey + ":").toString("base64"),
        "Content-Type": "application/json",
      },
      json: {
        orderId: req.query.orderId,
        amount: req.query.amount,
      },
      responseType: "json",
    })
    .then(function (response) {
      console.log(response.body);
      // TODO: 구매 완료 비즈니스 로직 구현

      res.render("success", {
        title: "성공적으로 구매했습니다",
        amount: response.body.totalAmount,
      });
    })
    .catch(function (error) {
      res.redirect(
        `/fail?code=${error.response?.body?.code}&message=${error.response?.body?.message}`
      );
    });
});

토스 서버에 보낼 정보로 헤더에 secretKey(이전 글에서 발급받은 키 값)와 paymentKey , 그리고 json으로 orderId, amount 를 보내줘야 한다고 보여주고 있습니다.

여기서 paymentKey , orderId, amount 는 각각 주문키, 주문번호, 주문수량 으로
프론트에서 카드 인증 후, 결제를 진행 할 때 가맹점 서버(여기서는 우리가 만든 서버)로 전달 받은 정보로 벡엔드는 단지 이것을 알맞은 형식에 맞춰 토스 서버에 보내주기만 하면 됩니다.

위의 코드를 조금 수정해서 nest.js로 구현해보도록 하겠습니다.

구현하기 전에 위에서 언급한 대로 프론트에서 paymentKey, orderId, amount 를 다음과 같이 전달을 받아야 하는 상황입니다.

{
  "paymentKey": "페이먼트 키값",
  "orderId": "오더아이디 값"
  "amount": "어마운트 값"
 }

제가 구현한 프로젝트에서는 프론트와 JSON 객체가 아닌 쿼리로 정보를 주고 받는다고 설정했기 때문에 그에 맞춰 개발을 하겠습니다.
(다른 방식으로 프론트한테 데이터를 받는 경우 그 방식에 맞게 커스텀 해주세요)

진짜 구현 시작

먼저 결제를 담당할 pay 모듈을 만들고 그 안에 pay.controller.ts 파일, pay.service.ts 파일을 만들어 줍니다.

//pay.controller.ts
@Controller('pay')
export class PayController {
  constructor(private payService: PayService) {}

  /**토스 페이먼츠 api에 결제 요청하는 함수 */
  @Post('/success')
  successPay(@Body() payquerydto: PayQueryDto) {
    console.log(payquerydto);
    return this.payService.successPay(payquerydto);
  }
}

그리고 쿼리로 전달받은 데이터를 처리하기 위해 payQueryDto.dto.ts 파일을 만들어 줍니다.

import { IsNotEmpty } from 'class-validator';

export class PayQueryDto {
  @IsNotEmpty()
  paymentKey: string;

  @IsNotEmpty()
  orderId: string;

  @IsNotEmpty()
  amount: number;
}

이제 전달받은 정보로 pay.service.ts 파일에서 토스 페이먼츠에 전송해 주면 됩니다.

import { Injectable } from '@nestjs/common';
import axios from 'axios';
import { PayQueryDto } from './dto/payQuery.dto';

@Injectable()
export class PayService {
  tossUrl = 'https://api.tosspayments.com/v1/payments/';
  async successPay(payquerydto: PayQueryDto) {
    console.log('payquerydto', payquerydto);
    const { orderId, amount, paymentKey } = payquerydto;
    try {
      const pay = await axios.post(
        this.tossUrl + paymentKey,
        {
          orderId: orderId,
          amount: amount,
        },
        {
          headers: {
            Authorization:
              'Basic ' +
              Buffer.from(process.env.TOSS_TEST_KEY + ':').toString('base64'),
            'Content-Type': 'application/json',
          },
        },
      );
      console.log('pay', pay);
      return {
        title: '성공적으로 구매했습니다',
        // amount: response.body.totalAmount,
      };
    } catch (e) {
      console.log('토스 페이먼츠 에러 코드', e);
    }
  }
}

위의 코드들을 거쳐 성공적으로 통신을 하게 되면 토스 서버로 부터 응답으로 다음과 같은 JSON 객체를 받게 됩니다.

data: {
   mId: 'tvivarepublica',
   transactionKey: 'BDBF4B020B706CB18803580E3E796FEE',
   lastTransactionKey: 'BDBF4B020B706CB18803580E3E796FEE',
   paymentKey: '9xMljweGQBN5OWRapdA8d1WXJBGZP3o1zEqZKLPbmD70vk4y',
   orderId: 'brL9yXo4bVrpAHe7p5s1233',
   orderName: '토스 티셔츠 외 2건!',
   status: 'DONE',
   requestedAt: '2022-06-03T21:40:53+09:00',
   approvedAt: '2022-06-03T21:41:23+09:00',
   useEscrow: false,
   cultureExpense: false,
   card: {
   company: '',
   number: '',
   installmentPlanMonths: 0,
   isInterestFree: false,![](https://velog.velcdn.com/images/jonghyun3668/post/16b7b8fe-5772-4a7c-9b25-01e28b61139c/image.png)

   interestPayer: null,
   approveNo: '03094123',
   useCardPoint: false,
   cardType: '미확인',
   ownerType: '미확인',
   acquireStatus: 'READY',
   receiptUrl: 'https://dashboard.tosspayments.com/sales-slip?transactionId=8evGW0VSjmvK%2BI8plQVLFy9Uyq7Os8oXgn3bpQZiyv7fVGiQeuw%2Fn7%2BHNJJfafMJ&ref=PX',
   provider: '토스결제',
   amount: 0
 },

저번 글에서도 언급했지만
혹시나 배포환경에서 사업자 등록을 통해 발급받은 테스트 키나 라이브 키를 사용하지 않으면
401 에러를 만날 수도 있으니 혹시 이런 에러를 만났다면 사용한 키를 확인해 보시기 바랍니다.

마치며

이렇게 위에서 결제한 테스트 내역은 토스 페이먼츠 개발 테스트 -> 테스트 거래내역
메뉴에서 확인해 볼 수 있습니다.

테스트가 성공적으로 된 걸 보면 기부니가 좋다 이말이야~

사실 실제 코드로 구현하는 과정은 그리 어렵지 않은데
사실상 문서를 읽고 어떤 구조로 돌아가는지를 이해하는데 애를 많이 먹었습니다.
통신에는 성공했으나 실제 서비스를 하기 위해서는 토스 서버로 부터 받은 데이터를 가지고 디비에 잘 저장하고
각종 에러 처리를 해주고 응답해주는 과정을 더 만들어야 합니다.
(사실상 이제 시작이라는 소리 ㅋㅋ 끝난줄 알았지? 응~ 이제 지옥 시작이야~)

1개의 댓글

comment-user-thumbnail
2023년 7월 21일

이해하기 쉽게 설명해놓으신 글 잘 읽었습니다. ;-)

답글 달기