2026년 iPay API 결제 연동 방법

Ashley Innocent

Ashley Innocent

25 March 2026

2026년 iPay API 결제 연동 방법

TL;DR

iPay API를 통해 개발자는 결제 처리, 인보이스 발행, 금융 거래를 프로그래밍 방식으로 통합할 수 있습니다. 이 API는 OAuth 2.0 및 API 키 인증 방식을 사용하며, 결제, 환불, 거래, 정산을 위한 RESTful 엔드포인트를 제공하고, PCI DSS 규정 준수 요구사항 및 업계 표준 속도 제한을 따릅니다. 이 가이드에서는 인증 설정, 결제 처리, 웹훅 통합, 보안 규정 준수, 그리고 프로덕션 배포 전략을 다룹니다.

소개

디지털 결제 처리는 매년 전 세계적으로 8조 달러 이상을 처리합니다. 전자상거래 플랫폼, SaaS 애플리케이션 또는 마켓플레이스 솔루션을 구축하는 개발자에게 결제 API 통합은 선택 사항이 아니라, 고객 결제를 안전하고 규정을 준수하며 수락하기 위한 필수 요소입니다.

현실은 이렇습니다: 기업들은 실패한 결제, 수동 정산, 결제 사기로 인해 매출의 5-10%를 손실합니다. 견고한 결제 API 통합은 결제 처리를 자동화하고, 스마트한 재시도 로직으로 실패율을 줄이며, 자동 정산을 가능하게 하고, 사기 탐지를 구현합니다.

이 가이드에서는 전체 결제 API 통합 프로세스를 안내합니다. 인증 설정, 결제 처리, 환불 관리, 웹훅 처리, PCI DSS 규정 준수, 보안 모범 사례, 그리고 프로덕션 배포 전략을 배우게 됩니다. 마지막에는 프로덕션 준비가 완료된 결제 통합을 갖추게 될 것입니다.

💡
Apidog는 결제 API 테스트를 간소화합니다. 샌드박스 모드에서 결제 엔드포인트를 테스트하고, 웹훅 서명을 검증하며, 거래 응답을 검사하고, 하나의 작업 공간에서 통합 문제를 디버깅할 수 있습니다. API 사양을 가져오고, 응답을 모의하며, 테스트 시나리오를 팀과 공유하세요.
버튼

참고: 이 가이드는 iPay 및 유사한 결제 처리기에 적용될 수 있는 일반적인 결제 API 통합 패턴을 다룹니다. 특정 엔드포인트 URL 및 인증 세부 정보는 다를 수 있습니다. 항상 iPay 공식 문서를 참조하여 구현 세부 정보를 확인하세요.

iPay API란 무엇인가요?

iPay와 같은 결제 API는 금융 거래를 처리하기 위한 RESTful 인터페이스를 제공합니다. 이 API는 다음을 처리합니다:

주요 기능

기능 설명
RESTful API JSON 기반 엔드포인트
OAuth 2.0 + API 키 보안 인증
웹훅 실시간 결제 알림
토큰화 보안 카드 저장
3D Secure SCA 규정 준수
PCI DSS 레벨 1 규정 준수 필수
다중 통화 100개 이상의 통화 지원
사기 방지 도구 위험 점수, 속도 확인

결제 흐름 개요

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   고객      │───▶│   판매자    │───▶│  결제       │
│   (브라우저)  │    │   (서버)    │    │  게이트웨이 │
└─────────────┘    └─────────────┘    └─────────────┘
     │                    │                    │
     │  1. 카드 입력      │                    │
     │───────────────────▶│                    │
     │                    │                    │
     │  2. 토큰화         │                    │
     │───────────────────▶│  3. 결제 의도 생성 │
     │                    │───────────────────▶│
     │                    │                    │
     │                    │  4. 결제 확인      │
     │                    │───────────────────▶│
     │                    │                    │
     │                    │  5. 결과           │
     │                    │◀───────────────────│
     │                    │                    │
     │  6. 영수증         │                    │
     │◀───────────────────│                    │

