Files
sam-sales/ecard/카드사용내역조회.md

957 lines
31 KiB
Markdown
Raw Normal View History

# 바로빌 카드 사용내역 조회 - 멀티테넌시 개발 문서
## 목차
1. [개요](#개요)
2. [시스템 아키텍처](#시스템-아키텍처)
3. [데이터베이스 설계](#데이터베이스-설계)
4. [API 구조](#api-구조)
5. [보안 고려사항](#보안-고려사항)
6. [구현 가이드](#구현-가이드)
---
## 개요
### 목적
멀티테넌시 환경에서 각 업체(테넌트)별로 독립적인 바로빌 카드 사용내역 조회 서비스를 제공하기 위한 개발 문서입니다.
### 주요 기능
- 업체별 바로빌 인증 정보 관리
- 업체별 카드 목록 조회
- 업체별 카드 사용내역 조회
- 데이터 격리 및 보안 관리
### 기술 스택
- **백엔드**: PHP 7.3+
- **데이터베이스**: MySQL/MariaDB
- **외부 API**: 바로빌 SOAP 웹서비스
- **프론트엔드**: React (ecard/index.php)
---
## 시스템 아키텍처
### 데이터 흐름도
```
[업체 사용자]
[ecard/index.php] (프론트엔드)
↓ (업체 ID 전달)
[API Layer]
├─ cards.php (카드 목록 조회)
└─ usage.php (사용내역 조회)
↓ (업체별 인증 정보 조회)
[Database]
├─ companies (업체 정보)
└─ barobill_credentials (바로빌 인증 정보)
↓ (바로빌 API 호출)
[바로빌 SOAP API]
├─ GetCardEx2 (카드 목록)
└─ GetPeriodCardApprovalLog (사용내역)
↓ (응답)
[API Layer] → [프론트엔드] → [사용자]
```
### 멀티테넌시 구조
```
┌─────────────────────────────────────────┐
│ 업체 A (Company A) │
│ ┌───────────────────────────────────┐ │
│ │ 바로빌 인증 정보 │ │
│ │ - CERTKEY: AAAAA │ │
│ │ - 사업자번호: 123-45-67890 │ │
│ │ - 사용자 ID: user_a │ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ 카드 목록 (바로빌에서 조회) │ │
│ │ - 카드1, 카드2, 카드3 │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 업체 B (Company B) │
│ ┌───────────────────────────────────┐ │
│ │ 바로빌 인증 정보 │ │
│ │ - CERTKEY: BBBBB │ │
│ │ - 사업자번호: 987-65-43210 │ │
│ │ - 사용자 ID: user_b │ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ 카드 목록 (바로빌에서 조회) │ │
│ │ - 카드4, 카드5 │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
```
---
## 데이터베이스 설계
### 1. companies (업체 기본 정보 테이블)
업체의 기본 정보를 저장하는 테이블입니다.
```sql
CREATE TABLE companies (
id INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '업체 ID',
company_name VARCHAR(255) NOT NULL COMMENT '업체명',
business_number VARCHAR(20) NOT NULL COMMENT '사업자번호 (하이픈 포함)',
business_number_clean VARCHAR(20) NOT NULL COMMENT '사업자번호 (하이픈 제거)',
status ENUM('active', 'inactive', 'suspended') DEFAULT 'active' COMMENT '상태',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '생성일시',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일시',
deleted_at DATETIME NULL COMMENT '삭제일시 (소프트 삭제)',
UNIQUE KEY uk_business_number (business_number_clean),
INDEX idx_status (status),
INDEX idx_deleted_at (deleted_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='업체 기본 정보';
```
**주요 필드 설명:**
- `id`: 업체 고유 ID (다른 테이블에서 외래키로 사용)
- `company_name`: 업체명
- `business_number`: 사업자번호 (표시용, 하이픈 포함)
- `business_number_clean`: 사업자번호 (검색용, 하이픈 제거)
- `status`: 업체 상태 (active=활성, inactive=비활성, suspended=정지)
---
### 2. barobill_credentials (바로빌 인증 정보 테이블)
각 업체별 바로빌 API 인증 정보를 저장하는 테이블입니다.
```sql
CREATE TABLE barobill_credentials (
id INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '인증 정보 ID',
company_id INT(11) UNSIGNED NOT NULL COMMENT '업체 ID',
cert_key VARCHAR(500) NOT NULL COMMENT '바로빌 CERTKEY (암호화 권장)',
corp_num VARCHAR(20) NOT NULL COMMENT '사업자번호 (하이픈 제거)',
user_id VARCHAR(100) NULL COMMENT '바로빌 사용자 ID (선택사항, 빈값이면 전체 카드 조회)',
test_mode TINYINT(1) DEFAULT 0 COMMENT '테스트 모드 (0=운영, 1=테스트)',
status ENUM('active', 'inactive') DEFAULT 'active' COMMENT '상태',
last_api_call DATETIME NULL COMMENT '마지막 API 호출 일시',
last_error_message TEXT NULL COMMENT '마지막 에러 메시지',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '생성일시',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일시',
UNIQUE KEY uk_company_id (company_id),
INDEX idx_status (status),
INDEX idx_company_id (company_id),
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='바로빌 인증 정보';
```
**주요 필드 설명:**
- `company_id`: 업체 ID (companies 테이블 참조)
- `cert_key`: 바로빌 CERTKEY (⚠️ 민감정보, 암호화 권장)
- `corp_num`: 사업자번호 (하이픈 제거)
- `user_id`: 바로빌 사용자 ID (특정 사용자 카드만 조회 시 사용, NULL이면 전체)
- `test_mode`: 테스트 모드 여부 (0=운영, 1=테스트)
- `status`: 인증 정보 상태
- `last_api_call`: 마지막 API 호출 일시 (모니터링용)
- `last_error_message`: 마지막 에러 메시지 (디버깅용)
**보안 고려사항:**
- `cert_key`는 민감정보이므로 암호화 저장 권장
- 데이터베이스 접근 권한 최소화
- 로그에 민감정보 출력 금지
---
### 3. barobill_cards (카드 정보 캐시 테이블)
바로빌에서 조회한 카드 정보를 캐싱하는 테이블입니다. (선택사항)
```sql
CREATE TABLE barobill_cards (
id INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '카드 ID',
company_id INT(11) UNSIGNED NOT NULL COMMENT '업체 ID',
card_num VARCHAR(50) NOT NULL COMMENT '카드번호',
card_company_code VARCHAR(10) NULL COMMENT '카드사 코드',
card_company_name VARCHAR(50) NULL COMMENT '카드사 이름',
card_brand VARCHAR(20) NULL COMMENT '카드 브랜드 (비자, 마스터카드 등)',
alias VARCHAR(100) NULL COMMENT '카드 별칭',
card_type TINYINT(1) NULL COMMENT '카드 종류 (1=개인, 2=법인)',
status TINYINT(1) NULL COMMENT '카드 상태 (0=대기중, 1=정상, 2=해지, 3=수집오류, 4=일시중지)',
collect_cycle TINYINT(1) NULL COMMENT '수집주기 (1=1일1회, 2=1일2회, 3=1일3회)',
last_collect_date DATE NULL COMMENT '마지막 수집일',
last_collect_result TINYINT(1) NULL COMMENT '마지막 수집결과',
regist_date DATE NULL COMMENT '등록일',
cached_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '캐시 일시',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일시',
UNIQUE KEY uk_company_card (company_id, card_num),
INDEX idx_company_id (company_id),
INDEX idx_status (status),
INDEX idx_cached_at (cached_at),
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='바로빌 카드 정보 캐시';
```
**주요 필드 설명:**
- `company_id`: 업체 ID
- `card_num`: 카드번호 (바로빌에서 조회한 카드번호)
- `card_company_code`: 카드사 코드 (01=BC, 02=KB, 04=삼성 등)
- `card_company_name`: 카드사 이름
- `card_brand`: 카드 브랜드 (비자, 마스터카드 등)
- `alias`: 카드 별칭
- `status`: 카드 상태
- `cached_at`: 캐시 일시 (캐시 만료 판단용)
**사용 목적:**
- 바로빌 API 호출 최소화 (성능 향상)
- 오프라인 조회 가능
- 카드 목록 변경 이력 추적
**캐시 전략:**
- 카드 목록은 1시간마다 갱신 권장
- 실시간 조회가 필요한 경우 캐시 사용 안 함
---
### 4. barobill_card_usage_logs (카드 사용내역 캐시 테이블)
바로빌에서 조회한 카드 사용내역을 캐싱하는 테이블입니다. (선택사항)
```sql
CREATE TABLE barobill_card_usage_logs (
id BIGINT(20) UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '사용내역 ID',
company_id INT(11) UNSIGNED NOT NULL COMMENT '업체 ID',
card_num VARCHAR(50) NOT NULL COMMENT '카드번호',
use_dt DATETIME NOT NULL COMMENT '사용일시',
use_key VARCHAR(100) NULL COMMENT '사용 키 (바로빌 고유값)',
approval_num VARCHAR(50) NULL COMMENT '승인번호',
approval_amount INT(11) DEFAULT 0 COMMENT '승인금액',
tax INT(11) DEFAULT 0 COMMENT '부가세',
service_charge INT(11) DEFAULT 0 COMMENT '봉사료',
total_amount INT(11) DEFAULT 0 COMMENT '총 금액',
approval_type TINYINT(1) NULL COMMENT '승인유형 (1=승인, 2=취소)',
payment_plan VARCHAR(10) NULL COMMENT '할부개월 (0=일시불)',
currency_code VARCHAR(3) DEFAULT 'KRW' COMMENT '통화코드',
use_store_name VARCHAR(255) NULL COMMENT '가맹점명',
use_store_corp_num VARCHAR(20) NULL COMMENT '가맹점 사업자번호',
use_store_addr TEXT NULL COMMENT '가맹점 주소',
use_store_ceo VARCHAR(100) NULL COMMENT '가맹점 대표자명',
use_store_biz_type VARCHAR(100) NULL COMMENT '가맹점 업종',
use_store_tel VARCHAR(20) NULL COMMENT '가맹점 전화번호',
memo TEXT NULL COMMENT '메모',
card_company VARCHAR(10) NULL COMMENT '카드사 코드',
cached_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '캐시 일시',
UNIQUE KEY uk_use_key (company_id, use_key),
INDEX idx_company_id (company_id),
INDEX idx_card_num (card_num),
INDEX idx_use_dt (use_dt),
INDEX idx_company_use_dt (company_id, use_dt),
INDEX idx_approval_type (approval_type),
INDEX idx_cached_at (cached_at),
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='바로빌 카드 사용내역 캐시';
```
**주요 필드 설명:**
- `company_id`: 업체 ID
- `card_num`: 카드번호
- `use_dt`: 사용일시
- `use_key`: 바로빌 고유 사용 키 (중복 방지용)
- `approval_amount`: 승인금액
- `approval_type`: 승인유형 (1=승인, 2=취소)
- `use_store_name`: 가맹점명
- `cached_at`: 캐시 일시
**인덱스 전략:**
- `idx_company_use_dt`: 업체별 기간 조회 최적화
- `idx_use_dt`: 전체 기간 조회 최적화
- `uk_use_key`: 중복 데이터 방지
**사용 목적:**
- 바로빌 API 호출 최소화
- 빠른 조회 성능
- 데이터 분석 및 리포트 생성
**캐시 전략:**
- 최근 3개월 데이터는 캐시 유지
- 오래된 데이터는 주기적으로 정리
- 실시간 조회가 필요한 경우 바로빌 API 직접 호출
---
### 5. barobill_api_logs (API 호출 로그 테이블)
바로빌 API 호출 이력을 기록하는 테이블입니다. (선택사항, 모니터링용)
```sql
CREATE TABLE barobill_api_logs (
id BIGINT(20) UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '로그 ID',
company_id INT(11) UNSIGNED NOT NULL COMMENT '업체 ID',
api_method VARCHAR(50) NOT NULL COMMENT 'API 메서드명',
request_params TEXT NULL COMMENT '요청 파라미터 (JSON)',
response_status VARCHAR(20) NULL COMMENT '응답 상태 (success/failure)',
response_data TEXT NULL COMMENT '응답 데이터 (JSON, 일부만 저장)',
error_message TEXT NULL COMMENT '에러 메시지',
execution_time INT(11) NULL COMMENT '실행 시간 (밀리초)',
ip_address VARCHAR(45) NULL COMMENT '요청 IP 주소',
user_agent VARCHAR(255) NULL COMMENT '사용자 에이전트',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '생성일시',
INDEX idx_company_id (company_id),
INDEX idx_api_method (api_method),
INDEX idx_response_status (response_status),
INDEX idx_created_at (created_at),
INDEX idx_company_created (company_id, created_at),
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='바로빌 API 호출 로그';
```
**주요 필드 설명:**
- `company_id`: 업체 ID
- `api_method`: API 메서드명 (GetCardEx2, GetPeriodCardApprovalLog 등)
- `request_params`: 요청 파라미터 (JSON 형식)
- `response_status`: 응답 상태 (success/failure)
- `error_message`: 에러 메시지
- `execution_time`: 실행 시간 (성능 모니터링용)
**사용 목적:**
- API 호출 이력 추적
- 에러 디버깅
- 성능 모니터링
- 사용량 통계
**데이터 보관 정책:**
- 최근 6개월 데이터 보관
- 오래된 데이터는 주기적으로 아카이빙 또는 삭제
---
## 테이블 관계도
```
companies (업체)
├── 1:1 ── barobill_credentials (바로빌 인증 정보)
├── 1:N ── barobill_cards (카드 정보 캐시)
├── 1:N ── barobill_card_usage_logs (사용내역 캐시)
└── 1:N ── barobill_api_logs (API 호출 로그)
```
---
## API 구조
### 1. 카드 목록 조회 API
**엔드포인트**: `GET /ecard/api/cards.php`
**요청 파라미터:**
```php
[
'company_id' => 1, // 업체 ID (필수)
'availOnly' => 0 // 0=전체, 1=사용가능한 카드만
]
```
**응답 예시:**
```json
{
"success": true,
"cards": [
{
"cardNum": "1234567890123456",
"cardNumMasked": "1234-****-****-3456",
"cardCompany": "04",
"cardCompanyName": "삼성카드",
"cardBrand": "비자",
"alias": "법인카드1",
"status": "1",
"statusName": "정상"
}
],
"count": 1
}
```
**구현 로직:**
1. `company_id``barobill_credentials` 테이블에서 인증 정보 조회
2. 바로빌 SOAP API 호출 (GetCardEx2)
3. 응답 데이터 변환 및 반환
---
### 2. 카드 사용내역 조회 API
**엔드포인트**: `GET /ecard/api/usage.php`
**요청 파라미터:**
```php
[
'company_id' => 1, // 업체 ID (필수)
'type' => 'period', // daily/monthly/period
'cardNum' => '', // 카드번호 (빈값이면 전체)
'startDate' => '20240101', // 시작일 (YYYYMMDD)
'endDate' => '20240131', // 종료일 (YYYYMMDD)
'page' => 1, // 페이지 번호
'limit' => 50 // 페이지당 건수
]
```
**응답 예시:**
```json
{
"success": true,
"data": {
"logs": [
{
"cardNum": "1234-****-****-3456",
"approvalDateTime": "2024-01-15 14:30:00",
"merchantName": "스타벅스 강남점",
"merchantBizNum": "123-45-67890",
"amount": 5000,
"approvalType": "1",
"approvalTypeName": "승인"
}
],
"pagination": {
"currentPage": 1,
"maxPageNum": 10,
"totalCount": 500
},
"summary": {
"totalAmount": 1000000,
"count": 500,
"approvalCount": 480,
"cancelCount": 20
}
}
}
```
**구현 로직:**
1. `company_id``barobill_credentials` 테이블에서 인증 정보 조회
2. 바로빌 SOAP API 호출 (GetPeriodCardApprovalLog)
3. 응답 데이터 변환 및 반환
4. (선택) 캐시 테이블에 저장
---
## 보안 고려사항
### 1. 데이터 격리
- **업체별 데이터 격리**: 모든 쿼리에 `company_id` 조건 필수
- **권한 검증**: 세션에서 `company_id` 확인 후 접근 허용
- **SQL Injection 방지**: Prepared Statement 사용
```php
// 올바른 예시
$stmt = $pdo->prepare("SELECT * FROM barobill_credentials WHERE company_id = ?");
$stmt->execute([$company_id]);
// 잘못된 예시 (SQL Injection 취약)
$sql = "SELECT * FROM barobill_credentials WHERE company_id = $company_id";
```
### 2. 인증 정보 보호
- **CERTKEY 암호화**: 데이터베이스에 저장 시 암호화
- **접근 로그**: 인증 정보 조회 시 로그 기록
- **최소 권한 원칙**: 필요한 최소한의 정보만 조회
```php
// CERTKEY 암호화 예시 (간단한 방법)
function encryptCertKey($certKey) {
// 실제 운영 환경에서는 더 강력한 암호화 사용 권장
return base64_encode(openssl_encrypt($certKey, 'AES-256-CBC', $encryptionKey));
}
function decryptCertKey($encryptedCertKey) {
return openssl_decrypt(base64_decode($encryptedCertKey), 'AES-256-CBC', $encryptionKey);
}
```
### 3. API 호출 제한
- **Rate Limiting**: 업체별 API 호출 횟수 제한
- **에러 처리**: 에러 발생 시 민감정보 노출 금지
- **타임아웃 설정**: API 호출 타임아웃 설정
```php
// Rate Limiting 예시
function checkRateLimit($company_id) {
$stmt = $pdo->prepare("
SELECT COUNT(*) as count
FROM barobill_api_logs
WHERE company_id = ?
AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)
");
$stmt->execute([$company_id]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if ($result['count'] > 1000) { // 시간당 1000회 제한
throw new Exception('API 호출 한도 초과');
}
}
```
---
## 구현 가이드
### 1. barobill_card_config.php 수정
멀티테넌시를 지원하도록 수정합니다.
```php
<?php
/**
* 바로빌 카드 API 설정 파일 (멀티테넌시 지원)
*/
require_once($_SERVER['DOCUMENT_ROOT'] . '/lib/mydb.php');
/**
* 업체별 바로빌 인증 정보 조회
*
* @param int $company_id 업체 ID
* @return array 인증 정보
*/
function getBarobillCredentials($company_id) {
global $pdo, $DB;
$stmt = $pdo->prepare("
SELECT
bc.cert_key,
bc.corp_num,
bc.user_id,
bc.test_mode,
bc.status
FROM {$DB}.barobill_credentials bc
WHERE bc.company_id = ?
AND bc.status = 'active'
");
$stmt->execute([$company_id]);
$credentials = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$credentials) {
return [
'success' => false,
'error' => '바로빌 인증 정보가 등록되지 않았습니다.'
];
}
// CERTKEY 복호화 (암호화된 경우)
if (function_exists('decryptCertKey')) {
$credentials['cert_key'] = decryptCertKey($credentials['cert_key']);
}
return [
'success' => true,
'data' => $credentials
];
}
/**
* 바로빌 카드 SOAP 웹서비스 호출 함수 (멀티테넌시 지원)
*
* @param int $company_id 업체 ID
* @param string $method SOAP 메서드명
* @param array $params SOAP 메서드 파라미터
* @return array 응답 데이터
*/
function callBarobillCardSOAPForCompany($company_id, $method, $params = []) {
// 인증 정보 조회
$credentials = getBarobillCredentials($company_id);
if (!$credentials['success']) {
return $credentials;
}
$certKey = $credentials['data']['cert_key'];
$corpNum = $credentials['data']['corp_num'];
$isTestMode = $credentials['data']['test_mode'] == 1;
// SOAP URL 설정
$soapUrl = $isTestMode
? 'https://testws.baroservice.com/CARD.asmx?WSDL'
: 'https://ws.baroservice.com/CARD.asmx?WSDL';
// SOAP 클라이언트 생성
try {
$soapClient = new SoapClient($soapUrl, [
'trace' => true,
'encoding' => 'UTF-8',
'exceptions' => true,
'connection_timeout' => 30
]);
} catch (Exception $e) {
return [
'success' => false,
'error' => 'SOAP 클라이언트 생성 실패: ' . $e->getMessage()
];
}
// CERTKEY와 CorpNum 자동 추가
if (!isset($params['CERTKEY'])) {
$params['CERTKEY'] = $certKey;
}
if (!isset($params['CorpNum'])) {
$params['CorpNum'] = $corpNum;
}
// API 호출 로그 기록
$startTime = microtime(true);
try {
$result = $soapClient->$method($params);
$executionTime = (microtime(true) - $startTime) * 1000; // 밀리초
// API 호출 로그 저장
logBarobillApiCall($company_id, $method, $params, 'success', null, $executionTime);
$resultProperty = $method . 'Result';
if (isset($result->$resultProperty)) {
$resultData = $result->$resultProperty;
// 에러 코드 체크
if (is_numeric($resultData) && $resultData < 0) {
logBarobillApiCall($company_id, $method, $params, 'failure', '에러 코드: ' . $resultData, $executionTime);
return [
'success' => false,
'error' => '바로빌 카드 API 오류 코드: ' . $resultData,
'error_code' => $resultData
];
}
return [
'success' => true,
'data' => $resultData
];
}
return [
'success' => true,
'data' => $result
];
} catch (SoapFault $e) {
$executionTime = (microtime(true) - $startTime) * 1000;
logBarobillApiCall($company_id, $method, $params, 'failure', $e->getMessage(), $executionTime);
return [
'success' => false,
'error' => 'SOAP 오류: ' . $e->getMessage(),
'error_code' => $e->getCode()
];
} catch (Exception $e) {
$executionTime = (microtime(true) - $startTime) * 1000;
logBarobillApiCall($company_id, $method, $params, 'failure', $e->getMessage(), $executionTime);
return [
'success' => false,
'error' => 'API 호출 오류: ' . $e->getMessage()
];
}
}
/**
* API 호출 로그 저장
*/
function logBarobillApiCall($company_id, $method, $params, $status, $error_message = null, $execution_time = null) {
global $pdo, $DB;
try {
$stmt = $pdo->prepare("
INSERT INTO {$DB}.barobill_api_logs
(company_id, api_method, request_params, response_status, error_message, execution_time, ip_address, user_agent)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$company_id,
$method,
json_encode($params, JSON_UNESCAPED_UNICODE),
$status,
$error_message,
$execution_time,
$_SERVER['REMOTE_ADDR'] ?? null,
$_SERVER['HTTP_USER_AGENT'] ?? null
]);
} catch (Exception $e) {
// 로그 저장 실패는 무시 (시스템 오류 방지)
error_log('API 로그 저장 실패: ' . $e->getMessage());
}
}
/**
* 업체별 카드 목록 조회 (멀티테넌시 지원)
*/
function getCardListForCompany($company_id, $availOnly = 0) {
$result = callBarobillCardSOAPForCompany($company_id, 'GetCardEx2', [
'AvailOnly' => $availOnly
]);
if (!$result['success']) {
return $result;
}
$cards = [];
$data = $result['data'];
if (!isset($data->CardEx)) {
return ['success' => true, 'data' => []];
}
if (!is_array($data->CardEx)) {
$cards = [$data->CardEx];
} else {
$cards = $data->CardEx;
}
// 에러 체크
if (count($cards) == 1 && isset($cards[0]->CardNum) && $cards[0]->CardNum < 0) {
return [
'success' => false,
'error' => '카드 목록 조회 실패',
'error_code' => $cards[0]->CardNum
];
}
// (선택) 캐시 테이블에 저장
// saveCardsToCache($company_id, $cards);
return ['success' => true, 'data' => $cards];
}
/**
* 업체별 기간별 카드 사용내역 조회 (멀티테넌시 지원)
*/
function getPeriodCardUsageForCompany($company_id, $cardNum = '', $startDate = '', $endDate = '', $countPerPage = 50, $currentPage = 1, $orderDirection = 2, $userId = '') {
// 인증 정보 조회
$credentials = getBarobillCredentials($company_id);
if (!$credentials['success']) {
return $credentials;
}
$barobillUserId = $credentials['data']['user_id'] ?? '';
if (!empty($userId)) {
$barobillUserId = $userId;
}
$result = callBarobillCardSOAPForCompany($company_id, 'GetPeriodCardApprovalLog', [
'ID' => $barobillUserId,
'CardNum' => $cardNum,
'StartDate' => $startDate,
'EndDate' => $endDate,
'CountPerPage' => $countPerPage,
'CurrentPage' => $currentPage,
'OrderDirection' => $orderDirection
]);
if (!$result['success']) {
return $result;
}
return parseCardUsageResult($result['data']);
}
// 기존 parseCardUsageResult 함수는 그대로 사용
// ... (기존 코드 유지)
?>
```
### 2. cards.php 수정
멀티테넌시를 지원하도록 수정합니다.
```php
<?php
/**
* 등록된 카드 목록 조회 API (멀티테넌시 지원)
*/
header('Content-Type: application/json; charset=utf-8');
require_once('barobill_card_config.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/session.php');
try {
// 업체 ID 확인 (세션 또는 파라미터에서)
$company_id = $_SESSION['company_id'] ?? $_GET['company_id'] ?? null;
if (!$company_id) {
echo json_encode([
'success' => false,
'error' => '업체 ID가 필요합니다.'
], JSON_UNESCAPED_UNICODE);
exit;
}
$availOnly = isset($_GET['availOnly']) ? intval($_GET['availOnly']) : 0;
$result = getCardListForCompany($company_id, $availOnly);
if ($result['success']) {
$cards = [];
foreach ($result['data'] as $card) {
// ... (기존 변환 로직)
}
echo json_encode([
'success' => true,
'cards' => $cards,
'count' => count($cards)
], JSON_UNESCAPED_UNICODE);
} else {
echo json_encode([
'success' => false,
'error' => $result['error'],
'error_code' => $result['error_code'] ?? null
], JSON_UNESCAPED_UNICODE);
}
} catch (Exception $e) {
echo json_encode([
'success' => false,
'error' => '서버 오류: ' . $e->getMessage()
], JSON_UNESCAPED_UNICODE);
}
?>
```
### 3. usage.php 수정
멀티테넌시를 지원하도록 수정합니다.
```php
<?php
/**
* 카드 사용내역 조회 API (멀티테넌시 지원)
*/
header('Content-Type: application/json; charset=utf-8');
require_once('barobill_card_config.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/session.php');
try {
// 업체 ID 확인
$company_id = $_SESSION['company_id'] ?? $_GET['company_id'] ?? null;
if (!$company_id) {
echo json_encode([
'success' => false,
'error' => '업체 ID가 필요합니다.'
], JSON_UNESCAPED_UNICODE);
exit;
}
$type = $_GET['type'] ?? 'period';
$cardNum = $_GET['cardNum'] ?? '';
$page = max(1, intval($_GET['page'] ?? 1));
$limit = min(100, max(10, intval($_GET['limit'] ?? 50)));
$orderDirection = intval($_GET['order'] ?? 2);
// ... (기존 로직을 getPeriodCardUsageForCompany로 변경)
$result = getPeriodCardUsageForCompany(
$company_id,
$cardNum,
$startDate,
$endDate,
$limit,
$page,
$orderDirection
);
// ... (기존 응답 로직)
} catch (Exception $e) {
echo json_encode([
'success' => false,
'error' => '서버 오류: ' . $e->getMessage()
], JSON_UNESCAPED_UNICODE);
}
?>
```
---
## 마이그레이션 가이드
### 기존 단일 테넌트에서 멀티테넌트로 전환
1. **데이터베이스 마이그레이션**
```sql
-- 1. companies 테이블 생성
-- 2. barobill_credentials 테이블 생성
-- 3. 기존 파일 기반 설정을 DB로 마이그레이션
INSERT INTO companies (company_name, business_number, business_number_clean, status)
VALUES ('기본 업체', '123-45-67890', '1234567890', 'active');
INSERT INTO barobill_credentials (company_id, cert_key, corp_num, user_id, test_mode, status)
VALUES (
1,
(SELECT cert_key FROM file), -- 파일에서 읽은 CERTKEY
(SELECT corp_num FROM file), -- 파일에서 읽은 사업자번호
NULL,
0,
'active'
);
```
2. **코드 수정**
- `barobill_card_config.php`: 파일 기반 → DB 기반으로 변경
- `cards.php`, `usage.php`: `company_id` 파라미터 추가
- 세션에 `company_id` 저장
3. **테스트**
- 각 업체별로 독립적인 카드 조회 확인
- 데이터 격리 확인
- 권한 검증 확인
---
## 모니터링 및 유지보수
### 1. 주요 모니터링 지표
- API 호출 성공률
- API 호출 응답 시간
- 에러 발생 빈도
- 캐시 적중률 (캐시 사용 시)
### 2. 정기 점검 사항
- 인증 정보 만료 확인
- 캐시 데이터 정리
- API 로그 분석
- 성능 최적화
---
## 참고 자료
- [바로빌 개발자 문서](https://dev.barobill.co.kr/)
- [바로빌 카드 API 레퍼런스](https://dev.barobill.co.kr/docs/references/카드조회-API)
- PHP SOAP 클라이언트 문서
---
## 변경 이력
| 버전 | 날짜 | 변경 내용 | 작성자 |
|------|------|----------|--------|
| 1.0 | 2025-12-08 | 초기 문서 작성 | - |
---
**문서 작성일**: 2025년 12월
**최종 수정일**: 2025년 12월
**문서 버전**: 1.0