통합 결제 시스템과 PG사 연동 라이브러리 모음.
본 저장소 @samchon/payments
는 통합 결제 시스템과 PG 사 연동 라이브러리를 모아놓은 저장소이다. 여기서 말하는 통합 결제 시스템이란, 아임포트와 토스 페이먼츠 등의 PG사들을 일괄 관리할 수 있는 시스템을 뜻한다. 더하여 통합 결제 시스템은 MSA (Micro Service Architecture) 를 고려하여 설계된 프로젝트로써, 귀하의 서비스 중 결제 부문만을 따로이 분리하여 관리할 수 있게 해 준다.
더하여 통합 결제 시스템이 연동하게 될 PG 사들의 목업 서버를 구현, 이들을 통하여 백엔드 수준의 테스트 자동화 프로그램을 구성할 수 있도록 해준다. 이는 결제 PG 사들이 프론트 어플리케이션과 연동한 수기 테스트를 필요로 하기에, 통합 결제 시스템이 자동화된 테스트 프로그램을 구성할 수 없음에 따른, 테스트 커버리지의 하락을 해결하기 위함이다.
@samchon/payment-backend
: 통합 결제 시스템fake-iamport-server
: 아임포트 목업 서버fake-toss-payments-server
: 토스 페이먼츠 목업 서버
더불어 본 저장소 @samchon/payments
는 통합 결제 시스템 및 각각의 PG 사와 연동할 수 있는 SDK (Software Development Kit) 라이브러리들을 제공한다. 귀하는 이를 통하여 통합 결제 시스템 및 PG 사 서버와 매우 간편하게, 또한 타입 안전하게 연동할 수 있다. 아래 예제 코드 또한, 이러한 SDK 를 활용한 간편하고 타입 안전한 개발 사례의 하나.
@samchon/payment-api
: 통합 결제 시스템 연동 APIiamport-server-api
: 아임포트 서버 연동 APItoss-payments-server-api
: 토스 페이먼츠 서버 연동 API
import { TestValidator } from "@nestia/e2e";
import api from "@samchon/payment-api";
import { IPaymentHistory } from "@samchon/payment-api/lib/structures/payments/IPaymentHistory";
import { IPaymentWebhookHistory } from "@samchon/payment-api/lib/structures/payments/IPaymentWebhookHistory";
import toss from "toss-payments-server-api";
import { ITossPayment } from "toss-payments-server-api/lib/structures/ITossPayment";
import { sleep_for } from "tstl/thread/global";
import typia from "typia";
import { v4 } from "uuid";
import { PaymentConfiguration } from "../../../src";
import { FakePaymentStorage } from "../../../src/providers/payments/FakePaymentStorage";
import { TossAsset } from "../../../src/services/toss/TossAsset";
export async function test_api_toss_vbank_payment(
connection: api.IConnection,
): Promise<IPaymentHistory> {
//----
// 결제의 원천이 되는 주문 정보
//----
/**
* 귀하의 백엔드 서버가 발행한 주문 ID.
*/
const yourOrderId: string = v4();
/**
* 주문 금액.
*/
const yourOrderPrice: number = 19_900;
/* -----------------------------------------------------------
결제 내역 등록
----------------------------------------------------------- */
/**
* 토스 페이먼츠 시뮬레이션
*
* 고객이 프론트 어플리케이션에서, 토스 페이먼츠가 제공하는 팝업 창을 이용, 카드 결제를
* 하는 상황을 시뮬레이션 한다. 고객이 카드 결제를 마치거든, 프론트 어플리케이션에
* {@link ITossPayment.paymentKey} 가 전달된다.
*
* 이 {@link ITossPayment.paymentKey} 와 귀하의 백엔드에서 직접 생성한
* {@link ITossPayment.orderId yourOrderId} 를 잘 기억해두었다가, 이를 다음 단계인
* {@link IPaymentHistory} 등록에 사용하도록 하자.
*/
const payment: ITossPayment =
await toss.functional.v1.virtual_accounts.create(
await TossAsset.connection("test-toss-payments-create-id"),
{
// 가상 계좌 정보
method: "virtual-account",
bank: "신한",
customerName: "Samchon",
// 주문 정보
orderId: yourOrderId,
orderName: "something",
amount: yourOrderPrice,
// 고의 미승인 처리
__approved: false,
},
);
typia.assert(payment);
/**
* 웹훅 URL 설정하기.
*
* 웹훅 URL 을 테스트용 API 주소, internal.webhook 으로 설정.
*/
const webhook_url: string = `http://127.0.0.1:${PaymentConfiguration.API_PORT()}${
api.functional.payments.internal.webhook.METADATA.path
}`;
/**
* 결제 이력 등록하기.
*
* 앞서 토스 페이먼츠의 팝업 창을 이용하여 가상 계좌 결제를 진행하고 발급받은
* {@link ITossPayment.paymentKey}, 그리고 귀하의 백엔드에서 직접 생성한
* {@link ITossPayment.orderId yourOrderId} 를 각각 {@link IPaymentVendor.uid} 와
* {@link IPaymentSource.id} 로 할당하여 {@link IPaymentReservation} 레코드를
* 발행한다.
*
* 참고로 결제 이력을 등록할 때 반드시 비밀번호를 설정해야 하는데, 향후 결제 이력을
* 조회할 때 필요하니, 이를 반드시 귀하의 백엔드 서버에 저장해두도록 한다.
*/
const history: IPaymentHistory =
await api.functional.payments.histories.create(connection, {
vendor: {
code: "toss.payments",
store_id: "test-toss-payments-create-id",
uid: payment.paymentKey,
},
source: {
schema: "some-schema",
table: "some-table",
id: yourOrderId,
},
webhook_url, // 테스트용 웹훅 URL
price: yourOrderPrice,
password: "some-password",
});
typia.assert(history);
/* -----------------------------------------------------------
웹훅 이벤트 리스닝
----------------------------------------------------------- */
/**
* 입금 시뮬레이션하기.
*
* 고객이 자신 앞을 발급된 계좌에, 결제 금액을 입금하는 상황 시뮬레이션.
*/
await toss.functional.internal.deposit(
await TossAsset.connection("test-toss-payments-create-id"),
payment.paymentKey,
);
// 웹훅 이벤트가 귀하의 백엔드 서버로 전달되기를 기다림.
await sleep_for(1_000);
/**
* 웹흑 리스닝 시뮬레이션.
*
* 귀하의 백엔드 서버가 웹훅 이벤트를 수신한 상황을 가정한다.
*/
const webhook: IPaymentWebhookHistory | undefined =
FakePaymentStorage.webhooks.back();
// 이하 웹훅 데이터를 통한 입금 여부 검증
TestValidator.equals("webhook")(!!webhook)(true);
TestValidator.equals("history.id")(history.id)(webhook?.current.id);
TestValidator.equals("paid_at")(!!webhook?.previous.paid_at)(false);
TestValidator.equals("paid_at")(!!webhook?.current.paid_at)(true);
// 웹훅 데이터 삭제
FakePaymentStorage.webhooks.pop_back();
return history;
}
본 통합 결제 시스템은 NodeJS 및 Postgres 가 필요하다.
통합 결제 시스템의 설치 매뉴얼을 읽고, 이를 잘 따라하도록 하자.