간편하고 안전한 결제 시스템 통합
마카롱 페이먼츠 API를 사용하여 전자상거래 사이트에 결제 기능을 쉽게 통합하세요.
실시간 매칭 시스템과 웹훅 알림을 제공합니다.
📚 목차
📋 개요
마카롱 페이먼츠는 전자상거래 사이트를 위한 안전하고 간편한 결제 시스템입니다.
주요 특징
- • 실시간 입금/출금 매칭
- • 부분 매칭 지원 (여러 건 매칭 가능)
- • 10분 검증 타임아웃
- • 2분 지연 웹훅 알림 시스템
- • RESTful API 설계
- • 토큰 기반 인증
지원 기능
- • 입금 요청 생성 및 관리
- • 출금 요청 생성 및 관리
- • 실시간 상태 확인 (매칭/잔여 금액)
- • 웹훅 기반 알림
- • OCR 스크린샷 검증
- • 사이트별 매칭 격리
매칭 시스템
- • 부분 매칭: 2000원 출금이 1000원 입금 2건과 매칭 가능
- • 정확한 금액 우선: 같은 금액끼리 먼저 매칭
- • 검증 타임아웃: 10분 내 미검증 시 자동 취소 및 재매칭
- • 사이트 격리: 동일 사이트 내에서만 매칭하여 보안 강화
🔐 인증
토큰 발급
POST /v1/site-auth/token요청 예제
{
"siteId": "your-site-id",
"siteSecret": "your-site-secret"
}🔌 API 엔드포인트
입금 요청
POST /v1/deposit-requests필수 파라미터
- •
siteUserId: 사이트 사용자 ID - •
amount: 입금 금액 - •
depositRequestId: 고유 요청 ID
출금 요청
POST /v1/withdrawal-requests필수 파라미터
- •
siteUserId: 사이트 사용자 ID - •
amount: 출금 금액 - •
kakaoPayUrl: 카카오페이 송금 URL - •
kakaoPayName: 카카오페이 등록 이름 (OCR 검증용) - •
withdrawalRequestId: 고유 요청 ID
📄 응답 형식
입금/출금 요청 응답
{
"success": true,
"data": {
"depositRequestId": "dep_123456789",
"siteId": "site_abc",
"siteUserId": "user_123",
"depositAmount": 10000,
"matchedAmount": 5000, // 매칭된 금액
"remainingAmount": 5000, // 잔여 금액
"status": "MATCHED",
"webhookUrl": "https://example.com/webhook",
"MatchLog": [
{
"matchLogId": "match_xxx",
"amount": 5000,
"status": "VERIFICATION_PENDING"
}
],
"createdAt": "2025-01-22T10:00:00Z",
"updatedAt": "2025-01-22T10:05:00Z"
}
}💡 부분 매칭: 하나의 입출금 요청이 여러 건과 매칭될 수 있습니다.matchedAmount는 현재까지 매칭된 총 금액,remainingAmount는 아직 매칭되지 않은 잔여 금액입니다.
오류 응답
{
"success": false,
"error": {
"code": "INVALID_AMOUNT",
"message": "입금 금액이 유효하지 않습니다.",
"details": "최소 1,000원 이상이어야 합니다."
}
}⚠️ 오류 처리
주요 오류 코드
INVALID_TOKEN토큰이 유효하지 않음INVALID_AMOUNT금액이 유효하지 않음DUPLICATE_REQUEST중복된 요청 IDRATE_LIMITED요청 횟수 제한 초과권장 처리 방법
- • HTTP 상태 코드와 함께 오류 코드를 확인하세요
- • 토큰 만료 시 자동으로 재발급하세요
- • 일시적 오류는 지수 백오프로 재시도하세요
- • 사용자에게 명확한 오류 메시지를 제공하세요
💻 코드 예제
// 토큰 발급
async function getSiteToken(siteId, siteSecret) {
const response = await fetch('/v1/site-auth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ siteId, siteSecret })
});
return (await response.json()).data.accessToken;
}
// 입금 요청 생성
async function createDepositRequest(token, requestData) {
const response = await fetch('/v1/deposit-requests', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(requestData)
});
return await response.json();
}🎯 매칭 규칙
자동 매칭 알고리즘
입금/출금 요청 생성 시 2분 후 첫 매칭을 시도하며, 실패 시 30초마다 재시도합니다 (최대 5분).
1순위: 동일 금액 매칭
정확히 같은 금액끼리 우선 매칭합니다.
예: 10,000원 입금 ↔ 10,000원 출금
2순위: 큰 매칭 가능 금액
부분 매칭 시 더 많이 매칭 가능한 것을 우선합니다.
예: 15,000원 입금 시 → 12,000원 출금 > 8,000원 출금
3순위: 잔여 금액이 작은 순서
완전 소진 가능성이 높은 것을 우선 매칭합니다.
예: 잔여 3,000원 > 잔여 5,000원
4순위: 생성 시간 순서
먼저 생성된 요청을 우선 처리합니다 (FIFO).
부분 매칭 예시
사이트 격리 (Security)
🔒 동일 사이트 내에서만 매칭됩니다.
사이트 A의 입금과 사이트 B의 출금은 절대 매칭되지 않아 보안이 강화됩니다.
⏱️ 타임아웃 로직
1. 자동 수동처리 (5분)
입금/출금 요청이 5분간 매칭되지 않음
상태가 MANUAL_PENDING으로 자동 변경되어 관리자가 수동으로 처리합니다.
2. 검증 타임아웃 (10분)
매칭 완료 후 10분 내에 스크린샷 검증을 하지 않음
- • 매칭이 자동으로 취소됩니다
- • 입금자:
CANCELLED상태로 변경 - • 출금자:
PENDING또는MATCHED상태로 복원 (재매칭 가능) - • 취소 웹훅 (
match_cancelled) 즉시 발송
전체 타임라인
특수 케이스: 200만원 초과
💰 200만원 초과 금액은 자동 매칭 없이 즉시 MANUAL_PENDING 상태로 생성되어 관리자가 수동으로 처리합니다.
📡 웹훅 가이드
매칭 완료 후 2분 지연하여 웹훅이 전송됩니다. 웹훅 수신 주소를 구현한 후 관리자에게 등록을 요청하세요.
웹훅 이벤트 타입
1️⃣ MATCH_NOTIFICATION (매칭 완료)
입금/출금이 매칭된 후 2분 후에 발송됩니다. 입금자와 출금자 모두에게 발송됩니다.
{
"type": "MATCH_NOTIFICATION",
"matchId": "match_xxx",
"amount": 10000,
"siteName": "사이트명",
"depositRequest": {
"depositRequestId": "dep_xxx",
"amount": 10000,
"siteUserId": "user123"
},
"withdrawalRequest": {
"withdrawalRequestId": "with_xxx",
"amount": 10000,
"kakaoPayUrl": "https://...",
"siteUserId": "user456"
},
"matchedAt": "2025-01-22T10:00:00Z"
}2️⃣ VERIFICATION_COMPLETED (검증 완료)
OCR 스크린샷 검증 성공 시 즉시 발송됩니다. 입금자와 출금자 모두에게 발송됩니다.
{
"type": "VERIFICATION_COMPLETED",
"matchId": "match_xxx",
"amount": 10000,
"ocrExtractedAmount": 10000,
"screenshotUrl": "https://...",
"verifiedAt": "2025-01-22T10:05:00Z",
"siteName": "사이트명",
"depositRequest": { ... },
"withdrawalRequest": { ... }
}3️⃣ MATCH_CANCELLED_BY_TIMEOUT (타임아웃 취소)
검증 타임아웃(10분) 시 즉시 발송됩니다. 입금자에게만 발송됩니다. (출금자는 재매칭 대기)
{
"type": "MATCH_CANCELLED_BY_TIMEOUT",
"matchId": "match_xxx",
"amount": 10000,
"reason": "검증 타임아웃 (10분 경과) - 입금 미완료",
"cancelledBy": "timeout",
"cancelledAt": "2025-01-22T10:10:00Z",
"depositRequest": { ... },
"withdrawalRequest": { ... }
}4️⃣ DEPOSIT_REQUEST_CANCELLED (입금신청 취소)
관리자가 입금신청을 취소할 때 즉시 발송됩니다. 입금자에게만 발송됩니다. (출금자는 재매칭 대기)
{
"type": "DEPOSIT_REQUEST_CANCELLED",
"matchId": "match_xxx",
"amount": 10000,
"reason": "입금신청 취소: 사용자 요청",
"cancelledBy": "deposit_cancelled",
"cancelledAt": "2025-01-22T10:15:00Z",
"depositRequest": { ... },
"withdrawalRequest": { ... }
}5️⃣ WITHDRAWAL_REQUEST_CANCELLED (출금신청 취소)
관리자가 출금신청을 취소할 때 즉시 발송됩니다. 출금자에게만 발송됩니다. (입금자는 재매칭 대기)
{
"type": "WITHDRAWAL_REQUEST_CANCELLED",
"matchId": "match_xxx",
"amount": 10000,
"reason": "출금신청 취소: 관리자 판단",
"cancelledBy": "withdrawal_cancelled",
"cancelledAt": "2025-01-22T10:20:00Z",
"depositRequest": { ... },
"withdrawalRequest": { ... }
}6️⃣ MATCH_CANCELLED_BY_ADMIN (관리자 직접 취소)
관리자가 매칭을 직접 취소할 때 즉시 발송됩니다. 입금자와 출금자 모두에게 발송됩니다.
{
"type": "MATCH_CANCELLED_BY_ADMIN",
"matchId": "match_xxx",
"amount": 10000,
"reason": "매칭 취소: 관리자 판단",
"cancelledBy": "admin",
"cancelledAt": "2025-01-22T10:25:00Z",
"depositRequest": { ... },
"withdrawalRequest": { ... }
}웹훅 수신 예제
app.post('/webhook/macaron-payments', (req, res) => {
const payload = req.body;
console.log('웹훅 수신:', payload);
switch (payload.type) {
case 'MATCH_NOTIFICATION':
// 매칭 완료 (2분 지연, 양쪽 발송)
console.log(`매칭 완료: ${payload.matchId}, 금액: ${payload.amount}`);
// 사용자에게 매칭 완료 알림 발송
break;
case 'VERIFICATION_COMPLETED':
// 검증 완료 (즉시, 양쪽 발송)
console.log(`검증 완료: ${payload.matchId}`);
console.log(`OCR 금액: ${payload.ocrExtractedAmount}`);
// 최종 완료 처리 및 사용자 알림
break;
case 'MATCH_CANCELLED_BY_TIMEOUT':
// 타임아웃 취소 (즉시, 입금자만)
console.log(`타임아웃 취소: ${payload.matchId}`);
console.log(`사유: ${payload.reason}`);
// 입금자에게 취소 알림 발송
break;
case 'DEPOSIT_REQUEST_CANCELLED':
// 입금신청 취소 (즉시, 입금자만)
console.log(`입금신청 취소: ${payload.matchId}`);
console.log(`사유: ${payload.reason}`);
// 입금자에게 취소 알림 발송
break;
case 'WITHDRAWAL_REQUEST_CANCELLED':
// 출금신청 취소 (즉시, 출금자만)
console.log(`출금신청 취소: ${payload.matchId}`);
console.log(`사유: ${payload.reason}`);
// 출금자에게 취소 알림 발송
break;
case 'MATCH_CANCELLED_BY_ADMIN':
// 관리자 직접 취소 (즉시, 양쪽 발송)
console.log(`관리자 취소: ${payload.matchId}`);
console.log(`사유: ${payload.reason}`);
// 양쪽 사용자에게 취소 알림 발송
break;
default:
console.log('알 수 없는 이벤트:', payload.type);
}
res.status(200).json({ success: true });
});📸 OCR 스크린샷 검증
매칭 완료 후 사용자가 업로드한 송금 완료 스크린샷을 OCR로 자동 검증합니다. OCR 기술을 사용하여 금액과 완료 메시지를 확인합니다.
검증 플로우
⏱️ 검증 타임아웃 (10분)
- • 매칭 후 10분 내에 검증하지 않으면 자동으로 매칭이 취소됩니다
- • 입금자는 CANCELLED 상태로 변경되며, 취소 웹훅이 발송됩니다
- • 출금자는 PENDING 또는 MATCHED 상태로 복원되어 재매칭 가능합니다
API 엔드포인트
POST /v1/match-logs/verify-screenshot// FormData를 사용한 multipart/form-data 전송
const formData = new FormData();
formData.append('matchLogId', 'cm5abc...');
formData.append('screenshot', imageFile); // File 객체
const response = await fetch('/v1/match-logs/verify-screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${siteToken}`
},
body: formData
});검증 조건 (모두 충족 필요)
✅ 금액 검증
- • OCR로 추출한 금액과 실제 매칭 금액 비교
- • ±1% 오차 허용
- • “송금액”, “보냈어요” 등 키워드 근처의 숫자 우선 인식
✅ 완료 메시지 검증
- • “송금을 완료했어요”
- • “보냈어요”, “보냈습니다”
- • “송금 완료”, “이체 완료”
- • 기타 완료 관련 메시지
사용자 스크린샷 가이드
✅ 올바른 스크린샷
- • “송금을 완료했어요” 텍스트 포함
- • 금액이 명확히 보임
- • 선명한 이미지
- • 송금 완료 후 화면
❌ 잘못된 스크린샷
- • 송금 전 화면 (완료 메시지 없음)
- • 흐릿하거나 각도가 있는 이미지
- • 금액이 잘 안 보임
- • 광고/배너만 가득한 화면
응답 예제
검증 성공
{
"message": "OCR 검증이 완료되었습니다",
"data": {
"matchLogId": "cm5abc...",
"status": "VERIFICATION_COMPLETED",
"ocrExtractedAmount": 50000,
"verifiedAt": "2025-10-11T09:45:00.000Z",
"hasCompletionMessage": true
}
}검증 실패 (금액 불일치)
{
"message": "금액 불일치 (추출: 45000원, 실제: 50000원)"
}검증 실패 (완료 메시지 없음)
{
"message": "\"송금을 완료했어요\" 등의 완료 메시지가 스크린샷에 포함되어야 합니다"
}구현 팁
- • 사용자 가이드 제공: 올바른 스크린샷 촬영 방법을 명확히 안내하세요
- • 재업로드 옵션: 검증 실패 시 재업로드할 수 있는 UI를 제공하세요
- • 이미지 최적화: 업로드 전에 이미지 크기를 적절히 조정하세요 (권장: 1MB 이하)
- • 로딩 상태: OCR 처리에 시간이 걸릴 수 있으므로 로딩 인디케이터를 표시하세요
- • 웹훅 처리: VERIFICATION_COMPLETED 웹훅을 받아 최종 완료 처리를 하세요
⭐ 모범 사례
보안
- • 토큰 관리: 액세스 토큰을 안전하게 저장하고 정기적으로 갱신하세요
- • HTTPS 필수: 프로덕션 환경에서는 반드시 HTTPS를 사용하세요
- • 웹훅 검증: 웹훅 요청의 출처를 검증하세요
- • 민감 정보 보호: 사이트 시크릿을 소스코드에 하드코딩하지 마세요
성능
- • 중복 요청 방지: 동일한 요청 ID에 대해 중복 호출을 방지하세요
- • 적절한 타임아웃: API 호출 시 적절한 타임아웃을 설정하세요
- • 연결 풀링: HTTP 연결을 재사용하여 성능을 향상시키세요
- • 비동기 처리: 웹훅 처리를 비동기로 구현하세요
안정성
- • 재시도 로직: 일시적 오류에 대해 지수 백오프 재시도를 구현하세요
- • 로깅: 모든 API 호출과 응답을 상세히 로깅하세요
- • 모니터링: API 호출 성공률과 응답 시간을 모니터링하세요
- • 장애 대응: 서비스 중단 시 사용자에게 명확한 안내를 제공하세요
사용자 경험
- • 실시간 상태: 요청 상태를 실시간으로 사용자에게 보여주세요
- • 명확한 메시지: 오류 발생 시 사용자가 이해할 수 있는 메시지를 제공하세요
- • 진행 상태: 매칭 진행 상황을 시각적으로 표시하세요
- • 2분 지연 안내: 웹훅이 2분 지연된다는 점을 사용자에게 미리 안내하세요