API 환경

환경 URL 사용 사례
샌드박스 https://sandbox.ipay.com/api 개발, 테스트
프로덕션 https://api.ipay.com/api 실제 거래

시작하기: 인증 설정

1단계: iPay 계정 생성

API에 접근하기 전에:

  1. iPay 판매자 등록 페이지 방문
  2. 사업자 인증(KYB) 완료
  3. 필요한 서류 제출:
  1. 승인 대기 (1-3 영업일)

2단계: API 자격 증명 받기

API 자격 증명 생성:

  1. iPay 판매자 대시보드 로그인
  2. 설정 > API 키로 이동
  3. 새 API 키 생성
  4. 자격 증명을 안전하게 복사
# .env 파일 (절대 git에 커밋하지 마세요)
IPAY_API_KEY="live_xxxxxxxxxxxxxxxxxxxx"
IPAY_API_SECRET="secret_xxxxxxxxxxxxxxxxxxxx"
IPAY_WEBHOOK_SECRET="whsec_xxxxxxxxxxxxxxxxxxxx"

보안 참고: 샌드박스와 프로덕션 환경에 각각 다른 키를 사용하세요.

3단계: 인증 방법 이해

iPay는 여러 인증 방법을 지원합니다:

방법 가장 적합한 용도 보안 수준
기본 인증 (Basic Auth) 서버-서버 통신 높음
OAuth 2.0 다중 테넌트 앱 더 높음
JWT 마이크로서비스 높음

4단계: 인증된 API 호출 수행

재사용 가능한 API 클라이언트 생성:

const IPAY_BASE_URL = process.env.IPAY_SANDBOX
  ? 'https://sandbox.ipay.com/api'
  : 'https://api.ipay.com/api';

const ipayRequest = async (endpoint, options = {}) => {
  const apiKey = process.env.IPAY_API_KEY;
  const apiSecret = process.env.IPAY_API_SECRET;

  // 기본 인증 (Base64 인코딩)
  const authHeader = Buffer.from(`${apiKey}:${apiSecret}`).toString('base64');

  const response = await fetch(`${IPAY_BASE_URL}${endpoint}`, {
    ...options,
    headers: {
      'Authorization': `Basic ${authHeader}`,
      'Content-Type': 'application/json',
      'Idempotency-Key': options.idempotencyKey || generateIdempotencyKey(),
      ...options.headers
    }
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`iPay API Error: ${error.message}`);
  }

  return response.json();
};

function generateIdempotencyKey() {
  return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}

// 사용 예시
const account = await ipayRequest('/account');
console.log(`판매자: ${account.business_name}`);

결제 처리

결제 의도 생성

결제 초기화:

const createPayment = async (paymentData) => {
  const payment = {
    amount: paymentData.amount, // 가장 작은 통화 단위 (센트)
    currency: paymentData.currency || 'USD',
    customer: paymentData.customerId,
    payment_method: paymentData.paymentMethodId,
    confirm: true,
    description: paymentData.description,
    metadata: {
      orderId: paymentData.orderId,
      customerId: paymentData.customerId
    },
    capture_method: paymentData.captureMethod || 'automatic', // 'automatic' 또는 'manual'
    statement_descriptor: paymentData.statementDescriptor || 'MYCOMPANY'
  };

  const response = await ipayRequest('/payments', {
    method: 'POST',
    body: JSON.stringify(payment),
    idempotencyKey: paymentData.idempotencyKey
  });

  return response;
};

// 사용 예시
const payment = await createPayment({
  amount: 2999, // $29.99
  currency: 'USD',
  customerId: 'cus_12345',
  paymentMethodId: 'pm_67890',
  description: '주문 #ORD-001',
  orderId: 'ORD-001',
  statementDescriptor: 'MYCOMPANY INC'
});

