DEV Community

Cover image for 2026년 iPay API 결제 연동 방법
Rihpig
Rihpig

Posted on • Originally published at apidog.com

2026년 iPay API 결제 연동 방법

TL;DR

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

지금 Apidog를 사용해보세요


소개

디지털 결제 처리는 전 세계적으로 매년 8조 달러 이상을 처리합니다. 전자상거래, SaaS, 마켓플레이스 등에서 결제 API 통합은 필수입니다. 실패한 결제, 수동 정산, 결제 사기로 인해 매출의 5~10%가 손실됩니다. 견고한 결제 API 통합은 결제를 자동화하고, 실패율을 줄이며, 자동 정산 및 사기 탐지를 구현할 수 있습니다.

이 가이드에서는 인증, 결제 처리, 환불 관리, 웹훅 처리, PCI DSS 준수, 보안, 프로덕션 배포 전략까지 단계별로 실무 적용법을 다룹니다.

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

참고: 이 가이드는 iPay 및 유사 결제 처리기의 일반적인 API 통합 패턴을 다룹니다. 엔드포인트 URL·인증 세부 정보는 공식 문서를 참고하세요.


iPay API란 무엇인가요?

iPay와 같은 결제 API는 금융 거래 처리를 위한 RESTful 인터페이스를 제공하며, 아래 기능을 지원합니다:

  • 결제 승인/캡처
  • 환불/차지백
  • 거래 내역/보고
  • 고객 토큰화(금고)
  • 구독 및 반복 청구
  • 인보이스 생성/관리
  • 정산/결제
  • 사기 탐지 및 예방

주요 기능

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

결제 흐름 개요

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

API 환경

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

시작하기: 인증 설정

1단계: iPay 계정 생성

  1. iPay 판매자 등록 페이지에서 회원가입
  2. 사업자 인증(KYB) 제출
  3. 필요한 서류 제출 (사업자 등록증, 은행 계좌 정보, 신분증)
  4. 승인 대기 (1~3 영업일)

2단계: 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"
Enter fullscreen mode Exit fullscreen mode

보안 팁: 샌드박스와 프로덕션 환경에서 각각 별도의 키를 사용하세요.

3단계: 인증 방법 이해

방법 적합 용도 보안 수준
기본 인증 서버-서버 통신 높음
OAuth 2.0 다중 테넌트 앱 매우 높음
JWT 마이크로서비스 높음

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

재사용 가능한 API 클라이언트 예제 (Node.js):

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;
  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}`);
Enter fullscreen mode Exit fullscreen mode

결제 처리

결제 의도 생성

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',
    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}`);
Enter fullscreen mode Exit fullscreen mode

결제 상태 흐름

requires_payment_method → requires_confirmation → requires_action
                         → processing → requires_capture → succeeded
                                                        → failed
                                                        → canceled
Enter fullscreen mode Exit fullscreen mode

결제 수단

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

카드 정보 토큰화

클라이언트 측에서 카드 토큰화 → 서버에 토큰 전달:

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;
};
Enter fullscreen mode Exit fullscreen mode

3D Secure 인증

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 };
};

// 클라이언트: iPay.js나 모바일 SDK로 3DS 인증 처리
Enter fullscreen mode Exit fullscreen mode

환불 관리

전액 환불 처리

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}`);
Enter fullscreen mode Exit fullscreen mode

부분 환불 처리

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}`);
Enter fullscreen mode Exit fullscreen mode

환불 사유

사유 코드 설명
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}`);
Enter fullscreen mode Exit fullscreen mode

고객에게 결제 수단 연결

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');
Enter fullscreen mode Exit fullscreen mode

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

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}`);
});
Enter fullscreen mode Exit fullscreen mode

웹훅

웹훅 구성

  1. iPay 대시보드 → 개발자웹훅
  2. 엔드포인트 추가
  3. HTTPS URL 입력
  4. 구독 이벤트 선택

웹훅 이벤트

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

웹훅 처리 예시 (Express.js)

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
  });
}
Enter fullscreen mode Exit fullscreen mode

보안 및 규정 준수

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. 환경 변수로 시크릿 관리 (하드코딩 금지)
const apiKey = process.env.IPAY_API_KEY;

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

프로덕션 배포 체크리스트

실거래 전 아래 체크리스트 점검:

  • [ ] PCI DSS 자체 평가 설문지 제출
  • [ ] 모든 엔드포인트 HTTPS 적용
  • [ ] API 키를 시크릿 관리 시스템에 저장
  • [ ] 웹훅 서명 검증 구현
  • [ ] 모든 결제 작업에 멱등성 적용
  • [ ] 민감하지 않은 포괄적 로깅
  • [ ] 사기 탐지 규칙 적용
  • [ ] 환불/분쟁 흐름 테스트
  • [ ] 결제 실패 대응 런북 준비
  • [ ] 모니터링 및 알림 구성
  • [ ] 백업 결제 처리기 준비

실제 사용 사례

전자상거래 결제

  • 과제: 수동 결제 처리, 이탈률 높음
  • 해결책: 토큰화 카드로 원페이지 결제 구현
  • 결과: 전환율 35% 증가, 즉시 결제

SaaS 구독 청구

  • 과제: 수동 인보이스 및 수금
  • 해결책: 자동 재시도 포함 반복 결제
  • 결과: 95% 정시결제, 관리 시간 80% 절감

마켓플레이스 에스크로

  • 과제: 판매자 간 복잡한 분할 결제
  • 해결책: 송금 일정이 포함된 결제 의도 사용
  • 결과: 자동화된 판매자 지급, 사기 감소

결론

결제 API 통합은 보안·규정 준수·오류 처리에 각별한 주의가 필요합니다.

  • 원시 카드 데이터 직접 다루지 말고 토큰화 사용
  • 모든 결제 작업에 멱등성(idempotency) 적용
  • 사기 방지 위해 웹훅 서명 검증 구현
  • PCI DSS 요구사항 철저 준수
  • 프로덕션 전 샌드박스에서 충분히 테스트
  • Apidog로 API 테스트 및 협업 자동화

자주 묻는 질문 (FAQ)

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

API 키/시크릿을 사용하는 기본 인증(Basic authentication) 또는 OAuth 2.0을 사용하세요.

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

네, 다만 PCI DSS 준수 필요. iPay 금고에 토큰화 방식으로 저장하세요.

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

지수 백오프(exponential backoff) 재시도, 고객 알림, 대체 결제 수단 제공 로직을 구현하세요.

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

동일한 키의 중복 요청이 결과를 동일하게 보장해 중복 청구 방지에 필수적입니다.

카드를 청구하지 않고 결제를 테스트하려면?

iPay 문서의 테스트 카드 번호 활용, 샌드박스 환경 이용.

웹훅 서명이란?

iPay에서 보낸 웹훅임을 암호화 서명으로 검증하는 방식입니다.

Top comments (0)