Skip to Content
개발자SDKJavaScript

JavaScript/TypeScript SDK

@payri/sdk는 의존성이 없고 Node.js 20+, 브라우저, 엣지 런타임에서 동작해요.
NestJS를 쓴다면 모듈·가드를 제공하는 @payri/nest를 함께 설치해요.

npm install @payri/sdk

전체 실행 가능한 예제(Express·Next.js·NestJS)는 sdk-js 저장소의 examples 에 있어요.

서버에서 시작하기

서버에서는 Payri 클라이언트에 secret 키를 넣어 사용해요.

import { Payri } from '@payri/sdk' const payri = new Payri({ apiKey: process.env.PAYRI_API_KEY! }) const products = await payri.products.list({ page: 1, limit: 20 }) const result = await payri.licenses.verify({ key: 'XXXX-YYYY-ZZZZ' }) // 체크아웃 세션 — 반환된 url로 구매자를 리다이렉트해요. const session = await payri.checkout.create({ linkId: 'link_abc' })

API 키는 서버 전용 비밀키예요.
브라우저·모바일 앱 번들에 포함하거나 NEXT_PUBLIC_ 같은 공개 환경변수에 넣으면 안 돼요.
브라우저에서는 키가 필요 없는 PayriPublic을 쓰세요.

생성자 옵션

옵션기본값설명
apiKey(필수)서버 전용 secret 키 (key_live_* / key_test_*)
baseUrlhttps://api.payri.krAPI base URL. 스테이징 등 다른 환경을 겨눌 때만 변경해요
timeoutMs30000요청 타임아웃(ms)
maxRetries0429/5xx 응답 시 재시도 횟수
fetch전역 fetch커스텀 fetch 구현 (테스트·엣지 런타임용)

리소스

리소스메서드REST 문서
payri.productslist() · get(id)Products
payri.orderslist() · get(id) · refund(id, params)Orders · Refunds
payri.subscriptionslist() · get(id) · cancel(id, params)Subscriptions
payri.checkoutcreate(params) · get(id)Checkout
payri.licensesverify(params) · activate(params) · deactivate(params)Licenses

브라우저에서 사용하기

PayriPublic은 API 키를 받지 않아서 브라우저(React/Next 클라이언트 컴포넌트 포함)에서 안전하게 쓸 수 있어요.
구매자가 이름·이메일 본인확인으로 자기 주문의 라이선스를 조회하는 용도예요.

