차지백·이의제기 관리: 자동화로 분쟁 손실 최소화하기

이커머스

차지백결제 분쟁사기 방지결제 보안리스크 관리

이 글은 누구를 위한 것인가

  • 차지백 비율이 높아 카드사로부터 경고를 받는 이커머스 팀
  • 차지백 이의제기 프로세스가 없어 무조건 패소하는 팀
  • 결제 사기를 줄이고 싶은 리스크 관리 담당자

들어가며

차지백 비율 1%를 넘으면 카드사로부터 High-Risk 판정을 받고 수수료가 올라간다. 0.9%를 넘으면 Visa Dispute Monitoring Program에 들어간다. 차지백 관리는 이커머스 생존의 문제다.

이 글은 bluefoxdev.kr의 결제 리스크 관리 가이드 를 참고하여 작성했습니다.


1. 차지백 유형과 대응

[차지백 발생 이유별 대응]

사기(Fraud) 차지백 — 50% 이상:
  고객이 실제로 구매하지 않았다고 주장
  → 방어: IP 로그, 배송 추적, 기기 핑거프린트
  → 3DS2 인증 사용 시 카드사 책임으로 이전

서비스 미제공:
  배송 받지 못했다고 주장
  → 방어: 배송 추적 + 서명 요구 (고가 상품)
  → 사전: 정확한 배송 추적 제공

상품 불일치:
  받은 상품이 다르다고 주장
  → 방어: 상품 사진, 품질검사 기록
  → 사전: 명확한 상품 설명, 고품질 사진

중복 청구:
  같은 건이 두 번 결제됐다고 주장
  → 방어: 결제 로그
  → 사전: 멱등성 키로 중복 방지

[차지백 비율 목표]
  일반: 0.5% 미만
  주의: 0.5~0.9%
  위험: 0.9% 이상 (카드사 제재)

2. 자동 증거 수집 시스템

from datetime import datetime

async def auto_collect_chargeback_evidence(order_id: str, chargeback_id: str) -> dict:
    """차지백 이의제기 증거 자동 수집"""
    
    order = await get_order(order_id)
    evidence = {
        "chargeback_id": chargeback_id,
        "order_id": order_id,
        "collected_at": datetime.utcnow().isoformat(),
        "documents": []
    }
    
    # 1. 주문 상세 정보
    evidence["documents"].append({
        "type": "order_receipt",
        "data": {
            "order_date": order["created_at"],
            "total_amount": order["total_amount"],
            "items": order["items"],
            "customer_email": order["customer_email"],
            "ip_address": order["ip_address"],
            "user_agent": order["user_agent"],
        }
    })
    
    # 2. 배송 추적 정보
    tracking = await get_tracking_info(order["tracking_number"])
    if tracking:
        evidence["documents"].append({
            "type": "shipping_tracking",
            "data": {
                "carrier": tracking["carrier"],
                "tracking_number": tracking["number"],
                "shipped_at": tracking["shipped_at"],
                "delivered_at": tracking["delivered_at"],
                "delivery_address": tracking["delivery_address"],
                "signature": tracking.get("signature_name"),
                "delivery_photo_url": tracking.get("delivery_photo"),
            }
        })
    
    # 3. 고객 활동 로그 (구매 전후)
    activity_logs = await get_user_activity(
        user_id=order["user_id"],
        from_dt=order["created_at"],
        days=7
    )
    evidence["documents"].append({
        "type": "customer_activity",
        "data": activity_logs
    })
    
    # 4. 3DS 인증 결과
    auth_result = await get_3ds_result(order["payment_id"])
    if auth_result:
        evidence["documents"].append({
            "type": "3ds_authentication",
            "data": auth_result
        })
    
    # 5. 이전 성공적 구매 이력 (동일 고객)
    purchase_history = await get_purchase_history(order["user_id"])
    evidence["documents"].append({
        "type": "purchase_history",
        "data": {"previous_orders": len(purchase_history), "successful": True}
    })
    
    # 증거 저장
    await save_chargeback_evidence(chargeback_id, evidence)
    return evidence

async def submit_chargeback_rebuttal(chargeback_id: str, evidence: dict):
    """카드사에 이의제기 서류 제출"""
    response = await payment_gateway.submit_dispute(
        chargeback_id=chargeback_id,
        evidence_type="compelling_evidence",
        evidence_documents=evidence["documents"],
        rebuttal_text=generate_rebuttal_letter(evidence),
    )
    return response

3. 사기 패턴 자동 감지

FRAUD_SIGNALS = {
    "high_value_first_order": 100000,  # 첫 주문 10만원 이상
    "multiple_cards_same_address": 3,   # 동일 주소에서 다른 카드 3개 이상
    "velocity_check": 5,                # 1시간 내 5회 이상 시도
    "suspicious_email": ["mailinator", "guerrillamail", "throwam"],
    "high_risk_countries": ["NG", "RO", "UA"],  # 리스크 국가
}

def calculate_fraud_score(order: dict) -> int:
    """주문 사기 위험도 점수 (0-100)"""
    score = 0
    
    # 신규 고객 + 고액 주문
    if order["is_first_order"] and order["amount"] >= FRAUD_SIGNALS["high_value_first_order"]:
        score += 30
    
    # 이메일 도메인 체크
    email_domain = order["email"].split("@")[1]
    if any(sus in email_domain for sus in FRAUD_SIGNALS["suspicious_email"]):
        score += 40
    
    # 배송지/청구지 불일치
    if order.get("billing_address") != order.get("shipping_address"):
        score += 15
    
    # 동일 IP에서 다른 이름으로 여러 주문
    same_ip_orders = count_orders_by_ip(order["ip_address"])
    if same_ip_orders > 3:
        score += 25
    
    return min(score, 100)

def should_require_3ds(fraud_score: int) -> bool:
    return fraud_score >= 50

def should_hold_for_review(fraud_score: int) -> bool:
    return fraud_score >= 70

마무리

차지백은 사후 대응보다 사전 예방이 훨씬 효과적이다. 3DS2 인증으로 사기 책임을 카드사로 이전하고, 배송 추적과 서명을 자동으로 수집하면 이의제기 승률이 크게 높아진다. 차지백 증거 수집을 수동으로 하면 기한(보통 20일)을 놓치기 쉽다 — 반드시 자동화하라.