JSPM

@openmkt/payment-sdk

1.1.0
    • ESM via JSPM
    • ES Module Entrypoint
    • Export Map
    • Keywords
    • License
    • Repository URL
    • TypeScript Types
    • README
    • Created
    • Published
    • Downloads 11
    • Score
      100M100P100Q71214F
    • License MIT

    PortOne V2 기반 서버사이드 결제 SDK — 결제·취소·조회·빌링키·본인인증·ECDSA 보안

    Package Exports

    • @openmkt/payment-sdk

    Readme

    @openmkt/payment-sdk

    PortOne V2 기반 서버사이드 결제 SDK
    HMAC-SHA256 / ECDSA P-256 서명 · 멱등성 · Replay 방지 · 본인인증

    npm Node.js

    설치

    npm install @openmkt/payment-sdk

    변경 이력

    v1.1.0

    • ECDSA P-256 서명 지원 (ecdsaPrivateKey 옵션)
    • PaymentSDK.generateEcdsaKeyPair() 키 쌍 생성 유틸리티 추가
    • 보안 강화: Nonce TTL 10분 → 24시간, Webhook 이벤트 TTL 72시간

    v1.0.0

    • 최초 릴리스: 결제·취소·조회·빌링키·본인인증

    초기화

    기본 (HMAC-SHA256)

    import { PaymentSDK } from '@openmkt/payment-sdk';
    
    const sdk = new PaymentSDK({
      apiKey:        process.env.SDK_API_KEY!,
      signingSecret: process.env.SDK_SIGNING_SECRET!,
      baseUrl:       process.env.PAYMENT_SERVER_URL!,
    });

    고보안 (ECDSA P-256) — v1.1.0+

    import { PaymentSDK } from '@openmkt/payment-sdk';
    
    // 키 쌍 생성 (최초 1회 — 개인키는 안전하게 보관)
    const { privateKey, publicKey } = PaymentSDK.generateEcdsaKeyPair();
    // publicKey → 서버 Tenant.ecdsaPublicKey 에 등록
    
    const sdk = new PaymentSDK({
      apiKey:          process.env.SDK_API_KEY!,
      signingSecret:   process.env.SDK_SIGNING_SECRET!,
      baseUrl:         process.env.PAYMENT_SERVER_URL!,
      ecdsaPrivateKey: privateKey,   // ECDSA 모드 활성화
    });
    // 이후 모든 요청이 ECDSA P-256으로 서명됨
    // 개인키가 서버에 없으므로 시크릿 유출 시에도 위조 불가

    API 레퍼런스

    결제 생성 (checkout)

    const result = await sdk.checkout({
      provider:      'portone',
      pgProvider:    'KAKAOPAY',   // SMARTRO_V2 | INICIS | KAKAOPAY | KCP | TOSSPAYMENTS
      orderId:       'order001',   // 영문+숫자만 (특수문자 불가)
      amount:        15000,        // 원 단위 정수
      currency:      'KRW',
      customerEmail: 'buyer@example.com',
      returnUrl:     'https://yourshop.com/success',
    });
    // result.merchantOrderRef → PortOne 프론트 SDK의 paymentId로 전달
    // result.channelKey       → PortOne 프론트 SDK의 channelKey로 전달

    결제 검증 (verify)

    const result = await sdk.verify({
      provider:          'portone',
      orderId:           'order001',
      providerPaymentId: result.merchantOrderRef,  // checkout 응답값
      amount:            15000,
      currency:          'KRW',
    });
    if (result.status === 'PAID') {
      // 결제 완료 처리
    }

    결제 취소 (cancel)

    // 전액 취소
    await sdk.cancel({ provider: 'portone', paymentId: 'payment_abc123', reason: '고객 요청' });
    
    // 부분 취소
    await sdk.cancel({ provider: 'portone', paymentId: 'payment_abc123', reason: '부분 환불', amount: 5000 });

    결제 조회 (getPayment)

    const payment = await sdk.getPayment({ provider: 'portone', paymentId: 'payment_abc123' });

    빌링키 발급 (정기결제 카드 등록)

    const { billingKey } = await sdk.issueBillingKey({
      provider:   'portone',
      storeId:    'store-xxxx',
      channelKey: 'channel-key-xxxx',
      customer:   { email: 'user@example.com' },
    });

    빌링키 결제

    await sdk.payWithBillingKey({
      provider:   'portone',
      paymentId:  `pay${Date.now()}`,
      billingKey: 'billing-key-xxxx',
      orderName:  '월정액 구독',
      amount:     9900,
      currency:   'KRW',
    });

    본인인증

    // 방법 A: 프론트 PortOne 브라우저 SDK 완료 후 서버 조회
    const result = await sdk.getIdentityVerification({ identityVerificationId: 'iv001' });
    
    // 방법 B: 서버 주도 OTP
    await sdk.sendIdentityVerification({
      identityVerificationId: 'iv001',
      customer: { name: '홍길동', phoneNumber: '01012345678' },
    });
    const verified = await sdk.confirmIdentityVerification({ identityVerificationId: 'iv001', otp: '123456' });
    // verified.status    === 'VERIFIED'
    // verified.name, verified.phone, verified.birthdate, verified.gender, verified.ci

    에러 처리

    import { SdkError } from '@openmkt/payment-sdk';
    
    try {
      await sdk.verify({ ... });
    } catch (err) {
      if (err instanceof SdkError) {
        switch (err.code) {
          case 'AMOUNT_EXCEEDS_LIMIT':  // 1회 한도 초과
          case 'DAILY_LIMIT_EXCEEDED':  // 일일 한도 초과
          case 'VELOCITY_EXCEEDED':     // 단시간 과다 요청
          case 'VERIFY_AMOUNT_MISMATCH':// 결제 금액 위변조
          case 'PROVIDER_ERROR':        // PortOne API 오류
          case 'REPLAY_DETECTED':       // Nonce 재사용 공격
          case 'UNAUTHORIZED':          // 인증 실패
          case 'API_KEY_EXPIRED':       // 만료된 API 키
            console.error(err.code, err.status, err.requestId);
        }
      }
    }

    지원 PG사

    pgProvider PG사 비고
    INICIS KG이니시스 기본값
    SMARTRO_V2 스마트로
    KAKAOPAY 카카오페이
    TOSSPAYMENTS 토스페이먼츠
    KCP NHN KCP

    주의: 스마트로는 orderId에 영문+숫자만 허용 (특수문자 불가)


    보안 기능

    기능 설명
    HMAC-SHA256 기본 요청 서명 방식
    ECDSA P-256 고보안 비대칭 서명 (v1.1.0+)
    Nonce 재사용 차단 24시간 TTL로 Replay 공격 방지
    타임스탬프 검증 ±5분 스큐 초과 요청 거부
    자동 IP 차단 반복 공격 시 테넌트 자동 블록
    거래 한도 1회/일일/월간 한도 설정 가능
    속도 감지 N분 내 과다 요청 자동 차단
    API 키 로테이션 유예 기간 병행 인증 지원

    빌드

    npm run build      # dist/ 생성 (CJS + ESM + .d.ts)
    npm run typecheck  # TypeScript 타입 검사