import { PayriPublic } from '@payri/sdk' const pub = new PayriPublic() const license = await pub.lookupLicense('ord_123', { name: '홍길동', email: 'hong@example.com', }) if (!license) { // 주문이 없거나 이름·이메일이 일치하지 않는 경우 — 존재 여부는 알려주지 않아요. }

일치하지 않으면 에러 대신 null을 반환해요.
주문 존재 여부를 노출하지 않기 위한 동작이라, 실패 사유를 구분해 보여줄 수 없어요.

웹훅 검증

Webhooks.constructEventStandard Webhooks  서명을 검증하고 { type, data } 이벤트를 반환해요.
검증에는 원본(raw) 본문이 필요해요 — 프레임워크가 JSON으로 파싱한 객체를 다시 직렬화해 넘기면 서명이 깨져요.
검증 실패 시 PayriWebhookError를 던지고, reason으로 사유를 확인할 수 있어요.

Express

express.json()보다 먼저 해당 라우트만 express.raw()로 받아요.

import express from 'express' import { Webhooks, PayriWebhookError } from '@payri/sdk' const app = express() // 서명 검증엔 원본 바디가 필요해요 — express.json()보다 먼저 raw로 받아요. app.post('/webhooks/payri', express.raw({ type: 'application/json' }), async (req, res) => { try { const event = await Webhooks.constructEvent({ payload: req.body, headers: req.headers, secret: process.env.PAYRI_WEBHOOK_SECRET!, }) // TODO: webhook-id로 멱등 확인 후 event.type에 따라 처리 res.sendStatus(200) } catch (err) { if (err instanceof PayriWebhookError) { return res.status(400).json({ error: err.reason }) } throw err } }) app.use(express.json())

Next.js (App Router)

Route Handler에서 request.text()로 원본 본문을 읽어요.

// app/api/webhooks/payri/route.ts import { NextResponse } from 'next/server' import { Webhooks, PayriWebhookError } from '@payri/sdk' export async function POST(request: Request) { // request.json()으로 파싱하면 서명이 깨져요 — text()로 원본을 읽어요. const payload = await request.text() try { const event = await Webhooks.constructEvent({ payload, headers: Object.fromEntries(request.headers), secret: process.env.PAYRI_WEBHOOK_SECRET!, }) return NextResponse.json({ received: true }) } catch (err) { if (err instanceof PayriWebhookError) { return NextResponse.json({ error: err.reason }, { status: 400 }) } throw err } }

이벤트 종류와 페이로드 형식은 이벤트 종류에서 확인해요.

NestJS: @payri/nest

npm install @payri/nest @payri/sdk

웹훅 가드는 원본 바디가 필요해요.
반드시 NestFactory.create(AppModule, { rawBody: true })로 앱을 생성하세요.

모듈 등록

import { Module } from '@nestjs/common' import { PayriModule } from '@payri/nest' @Module({ imports: [ PayriModule.forRoot({ apiKey: process.env.PAYRI_API_KEY!, webhookSecret: process.env.PAYRI_WEBHOOK_SECRET, }), ], }) export class AppModule {}

기본으로 전역 모듈로 등록돼요 (global: false로 끌 수 있어요).
ConfigService 등에서 옵션을 읽어야 하면 forRootAsync({ useFactory, inject })를 사용해요.

PayriService 주입

import { Injectable } from '@nestjs/common' import { PayriService } from '@payri/nest' @Injectable() export class StoreService { constructor(private readonly payri: PayriService) {} listProducts() { return this.payri.products.list() } }

payri.products처럼 리소스를 바로 쓸 수 있고, 전체 클라이언트가 필요하면 payri.client로 접근해요.

웹훅 가드

PayriWebhookGuard가 서명을 검증하고, 검증된 이벤트를 @PayriEvent()로 핸들러에 주입해요.
검증에 실패하면 401을 반환해요.

import { Controller, Post, HttpCode, UseGuards } from '@nestjs/common' import { PayriWebhookGuard, PayriEvent } from '@payri/nest' import type { WebhookEvent } from '@payri/nest' @Controller('webhooks') export class WebhookController { @Post('payri') @HttpCode(200) @UseGuards(PayriWebhookGuard) handle(@PayriEvent() event: WebhookEvent) { // TODO: webhook-id로 멱등 확인 후 event.type에 따라 처리 return { received: true } } }

에러 처리

모든 에러는 PayriError를 상속해요. (에러 계층)

import { PayriApiError, PayriAuthError, PayriConnectionError } from '@payri/sdk' try { await payri.orders.get('ord_123') } catch (err) { if (err instanceof PayriAuthError) { // 401/403 — 키 누락·무효, 스코프 부족, IP 제한 } else if (err instanceof PayriApiError) { // 그 외 4xx/5xx — err.status, err.code, err.body 확인 } else if (err instanceof PayriConnectionError) { // 네트워크 실패·타임아웃 — 응답을 받지 못했어요 } }

PayriAuthErrorPayriApiError를 상속하므로 순서대로 검사해요.
에러 코드 목록은 에러 코드에서 확인해요.

다음 단계

웹훅 이벤트 종류

구독할 수 있는 이벤트와 페이로드 형식을 확인해요.

재전송과 멱등성

중복 이벤트를 안전하게 처리해요.

라이선스 연동

키 발급 방식을 내 시스템에 맞게 연결해요.

Last updated on