01 SOFTWARE
SDK

Server API

서버 전용 API 메서드

Server API

ServerClient는 서버 환경에서만 사용할 수 있는 특별한 API 메서드를 제공합니다.

Server API는 secretKey가 필요하며, 브라우저에서 절대 노출되어서는 안 됩니다.

클라이언트 생성

import { createServerClient } from '@01.software/sdk'

const serverClient = createServerClient({
  clientKey: process.env.NEXT_PUBLIC_SOFTWARE_CLIENT_KEY!,
  secretKey: process.env.SOFTWARE_SECRET_KEY!,
})

주문 API

createOrder()

새 주문을 생성합니다.

import { generateOrderNumber } from '@01.software/sdk'

const order = await serverClient.api.createOrder({
  // 필수 필드
  paymentId: 'pay_123456',
  orderNumber: generateOrderNumber(),
  email: 'customer@example.com',
  orderProducts: [
    {
      product: 'product_id',
      variant: 'variant_id',
      quantity: 2,
      price: 10000,
    }
  ],
  totalAmount: 20000,

  // 선택 필드
  customerName: '홍길동',
  phone: '010-1234-5678',
  shippingAddress: {
    address: '서울시 강남구 테헤란로 123',
    zipCode: '06234',
  },
  memo: '문 앞에 놓아주세요',
})

console.log(order.id)
console.log(order.orderNumber)

파라미터

필드타입필수설명
paymentIdstringO결제 ID (PG사에서 발급)
orderNumberstringO주문 번호
emailstringO고객 이메일
orderProductsarrayO주문 상품 목록
totalAmountnumberO총 결제 금액
customerNamestringX고객 이름
phonestringX연락처
shippingAddressobjectX배송 주소
memostringX배송 메모

orderProducts 항목

필드타입필수설명
productstringO상품 ID
variantstringX상품 변형 ID
quantitynumberO수량
pricenumberO단가

updateOrder()

기존 주문을 업데이트합니다.

await serverClient.api.updateOrder('order_id', {
  status: 'shipped',
  trackingNumber: '1234567890',
  shippedAt: new Date().toISOString(),
})

업데이트 가능한 필드

interface OrderUpdateData {
  status?: 'pending' | 'confirmed' | 'shipped' | 'delivered' | 'cancelled'
  trackingNumber?: string
  shippedAt?: string
  deliveredAt?: string
  cancelledAt?: string
  cancelReason?: string
}

주문 상태 흐름

pending → confirmed → shipped → delivered

cancelled

트랜잭션 API

updateTransaction()

결제 트랜잭션 상태를 업데이트합니다.

await serverClient.api.updateTransaction('transaction_id', {
  status: 'paid',
  paidAt: new Date().toISOString(),
})

트랜잭션 상태

상태설명
pending결제 대기
paid결제 완료
failed결제 실패
refunded환불 완료
partially_refunded부분 환불

유틸리티 함수

generateOrderNumber()

고유한 주문 번호를 생성합니다.

import { generateOrderNumber } from '@01.software/sdk'

const orderNumber = generateOrderNumber()
// 예: "ORD-20240115-A1B2C3D4"

formatOrderName()

주문 상품들을 요약 문자열로 변환합니다.

import { formatOrderName } from '@01.software/sdk'

const orderName = formatOrderName([
  { name: '아이폰 15', quantity: 1 },
  { name: '에어팟 프로', quantity: 2 },
])
// 결과: "아이폰 15 외 1건"

실전 예제

결제 완료 처리

app/api/payment/confirm/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { serverClient, generateOrderNumber } from '@01.software/sdk'