console.log(`결제 상태: ${payment.status}`);
console.log(`결제 ID: ${payment.id}`);

결제 상태 흐름

requires_payment_method → requires_confirmation → requires_action
                         → processing → requires_capture → succeeded
                                                        → failed
                                                        → canceled

결제 수단

방법 유형 사용 사례
card 신용/직불카드 표준 결제
bank_transfer ACH, SEPA 저렴한 수수료 이체
digital_wallet Apple Pay, Google Pay 모바일 결제
buy_now_pay_later Klarna, Afterpay 할부 결제

카드 정보 토큰화

향후 사용을 위해 카드를 안전하게 저장:

const tokenizeCard = async (cardData) => {
  // 절대로 원시 카드 데이터를 서버로 보내지 마세요
  // 대신 클라이언트 측 토큰화를 사용하세요

  // 클라이언트 측 (브라우저/모바일)
  const response = await fetch(`${IPAY_BASE_URL}/tokens`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${CLIENT_PUBLISHABLE_KEY}`
    },
    body: JSON.stringify({
      card: {
        number: cardData.number,
        exp_month: cardData.expMonth,
        exp_year: cardData.expYear,
        cvc: cardData.cvc
      }
    })
  });

  const token = await response.json();
  return token; // token.id를 서버로 보냅니다
};

// 서버 측: 토큰을 사용하여 결제 수단 생성
const createPaymentMethod = async (tokenId, customerId) => {
  const response = await ipayRequest('/payment_methods', {
    method: 'POST',
    body: JSON.stringify({
      type: 'card',
      token: tokenId,
      customer: customerId
    })
  });

  return response;
};

3D Secure 인증

SCA 규정 준수 구현:

const createPaymentWith3DS = async (paymentData) => {
  const payment = await createPayment({
    ...paymentData,
    confirmation_token: true // 3DS를 위한 클라이언트 시크릿 반환
  });

  if (payment.status === 'requires_action') {
    // 클라이언트는 3DS 챌린지를 완료해야 합니다
    return {
      requiresAction: true,
      clientSecret: payment.client_secret,
      nextAction: payment.next_action
    };
  }

  return { success: true, payment };
};

// 클라이언트 측: 3DS 챌린지 처리
// iPay.js 또는 모바일 SDK를 사용하여 인증 챌린지 표시

환불 관리

전액 환불 처리

전체 결제 환불:

const refundPayment = async (paymentId, reason = null) => {
  const refund = {
    payment: paymentId,
    reason: reason || 'requested_by_customer'
  };

  const response = await ipayRequest('/refunds', {
    method: 'POST',
    body: JSON.stringify(refund),
    idempotencyKey: `refund_${paymentId}_${Date.now()}`
  });

  return response;
};

// 사용 예시
const refund = await refundPayment('pay_12345', 'duplicate');
console.log(`환불 상태: ${refund.status}`);
console.log(`환불 ID: ${refund.id}`);

부분 환불 처리

결제 금액 일부 환불:

const partialRefund = async (paymentId, amount, reason = null) => {
  const refund = {
    payment: paymentId,
    amount: amount, // 가장 작은 통화 단위
    reason: reason || 'requested_by_customer'
  };

  const response = await ipayRequest('/refunds', {
    method: 'POST',
    body: JSON.stringify(refund),
    idempotencyKey: `refund_${paymentId}_${amount}_${Date.now()}`
  });

  return response;
};

// 사용 예시 - $29.99 결제 중 $15.00 환불
const refund = await partialRefund('pay_12345', 1500, 'partial_ship');
console.log(`환불 금액: $${refund.amount / 100}`);

환불 사유

사유 코드 설명
duplicate 중복 청구
fraudulent 사기성 거래
requested_by_customer 고객 요청
order_canceled 주문 취소
product_not_received 상품 미수령
product_not_as_described 설명과 다른 상품

고객 관리

고객 생성

반복 결제를 위해 고객 저장:

const createCustomer = async (customerData) => {
  const customer = {
    email: customerData.email,
    name: customerData.name,
    phone: customerData.phone,
    metadata: {
      internalId: customerData.internalId,
      tier: customerData.tier
    }
  };

  const response = await ipayRequest('/customers', {
    method: 'POST',
    body: JSON.stringify(customer)
  });

  return response;
};

// 사용 예시
const customer = await createCustomer({
  email: 'customer@example.com',
  name: 'John Doe',
  phone: '+1-555-0123',
  internalId: 'USR-12345',
  tier: 'premium'
});

console.log(`고객 생성: ${customer.id}`);

고객에게 결제 수단 연결

향후 사용을 위해 카드 저장:

const attachPaymentMethod = async (paymentMethodId, customerId) => {
  const response = await ipayRequest(`/payment_methods/${paymentMethodId}/attach`, {
    method: 'POST',
    body: JSON.stringify({
      customer: customerId
    })
  });

  return response;
};

// 사용 예시
await attachPaymentMethod('pm_67890', 'cus_12345');

고객 결제 수단 목록 가져오기

저장된 카드 가져오기:

const getCustomerPaymentMethods = async (customerId) => {
  const response = await ipayRequest(`/customers/${customerId}/payment_methods`);
  return response;
};

// 사용 예시
const methods = await getCustomerPaymentMethods('cus_12345');
methods.data.forEach(method => {
  console.log(`${method.card.brand} 마지막 4자리: ${method.card.last4}`);
  console.log(`만료일: ${method.card.exp_month}/${method.card.exp_year}`);
});

웹훅

웹훅 구성

웹훅 엔드포인트 설정:

  1. iPay 대시보드에 로그인
  2. 개발자 > 웹훅으로 이동
  3. 엔드포인트 추가 클릭
  4. HTTPS URL 입력
  5. 구독할 이벤트 선택

웹훅 이벤트

이벤트 트리거
payment.succeeded 결제 완료
payment.failed 결제 거부
payment.refunded 환불 처리됨
payment.disputed 차지백 접수됨
customer.created 새 고객 생성
customer.subscription.updated 구독 변경됨

웹훅 처리

const express = require('express');
const crypto = require('crypto');
const app = express();

app.post('/webhooks/ipay', express.raw({ type: 'application/json' }), async (req, res) => {
  const signature = req.headers['ipay-signature'];
  const payload = req.body;

  // 웹훅 서명 검증
  const isValid = verifyWebhookSignature(payload, signature, process.env.IPAY_WEBHOOK_SECRET);

  if (!isValid) {
    console.error('유효하지 않은 웹훅 서명');
    return res.status(401).send('Unauthorized');
  }

  const event = JSON.parse(payload.toString());

  // 적절한 핸들러로 라우팅
  switch (event.type) {
    case 'payment.succeeded':
      await handlePaymentSucceeded(event.data);
      break;
    case 'payment.failed':
      await handlePaymentFailed(event.data);
      break;
    case 'payment.refunded':
      await handlePaymentRefunded(event.data);
      break;
    case 'payment.disputed':
      await handlePaymentDisputed(event.data);
      break;
    default:
      console.log('처리되지 않은 이벤트 유형:', event.type);
  }

  // 수신 확인
  res.status(200).send('OK');
});

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

async function handlePaymentSucceeded(data) {
  console.log(`결제 성공: ${data.id}`);

  // 주문 상태 업데이트
  await db.orders.update(data.metadata.orderId, {
    status: 'paid',
    paymentId: data.id,
    paidAt: new Date()
  });

  // 확인 이메일 전송
  await sendOrderConfirmation(data.metadata.orderId);
}

async function handlePaymentFailed(data) {
  console.log(`결제 실패: ${data.id} - ${data.failure_code}`);

  // 고객에게 알림
  await sendPaymentFailedEmail(data.customer, data.failure_message);

  // 재시도 로직 또는 주문을 실패로 표시
  await db.orders.update(data.metadata.orderId, {
    status: 'payment_failed',
    failureReason: data.failure_message
  });
}

보안 및 규정 준수

PCI DSS 요구사항

결제 통합은 PCI DSS를 준수해야 합니다:

요구사항 구현
보안 네트워크 HTTPS, 방화벽, 보안 구성 사용
카드 소유자 데이터 보호 절대 CVV를 저장하지 않고, PAN 암호화
취약점 관리 정기적인 보안 업데이트, 안티바이러스
접근 제어 최소 권한, MFA, 고유 ID
모니터링 로깅, 침입 탐지
보안 정책 문서화된 정책, 정기 교육

보안 모범 사례

// 1. 토큰화 사용 - 절대 원시 카드 데이터를 다루지 마세요
const token = await tokenizeCard(cardData); // 클라이언트 측

// 2. 모든 결제 작업에 대해 멱등성(idempotency) 구현
const idempotencyKey = `pay_${orderId}_${Date.now()}`;

// 3. 서버 측에서 금액 유효성 검사
if (req.body.amount !== calculatedAmount) {
  throw new Error('금액 불일치 - 변조 가능성 있음');
}

// 4. 모든 결제 작업 로깅 (민감한 데이터 없이)
logger.info('결제 시도', {
  orderId,
  amount,
  currency,
  customerId,
  timestamp: new Date().toISOString()
  // 절대로 로깅하지 마세요: 카드 번호, CVV, 전체 결제 수단 정보
});

// 5. 시크릿(secret)을 위해 환경 변수 사용
const apiKey = process.env.IPAY_API_KEY; // 하드코딩하지 않음

// 6. 결제 엔드포인트에 속도 제한 구현
const paymentLimiter = rateLimit({
  windowMs: 60000,
  max: 10 // 분당 10회 결제 시도 제한
});

프로덕션 배포 체크리스트

실제 결제를 처리하기 전에:

실제 사용 사례

전자상거래 결제

온라인 소매업체가 결제를 통합합니다:

SaaS 구독 청구

소프트웨어 회사가 청구를 자동화합니다:

마켓플레이스 에스크로

플랫폼이 다자간 결제를 처리합니다:

결론

결제 API 통합은 보안, 규정 준수 및 오류 처리에 대한 세심한 주의가 필요합니다. 주요 요점:

버튼

자주 묻는 질문 (FAQ)

iPay API로 어떻게 인증하나요?

API 키와 시크릿을 사용하는 기본 인증(Basic authentication) 또는 다중 테넌트 애플리케이션을 위한 OAuth 2.0을 사용하세요.

고객 카드 정보를 저장할 수 있나요?

네, 하지만 PCI DSS를 준수해야 합니다. iPay의 금고에 카드를 안전하게 저장하기 위해 토큰화를 사용하세요.

실패한 결제는 어떻게 처리하나요?

지수 백오프(exponential backoff)를 사용하여 재시도 로직을 구현하고, 고객에게 알림을 보내며, 대체 결제 수단을 제공하세요.

멱등성(Idempotency)은 무엇이며 왜 중요한가요?

멱등성은 동일한 키를 가진 중복 요청이 동일한 결과를 생성하도록 보장하여 중복 청구를 방지합니다.

카드를 청구하지 않고 결제를 테스트하려면 어떻게 해야 하나요?

iPay 문서에 제공된 테스트 카드 번호를 사용하여 샌드박스 모드를 사용하세요.

웹훅 서명이란 무엇인가요?

웹훅이 악의적인 행위자가 아닌 iPay에서 전송되었음을 확인하는 암호화 서명입니다.

Apidog에서 API 설계-첫 번째 연습

API를 더 쉽게 구축하고 사용하는 방법을 발견하세요