export async function POST(request: NextRequest) {
  const body = await request.json()

  try {
    // 1. PG사 결제 검증
    const paymentResult = await verifyPayment(body.paymentKey)

    if (!paymentResult.success) {
      return NextResponse.json(
        { error: '결제 검증 실패' },
        { status: 400 }
      )
    }

    // 2. 주문 생성
    const order = await serverClient.api.createOrder({
      paymentId: body.paymentKey,
      orderNumber: generateOrderNumber(),
      email: body.email,
      customerName: body.customerName,
      phone: body.phone,
      orderProducts: body.products.map(p => ({
        product: p.productId,
        variant: p.variantId,
        quantity: p.quantity,
        price: p.price,
      })),
      totalAmount: paymentResult.amount,
      shippingAddress: body.shippingAddress,
    })

    // 3. 트랜잭션 상태 업데이트 (자동 생성된 경우)
    if (order.transaction) {
      await serverClient.api.updateTransaction(order.transaction, {
        status: 'paid',
        paidAt: new Date().toISOString(),
      })
    }

    return NextResponse.json({
      success: true,
      orderId: order.id,
      orderNumber: order.orderNumber,
    })

  } catch (error) {
    console.error('Order creation failed:', error)
    return NextResponse.json(
      { error: '주문 생성 실패' },
      { status: 500 }
    )
  }
}

배송 상태 업데이트

app/api/orders/[id]/ship/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { serverClient } from '@01.software/sdk'

export async function POST(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  const body = await request.json()

  try {
    await serverClient.api.updateOrder(params.id, {
      status: 'shipped',
      trackingNumber: body.trackingNumber,
      shippedAt: new Date().toISOString(),
    })

    // 알림 발송
    await sendShippingNotification(params.id, body.trackingNumber)

    return NextResponse.json({ success: true })

  } catch (error) {
    return NextResponse.json(
      { error: '배송 상태 업데이트 실패' },
      { status: 500 }
    )
  }
}

주문 취소

app/api/orders/[id]/cancel/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { serverClient } from '@01.software/sdk'

export async function POST(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  const body = await request.json()

  try {
    // 1. 주문 취소
    await serverClient.api.updateOrder(params.id, {
      status: 'cancelled',
      cancelledAt: new Date().toISOString(),
      cancelReason: body.reason,
    })

    // 2. 결제 취소 (PG사)
    const order = await serverClient.from('orders').findById(params.id)
    if (order.data?.paymentId) {
      await cancelPayment(order.data.paymentId)
    }

    // 3. 트랜잭션 상태 업데이트
    if (order.data?.transaction) {
      await serverClient.api.updateTransaction(order.data.transaction, {
        status: 'refunded',
      })
    }

    return NextResponse.json({ success: true })

  } catch (error) {
    return NextResponse.json(
      { error: '주문 취소 실패' },
      { status: 500 }
    )
  }
}

Server Action 예제

app/actions/orders.ts
'use server'

import { serverClient, generateOrderNumber } from '@01.software/sdk'
import { revalidatePath } from 'next/cache'

export async function createOrder(formData: FormData) {
  const email = formData.get('email') as string
  const products = JSON.parse(formData.get('products') as string)

  const order = await serverClient.api.createOrder({
    paymentId: formData.get('paymentId') as string,
    orderNumber: generateOrderNumber(),
    email,
    customerName: formData.get('customerName') as string,
    orderProducts: products,
    totalAmount: products.reduce(
      (sum: number, p: any) => sum + p.price * p.quantity,
      0
    ),
  })

  revalidatePath('/orders')

  return {
    success: true,
    orderId: order.id,
    orderNumber: order.orderNumber,
  }
}

export async function updateOrderStatus(orderId: string, status: string) {
  await serverClient.api.updateOrder(orderId, {
    status: status as any,
  })

  revalidatePath('/orders')
  revalidatePath(`/orders/${orderId}`)
}

보안 주의사항

Secret Key 보안

  1. 환경 변수 사용: Secret Key는 반드시 환경 변수로 관리하세요.
# .env.local
SOFTWARE_SECRET_KEY=your_secret_key_here
  1. 서버에서만 사용: Secret Key가 포함된 코드는 서버 환경에서만 실행되어야 합니다.
// ✅ 올바른 사용: Server Component, API Route, Server Action
// ❌ 잘못된 사용: Client Component, 브라우저 코드
  1. Git 제외: .env.local 파일은 반드시 .gitignore에 추가하세요.
.env.local
.env*.local

다음 단계

On this page